From 3ba388e63fce463baa0c45a358aa62281d441aa9 Mon Sep 17 00:00:00 2001 From: Kin Blas Date: Fri, 24 Jun 2011 16:34:36 -0700 Subject: [PATCH] - Moved the global link hijacking callback back to "click". - Refactored out the href/url calculation code from our hijacking callback into a utility function getAjaxUrlForLink(). - Added a touchend handler so we can catch link clicks and call preventDefault() to stop the iOS location bar from dropping down. - Removed the $.mobile.useFastClick option. --- js/jquery.mobile.core.js | 6 +- js/jquery.mobile.navigation.js | 146 +++++++++++++++++++-------------- 2 files changed, 84 insertions(+), 68 deletions(-) diff --git a/js/jquery.mobile.core.js b/js/jquery.mobile.core.js index 11487df5f15..4b511215b94 100644 --- a/js/jquery.mobile.core.js +++ b/js/jquery.mobile.core.js @@ -33,11 +33,6 @@ //automatically handle clicks and form submissions through Ajax, when same-domain ajaxEnabled: true, - //When enabled, clicks and taps that result in Ajax page changes will happen slightly sooner on touch devices. - //Also, it will prevent the address bar from appearing on platforms like iOS during page transitions. - //This option has no effect on non-touch devices, but enabling it may interfere with jQuery plugins that bind to click events - useFastClick: true, - //automatically load and show pages based on location.hash hashListeningEnabled: true, @@ -144,6 +139,7 @@ return this.removeData( $.mobile.nsNormalize(prop) ); }; + $.jqmRemoveData = function( elem, prop ){ return $.removeData( elem, $.mobile.nsNormalize(prop) ); }; diff --git a/js/jquery.mobile.navigation.js b/js/jquery.mobile.navigation.js index 08c1a81fbf6..f2ab0be3c53 100644 --- a/js/jquery.mobile.navigation.js +++ b/js/jquery.mobile.navigation.js @@ -371,8 +371,13 @@ } //remove active classes after page transition or error - function removeActiveLinkClass( forceRemoval ) { - if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) { + function removeActiveLinkClass( forceRemoval, delay ) { + if ( delay ) { + setTimeout( function() { removeActiveLinkClass( forceRemoval ); }, delay ); + return; + } + + if( !!$activeClickedLink && ( forceRemoval || !$activeClickedLink.closest( '.ui-page-active' ).length ) ) { $activeClickedLink.removeClass( $.mobile.activeBtnClass ); } $activeClickedLink = null; @@ -582,6 +587,9 @@ // within the same domain as the document base, it is the site relative // path. For cross-domain pages (Phone Gap only) the entire absolute Url // used to load the page. + + + dataUrl = path.convertUrlToDataUrl( absUrl ); // Make sure we have a pageContainer to work with. @@ -988,94 +996,106 @@ event.preventDefault(); }); + function getAjaxUrlForLink( $link ) { + var url = undefined; + if ( $.mobile.ajaxEnabled ) { + var baseUrl = getClosestBaseUrl( $link ), + + //get href, if defined, otherwise default to empty hash + url = 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 ( url.search( "#" ) != -1 ) { + url = url.replace( /[^#]*#/, "" ); + if ( url ) { + if ( path.isPath( url ) ) { + //we have apath so make it the href we want to load. + url = path.makeUrlAbsolute( href, baseUrl ); + } else { + //we have a simple id so use the documentUrl as its base. + href = path.makeUrlAbsolute( "#" + url, documentUrl.hrefNoHash ); + } + } + } + + 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:" && url.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 || ( !isCrossDomainPageLoad) && path.isExternal( url ); + + if ( isExternal ) { + url = undefined; + } + } + return url; + } + + $( document ).bind( "touchend", function( event ) { + var link = findClosestLink( event.target ); + if ( link ) { + var url = getAjaxUrlForLink( $( link ) ); + if ( url && url.replace( /[^#]*#/, "" ) ) { + event.preventDefault(); + } + } + }); + //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 ); + $activeClickedLink = $( 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 - $( document ).bind( $.mobile.useFastClick ? "vclick" : "click", function( event ) { + $( document ).bind( "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 ); - }; + var $link = $( link ); //if there's a data-rel=back attr, go back in history if( $link.is( ":jqmData(rel='back')" ) ) { + removeActiveLinkClass( true, 200 ); 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 ) { + var href = getAjaxUrlForLink( $link ), + hashOrUrl = href && href.replace( /[^#]*#/, "" ); + + if ( !href || !hashOrUrl ) { + //use default click handling + if (! hashOrUrl ) { //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 + removeActiveLinkClass( true, 200 ); return; }