1 (function( $, undefined ) {
4 $.widget( "mobile.fixedtoolbar", $.mobile.widget, {
6 visibleOnPageShow: true,
8 transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
11 tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed, .ui-popup",
12 hideDuringFocus: "input, textarea, select",
13 updatePagePadding: true,
14 trackPersistentToolbars: true,
16 // Browser detection! Weeee, here we go...
17 // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
18 // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
19 // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
20 // The following function serves to rule out some popular browsers with known fixed-positioning issues
21 // This is a plugin option like any other, so feel free to improve or overwrite it
22 supportBlacklist: function() {
24 ua = navigator.userAgent,
25 platform = navigator.platform,
26 // Rendering engine is Webkit, and capture major version
27 wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
28 wkversion = !!wkmatch && wkmatch[ 1 ],
29 ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
30 ffversion = !!ffmatch && ffmatch[ 1 ],
31 operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ),
32 omversion = !!operammobilematch && operammobilematch[ 1 ];
35 // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
36 ( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 ) ||
38 ( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" ) ||
39 ( operammobilematch && omversion < 7458 ) ||
40 //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
41 ( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 ) ||
42 // Firefox Mobile before 6.0 -
43 ( ffversion && ffversion < 6 ) ||
45 ( "palmGetResource" in window && wkversion && wkversion < 534 ) ||
47 ( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 ) ) {
53 initSelector: ":jqmData(position='dummy')"
61 tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer",
62 $page = $el.closest( ".ui-page" );
64 // Feature detecting support for
65 if ( o.supportBlacklist() ) {
70 $el.addClass( "ui-"+ tbtype +"-fixed" );
72 // "fullscreen" overlay positioning
74 $el.addClass( "ui-"+ tbtype +"-fullscreen" );
75 $page.addClass( "ui-page-" + tbtype + "-fullscreen" );
77 // If not fullscreen, add class to page to set top or bottom padding
79 $page.addClass( "ui-page-" + tbtype + "-fixed" );
82 self._addTransitionClass();
83 self._bindPageEvents();
84 self._bindToggleHandlers();
87 _addTransitionClass: function() {
88 var tclass = this.options.transition;
90 if ( tclass && tclass !== "none" ) {
91 // use appropriate slide for header or footer
92 if ( tclass === "slide" ) {
93 tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
96 this.element.addClass( tclass );
100 _bindPageEvents: function() {
105 //page event bindings
106 // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
107 // This method is meant to disable zoom while a fixed-positioned toolbar page is visible
108 $el.closest( ".ui-page" )
109 .bind( "pagebeforeshow", function() {
110 if ( o.disablePageZoom ) {
111 $.mobile.zoom.disable( true );
113 if ( !o.visibleOnPageShow ) {
117 .bind( "webkitAnimationStart animationstart updatelayout", function() {
119 if ( o.updatePagePadding ) {
120 self.updatePagePadding( thisPage );
123 .bind( "pageshow", function() {
125 self.updatePagePadding( thisPage );
126 if ( o.updatePagePadding ) {
127 $.mobile.$window.bind( "throttledresize." + self.widgetName, function() {
128 self.updatePagePadding( thisPage );
132 .bind( "pagebeforehide", function( e, ui ) {
133 if ( o.disablePageZoom ) {
134 $.mobile.zoom.enable( true );
136 if ( o.updatePagePadding ) {
137 $.mobile.$window.unbind( "throttledresize." + self.widgetName );
140 if ( o.trackPersistentToolbars ) {
141 var thisFooter = $( ".ui-footer-fixed:jqmData(id)", this ),
142 thisHeader = $( ".ui-header-fixed:jqmData(id)", this ),
143 nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ) || $(),
144 nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage ) || $();
146 if ( nextFooter.length || nextHeader.length ) {
148 nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer );
150 ui.nextPage.one( "pageshow", function() {
151 nextFooter.add( nextHeader ).appendTo( this );
160 // This will set the content element's top or bottom padding equal to the toolbar's height
161 updatePagePadding: function( tbPage ) {
162 var $el = this.element,
163 header = $el.is( ".ui-header" );
165 // This behavior only applies to "fixed", not "fullscreen"
166 if ( this.options.fullscreen ) { return; }
168 tbPage = tbPage || $el.closest( ".ui-page" );
169 $( tbPage ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() );
172 _useTransition: function( notransition ) {
173 var $win = $.mobile.$window,
175 scroll = $win.scrollTop(),
176 elHeight = $el.height(),
177 pHeight = $el.closest( ".ui-page" ).height(),
178 viewportHeight = $.mobile.getScreenHeight(),
179 tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer";
181 return !notransition &&
182 ( this.options.transition && this.options.transition !== "none" &&
184 ( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
185 ( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
186 ) || this.options.fullscreen
190 show: function( notransition ) {
191 var hideClass = "ui-fixed-hidden",
194 if ( this._useTransition( notransition ) ) {
196 .removeClass( "out " + hideClass )
200 $el.removeClass( hideClass );
202 this._visible = true;
205 hide: function( notransition ) {
206 var hideClass = "ui-fixed-hidden",
208 // if it's a slide transition, our new transitions need the reverse class as well to slide outward
209 outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" );
211 if( this._useTransition( notransition ) ) {
213 .addClass( outclass )
215 .animationComplete(function() {
216 $el.addClass( hideClass ).removeClass( outclass );
220 $el.addClass( hideClass ).removeClass( outclass );
222 this._visible = false;
226 this[ this._visible ? "hide" : "show" ]();
229 _bindToggleHandlers: function() {
235 $el.closest( ".ui-page" )
236 .bind( "vclick", function( e ) {
237 if ( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ) {
241 .bind( "focusin focusout", function( e ) {
242 if ( screen.width < 500 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ) {
243 self[ ( e.type === "focusin" && self._visible ) ? "hide" : "show" ]();
248 destroy: function() {
249 this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
250 this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
255 //auto self-init widgets
257 .bind( "pagecreate create", function( e ) {
259 // DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
260 // This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
261 if ( $( e.target ).jqmData( "fullscreen" ) ) {
262 $( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true );
265 $.mobile.fixedtoolbar.prototype.enhanceWithin( e.target );