moved page load event bindings to a post-mobileinit callback so that the useFastClick option can be set before it is used. Unit test included. Fixes #1869

This commit is contained in:
scottjehl 2011-06-21 11:33:38 -04:00
parent 40b1e16693
commit ffdfa4e7c2
4 changed files with 184 additions and 162 deletions

View file

@ -101,6 +101,9 @@
}
});
//initialize events now, after mobileinit has occurred
$.mobile._registerInternalEvents();
//check which scrollTop value should be used by scrolling to 1 immediately at domready
//then check what the scroll top is. Android will report 0... others 1
//note that this initial scroll won't hide the address bar. It's just for the check.

View file

@ -981,168 +981,175 @@
return path.makeUrlAbsolute( url, base);
}
//add active state on vclick
$( document ).bind( "vclick", function( event ) {
var link = findClosestLink( event.target );
if ( link ) {
if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
$( link ).closest( ".ui-btn" ).not( ".ui-disabled" ).addClass( $.mobile.activeBtnClass );
$( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
}
}
});
// click routing - direct to HTTP or Ajax, accordingly
// TODO: most of the time, vclick will be all we need for fastClick bulletproofing.
// However, it seems that in Android 2.1, a click event
// will occasionally arrive independently of the bound vclick
// binding to click as well seems to help in this edge case
// we'll dig into this further in the next release cycle
$( document ).bind( $.mobile.useFastClick ? "vclick click" : "click", function( event ) {
var link = findClosestLink( event.target );
if ( !link ) {
return;
}
var $link = $( link ),
//remove active link class if external (then it won't be there if you come back)
httpCleanup = function(){
window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
};
//if there's a data-rel=back attr, go back in history
if( $link.is( ":jqmData(rel='back')" ) ) {
window.history.back();
return false;
}
//if ajax is disabled, exit early
if( !$.mobile.ajaxEnabled ){
httpCleanup();
//use default click handling
return;
}
var baseUrl = getClosestBaseUrl( $link ),
//get href, if defined, otherwise default to empty hash
href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
// XXX_jblas: Ideally links to application pages should be specified as
// an url to the application document with a hash that is either
// the site relative path or id to the page. But some of the
// internal code that dynamically generates sub-pages for nested
// lists and select dialogs, just write a hash in the link they
// create. This means the actual URL path is based on whatever
// the current value of the base tag is at the time this code
// is called. For now we are just assuming that any url with a
// hash in it is an application page reference.
if ( href.search( "#" ) != -1 ) {
href = href.replace( /[^#]*#/, "" );
if ( !href ) {
//link was an empty hash meant purely
//for interaction, so we ignore it.
event.preventDefault();
return;
} else if ( path.isPath( href ) ) {
//we have apath so make it the href we want to load.
href = path.makeUrlAbsolute( href, baseUrl );
} else {
//we have a simple id so use the documentUrl as its base.
href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash );
}
}
// Should we handle this link, or let the browser deal with it?
var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ),
// Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
// requests if the document doing the request was loaded via the file:// protocol.
// This is usually to allow the application to "phone home" and fetch app specific
// data. We normally let the browser handle external/cross-domain urls, but if the
// allowCrossDomainPages option is true, we will allow cross-domain http/https
// requests to go through our page loading logic.
isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ),
//check for protocol or rel and its not an embedded page
//TODO overlap in logic from isExternal, rel=external check should be
// moved into more comprehensive isExternalLink
isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
$activeClickedLink = $link.closest( ".ui-btn" );
if( isExternal ) {
httpCleanup();
//use default click handling
return;
}
//use ajax
var transition = $link.jqmData( "transition" ),
direction = $link.jqmData( "direction" ),
reverse = ( direction && direction === "reverse" ) ||
// deprecated - remove by 1.0
$link.jqmData( "back" ),
//this may need to be more specific as we use data-rel more
role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
$.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } );
event.preventDefault();
});
//hashchange event handler
$window.bind( "hashchange", function( e, triggered ) {
//find first page via hash
var to = path.stripHash( location.hash ),
//transition is false if it's the first page, undefined otherwise (and may be overridden by default)
transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined;
//if listening is disabled (either globally or temporarily), or it's a dialog hash
if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
urlHistory.ignoreNextHashChange = false;
return;
}
// special case for dialogs
if( urlHistory.stack.length > 1 &&
to.indexOf( dialogHashKey ) > -1 ) {
// If current active page is not a dialog skip the dialog and continue
// in the same direction
if(!$.mobile.activePage.is( ".ui-dialog" )) {
//determine if we're heading forward or backward and continue accordingly past
//the current dialog
urlHistory.directHashChange({
currentUrl: to,
isBack: function() { window.history.back(); },
isForward: function() { window.history.forward(); }
});
// prevent changepage
return;
} else {
var setTo = function() { to = $.mobile.urlHistory.getActive().page; };
// if the current active page is a dialog and we're navigating
// to a dialog use the dialog objected saved in the stack
urlHistory.directHashChange({ currentUrl: to, isBack: setTo, isForward: setTo });
}
}
//if to is defined, load it
if ( to ) {
to = ( typeof to === "string" && !path.isPath( to ) ) ? ( '#' + to ) : to;
$.mobile.changePage( to, { transition: transition, changeHash: false, fromHashChange: true } );
}
//there's no hash, go to the first page in the dom
else {
$.mobile.changePage( $.mobile.firstPage, { transition: transition, changeHash: false, fromHashChange: true } );
}
});
//set page min-heights to be device specific
$( document ).bind( "pageshow", resetActivePageHeight );
$( window ).bind( "throttledresize", resetActivePageHeight );
//The following event bindings should be bound after mobileinit has been triggered
//the following function is called in the init file
$.mobile._registerInternalEvents = function(){
//add active state on vclick
$( document ).bind( "vclick", function( event ) {
var link = findClosestLink( event.target );
if ( link ) {
if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
$( link ).closest( ".ui-btn" ).not( ".ui-disabled" ).addClass( $.mobile.activeBtnClass );
$( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
}
}
});
// click routing - direct to HTTP or Ajax, accordingly
// TODO: most of the time, vclick will be all we need for fastClick bulletproofing.
// However, it seems that in Android 2.1, a click event
// will occasionally arrive independently of the bound vclick
// binding to click as well seems to help in this edge case
// we'll dig into this further in the next release cycle
$( document ).bind( $.mobile.useFastClick ? "vclick click" : "click", function( event ) {
var link = findClosestLink( event.target );
if ( !link ) {
return;
}
var $link = $( link ),
//remove active link class if external (then it won't be there if you come back)
httpCleanup = function(){
window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
};
//if there's a data-rel=back attr, go back in history
if( $link.is( ":jqmData(rel='back')" ) ) {
window.history.back();
return false;
}
//if ajax is disabled, exit early
if( !$.mobile.ajaxEnabled ){
httpCleanup();
//use default click handling
return;
}
var baseUrl = getClosestBaseUrl( $link ),
//get href, if defined, otherwise default to empty hash
href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
// XXX_jblas: Ideally links to application pages should be specified as
// an url to the application document with a hash that is either
// the site relative path or id to the page. But some of the
// internal code that dynamically generates sub-pages for nested
// lists and select dialogs, just write a hash in the link they
// create. This means the actual URL path is based on whatever
// the current value of the base tag is at the time this code
// is called. For now we are just assuming that any url with a
// hash in it is an application page reference.
if ( href.search( "#" ) != -1 ) {
href = href.replace( /[^#]*#/, "" );
if ( !href ) {
//link was an empty hash meant purely
//for interaction, so we ignore it.
event.preventDefault();
return;
} else if ( path.isPath( href ) ) {
//we have apath so make it the href we want to load.
href = path.makeUrlAbsolute( href, baseUrl );
} else {
//we have a simple id so use the documentUrl as its base.
href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash );
}
}
// Should we handle this link, or let the browser deal with it?
var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ),
// Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
// requests if the document doing the request was loaded via the file:// protocol.
// This is usually to allow the application to "phone home" and fetch app specific
// data. We normally let the browser handle external/cross-domain urls, but if the
// allowCrossDomainPages option is true, we will allow cross-domain http/https
// requests to go through our page loading logic.
isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ),
//check for protocol or rel and its not an embedded page
//TODO overlap in logic from isExternal, rel=external check should be
// moved into more comprehensive isExternalLink
isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
$activeClickedLink = $link.closest( ".ui-btn" );
if( isExternal ) {
httpCleanup();
//use default click handling
return;
}
//use ajax
var transition = $link.jqmData( "transition" ),
direction = $link.jqmData( "direction" ),
reverse = ( direction && direction === "reverse" ) ||
// deprecated - remove by 1.0
$link.jqmData( "back" ),
//this may need to be more specific as we use data-rel more
role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
$.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } );
event.preventDefault();
});
//hashchange event handler
$window.bind( "hashchange", function( e, triggered ) {
//find first page via hash
var to = path.stripHash( location.hash ),
//transition is false if it's the first page, undefined otherwise (and may be overridden by default)
transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined;
//if listening is disabled (either globally or temporarily), or it's a dialog hash
if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
urlHistory.ignoreNextHashChange = false;
return;
}
// special case for dialogs
if( urlHistory.stack.length > 1 &&
to.indexOf( dialogHashKey ) > -1 ) {
// If current active page is not a dialog skip the dialog and continue
// in the same direction
if(!$.mobile.activePage.is( ".ui-dialog" )) {
//determine if we're heading forward or backward and continue accordingly past
//the current dialog
urlHistory.directHashChange({
currentUrl: to,
isBack: function() { window.history.back(); },
isForward: function() { window.history.forward(); }
});
// prevent changepage
return;
} else {
var setTo = function() { to = $.mobile.urlHistory.getActive().page; };
// if the current active page is a dialog and we're navigating
// to a dialog use the dialog objected saved in the stack
urlHistory.directHashChange({ currentUrl: to, isBack: setTo, isForward: setTo });
}
}
//if to is defined, load it
if ( to ) {
to = ( typeof to === "string" && !path.isPath( to ) ) ? ( '#' + to ) : to;
$.mobile.changePage( to, { transition: transition, changeHash: false, fromHashChange: true } );
}
//there's no hash, go to the first page in the dom
else {
$.mobile.changePage( $.mobile.firstPage, { transition: transition, changeHash: false, fromHashChange: true } );
}
});
//set page min-heights to be device specific
$( document ).bind( "pageshow", resetActivePageHeight );
$( window ).bind( "throttledresize", resetActivePageHeight );
};//_registerInternalEvents callback
})( jQuery );

View file

@ -15,6 +15,7 @@
<script src="../../../js/jquery.mobile.init.js"></script>
<link rel="stylesheet" href="../../../themes/default" />
<link rel="stylesheet" href="../../../external/qunit.css"/>
</head>
<body>

View file

@ -72,6 +72,17 @@
ok($("html").hasClass("ui-mobile"));
});
test( "useFastClick is configurable via mobileinit", function(){
$(document).bind( "mobileinit", function(){
$.mobile.useFastClick = false;
});
$.testHelper.reloadLib(libName);
ok( $.mobile.useFastClick == false );
$.mobile.useFastClick = true;
});