mirror of
https://github.com/Hopiu/jquery-mobile.git
synced 2026-04-28 09:54:44 +00:00
Refactored urlStack and updated dialog and page plugins to match. jQuery Mobile's internal history now attempts to follow the history menu when urls change from hashchange, even for changes that go multiple steps forward or back. The internal history stack is now pruned based on whether a user goes back and then changes direction, whereas before a back-button click would result in a pop off the history. Instead we now maintain an active index number in the history stack, which allows us to maintain references to transitions that are saved on pages reached through clicking the forward button as well. This fixes #636.
In the process, some other small changes should be noted: urlStack is now urlHistory, a hash of methods and properties used for history stack management (stack, activeIndex, getActive, getPrev, getNext, addNew, clearForward, and listening Enabled). All these are documented inline and exposed on $.mobile.urlHistory (I'm not sure these will be publicly documented, but just exposed internally for plugins for now). $.changePage has two argument changes: the "back" argument is now called "reverse"; this results in no change from an end-user standpoint, but reflects the fact that it only reverses the direction of a transition without affecting the internal history stack, and second, a new argument at the end defines whether changePage was called from a hashChange which makes that url open to history menu guessing.
This commit is contained in:
parent
c63d570b60
commit
9b57c46349
4 changed files with 133 additions and 62 deletions
|
|
@ -40,7 +40,7 @@
|
|||
</dd>
|
||||
|
||||
<dd><code>transition</code> (<em>string</em>, examples: "pop", "slide"," "none")</dd>
|
||||
<dd><code>back</code> (<em>boolean</em>, default: false). True will cause a reverse-direction transition.</dd>
|
||||
<dd><code>reverse</code> (<em>boolean</em>, default: false). True will cause a reverse-direction transition.</dd>
|
||||
<dd><code>changeHash</code> (<em>boolean</em>, default: true). Update the hash to the to page's URL when page change is complete.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ $.widget( "mobile.dialog", $.mobile.widget, {
|
|||
_create: function(){
|
||||
var self = this,
|
||||
$el = self.element,
|
||||
$prevPage = $.mobile.activePage,
|
||||
$closeBtn = $('<a href="#" data-icon="delete" data-iconpos="notext">Close</a>'),
|
||||
|
||||
dialogClickHandler = function(e){
|
||||
|
|
@ -40,8 +39,15 @@ $.widget( "mobile.dialog", $.mobile.widget, {
|
|||
|
||||
this.element
|
||||
.bind("pageshow",function(){
|
||||
self.thisPage = $.mobile.urlHistory.getActive();
|
||||
self.prevPage = $.mobile.urlHistory.getPrev();
|
||||
return false;
|
||||
})
|
||||
.bind("pagehide", function(){
|
||||
$.mobile.urlHistory.stack = $.mobile.urlHistory.stack.slice(0,$.mobile.urlHistory.stack.length-2);
|
||||
$.mobile.urlHistory.activeIndex = $.mobile.urlHistory.stack.length -1;
|
||||
})
|
||||
|
||||
//add ARIA role
|
||||
.attr("role","dialog")
|
||||
.addClass('ui-page ui-dialog ui-body-a')
|
||||
|
|
@ -56,11 +62,15 @@ $.widget( "mobile.dialog", $.mobile.widget, {
|
|||
.last()
|
||||
.addClass('ui-corner-bottom ui-overlay-shadow');
|
||||
|
||||
|
||||
|
||||
$(window).bind('hashchange',function(){
|
||||
$.mobile.urlHistory.listeningEnabled = true;
|
||||
if( $el.is('.ui-page-active') ){
|
||||
self.close();
|
||||
$el.bind('pagehide',function(){
|
||||
$.mobile.updateHash( $prevPage.attr('data-url'), true);
|
||||
$.mobile.urlHistory.listeningEnabled = false;
|
||||
$.mobile.updateHash( self.prevPage.url );
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -68,7 +78,7 @@ $.widget( "mobile.dialog", $.mobile.widget, {
|
|||
},
|
||||
|
||||
close: function(){
|
||||
$.mobile.changePage([this.element, $.mobile.activePage], undefined, true, true );
|
||||
$.mobile.changePage([this.element, $.mobile.activePage], this.thisPage.transition, true, true, true );
|
||||
}
|
||||
});
|
||||
})( jQuery );
|
||||
|
|
@ -28,11 +28,8 @@
|
|||
return path && path.indexOf( splitkey ) > -1 ? path.split( splitkey )[0] : path;
|
||||
},
|
||||
|
||||
//set location hash to path, optionally disable hash listening
|
||||
set: function( path, disableListening){
|
||||
if( disableListening ) {
|
||||
hashListener = false;
|
||||
}
|
||||
//set location hash to path
|
||||
set: function( path ){
|
||||
location.hash = path;
|
||||
},
|
||||
|
||||
|
|
@ -76,24 +73,56 @@
|
|||
|
||||
//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)
|
||||
urlStack = [ {
|
||||
url: location.hash.replace( /^#/, "" ),
|
||||
transition: undefined
|
||||
} ],
|
||||
|
||||
//urlHistory is purely here to make guesses at whether the back or forward button was clicked
|
||||
//and provide an appropriate transition
|
||||
urlHistory = {
|
||||
//array of pages that are visited during a single page load. each has a url and optional transition
|
||||
stack: [],
|
||||
|
||||
//maintain an index number for the active page in the stack
|
||||
activeIndex: 0,
|
||||
|
||||
//get active
|
||||
getActive: function(){
|
||||
return urlHistory.stack[ urlHistory.activeIndex ];
|
||||
},
|
||||
|
||||
getPrev: function(){
|
||||
return urlHistory.stack[ urlHistory.activeIndex - 1 ];
|
||||
},
|
||||
|
||||
getNext: function(){
|
||||
return urlHistory.stack[ urlHistory.activeIndex + 1 ];
|
||||
},
|
||||
|
||||
// addNew is used whenever a new page is added
|
||||
addNew: function( url, transition ){
|
||||
//if there's forward history, wipe it
|
||||
if( urlHistory.getNext() ){
|
||||
urlHistory.clearForward();
|
||||
}
|
||||
|
||||
urlHistory.stack.push( {url : url, transition: transition } );
|
||||
|
||||
urlHistory.activeIndex = urlHistory.stack.length - 1;
|
||||
},
|
||||
|
||||
//wipe urls ahead of active index
|
||||
clearForward: function(){
|
||||
urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
|
||||
},
|
||||
|
||||
//enable/disable hashchange event listener
|
||||
//toggled internally when location.hash is updated to match the url of a successful page load
|
||||
listeningEnabled: true
|
||||
},
|
||||
|
||||
//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;
|
||||
nextPageRole = null;
|
||||
|
||||
//existing base tag?
|
||||
var $base = $head.children("base"),
|
||||
|
|
@ -116,7 +145,7 @@
|
|||
//the href is a document relative url
|
||||
docBase = docLocation + href;
|
||||
//XXX: we need some code here to calculate the final path
|
||||
// just in case the docBase contains up-level (../) references.
|
||||
// just in case the docBase contains up-level (../) references.
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -200,29 +229,67 @@
|
|||
$.mobile.base = base;
|
||||
|
||||
//url stack, useful when plugins need to be aware of previous pages viewed
|
||||
$.mobile.urlStack = urlStack;
|
||||
|
||||
//TODO: deprecate this one
|
||||
$.mobile.urlstack = urlHistory.stack;
|
||||
|
||||
//history stack
|
||||
$.mobile.urlHistory = urlHistory;
|
||||
|
||||
// changepage function
|
||||
$.mobile.changePage = function( to, transition, back, changeHash){
|
||||
// TODO : consider moving args to an object hash
|
||||
$.mobile.changePage = function( to, transition, reverse, changeHash, fromHashChange ){
|
||||
|
||||
//from is always the currently viewed page
|
||||
var toIsArray = $.type(to) === "array",
|
||||
toIsObject = $.type(to) === "object",
|
||||
from = toIsArray ? to[0] : $.mobile.activePage,
|
||||
to = toIsArray ? to[1] : to,
|
||||
url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null,
|
||||
url = fileUrl = $.type(to) === "string" ? path.stripHash( to ) : "",
|
||||
data = undefined,
|
||||
type = 'get',
|
||||
isFormRequest = false,
|
||||
duplicateCachedPage = null,
|
||||
back = (back !== undefined) ? back : ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url );
|
||||
|
||||
//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 ) {
|
||||
currPage = urlHistory.getActive(),
|
||||
back = false,
|
||||
forward = false;
|
||||
|
||||
// If we are trying to transition to the same page that we are currently on ignore the request.
|
||||
// an illegal same page request is defined by the current page being the same as the url, as long as there's history
|
||||
// and to is not an array or object (those are allowed to be "same")
|
||||
if( currPage && urlHistory.stack.length > 1 && currPage.url === url && !toIsArray && !toIsObject ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the changePage was sent from a hashChange event
|
||||
// guess if it came from the history menu
|
||||
if( fromHashChange ){
|
||||
|
||||
// check if url is in history and if it's ahead or behind current page
|
||||
$.each( urlHistory.stack, function( i ){
|
||||
//if the url is in the stack, it's a forward or a back
|
||||
if( this.url == url ){
|
||||
urlIndex = i;
|
||||
//define back and forward by whether url is older or newer than current page
|
||||
back = i < urlHistory.activeIndex;
|
||||
//forward set to opposite of back
|
||||
forward = !back;
|
||||
//reset activeIndex to this one
|
||||
urlHistory.activeIndex = i;
|
||||
}
|
||||
});
|
||||
|
||||
//if it's a back, use reverse animation
|
||||
if( back ){
|
||||
reverse = true;
|
||||
transition = transition || currPage.transition;
|
||||
}
|
||||
else if ( forward ){
|
||||
transition = transition || urlHistory.getActive().transition;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( $.type(to) === "object" && to.url ){
|
||||
if( toIsObject && to.url ){
|
||||
url = to.url,
|
||||
data = to.data,
|
||||
type = to.type,
|
||||
|
|
@ -246,25 +313,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// if the new href is the same as the previous one
|
||||
if ( back ) {
|
||||
var pop = urlStack.pop();
|
||||
|
||||
// prefer the explicitly set transition
|
||||
if( pop && !transition ){
|
||||
transition = pop.transition;
|
||||
}
|
||||
|
||||
// ensure a transition has been set where pop is undefined
|
||||
defaultTransition();
|
||||
} else {
|
||||
// If no transition has been passed
|
||||
defaultTransition();
|
||||
|
||||
// push the url and transition onto the stack
|
||||
urlStack.push({ url: url, transition: transition });
|
||||
}
|
||||
|
||||
//function for transitioning between two existing pages
|
||||
function transitionPages() {
|
||||
|
||||
|
|
@ -286,8 +334,18 @@
|
|||
reFocus( to );
|
||||
|
||||
if( changeHash !== false && url ){
|
||||
path.set(url, (back !== true));
|
||||
if( !back ){
|
||||
urlHistory.listeningEnabled = false;
|
||||
}
|
||||
path.set( url );
|
||||
urlHistory.listeningEnabled = true;
|
||||
}
|
||||
|
||||
//add page to history stack if it's not back or forward, or a dialog
|
||||
if( !back && !forward ){
|
||||
urlHistory.addNew( url, transition );
|
||||
}
|
||||
|
||||
removeActiveLinkClass();
|
||||
|
||||
//jump to top or prev scroll, if set
|
||||
|
|
@ -326,9 +384,9 @@
|
|||
addContainerClass('ui-mobile-viewport-transitioning');
|
||||
|
||||
// animate in / out
|
||||
from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
|
||||
from.addClass( transition + " out " + ( reverse ? "reverse" : "" ) );
|
||||
to.addClass( $.mobile.activePageClass + " " + transition +
|
||||
" in " + ( back ? "reverse" : "" ) );
|
||||
" in " + ( reverse ? "reverse" : "" ) );
|
||||
|
||||
// callback - remove classes, etc
|
||||
to.animationComplete(function() {
|
||||
|
|
@ -374,6 +432,9 @@
|
|||
fileUrl = toIDfileurl;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure a transition has been set where pop is undefined
|
||||
defaultTransition();
|
||||
|
||||
// find the "to" page, either locally existing in the dom or by creating it through ajax
|
||||
if ( to.length && !isFormRequest ) {
|
||||
|
|
@ -512,7 +573,7 @@
|
|||
else {
|
||||
//use ajax
|
||||
var transition = $this.data( "transition" ),
|
||||
back = $this.data( "back" );
|
||||
reverse = $this.data( "back" );
|
||||
|
||||
nextPageRole = $this.attr( "data-rel" );
|
||||
|
||||
|
|
@ -523,7 +584,7 @@
|
|||
|
||||
url = path.stripHash( url );
|
||||
|
||||
$.mobile.changePage( url, transition, back);
|
||||
$.mobile.changePage( url, transition, reverse);
|
||||
}
|
||||
event.preventDefault();
|
||||
});
|
||||
|
|
@ -532,8 +593,7 @@
|
|||
|
||||
//hashchange event handler
|
||||
$window.bind( "hashchange", function(e, triggered) {
|
||||
if( !hashListener ){
|
||||
hashListener = true;
|
||||
if( !urlHistory.listeningEnabled ){
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -541,20 +601,21 @@
|
|||
return;
|
||||
}
|
||||
|
||||
var to = location.hash,
|
||||
var to = path.stripHash( location.hash ),
|
||||
transition = triggered ? false : undefined;
|
||||
|
||||
//if to is defined, use it
|
||||
if ( to ){
|
||||
$.mobile.changePage( to, transition, undefined, false);
|
||||
$.mobile.changePage( to, transition, undefined, false, true );
|
||||
}
|
||||
//there's no hash, the active page is not the start page, and it's not manually triggered hashchange
|
||||
//we probably backed out to the first page visited
|
||||
else if( $.mobile.activePage.length && $.mobile.startPage[0] !== $.mobile.activePage[0] && !triggered ) {
|
||||
$.mobile.changePage( $.mobile.startPage, transition, true, false );
|
||||
$.mobile.changePage( $.mobile.startPage, transition, true, false, true );
|
||||
}
|
||||
//probably the first page - show it
|
||||
else{
|
||||
urlHistory.addNew( "" );
|
||||
$.mobile.startPage.trigger("pagebeforeshow", {prevPage: $('')});
|
||||
$.mobile.startPage.addClass( $.mobile.activePageClass );
|
||||
$.mobile.pageLoading( true );
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ $.widget( "mobile.page", $.mobile.widget, {
|
|||
|
||||
// auto-add back btn on pages beyond first view
|
||||
if ( o.addBackBtn && role === "header" &&
|
||||
($.mobile.urlStack.length > 1 || $(".ui-page").length > 1) &&
|
||||
($.mobile.urlHistory.getPrev() || $(".ui-page").length > 1) &&
|
||||
!leftbtn && $this.data( "backbtn" ) !== false ) {
|
||||
|
||||
$( "<a href='#' class='ui-btn-left' data-icon='arrow-l'>"+ o.backBtnText +"</a>" )
|
||||
|
|
|
|||
Loading…
Reference in a new issue