1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Behavior for "fixed" headers and footers
3 //>>label: Toolbars: Fixed
5 //>>css: ../css/themes/default/jquery.mobile.theme.css,../css/structure/jquery.mobile.fixedToolbar.css
7 define( [ "jquery", "./jquery.mobile.widget", "./jquery.mobile.core", "./jquery.mobile.navigation", "./jquery.mobile.page", "./jquery.mobile.page.sections", "./jquery.mobile.zoom" ], function( $ ) {
8 //>>excludeEnd("jqmBuildExclude");
9 (function( $, undefined ) {
12 $.widget( "mobile.fixedtoolbar", $.mobile.widget, {
14 visibleOnPageShow: true,
15 disablePageZoom: true,
16 transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
19 tapToggleBlacklist: "a, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
20 hideDuringFocus: "input, textarea, select",
21 updatePagePadding: true,
22 trackPersistentToolbars: true,
24 // Browser detection! Weeee, here we go...
25 // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
26 // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
27 // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
28 // The following function serves to rule out some popular browsers with known fixed-positioning issues
29 // This is a plugin option like any other, so feel free to improve or overwrite it
30 supportBlacklist: function(){
32 ua = navigator.userAgent,
33 platform = navigator.platform,
34 // Rendering engine is Webkit, and capture major version
35 wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
36 wkversion = !!wkmatch && wkmatch[ 1 ],
37 ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
38 ffversion = !!ffmatch && ffmatch[ 1 ],
39 operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ),
40 omversion = !!operammobilematch && operammobilematch[ 1 ];
43 // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
44 ( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 )
47 ( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" )
49 ( operammobilematch && omversion < 7458 )
51 //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
52 ( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 )
54 // Firefox Mobile before 6.0 -
55 ( ffversion && ffversion < 6 )
58 ( "palmGetResource" in window && wkversion && wkversion < 534 )
61 ( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 )
68 initSelector: ":jqmData(position='fixed')"
76 tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer",
77 $page = $el.closest(".ui-page");
79 // Feature detecting support for
80 if( o.supportBlacklist() ){
85 $el.addClass( "ui-"+ tbtype +"-fixed" );
87 // "fullscreen" overlay positioning
89 $el.addClass( "ui-"+ tbtype +"-fullscreen" );
90 $page.addClass( "ui-page-" + tbtype + "-fullscreen" );
92 // If not fullscreen, add class to page to set top or bottom padding
94 $page.addClass( "ui-page-" + tbtype + "-fixed" );
97 self._addTransitionClass();
98 self._bindPageEvents();
99 self._bindToggleHandlers();
102 _addTransitionClass: function(){
103 var tclass = this.options.transition;
105 if( tclass && tclass !== "none" ){
106 // use appropriate slide for header or footer
107 if( tclass === "slide" ){
108 tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
111 this.element.addClass( tclass );
115 _bindPageEvents: function(){
120 //page event bindings
121 // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
122 // This method is meant to disable zoom while a fixed-positioned toolbar page is visible
123 $el.closest( ".ui-page" )
124 .bind( "pagebeforeshow", function(){
125 if( o.disablePageZoom ){
126 $.mobile.zoom.disable( true );
128 if( !o.visibleOnPageShow ){
132 .bind( "webkitAnimationStart animationstart updatelayout", function(){
133 if( o.updatePagePadding ){
134 self.updatePagePadding();
137 .bind( "pageshow", function(){
138 self.updatePagePadding();
139 if( o.updatePagePadding ){
140 $( window ).bind( "throttledresize." + self.widgetName, function(){
141 self.updatePagePadding();
145 .bind( "pagebeforehide", function( e, ui ){
146 if( o.disablePageZoom ){
147 $.mobile.zoom.enable( true );
149 if( o.updatePagePadding ){
150 $( window ).unbind( "throttledresize." + self.widgetName );
153 if( o.trackPersistentToolbars ){
154 var thisFooter = $( ".ui-footer-fixed:jqmData(id)", this ),
155 thisHeader = $( ".ui-header-fixed:jqmData(id)", this ),
156 nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ),
157 nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage );
159 nextFooter = nextFooter || $();
161 if( nextFooter.length || nextHeader.length ){
163 nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer );
165 ui.nextPage.one( "pageshow", function(){
166 nextFooter.add( nextHeader ).appendTo( this );
175 // This will set the content element's top or bottom padding equal to the toolbar's height
176 updatePagePadding: function() {
177 var $el = this.element,
178 header = $el.is( ".ui-header" );
180 // This behavior only applies to "fixed", not "fullscreen"
181 if( this.options.fullscreen ){ return; }
183 $el.closest( ".ui-page" ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() );
186 _useTransition: function( notransition ){
187 var $win = $( window ),
189 scroll = $win.scrollTop(),
190 elHeight = $el.height(),
191 pHeight = $el.closest( ".ui-page" ).height(),
192 viewportHeight = $.mobile.getScreenHeight(),
193 tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer";
195 return !notransition &&
196 ( this.options.transition && this.options.transition !== "none" &&
198 ( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
199 ( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
200 ) || this.options.fullscreen
204 show: function( notransition ){
205 var hideClass = "ui-fixed-hidden",
208 if( this._useTransition( notransition ) ){
210 .removeClass( "out " + hideClass )
214 $el.removeClass( hideClass );
216 this._visible = true;
219 hide: function( notransition ){
220 var hideClass = "ui-fixed-hidden",
222 // if it's a slide transition, our new transitions need the reverse class as well to slide outward
223 outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" );
225 if( this._useTransition( notransition ) ){
227 .addClass( outclass )
229 .animationComplete( function(){
230 $el.addClass( hideClass ).removeClass( outclass );
234 $el.addClass( hideClass ).removeClass( outclass );
236 this._visible = false;
240 this[ this._visible ? "hide" : "show" ]();
243 _bindToggleHandlers: function(){
249 $el.closest( ".ui-page" )
250 .bind( "vclick", function( e ){
251 if( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ){
255 .bind( "focusin focusout", function( e ){
256 if( screen.width < 500 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ){
257 self[ ( e.type === "focusin" && self._visible ) ? "hide" : "show" ]();
263 this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
264 this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
269 //auto self-init widgets
271 .bind( "pagecreate create", function( e ){
273 // DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
274 // This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
275 if( $( e.target ).jqmData( "fullscreen" ) ){
276 $( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true );
279 $.mobile.fixedtoolbar.prototype.enhanceWithin( e.target );
283 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
285 //>>excludeEnd("jqmBuildExclude");