1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Set a layout of pages
4 //>>group: Tizen:Widgets
6 define( [ '../jquery.mobile.tizen.core' ], function ( ) {
7 //>>excludeEnd("jqmBuildExclude");
9 /* ***************************************************************************
10 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
19 * The above copyright notice and this permission notice shall be included in
20 * all copies or substantial portions of the Software.
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 * ***************************************************************************
31 * Author: Jinhyuk Jun <jinhyuk.jun@samsung.com>
34 (function ( $, undefined ) {
36 $.widget( "mobile.pagelayout", $.mobile.widget, {
38 visibleOnPageShow: true,
39 disablePageZoom: true,
40 transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
43 tapToggleBlacklist: "a, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
44 hideDuringFocus: "input, textarea, select",
45 updatePagePadding: true,
46 // Browser detection! Weeee, here we go...
47 // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
48 // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
49 // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
50 // The following function serves to rule out some popular browsers with known fixed-positioning issues
51 // This is a plugin option like any other, so feel free to improve or overwrite it
52 supportBlacklist: function () {
54 ua = navigator.userAgent,
55 platform = navigator.platform,
56 // Rendering engine is Webkit, and capture major version
57 wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
58 wkversion = !!wkmatch && wkmatch[ 1 ],
59 ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
60 ffversion = !!ffmatch && ffmatch[ 1 ],
61 operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ),
62 omversion = !!operammobilematch && operammobilematch[ 1 ];
65 // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
66 ( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 ) ||
68 ( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" ) ||
69 ( operammobilematch && omversion < 7458 ) ||
70 //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
71 ( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 ) ||
72 // Firefox Mobile before 6.0 -
73 ( ffversion && ffversion < 6 ) ||
75 ( window.palmGetResource !== undefined && wkversion && wkversion < 534 ) ||
77 ( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 )
84 initSelector: ":jqmData(role='content')"
87 _create: function () {
93 // Feature detecting support for
94 if ( o.supportBlacklist() ) {
99 self._addFixedClass();
100 self._addTransitionClass();
101 self._bindPageEvents();
104 self._bindContentControlEvents();
106 // Store back-button, to show again
107 self._backBtnQueue = [];
110 /* add minimum fixed css style to bar(header/footer) and content
111 * it need to update when core source modified(jquery.mobile.page.section.js)
112 * modified from core source cuz initSelector different */
113 _addFixedClass: function () {
117 $elHeader = $el.siblings( ":jqmData(role='header')" ),
118 $elFooter = $el.siblings( ":jqmData(role='footer')" ),
119 $elPage = $el.closest(".ui-page");
121 $elHeader.addClass( "ui-header-fixed" );
122 $elFooter.addClass( "ui-footer-fixed" );
124 // "fullscreen" overlay positioning
125 if ( o.fullscreen ) {
126 $elHeader.addClass( "ui-header-fullscreen" );
127 $elFooter.addClass( "ui-footer-fullscreen" );
129 .addClass( "ui-page-header-fullscreen" )
130 .addClass( "ui-page-footer-fullscreen" );
132 // If not fullscreen, add class to page to set top or bottom padding
133 $elPage.addClass( "ui-page-header-fixed" )
134 .addClass( "ui-page-footer-fixed" );
138 /* original core source(jquery.mobile.fixedToolbar.js)
140 _addTransitionClass: function () {
141 var tclass = this.options.transition;
143 if ( tclass && tclass !== "none" ) {
144 // use appropriate slide for header or footer
145 if ( tclass === "slide" ) {
146 tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
149 this.element.addClass( tclass );
154 /* Set default page positon
155 * 1. add title style to header
156 * 2. Set default header/footer position */
157 setHeaderFooter: function ( thisPage ) {
158 var $elPage = $( thisPage ),
159 $elHeader = $elPage.find( ":jqmData(role='header')" ).length ? $elPage.find( ":jqmData(role='header')") : $elPage.siblings( ":jqmData(role='header')"),
160 $elContent = $elPage.find( ".ui-content" ),
161 $elFooter = $elPage.find( ":jqmData(role='footer')" ),
162 $elFooterGroup = $elFooter.find( ":jqmData(role='fieldcontain')" ),
163 $elFooterControlGroup = $elFooter.find( ".ui-controlgroup" );
165 // divide content mode scrollview and non-scrollview
166 if ( !$elPage.is( ".ui-dialog" ) ) {
167 if ( $elHeader.jqmData("position") == "fixed" || ( $.support.scrollview && $.tizen.frameworkData.theme.match(/tizen/) ) ) {
169 .css( "position", "fixed" )
170 .css( "top", "0px" );
171 } else if ( !$.support.scrollview && $elHeader.jqmData("position") != "fixed" ) {
172 $elHeader.css( "position", "relative" );
176 /* set Title style */
177 if ( $elHeader.find("span.ui-title-text-sub").length ) {
178 $elHeader.addClass( "ui-title-multiline");
181 if ( $elFooterGroup.find( "div" ).is( ".ui-controlgroup-label" ) ) {
182 $elFooterGroup.find( "div.ui-controlgroup-label" ).remove();
185 if ( $elFooterControlGroup.length ) {
186 var anchorPer = 100 / $elFooterControlGroup.find( "a" ).length;
187 $elFooterControlGroup.find( "a" ).each( function ( i ) {
188 $elFooterControlGroup.find( "a" ).eq( i ).width( anchorPer + "%" );
193 _bindPageEvents: function () {
199 //page event bindings
200 // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
201 // This method is meant to disable zoom while a fixed-positioned toolbar page is visible
202 $el.closest( ".ui-page" )
203 .bind( "pagebeforeshow", function ( event ) {
205 if ( o.disablePageZoom ) {
206 $.mobile.zoom.disable( true );
208 if ( !o.visibleOnPageShow ) {
211 self.setHeaderFooter( thisPage );
212 self._setContentMinHeight( thisPage );
214 .bind( "webkitAnimationStart animationstart updatelayout", function ( e, data ) {
216 if ( o.updatePagePadding ) {
217 self.updatePagePadding(thisPage);
218 self.updatePageLayout( thisPage, data);
222 .bind( "pageshow", function ( event ) {
224 self._setContentMinHeight( thisPage );
225 self.updatePagePadding( thisPage );
226 self._updateHeaderArea( thisPage );
227 if ( o.updatePagePadding ) {
228 $( window ).bind( "throttledresize." + self.widgetName, function () {
229 self.updatePagePadding(thisPage);
231 self.updatePageLayout( thisPage, false);
232 self._updateHeaderArea( thisPage );
233 self._setContentMinHeight( thisPage );
238 .bind( "pagebeforehide", function ( e, ui ) {
239 if ( o.disablePageZoom ) {
240 $.mobile.zoom.enable( true );
242 if ( o.updatePagePadding ) {
243 $( window ).unbind( "throttledresize." + self.widgetName );
247 window.addEventListener( "softkeyboardchange", function ( e ) {
248 var $elDownBtn = $( "<div class='ui-btn-footer-down'></div>" ),
249 $elPage = $( ".ui-page-active" ),
251 backBtnPosition = "footer";
253 if ( $elPage.data( "addBackBtn" ) ) {
254 $elPage.data( "addBackBtn" ) == "header" ? backBtnPosition = "header" : backBtnPosition = "footer";
256 if ( e.state == "on" ) {
257 if ( !$elPage.find( ".ui-" + backBtnPosition + " .ui-btn-footer-down" ).length ) {
258 $elDownBtn.buttonMarkup( { icon: "down" } ).appendTo( $elPage.find( ".ui-" + backBtnPosition ) );
261 // N_SE-32900: If an app moves a page when the pop is shown, the .ui-page-active page
263 // In this case, the '.ui-page-active .ui-btn-back' selector indicates a
264 // new page's one, and the old page's .ui-btn-back button is still hidden.
265 // So, the current back button is remembered to be shown at the
266 // softkeyboardchange.off event.
267 backBtn = $( ".ui-page-active .ui-btn-back" );
269 self._backBtnQueue.push( backBtn ); // Store hidden backBtn
270 } else if ( e.state == "off" ) {
271 self._backBtnQueue.forEach( function ( b ) {
272 b.show(); // Show each backBtn,
274 self._backBtnQueue.length = 0; // and clear queue.
276 $( ".ui-btn-footer-down" ).remove();
283 _bindContentControlEvents: function () {
288 $el.closest( ".ui-page" )
289 .bind( "pagebeforeshow", function ( event ) {
294 _setContentMinHeight : function ( thisPage ) {
295 var $elPage = $( thisPage ),
296 $elHeader = $elPage.find( ":jqmData(role='header')" ),
297 $elFooter = $elPage.find( ":jqmData(role='footer')" ),
298 $elContent = $elPage.find( ":jqmData(role='content')" ),
301 layoutInnerHeight = window.innerHeight;
303 if ( !$.support.scrollview || ($.support.scrollview && $elContent.jqmData("scroll") === "none") ) {
304 dpr = window.outerWidth / window.innerWidth;
305 layoutInnerHeight = Math.floor( window.outerHeight / dpr );
307 layoutInnerHeight = window.innerHeight;
310 resultMinHeight = layoutInnerHeight - $elHeader.height() - $elFooter.height();
312 $elContent.css( "min-height", resultMinHeight - parseFloat( $elContent.css("padding-top") ) - parseFloat( $elContent.css("padding-bottom") ) + "px" );
313 if ( $.support.scrollview && $elContent.jqmData("scroll") !== "none" ) {
314 $elContent.children( ".ui-scrollview-view" ).css( "min-height", $elContent.css( "min-height" ) );
318 _updateHeaderArea : function ( thisPage ) {
319 var $elPage = $( thisPage ),
320 $elHeader = $elPage.find( ":jqmData(role='header')" ).length ? $elPage.find( ":jqmData(role='header')") : $elPage.siblings( ":jqmData(role='header')"),
321 headerBtnNum = $elHeader.children("a").length,
322 headerSrcNum = $elHeader.children("img").length;
324 if ( !$elPage.is( ".ui-dialog" ) ) {
325 $elHeader.find( "h1" ).css( "width", window.innerWidth - parseInt( $elHeader.find( "h1" ).css( "margin-left" ), 10 ) * 2 - $elHeader.children( "a" ).width() * headerBtnNum - $elHeader.children( "a" ).width() / 4 - $elHeader.children( "img" ).width() * headerSrcNum * 4 );
327 /* add half width for default space between text and button, and img tag area is too narrow, so multiply three for img width*/
332 // This will set the content element's top or bottom padding equal to the toolbar's height
333 updatePagePadding: function ( tbPage ) {
334 var $el = this.element,
335 header = $el.siblings( ".ui-header" ).length,
336 footer = $el.siblings( ".ui-footer" ).length;
338 // This behavior only applies to "fixed", not "fullscreen"
339 if ( this.options.fullscreen ) {
343 tbPage = tbPage || $el.closest( ".ui-page" );
345 if ( $el.siblings( ".ui-header" ).jqmData("position") == "fixed" || ($.support.scrollview && $el.jqmData("scroll") !== "none" )) {
346 $( tbPage ).css( "padding-top", ( header ? $el.siblings( ".ui-header" ).outerHeight() : 0 ) );
348 $( tbPage ).css( "padding-bottom", ( footer ? $el.siblings( ".ui-footer" ).outerHeight() : 0 ) );
351 /* 1. Calculate and update content height */
352 updatePageLayout: function ( thisPage, receiveType ) {
354 $elPage = $( thisPage ),
355 $elHeader = $elPage.find( ":jqmData(role='header')" ),
356 $elContent = $elPage.find( ":jqmData(role='content')" ),
357 resultContentHeight = 0,
358 resultFooterHeight = 0,
359 resultHeaderHeight = 0,
360 layoutInnerHeight = window.innerHeight,
363 if ( $elPage.length ) {
364 $elFooter = $elPage.find( ":jqmData(role='footer')" );
366 $elFooter = $( document ).find( ":jqmData(role='footer')" ).eq( 0 );
369 // calculate footer height
370 resultFooterHeight = ( $elFooter.css( "display" ) == "none" || $elFooter.length == 0 ) ? 0 : $elFooter.height();
371 resultHeaderHeight = ( $elHeader.css( "display" ) == "none" || $elHeader.length == 0 ) ? 0 : $elHeader.height();
373 if (resultFooterHeight != 0 ) {
374 $elFooter.css( "bottom", 0 );
377 if ( !$.support.scrollview || ($.support.scrollview && $elContent.jqmData("scroll") !== "none") ) {
378 dpr = window.outerWidth / window.innerWidth;
379 layoutInnerHeight = Math.floor( window.outerHeight / dpr );
381 layoutInnerHeight = window.innerHeight;
384 resultContentHeight = layoutInnerHeight - resultFooterHeight - resultHeaderHeight;
386 if ( $.support.scrollview && $elContent.jqmData("scroll") !== "none" ) {
387 $elContent.height( resultContentHeight -
388 parseFloat( $elContent.css("padding-top") ) -
389 parseFloat( $elContent.css("padding-bottom") ) );
392 // External call page( "refresh") - in case title changed
395 .css( "min-height", resultContentHeight )
396 .css( "padding-top", resultHeaderHeight )
397 .css( "padding-bottom", resultFooterHeight );
401 show: function ( notransition ) {
402 /* blank function: deprecated */
405 hide: function ( notransition ) {
406 /* blank function: deprecated */
409 toggle: function () {
410 this[ this._visible ? "hide" : "show" ]();
413 destroy: function () {
414 this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
415 this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
418 refresh: function () {
419 var $elPage = $( ".ui-page-active" );
420 this.setHeaderFooter( $elPage );
421 this._updateHeaderArea( $elPage );
425 //auto self-init widgets
427 .bind( "pagecreate create", function ( e ) {
428 // DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
429 // This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
430 if ( $( e.target ).jqmData( "fullscreen" ) ) {
431 $( $.mobile.pagelayout.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true );
433 $.mobile.pagelayout.prototype.enhanceWithin( e.target );
438 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
440 //>>excludeEnd("jqmBuildExclude");