text: /*!
   * jQuery Mobile Navigator @VERSION
   * http://jquerymobile.com
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license.
   * http://jquery.org/license
   */
  
  //>>label: Navigation Manager
  //>>group: Navigation
  //>>description: Manages URL history and information in conjunction with the navigate event
  
  ( function( factory ) {
  	if ( typeof define === "function" && define.amd ) {
  
  		// AMD. Register as an anonymous module.
  		define( [
  			"jquery",
  			"./../ns",
  			"../events/navigate",
  			"./path",
  			"./history" ], factory );
  	} else {
  
  		// Browser globals
  		factory( jQuery );
  	}
  } )( function( $ ) {
  
  var path = $.mobile.path,
  	initialHref = location.href;
  
  $.mobile.Navigator = function( history ) {
  	this.history = history;
  	this.ignoreInitialHashChange = true;
  
  	$.mobile.window.bind( {
  		"popstate.history": $.proxy( this.popstate, this ),
  		"hashchange.history": $.proxy( this.hashchange, this )
  	} );
  };
  
  $.extend( $.mobile.Navigator.prototype, {
  	historyEntryId: 0,
  	squash: function( url, data ) {
  		var state, href,
  			hash = path.isPath( url ) ? path.stripHash( url ) : url;
  
  		href = path.squash( url );
  
  		// make sure to provide this information when it isn't explicitly set in the
  		// data object that was passed to the squash method
  		state = $.extend( {
  			id: ++this.historyEntryId,
  			hash: hash,
  			url: href
  		}, data );
  
  		// 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.
  		//
  		//  Check is to make sure that jqm doesn't throw type errors in environments where
  		//  window.history is not available (e.g. Chrome packaged apps)
  		if ( window.history && window.history.replaceState ) {
  			window.history.replaceState( state, state.title || document.title, href );
  		}
  
  		// If we haven't yet received the initial popstate, we need to update the reference
  		// href so that we compare against the correct location
  		if ( this.ignoreInitialHashChange ) {
  			initialHref = href;
  		}
  
  		return state;
  	},
  
  	hash: function( url, href ) {
  		var parsed, loc, hash, resolved;
  
  		// Grab the hash for recording. If the passed url is a path
  		// we used the parsed version of the squashed url to reconstruct,
  		// otherwise we assume it's a hash and store it directly
  		parsed = path.parseUrl( url );
  		loc = path.parseLocation();
  
  		if ( loc.pathname + loc.search === parsed.pathname + parsed.search ) {
  			// If the pathname and search of the passed url is identical to the current loc
  			// then we must use the hash. Otherwise there will be no event
  			// eg, url = "/foo/bar?baz#bang", location.href = "http://example.com/foo/bar?baz"
  			hash = parsed.hash ? parsed.hash : parsed.pathname + parsed.search;
  		} else if ( path.isPath( url ) ) {
  			resolved = path.parseUrl( href );
  			// If the passed url is a path, make it domain relative and remove any trailing hash
  			hash = resolved.pathname + resolved.search + ( path.isPreservableHash( resolved.hash ) ? resolved.hash.replace( "#", "" ) : "" );
  		} else {
  			hash = url;
  		}
  
  		return hash;
  	},
  
  	// TODO reconsider name
  	go: function( url, data, noEvents ) {
  		var state, href, hash, popstateEvent,
  			isPopStateEvent = $.event.special.navigate.isPushStateEnabled();
  
  		// Get the url as it would look squashed on to the current resolution url
  		href = path.squash( url );
  
  		// sort out what the hash sould be from the url
  		hash = this.hash( url, href );
  
  		// Here we prevent the next hash change or popstate event from doing any
  		// history management. In the case of hashchange we don't swallow it
  		// if there will be no hashchange fired (since that won't reset the value)
  		// and will swallow the following hashchange
  		if ( noEvents && hash !== path.stripHash( path.parseLocation().hash ) ) {
  			this.preventNextHashChange = noEvents;
  		}
  
  		// IMPORTANT in the case where popstate is supported the event will be triggered
  		//      directly, stopping further execution - ie, interupting the flow of this
  		//      method call to fire bindings at this expression. Below the navigate method
  		//      there is a binding to catch this event and stop its propagation.
  		//
  		//      We then trigger a new popstate event on the window with a null state
  		//      so that the navigate events can conclude their work properly
  		//
  		// if the url is a path we want to preserve the query params that are available on
  		// the current url.
  		this.preventHashAssignPopState = true;
  		window.location.hash = hash;
  
  		// If popstate is enabled and the browser triggers `popstate` events when the hash
  		// is set (this often happens immediately in browsers like Chrome), then the
  		// this flag will be set to false already. If it's a browser that does not trigger
  		// a `popstate` on hash assignement or `replaceState` then we need avoid the branch
  		// that swallows the event created by the popstate generated by the hash assignment
  		// At the time of this writing this happens with Opera 12 and some version of IE
  		this.preventHashAssignPopState = false;
  
  		state = $.extend( {
  			url: href,
  			hash: hash,
  			title: document.title
  		}, data );
  
  		if ( isPopStateEvent ) {
  			popstateEvent = new $.Event( "popstate" );
  			popstateEvent.originalEvent = new $.Event( "popstate", { state: null } );
  
  			state.id = ( this.squash( url, state ) || {} ).id;
  
  			// Trigger a new faux popstate event to replace the one that we
  			// caught that was triggered by the hash setting above.
  			if ( !noEvents ) {
  				this.ignorePopState = true;
  				$.mobile.window.trigger( popstateEvent );
  			}
  		}
  
  		// 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
  		this.history.add( state.url, state );
  	},
  
  	// This binding is intended to catch the popstate events that are fired
  	// when execution of the `$.navigate` method stops at window.location.hash = url;
  	// and completely prevent them from propagating. The popstate event will then be
  	// retriggered after execution resumes
  	//
  	// TODO grab the original event here and use it for the synthetic event in the
  	//      second half of the navigate execution that will follow this binding
  	popstate: function( event ) {
  		var hash, state;
  
  		// Partly to support our test suite which manually alters the support
  		// value to test hashchange. Partly to prevent all around weirdness
  		if ( !$.event.special.navigate.isPushStateEnabled() ) {
  			return;
  		}
  
  		// If this is the popstate triggered by the actual alteration of the hash
  		// prevent it completely. History is tracked manually
  		if ( this.preventHashAssignPopState ) {
  			this.preventHashAssignPopState = false;
  			event.stopImmediatePropagation();
  			return;
  		}
  
  		// if this is the popstate triggered after the `replaceState` call in the go
  		// method, then simply ignore it. The history entry has already been captured
  		if ( this.ignorePopState ) {
  			this.ignorePopState = false;
  			return;
  		}
  
  		// If there is no state, and the history stack length is one were
  		// probably getting the page load popstate fired by browsers like chrome
  		// avoid it and set the one time flag to false.
  		// TODO: Do we really need all these conditions? Comparing location hrefs
  		// should be sufficient.
  		if ( !event.originalEvent.state &&
  				this.history.stack.length === 1 &&
  				this.ignoreInitialHashChange ) {
  			this.ignoreInitialHashChange = false;
  
  			if ( location.href === initialHref ) {
  				event.preventDefault();
  				return;
  			}
  		}
  
  		// account for direct manipulation of the hash. That is, we will receive a popstate
  		// when the hash is changed by assignment, and it won't have a state associated. We
  		// then need to squash the hash. See below for handling of hash assignment that
  		// matches an existing history entry
  		// TODO it might be better to only add to the history stack
  		//      when the hash is adjacent to the active history entry
  		hash = path.parseLocation().hash;
  		if ( !event.originalEvent.state && hash ) {
  			// squash the hash that's been assigned on the URL with replaceState
  			// also grab the resulting state object for storage
  			state = this.squash( hash );
  
  			// record the new hash as an additional history entry
  			// to match the browser's treatment of hash assignment
  			this.history.add( state.url, state );
  
  			// pass the newly created state information
  			// along with the event
  			event.historyState = state;
  
  			// do not alter history, we've added a new history entry
  			// so we know where we are
  			return;
  		}
  
  		// If all else fails this is a popstate that comes from the back or forward buttons
  		// make sure to set the state of our history stack properly, and record the directionality
  		this.history.direct( {
  			id: ( event.originalEvent.state || {} ).id,
  			url: ( event.originalEvent.state || {} ).url || hash,
  
  			// When the url is either forward or backward in history include the entry
  			// as data on the event object for merging as data in the navigate event
  			present: function( historyEntry, direction ) {
  				// make sure to create a new object to pass down as the navigate event data
  				event.historyState = $.extend( {}, historyEntry );
  				event.historyState.direction = direction;
  			}
  		} );
  	},
  
  	// 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?)
  	hashchange: function( event ) {
  		var history, hash;
  
  		// If hashchange listening is explicitly disabled or pushstate is supported
  		// avoid making use of the hashchange handler.
  		if ( !$.event.special.navigate.isHashChangeEnabled() ||
  				$.event.special.navigate.isPushStateEnabled() ) {
  			return;
  		}
  
  		// On occasion explicitly want to prevent the next hash from propagating because we only
  		// with to alter the url to represent the new state do so here
  		if ( this.preventNextHashChange ) {
  			this.preventNextHashChange = false;
  			event.stopImmediatePropagation();
  			return;
  		}
  
  		history = this.history;
  		hash = path.parseLocation().hash;
  
  		// If this is a hashchange caused by the back or forward button
  		// make sure to set the state of our history stack properly
  		this.history.direct( {
  			url: hash,
  
  			// When the url is either forward or backward in history include the entry
  			// as data on the event object for merging as data in the navigate event
  			present: function( historyEntry, direction ) {
  				// make sure to create a new object to pass down as the navigate event data
  				event.hashchangeState = $.extend( {}, historyEntry );
  				event.hashchangeState.direction = direction;
  			},
  
  			// When we don't find a hash in our history clearly we're aiming to go there
  			// record the entry as new for future traversal
  			//
  			// NOTE it's not entirely clear that this is the right thing to do given that we
  			//      can't know the users intention. It might be better to explicitly _not_
  			//      support location.hash assignment in preference to $.navigate calls
  			// TODO first arg to add should be the href, but it causes issues in identifying
  			//      embedded pages
  			missing: function() {
  				history.add( hash, {
  					hash: hash,
  					title: document.title
  				} );
  			}
  		} );
  	}
  } );
  
  return $.mobile.Navigator;
  } );
  