This repository has been archived by the owner on Oct 8, 2021. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
include the navigate method to do history tracking and state inclusio…
…n for hashchange
- Loading branch information
1 parent
ddcf7c7
commit 4677f5a
Showing
5 changed files
with
291 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude); | ||
//>>description: placeholder | ||
//>>label: AJAX Navigation System | ||
//>>group: Navigation | ||
define([ | ||
"jquery", | ||
"./../jquery.mobile.core", | ||
"./../jquery.mobile.support", | ||
"./events/navigate", | ||
"./path"], function( $ ) { | ||
//>>excludeEnd("jqmBuildExclude"); | ||
|
||
(function( $, undefined ) { | ||
var path = $.mobile.path, history; | ||
|
||
// TODO consider queueing navigation activity until previous activities have completed | ||
// so that end users don't have to think about it. Punting for now | ||
$.navigate = function( url, data ) { | ||
var href, state, | ||
// firefox auto decodes the url when using location.hash but not href | ||
hash = path.parseUrl(url).hash, | ||
isPath = path.isPath( hash ), | ||
resolutionUrl = isPath ? path.getLocation() : $.mobile.getDocumentUrl(); | ||
|
||
// #/foo/bar.html => /foo/bar.html | ||
// #foo => #foo | ||
hash = isPath ? hash.replace( "#", "" ) : hash; | ||
|
||
// make the hash abolute with the current href | ||
href = path.makeUrlAbsolute( hash, resolutionUrl ); | ||
|
||
if ( isPath ) { | ||
href = path.resetUIKeys( href ); | ||
} | ||
|
||
state = $.extend( data, { | ||
url: url, | ||
hash: hash, | ||
title: document.title | ||
}); | ||
|
||
// NOTE we currently _leave_ the appended hash in the hash in the interest | ||
// of seeing what happens and if we can support that before the hash is | ||
// pushed down | ||
|
||
// set the hash to be squashed by replace state or picked up by | ||
// the navigation special event | ||
window.location.hash = url; | ||
history.ignoreNextHashChange = true; | ||
|
||
if( $.support.pushState ) { | ||
// replace the current url with the new href and store the state | ||
// Note that in some cases we might be replacing an url with the | ||
// same url. We do this anyways because we need to make sure that | ||
// all of our history entries have a state object associated with | ||
// them. This allows us to work around the case where $.mobile.back() | ||
// is called to transition from an external page to an embedded page. | ||
// In that particular case, a hashchange event is *NOT* generated by the browser. | ||
// Ensuring each history entry has a state object means that onPopState() | ||
// will always trigger our hashchange callback even when a hashchange event | ||
// is not fired. | ||
window.history.replaceState( state, document.title, href ); | ||
} | ||
|
||
// record the history entry so that the information can be included | ||
// in hashchange event driven navigate events in a similar fashion to | ||
// the state that's provided by popstate | ||
|
||
history.add( url, state ); | ||
}; | ||
|
||
// NOTE must bind before `navigate` special event hashchange binding otherwise the | ||
// navigation data won't be attached to the hashchange event in time for those | ||
// bindings to attach it to the `navigate` special event | ||
// TODO add a check here that `hashchange.navigate` is bound already otherwise it's | ||
// broken (exception?) | ||
$( window ).bind( "hashchange.history", function( event ) { | ||
// If pushstate is supported the state will be included in the popstate event | ||
// data and appended to the navigate event. Late check here for late settings (eg tests) | ||
if( $.support.pushState ) { | ||
return; | ||
} | ||
|
||
// If the hashchange has been explicitly ignored or we have no history at | ||
// this point skip the history managment and the addition of the history | ||
// entry to the event for the `navigate` bindings | ||
if( history.ignoreNextHashChange || history.stack.length == 0 ) { | ||
history.ignoreNextHashChange = false; | ||
return; | ||
} | ||
|
||
|
||
history.direct({ | ||
currentUrl: path.parseLocation().hash , | ||
either: function( historyEntry ) { | ||
event.hashchangeState = historyEntry; | ||
} | ||
}); | ||
}); | ||
|
||
// expose the history on the navigate method in anticipation of full integration with | ||
// existing navigation functionalty that is tightly coupled to the history information | ||
$.navigate.history = history = { | ||
// Array of pages that are visited during a single page load. | ||
// Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs) | ||
stack: [], | ||
|
||
//maintain an index number for the active page in the stack | ||
activeIndex: 0, | ||
|
||
//get active | ||
getActive: function() { | ||
return this.stack[ this.activeIndex ]; | ||
}, | ||
|
||
getPrev: function() { | ||
return this.stack[ this.activeIndex - 1 ]; | ||
}, | ||
|
||
getNext: function() { | ||
return this.stack[ this.activeIndex + 1 ]; | ||
}, | ||
|
||
// addNew is used whenever a new page is added | ||
add: function( url, data ){ | ||
data = data || {}; | ||
|
||
//if there's forward history, wipe it | ||
if ( this.getNext() ) { | ||
this.clearForward(); | ||
} | ||
|
||
data.url = url; | ||
this.stack.push( data ); | ||
this.activeIndex = this.stack.length - 1; | ||
}, | ||
|
||
//wipe urls ahead of active index | ||
clearForward: function() { | ||
this.stack = this.stack.slice( 0, this.activeIndex + 1 ); | ||
}, | ||
|
||
direct: function( opts ) { | ||
var back, forward, newActiveIndex, prev = this.getActive(), a = this.activeIndex; | ||
|
||
// check if url is in history and if it's ahead or behind current page | ||
$.each( this.stack, function( i, historyEntry ) { | ||
//if the url is in the stack, it's a forward or a back | ||
if ( decodeURIComponent( opts.currentUrl ) === decodeURIComponent( historyEntry.url ) ) { | ||
//define back and forward by whether url is older or newer than current page | ||
back = i < this.activeIndex; | ||
forward = !back; | ||
newActiveIndex = i; | ||
} | ||
}); | ||
|
||
// save new page index, null check to prevent falsey 0 result | ||
this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex; | ||
|
||
if ( back ) { | ||
( opts.either || opts.isBack )( this.getActive() ); | ||
} else if ( forward ) { | ||
( opts.either || opts.isForward )( this.getActive() ); | ||
} | ||
}, | ||
|
||
//disable hashchange event listener internally to ignore one change | ||
//toggled internally when location.hash is updated to match the url of a successful page load | ||
ignoreNextHashChange: false | ||
}; | ||
|
||
// Set the initial url history state | ||
history.add( path.parseLocation().pathname + path.parseLocation().search, {}); | ||
})( jQuery ); | ||
|
||
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude); | ||
}); | ||
//>>excludeEnd("jqmBuildExclude"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<title>jQuery Mobile Navigate Events Test Suite</title> | ||
|
||
<script src="../../../external/requirejs/require.js"></script> | ||
<script src="../../../js/jquery.tag.inserter.js"></script> | ||
<script src="../../../tests/jquery.testHelper.js"></script> | ||
<script src="../../../external/qunit.js"></script> | ||
<link rel="stylesheet" href="../../../../external/qunit.css"/> | ||
<script> | ||
$.testHelper.asyncLoad([ | ||
[ | ||
"navigation/events/navigate", | ||
"navigation/navigate" | ||
], | ||
[ "navigate_method.js" ] | ||
]); | ||
</script> | ||
<script src="../swarminject.js"></script> | ||
</head> | ||
<body> | ||
<div id="qunit"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Check is the ?push-state=false is in the url and alter the tests accordingly | ||
$.testHelper.setPushState(); | ||
|
||
(function( $ ) { | ||
var url = $.mobile.path.parseLocation(), | ||
home = url.pathname + url.search; | ||
|
||
module( "navigate", { | ||
setup: function() { | ||
stop(); | ||
|
||
$( window ).one( "navigate", function() { | ||
start(); | ||
}); | ||
|
||
if( location.hash !== "#reset" ) { | ||
$.navigate( "#reset" ); | ||
} | ||
|
||
$.navigate.history.stack = []; | ||
} | ||
}); | ||
|
||
test( "navigation changes the url", function() { | ||
ok( location.hash.indexOf( "foo" ) == -1, "the hash is clean" ); | ||
|
||
$.navigate( "#foo" ); | ||
|
||
equal( location.hash, "#foo", "the hash has been altered" ); | ||
}); | ||
|
||
if( $.support.pushState ) { | ||
test( "navigation should squish the hash", function() { | ||
var destination = home + "#foo"; | ||
|
||
ok( location.hash.indexOf( "foo" ) == -1, "the hash is clean" ); | ||
ok( $.mobile.path.isPath(destination), "the destination is a path" ); | ||
|
||
$.navigate( destination ); | ||
|
||
equal( $.mobile.path.parseLocation().pathname, url.pathname, "the resulting url has the same pathname as the original test url" ); | ||
equal( location.hash, "#foo", "the hash has been altered" ); | ||
}); | ||
} else { | ||
test( "navigation should append the hash with a path", function() { | ||
var destination = home + "#foo"; | ||
|
||
ok( location.hash.indexOf(home) == -1, "the hash is clean" ); | ||
ok( $.mobile.path.isPath(destination), "the destination is a path" ); | ||
|
||
$.navigate( destination ); | ||
|
||
equal( $.mobile.path.parseLocation().hash, "#" + destination, "the resulting url has the same pathname as the original test url" ); | ||
}); | ||
} | ||
|
||
if( !$.support.pushState ) { | ||
asyncTest( "navigating backward should include the history state", function() { | ||
$( window ).one( "navigate", function() { | ||
$.navigate( "#bar" ); | ||
|
||
$( window ).one( "navigate", function() { | ||
window.history.back(); | ||
|
||
$( window ).one( "navigate", function( event, data ) { | ||
equal( data.state.foo, "bar", "the data that was appended in the navigation is popped with the backward movement" ); | ||
start(); | ||
}); | ||
}); | ||
}); | ||
|
||
$.navigate( "#foo", { foo: "bar" }); | ||
}); | ||
} | ||
})( jQuery ); |