Merge pull request #1959 from rwldrn/jquery.mobile.fixHeaderFooter.js

jQuery core style guide conformance: jquery.mobile.fixHeaderFooter.js
This commit is contained in:
Kin Blas 2011-06-29 09:14:51 -07:00
commit a68cdd9f33

View file

@ -4,29 +4,45 @@
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function($, undefined ) {
$.fn.fixHeaderFooter = function(options){
if( !$.support.scrollTop ){ return this; }
return this.each(function(){
var $this = $(this);
if( $this.jqmData('fullscreen') ){ $this.addClass('ui-page-fullscreen'); }
$this.find( ".ui-header:jqmData(position='fixed')" ).addClass('ui-header-fixed ui-fixed-inline fade'); //should be slidedown
$this.find( ".ui-footer:jqmData(position='fixed')" ).addClass('ui-footer-fixed ui-fixed-inline fade'); //should be slideup
(function( $, undefined ) {
$.fn.fixHeaderFooter = function( options ) {
if ( !$.support.scrollTop ) {
return this;
}
return this.each(function() {
var $this = $( this );
if ( $this.jqmData( "fullscreen" ) ) {
$this.addClass( "ui-page-fullscreen" );
}
// Should be slidedown
$this.find( ".ui-header:jqmData(position='fixed')" ).addClass( "ui-header-fixed ui-fixed-inline fade" );
// Should be slideup
$this.find( ".ui-footer:jqmData(position='fixed')" ).addClass( "ui-footer-fixed ui-fixed-inline fade" );
});
};
//single controller for all showing,hiding,toggling
$.fixedToolbars = (function(){
if( !$.support.scrollTop ){ return; }
var currentstate = 'inline',
//single controller for all showing,hiding,toggling
$.fixedToolbars = (function() {
if ( !$.support.scrollTop ) {
return;
}
var currentstate = "inline",
autoHideMode = false,
showDelay = 100,
delayTimer,
ignoreTargets = 'a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed',
toolbarSelector = '.ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last',
stickyFooter, //for storing quick references to duplicate footers
ignoreTargets = "a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed",
toolbarSelector = ".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last",
//for storing quick references to duplicate footers
stickyFooter,
supportTouch = $.support.touch,
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
touchStopEvent = supportTouch ? "touchend" : "mouseup",
@ -34,7 +50,7 @@ $.fixedToolbars = (function(){
scrollTriggered = false,
touchToggleEnabled = true;
function showEventCallback(event) {
function showEventCallback( event ) {
// An event that affects the dimensions of the visual viewport has
// been triggered. If the header and/or footer for the current page are in overlay
// mode, we want to hide them, and then fire off a timer to show them at a later
@ -44,103 +60,125 @@ $.fixedToolbars = (function(){
//
// If we are in autoHideMode, we don't do anything because we know the scroll
// callbacks for the plugin will fire off a show when the scrolling has stopped.
if (!autoHideMode && currentstate == 'overlay') {
if (!delayTimer)
$.fixedToolbars.hide(true);
if ( !autoHideMode && currentstate === "overlay" ) {
if ( !delayTimer ) {
$.fixedToolbars.hide( true );
}
$.fixedToolbars.startShowTimer();
}
}
$(function() {
$(document)
.bind( "vmousedown",function(event){
if( touchToggleEnabled ) {
var $document = $( document ),
$window = $(window);
$document
.bind( "vmousedown", function( event ) {
if ( touchToggleEnabled ) {
stateBefore = currentstate;
}
})
.bind( "vclick",function(event){
if( touchToggleEnabled ) {
if( $(event.target).closest(ignoreTargets).length ){ return; }
if( !scrollTriggered ){
$.fixedToolbars.toggle(stateBefore);
.bind( "vclick", function( event) {
if ( touchToggleEnabled ) {
if ( $(event.target).closest( ignoreTargets ).length ) {
return;
}
if ( !scrollTriggered ) {
$.fixedToolbars.toggle( stateBefore );
stateBefore = null;
}
}
})
.bind('silentscroll', showEventCallback);
.bind( "silentscroll", showEventCallback );
/*
The below checks first for a $(document).scrollTop() value, and if zero, binds scroll events to $(window) instead. If the scrollTop value is actually zero, both will return zero anyway.
Works with $(document), not $(window) : Opera Mobile (WinMO phone; kinda broken anyway)
Works with $(window), not $(document) : IE 7/8
Works with either $(window) or $(document) : Chrome, FF 3.6/4, Android 1.6/2.1, iOS
Needs work either way : BB5, Opera Mobile (iOS)
// The below checks first for a $(document).scrollTop() value, and if zero, binds scroll events to $(window) instead.
// If the scrollTop value is actually zero, both will return zero anyway.
//
// Works with $(document), not $(window) : Opera Mobile (WinMO phone; kinda broken anyway)
// Works with $(window), not $(document) : IE 7/8
// Works with either $(window) or $(document) : Chrome, FF 3.6/4, Android 1.6/2.1, iOS
// Needs work either way : BB5, Opera Mobile (iOS)
*/
( ( $document.scrollTop() === 0 ) ? $window : $document )
.bind( "scrollstart", function( event ) {
(( $(document).scrollTop() === 0 ) ? $(window) : $(document))
.bind('scrollstart',function(event){
scrollTriggered = true;
if(stateBefore === null){ stateBefore = currentstate; }
if ( stateBefore === null ) {
stateBefore = currentstate;
}
// We only enter autoHideMode if the headers/footers are in
// an overlay state or the show timer was started. If the
// show timer is set, clear it so the headers/footers don't
// show up until after we're done scrolling.
var isOverlayState = stateBefore == 'overlay';
var isOverlayState = stateBefore == "overlay";
autoHideMode = isOverlayState || !!delayTimer;
if (autoHideMode){
if ( autoHideMode ) {
$.fixedToolbars.clearShowTimer();
if (isOverlayState) {
$.fixedToolbars.hide(true);
if ( isOverlayState ) {
$.fixedToolbars.hide( true );
}
}
})
.bind('scrollstop',function(event){
if( $(event.target).closest(ignoreTargets).length ){ return; }
.bind( "scrollstop", function( event ) {
if ( $( event.target ).closest( ignoreTargets ).length ) {
return;
}
scrollTriggered = false;
if (autoHideMode) {
autoHideMode = false;
if ( autoHideMode ) {
$.fixedToolbars.startShowTimer();
autoHideMode = false;
}
stateBefore = null;
});
$(window).bind('resize', showEventCallback);
});
//before page is shown, check for duplicate footer
$('.ui-page').live('pagebeforeshow', function(event, ui){
var page = $(event.target),
footer = page.find( ":jqmData(role='footer')" ),
id = footer.data('id'),
prevPage = ui.prevPage,
prevFooter = prevPage && prevPage.find( ":jqmData(role='footer')" ),
prevFooterMatches = prevFooter.length && prevFooter.jqmData( "id" ) === id;
if( id && prevFooterMatches ){
stickyFooter = footer;
setTop( stickyFooter.removeClass( "fade in out" ).appendTo( $.mobile.pageContainer ) );
}
$window.bind( "resize", showEventCallback );
});
//after page is shown, append footer to new page
$('.ui-page').live('pageshow', function(event, ui){
var $this = $(this);
if( stickyFooter && stickyFooter.length ){
setTimeout(function(){
setTop( stickyFooter.appendTo( $this ).addClass("fade") );
stickyFooter = null;
}, 500);
}
$.fixedToolbars.show(true, this);
});
//When a collapsiable is hidden or shown we need to trigger the fixed toolbar to reposition itself (#1635)
// 1. Before page is shown, check for duplicate footer
// 2. After page is shown, append footer to new page
$( ".ui-page" )
.live( "pagebeforeshow", function( event, ui ) {
var page = $( event.target ),
footer = page.find( ":jqmData(role='footer')" ),
id = footer.data( "id" ),
prevPage = ui.prevPage,
prevFooter = prevPage && prevPage.find( ":jqmData(role='footer')" ),
prevFooterMatches = prevFooter.length && prevFooter.jqmData( "id" ) === id;
if ( id && prevFooterMatches ) {
stickyFooter = footer;
setTop( stickyFooter.removeClass( "fade in out" ).appendTo( $.mobile.pageContainer ) );
}
})
.live( "pageshow", function( event, ui ) {
var $this = $( this );
if ( stickyFooter && stickyFooter.length ) {
setTimeout(function() {
setTop( stickyFooter.appendTo( $this ).addClass( "fade" ) );
stickyFooter = null;
}, 500);
}
$.fixedToolbars.show( true, this );
});
//When a collapsiable is hidden or shown we need to trigger the fixed toolbar to reposition itself (#1635)
$( ".ui-collapsible-contain" ).live( "collapse expand", showEventCallback );
// element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
@ -151,118 +189,159 @@ $.fixedToolbars = (function(){
//
// TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core.
function getOffsetTop(ele) {
var top = 0;
if (ele) {
var op = ele.offsetParent, body = document.body;
function getOffsetTop( ele ) {
var top = 0,
op;
if ( ele ) {
op = ele.offsetParent, body = document.body;
top = ele.offsetTop;
while (ele && ele != body) {
while ( ele && ele != body ) {
top += ele.scrollTop || 0;
if (ele == op){
if ( ele == op ) {
top += op.offsetTop;
op = ele.offsetParent;
}
ele = ele.parentNode;
}
}
return top;
}
function setTop(el){
function setTop( el ) {
var fromTop = $(window).scrollTop(),
thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
thisCSStop = el.css('top') == 'auto' ? 0 : parseFloat(el.css('top')),
thisTop = getOffsetTop( el[ 0 ] ), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
thisCSStop = el.css( "top" ) == "auto" ? 0 : parseFloat(el.css( "top" )),
screenHeight = window.innerHeight,
thisHeight = el.outerHeight(),
useRelative = el.parents('.ui-page:not(.ui-page-fullscreen)').length,
useRelative = el.parents( ".ui-page:not(.ui-page-fullscreen)" ).length,
relval;
if( el.is('.ui-header-fixed') ){
if ( el.is( ".ui-header-fixed" ) ) {
relval = fromTop - thisTop + thisCSStop;
if( relval < thisTop){ relval = 0; }
return el.css('top', ( useRelative ) ? relval : fromTop);
if ( relval < thisTop) {
relval = 0;
}
return el.css( "top", useRelative ? relval : fromTop );
}
else{
//relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
//if( relval > thisTop ){ relval = 0; }
relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop);
return el.css('top', ( useRelative ) ? relval : fromTop + screenHeight - thisHeight );
// relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
// if ( relval > thisTop ) { relval = 0; }
relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop );
return el.css( "top", useRelative ? relval : fromTop + screenHeight - thisHeight );
}
}
//exposed methods
// Exposed methods
return {
show: function(immediately, page){
show: function( immediately, page ) {
$.fixedToolbars.clearShowTimer();
currentstate = 'overlay';
var $ap = page ? $(page) : ($.mobile.activePage ? $.mobile.activePage : $(".ui-page-active"));
return $ap.children( toolbarSelector ).each(function(){
var el = $(this),
fromTop = $(window).scrollTop(),
thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
currentstate = "overlay";
var $ap = page ? $( page ) :
( $.mobile.activePage ? $.mobile.activePage :
$( ".ui-page-active" ) );
return $ap.children( toolbarSelector ).each(function() {
var el = $( this ),
fromTop = $( window ).scrollTop(),
thisTop = getOffsetTop( el[ 0 ] ), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
screenHeight = window.innerHeight,
thisHeight = el.outerHeight(),
alreadyVisible = (el.is('.ui-header-fixed') && fromTop <= thisTop + thisHeight) || (el.is('.ui-footer-fixed') && thisTop <= fromTop + screenHeight);
//add state class
el.addClass('ui-fixed-overlay').removeClass('ui-fixed-inline');
if( !alreadyVisible && !immediately ){
el.animationComplete(function(){
el.removeClass('in');
}).addClass('in');
alreadyVisible = ( el.is( ".ui-header-fixed" ) && fromTop <= thisTop + thisHeight ) ||
( el.is( ".ui-footer-fixed" ) && thisTop <= fromTop + screenHeight );
// Add state class
el.addClass( "ui-fixed-overlay" ).removeClass( "ui-fixed-inline" );
if ( !alreadyVisible && !immediately ) {
el.animationComplete(function() {
el.removeClass( "in" );
}).addClass( "in" );
}
setTop(el);
});
});
},
hide: function(immediately){
currentstate = 'inline';
var $ap = $.mobile.activePage ? $.mobile.activePage : $(".ui-page-active");
return $ap.children( toolbarSelector ).each(function(){
var el = $(this);
var thisCSStop = el.css('top'); thisCSStop = thisCSStop == 'auto' ? 0 : parseFloat(thisCSStop);
//add state class
el.addClass('ui-fixed-inline').removeClass('ui-fixed-overlay');
if (thisCSStop < 0 || (el.is('.ui-header-fixed') && thisCSStop != 0))
{
if(immediately){
el.css('top',0);
}
else{
if( el.css('top') !== 'auto' && parseFloat(el.css('top')) !== 0 ){
var classes = 'out reverse';
el.animationComplete(function(){
el.removeClass(classes);
el.css('top',0);
}).addClass(classes);
hide: function( immediately ) {
currentstate = "inline";
var $ap = $.mobile.activePage ? $.mobile.activePage :
$( ".ui-page-active" );
return $ap.children( toolbarSelector ).each(function() {
var el = $(this),
thisCSStop = el.css( "top" ),
classes;
thisCSStop = thisCSStop == "auto" ? 0 :
parseFloat(thisCSStop);
// Add state class
el.addClass( "ui-fixed-inline" ).removeClass( "ui-fixed-overlay" );
if ( thisCSStop < 0 || ( el.is( ".ui-header-fixed" ) && thisCSStop !== 0 ) ) {
if ( immediately ) {
el.css( "top", 0);
} else {
if ( el.css( "top" ) !== "auto" && parseFloat( el.css( "top" ) ) !== 0 ) {
classes = "out reverse";
el.animationComplete(function() {
el.removeClass( classes ).css( "top", 0 );
}).addClass( classes );
}
}
}
});
},
startShowTimer: function(){
startShowTimer: function() {
$.fixedToolbars.clearShowTimer();
var args = $.makeArray(arguments);
delayTimer = setTimeout(function(){
delayTimer = setTimeout(function() {
delayTimer = undefined;
$.fixedToolbars.show.apply(null, args);
$.fixedToolbars.show.apply( null, args );
}, showDelay);
},
clearShowTimer: function() {
if (delayTimer) {
clearTimeout(delayTimer);
if ( delayTimer ) {
clearTimeout( delayTimer );
}
delayTimer = undefined;
},
toggle: function(from){
if(from){ currentstate = from; }
return (currentstate == 'overlay') ? $.fixedToolbars.hide() : $.fixedToolbars.show();
toggle: function( from ) {
if ( from ) {
currentstate = from;
}
return ( currentstate === "overlay" ) ? $.fixedToolbars.hide() :
$.fixedToolbars.show();
},
setTouchToggleEnabled: function(enabled) {
touchToggleEnabled = enabled;
}
setTouchToggleEnabled: function(enabled) {
touchToggleEnabled = enabled;
}
};
})();