blob: 880d451f033972dc3851fada2b23c605e173b50d [file] [log] [blame]
//>>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");