diff --git a/js/jquery.mobile.navigation.js b/js/jquery.mobile.navigation.js
index 17d6a537..b051e21a 100644
--- a/js/jquery.mobile.navigation.js
+++ b/js/jquery.mobile.navigation.js
@@ -1,9 +1,9 @@
/*
-* jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt,
+* jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt,
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
-*/
+*/
(function($, undefined ) {
//define vars for interal use
@@ -13,7 +13,7 @@
//url path helpers for use in relative url management
path = {
-
+
//get path from current hash, or from a file path
get: function( newPath ){
if( newPath == undefined ){
@@ -28,48 +28,48 @@
}
return newPath.join('/') + (newPath.length ? '/' : '');
},
-
- //return the substring of a filepath before the sub-page key, for making a server request
+
+ //return the substring of a filepath before the sub-page key, for making a server request
getFilePath: function( path ){
var splitkey = '&' + $.mobile.subPageUrlKey;
return path && path.indexOf( splitkey ) > -1 ? path.split( splitkey )[0] : path;
},
-
+
set: function( path, disableListening){
if(disableListening) { hashListener = false; }
location.hash = path;
},
-
+
//location pathname from intial directory request
origin: '',
-
+
setOrigin: function(){
path.origin = path.get( location.protocol + '//' + location.host + location.pathname );
}
},
-
+
//base element management, defined depending on dynamic base tag support
base = $.support.dynamicBaseTag ? {
-
+
//define base element, for use in routing asset urls that are referenced in Ajax-requested markup
element: $("", { href: path.origin }).prependTo( $head ),
-
+
//set the generated BASE element's href attribute to a new page's base path
set: function( href ){
base.element.attr('href', path.origin + path.get( href ));
},
-
+
//set the generated BASE element's href attribute to a new page's base path
reset: function(){
base.element.attr('href', path.origin );
}
-
- } : undefined,
-
+ } : undefined,
+
+
//will be defined when a link is clicked and given an active class
$activeClickedLink = null,
-
+
//array of pages that are visited during a single page load
//length will grow as pages are visited, and shrink as "back" link/button is clicked
//each item has a url (string matches ID), and transition (saved for reuse when "back" link/button is clicked)
@@ -77,23 +77,23 @@
url: location.hash.replace( /^#/, "" ),
transition: undefined
} ],
-
+
//define first selector to receive focus when a page is shown
focusable = "[tabindex],a,button:visible,select:visible,input",
-
+
//contains role for next page, if defined on clicked link via data-rel
nextPageRole = null,
-
+
//enable/disable hashchange event listener
//toggled internally when location.hash is updated to match the url of a successful page load
- hashListener = true;
-
+ hashListener = true;
+
//set location pathname from intial directory request
path.setOrigin();
-/*
+/*
internal utility functions
---------------------------------------*/
+--------------------------------------*/
//direct focus to the page title, or otherwise first focusable element
@@ -106,7 +106,7 @@
page.find( focusable ).eq(0).focus();
}
};
-
+
//remove active classes after page transition or error
function removeActiveLinkClass( forceRemoval ){
if( !!$activeClickedLink && (!$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval )){
@@ -115,7 +115,7 @@
$activeClickedLink = null;
};
-
+
//animation complete callback
$.fn.animationComplete = function( callback ){
if($.support.cssTransitions){
@@ -124,7 +124,7 @@
else{
callback();
}
- };
+ };
@@ -132,13 +132,13 @@
//update location.hash, with or without triggering hashchange event
$.mobile.updateHash = path.set;
-
- //url stack, useful when plugins need to be aware of previous pages viewed
- $.mobile.urlStack = urlStack;
- // changepage function
+ //url stack, useful when plugins need to be aware of previous pages viewed
+ $.mobile.urlStack = urlStack;
+
+ // changepage function
$.mobile.changePage = function( to, transition, back, changeHash){
-
+
//from is always the currently viewed page
var toIsArray = $.type(to) === "array",
from = toIsArray ? to[0] : $.mobile.activePage,
@@ -150,14 +150,14 @@
duplicateCachedPage = null,
back = (back !== undefined) ? back : ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ),
transition = (transition !== undefined) ? transition : $.mobile.defaultTransition;
-
+
//If we are trying to transition to the same page that we are currently on ignore the request.
if(urlStack.length > 1 && url === urlStack[urlStack.length -1].url && !toIsArray ) {
return;
}
-
-
+
+
if( $.type(to) === "object" && to.url ){
url = to.url,
data = to.data,
@@ -169,16 +169,16 @@
data = undefined;
}
}
-
-
-
-
+
+
+
+
//reset base to pathname for new request
if(base){ base.reset(); }
-
+
//kill the keyboard
$( window.document.activeElement ).add(':focus').blur();
-
+
// if the new href is the same as the previous one
if ( back ) {
var pop = urlStack.pop();
@@ -188,58 +188,78 @@
} else {
urlStack.push({ url: url, transition: transition });
}
-
+
//function for transitioning between two existing pages
function transitionPages() {
-
+
//get current scroll distance
- var currScroll = $window.scrollTop();
-
+ var currScroll = $window.scrollTop(),
+ perspectiveTransitions = ["flip"],
+ pageContainerClasses = [];
+
//set as data for returning to that spot
from.data('lastScroll', currScroll);
-
+
//trigger before show/hide events
from.data("page")._trigger("beforehide", {nextPage: to});
to.data("page")._trigger("beforeshow", {prevPage: from});
-
+
function loadComplete(){
$.mobile.pageLoading( true );
-
+
reFocus( to );
-
+
if( changeHash !== false && url ){
path.set(url, (back !== true));
}
removeActiveLinkClass();
-
+
//if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
if( duplicateCachedPage != null ){
duplicateCachedPage.remove();
}
-
+
//jump to top or prev scroll, if set
$.mobile.silentScroll( to.data( 'lastScroll' ) );
-
- //trigger show/hide events, allow preventing focus change through return false
+
+ //trigger show/hide events, allow preventing focus change through return false
from.data("page")._trigger("hide", null, {nextPage: to});
if( to.data("page")._trigger("show", null, {prevPage: from}) !== false ){
$.mobile.activePage = to;
}
};
-
- if(transition && (transition !== 'none')){
- $.mobile.pageContainer.addClass('ui-mobile-viewport-transitioning');
+
+ function addContainerClass(className){
+ $.mobile.pageContainer.addClass(className);
+ pageContainerClasses.push(className);
+ };
+
+ function removeContainerClasses(){
+ $.mobile
+ .pageContainer
+ .removeClass(pageContainerClasses.join(" "));
+
+ pageContainerClasses = [];
+ };
+
+ if(transition && (transition !== 'none')){
+ if( perspectiveTransitions.indexOf(transition) >= 0 ){
+ addContainerClass('ui-mobile-viewport-perspective');
+ }
+
+ addContainerClass('ui-mobile-viewport-transitioning');
+
// animate in / out
from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
to.addClass( $.mobile.activePageClass + " " + transition +
" in " + ( back ? "reverse" : "" ) );
-
+
// callback - remove classes, etc
to.animationComplete(function() {
from.add( to ).removeClass("out in reverse " + transition );
from.removeClass( $.mobile.activePageClass );
loadComplete();
- $.mobile.pageContainer.removeClass('ui-mobile-viewport-transitioning');
+ removeContainerClasses();
});
}
else{
@@ -248,10 +268,10 @@
loadComplete();
}
};
-
+
//shared page enhancements
function enhancePage(){
-
+
//set next page role, if defined
if ( nextPageRole || to.data('role') == 'dialog' ) {
changeHash = false;
@@ -260,8 +280,8 @@
nextPageRole = null;
}
}
-
- //run page plugin
+
+ //run page plugin
to.page();
};
@@ -273,26 +293,26 @@
else{ //find base url of element, if avail
var toID = to.attr('data-url'),
toIDfileurl = path.getFilePath(toID);
-
+
if(toID != toIDfileurl){
fileUrl = toIDfileurl;
- }
+ }
}
-
+
// find the "to" page, either locally existing in the dom or by creating it through ajax
if ( to.length && !isFormRequest ) {
if( fileUrl && base ){
base.set( fileUrl );
- }
+ }
enhancePage();
transitionPages();
- } else {
-
+ } else {
+
//if to exists in DOM, save a reference to it in duplicateCachedPage for removal after page change
if( to.length ){
duplicateCachedPage = to;
}
-
+
$.mobile.pageLoading();
$.ajax({
@@ -301,22 +321,22 @@
data: data,
success: function( html ) {
if(base){ base.set(fileUrl); }
-
+
var all = $("
");
//workaround to allow scripts to execute when included in page divs
all.get(0).innerHTML = html;
to = all.find('[data-role="page"], [data-role="dialog"]').first();
-
+
//rewrite src and href attrs to use a base url
if( !$.support.dynamicBaseTag ){
var newPath = path.get( fileUrl );
to.find('[src],link[href]').each(function(){
var thisAttr = $(this).is('[href]') ? 'href' : 'src',
thisUrl = $(this).attr(thisAttr);
-
+
//if full path exists and is same, chop it - helps IE out
thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
-
+
if( !/^(\w+:|#|\/)/.test(thisUrl) ){
$(this).attr(thisAttr, newPath + thisUrl);
}
@@ -326,7 +346,7 @@
to
.attr( "data-url", fileUrl )
.appendTo( $.mobile.pageContainer );
-
+
enhancePage();
transitionPages();
},
@@ -347,26 +367,26 @@
};
-
-/* Event Bindings - hashchange, submit, and click */
-
+
+/* Event Bindings - hashchange, submit, and click */
+
//bind to form submit events, handle with Ajax
$('form').live('submit', function(event){
if( !$.mobile.ajaxFormsEnabled ){ return; }
-
+
var type = $(this).attr("method"),
- url = $(this).attr( "action" ).replace( location.protocol + "//" + location.host, "");
-
+ url = $(this).attr( "action" ).replace( location.protocol + "//" + location.host, "");
+
//external submits use regular HTTP
if( /^(:?\w+:)/.test( url ) ){
return;
- }
-
+ }
+
//if it's a relative href, prefix href with base url
if( url.indexOf('/') && url.indexOf('#') !== 0 ){
url = path.get() + url;
}
-
+
$.mobile.changePage({
url: url,
type: type,
@@ -377,12 +397,12 @@
true
);
event.preventDefault();
- });
-
-
+ });
+
+
//click routing - direct to HTTP or Ajax, accordingly
$( "a" ).live( "click", function(event) {
-
+
if( !$.mobile.ajaxLinksEnabled ){ return; }
var $this = $(this),
//get href, remove same-domain protocol and host
@@ -396,13 +416,13 @@
//for links created purely for interaction - ignore
return false;
}
-
+
$activeClickedLink = $this.closest( ".ui-btn" ).addClass( $.mobile.activeBtnClass );
-
+
if( external || !$.mobile.ajaxLinksEnabled ){
//remove active link class if external
removeActiveLinkClass(true);
-
+
//deliberately redirect, in case click was triggered
if( target ){
window.open(href);
@@ -411,41 +431,41 @@
location.href = href;
}
}
- else {
+ else {
//use ajax
var transition = $this.data( "transition" ),
back = $this.data( "back" );
-
- nextPageRole = $this.attr( "data-rel" );
-
+
+ nextPageRole = $this.attr( "data-rel" );
+
//if it's a relative href, prefix href with base url
if( href.indexOf('/') && href.indexOf('#') !== 0 ){
href = path.get() + href;
}
-
+
href.replace(/^#/,'');
-
- $.mobile.changePage(href, transition, back);
+
+ $.mobile.changePage(href, transition, back);
}
event.preventDefault();
});
-
-
-
- //hashchange event handler
+
+
+
+ //hashchange event handler
$window.bind( "hashchange", function(e, triggered) {
- if( !hashListener ){
+ if( !hashListener ){
hashListener = true;
- return;
- }
-
+ return;
+ }
+
if( $(".ui-page-active").is("[data-role=" + $.mobile.nonHistorySelectors + "]") ){
return;
}
-
+
var to = location.hash,
transition = triggered ? false : undefined;
-
+
//if to is defined, use it
if ( to ){
$.mobile.changePage( to, transition, undefined, false);
@@ -460,7 +480,7 @@
$.mobile.startPage.trigger("pagebeforeshow", {prevPage: $('')});
$.mobile.startPage.addClass( $.mobile.activePageClass );
$.mobile.pageLoading( true );
-
+
if( $.mobile.startPage.trigger("pageshow", {prevPage: $('')}) !== false ){
reFocus($.mobile.startPage);
}
diff --git a/tests/unit/navigation/index.html b/tests/unit/navigation/index.html
new file mode 100644
index 00000000..16b6c952
--- /dev/null
+++ b/tests/unit/navigation/index.html
@@ -0,0 +1,57 @@
+
+
+
+
+ jQuery Mobile Navigation Test Suite
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+