| //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude); |
| //>>description: Behavior for "fixed" headers and footers |
| //>>label: Toolbars: Fixed |
| //>>group: Widgets |
| //>>css.structure: ../css/structure/jquery.mobile.fixedToolbar.css |
| //>>css.theme: ../css/themes/default/jquery.mobile.theme.css |
| |
| define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.core", "../jquery.mobile.navigation", "./page", "./page.sections", "../jquery.mobile.zoom" ], function( jQuery ) { |
| //>>excludeEnd("jqmBuildExclude"); |
| (function( $, undefined ) { |
| |
| |
| $.widget( "mobile.fixedtoolbar", $.mobile.widget, { |
| options: { |
| visibleOnPageShow: true, |
| disablePageZoom: true, |
| transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown) |
| fullscreen: false, |
| tapToggle: true, |
| tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed, .ui-popup", |
| hideDuringFocus: "input, textarea, select", |
| updatePagePadding: true, |
| trackPersistentToolbars: true, |
| |
| // Browser detection! Weeee, here we go... |
| // Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately. |
| // Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience. |
| // Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window |
| // The following function serves to rule out some popular browsers with known fixed-positioning issues |
| // This is a plugin option like any other, so feel free to improve or overwrite it |
| supportBlacklist: function() { |
| var w = window, |
| ua = navigator.userAgent, |
| platform = navigator.platform, |
| // Rendering engine is Webkit, and capture major version |
| wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ), |
| wkversion = !!wkmatch && wkmatch[ 1 ], |
| ffmatch = ua.match( /Fennec\/([0-9]+)/ ), |
| ffversion = !!ffmatch && ffmatch[ 1 ], |
| operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ), |
| omversion = !!operammobilematch && operammobilematch[ 1 ]; |
| |
| if( |
| // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5) |
| ( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 ) || |
| // Opera Mini |
| ( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" ) || |
| ( operammobilematch && omversion < 7458 ) || |
| //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2) |
| ( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 ) || |
| // Firefox Mobile before 6.0 - |
| ( ffversion && ffversion < 6 ) || |
| // WebOS less than 3 |
| ( "palmGetResource" in window && wkversion && wkversion < 534 ) || |
| // MeeGo |
| ( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 ) ) { |
| return true; |
| } |
| |
| return false; |
| }, |
| initSelector: ":jqmData(position='dummy')" |
| }, |
| |
| _create: function() { |
| |
| var self = this, |
| o = self.options, |
| $el = self.element, |
| tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer", |
| $page = $el.closest( ".ui-page" ); |
| |
| // Feature detecting support for |
| if ( o.supportBlacklist() ) { |
| self.destroy(); |
| return; |
| } |
| |
| $el.addClass( "ui-"+ tbtype +"-fixed" ); |
| |
| // "fullscreen" overlay positioning |
| if ( o.fullscreen ) { |
| $el.addClass( "ui-"+ tbtype +"-fullscreen" ); |
| $page.addClass( "ui-page-" + tbtype + "-fullscreen" ); |
| } |
| // If not fullscreen, add class to page to set top or bottom padding |
| else{ |
| $page.addClass( "ui-page-" + tbtype + "-fixed" ); |
| } |
| |
| self._addTransitionClass(); |
| self._bindPageEvents(); |
| self._bindToggleHandlers(); |
| }, |
| |
| _addTransitionClass: function() { |
| var tclass = this.options.transition; |
| |
| if ( tclass && tclass !== "none" ) { |
| // use appropriate slide for header or footer |
| if ( tclass === "slide" ) { |
| tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup"; |
| } |
| |
| this.element.addClass( tclass ); |
| } |
| }, |
| |
| _bindPageEvents: function() { |
| var self = this, |
| o = self.options, |
| $el = self.element; |
| |
| //page event bindings |
| // Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up |
| // This method is meant to disable zoom while a fixed-positioned toolbar page is visible |
| $el.closest( ".ui-page" ) |
| .bind( "pagebeforeshow", function() { |
| if ( o.disablePageZoom ) { |
| $.mobile.zoom.disable( true ); |
| } |
| if ( !o.visibleOnPageShow ) { |
| self.hide( true ); |
| } |
| } ) |
| .bind( "webkitAnimationStart animationstart updatelayout", function() { |
| var thisPage = this; |
| if ( o.updatePagePadding ) { |
| self.updatePagePadding( thisPage ); |
| } |
| }) |
| .bind( "pageshow", function() { |
| var thisPage = this; |
| self.updatePagePadding( thisPage ); |
| if ( o.updatePagePadding ) { |
| $.mobile.$window.bind( "throttledresize." + self.widgetName, function() { |
| self.updatePagePadding( thisPage ); |
| }); |
| } |
| }) |
| .bind( "pagebeforehide", function( e, ui ) { |
| if ( o.disablePageZoom ) { |
| $.mobile.zoom.enable( true ); |
| } |
| if ( o.updatePagePadding ) { |
| $.mobile.$window.unbind( "throttledresize." + self.widgetName ); |
| } |
| |
| if ( o.trackPersistentToolbars ) { |
| var thisFooter = $( ".ui-footer-fixed:jqmData(id)", this ), |
| thisHeader = $( ".ui-header-fixed:jqmData(id)", this ), |
| nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ) || $(), |
| nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage ) || $(); |
| |
| if ( nextFooter.length || nextHeader.length ) { |
| |
| nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer ); |
| |
| ui.nextPage.one( "pageshow", function() { |
| nextFooter.add( nextHeader ).appendTo( this ); |
| }); |
| } |
| } |
| }); |
| }, |
| |
| _visible: true, |
| |
| // This will set the content element's top or bottom padding equal to the toolbar's height |
| updatePagePadding: function( tbPage ) { |
| var $el = this.element, |
| header = $el.is( ".ui-header" ); |
| |
| // This behavior only applies to "fixed", not "fullscreen" |
| if ( this.options.fullscreen ) { return; } |
| |
| tbPage = tbPage || $el.closest( ".ui-page" ); |
| $( tbPage ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() ); |
| }, |
| |
| _useTransition: function( notransition ) { |
| var $win = $.mobile.$window, |
| $el = this.element, |
| scroll = $win.scrollTop(), |
| elHeight = $el.height(), |
| pHeight = $el.closest( ".ui-page" ).height(), |
| viewportHeight = $.mobile.getScreenHeight(), |
| tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer"; |
| |
| return !notransition && |
| ( this.options.transition && this.options.transition !== "none" && |
| ( |
| ( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) || |
| ( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight ) |
| ) || this.options.fullscreen |
| ); |
| }, |
| |
| show: function( notransition ) { |
| var hideClass = "ui-fixed-hidden", |
| $el = this.element; |
| |
| if ( this._useTransition( notransition ) ) { |
| $el |
| .removeClass( "out " + hideClass ) |
| .addClass( "in" ); |
| } |
| else { |
| $el.removeClass( hideClass ); |
| } |
| this._visible = true; |
| }, |
| |
| hide: function( notransition ) { |
| var hideClass = "ui-fixed-hidden", |
| $el = this.element, |
| // if it's a slide transition, our new transitions need the reverse class as well to slide outward |
| outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" ); |
| |
| if( this._useTransition( notransition ) ) { |
| $el |
| .addClass( outclass ) |
| .removeClass( "in" ) |
| .animationComplete(function() { |
| $el.addClass( hideClass ).removeClass( outclass ); |
| }); |
| } |
| else { |
| $el.addClass( hideClass ).removeClass( outclass ); |
| } |
| this._visible = false; |
| }, |
| |
| toggle: function() { |
| this[ this._visible ? "hide" : "show" ](); |
| }, |
| |
| _bindToggleHandlers: function() { |
| var self = this, |
| o = self.options, |
| $el = self.element; |
| |
| // tap toggle |
| $el.closest( ".ui-page" ) |
| .bind( "vclick", function( e ) { |
| if ( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ) { |
| self.toggle(); |
| } |
| }) |
| .bind( "focusin focusout", function( e ) { |
| if ( screen.width < 500 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ) { |
| self[ ( e.type === "focusin" && self._visible ) ? "hide" : "show" ](); |
| } |
| }); |
| }, |
| |
| destroy: function() { |
| this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" ); |
| this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" ); |
| } |
| |
| }); |
| |
| //auto self-init widgets |
| $.mobile.$document |
| .bind( "pagecreate create", function( e ) { |
| |
| // DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element. |
| // This line ensures it still works, but we recommend moving the attribute to the toolbars themselves. |
| if ( $( e.target ).jqmData( "fullscreen" ) ) { |
| $( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true ); |
| } |
| |
| $.mobile.fixedtoolbar.prototype.enhanceWithin( e.target ); |
| }); |
| |
| })( jQuery ); |
| //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude); |
| }); |
| //>>excludeEnd("jqmBuildExclude"); |