text: //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
  //>>description: Applies the AJAX navigation system to links and forms to enable page transitions
  //>>label: AJAX Navigation System
  //>>group: Navigation
  
  define( [
  	"jquery",
  	"./jquery.mobile.core",
  	"./jquery.mobile.events",
  	"./jquery.mobile.support",
  	"depend!./jquery.hashchange[jquery]",
  	"./widgets/page",
  	"./jquery.mobile.transition" ], function( $ ) {
  //>>excludeEnd("jqmBuildExclude");
  (function( $, undefined ) {
  
  	//define vars for interal use
  	var $window = $( window ),
  		$html = $( 'html' ),
  		$head = $( 'head' ),
  
  		//url path helpers for use in relative url management
  		path = {
  
  			// This scary looking regular expression parses an absolute URL or its relative
  			// variants (protocol, site, document, query, and hash), into the various
  			// components (protocol, host, path, query, fragment, etc that make up the
  			// URL as well as some other commonly used sub-parts. When used with RegExp.exec()
  			// or String.match, it parses the URL into a results array that looks like this:
  			//
  			//     [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
  			//     [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
  			//     [2]: http://jblas:password@mycompany.com:8080/mail/inbox
  			//     [3]: http://jblas:password@mycompany.com:8080
  			//     [4]: http:
  			//     [5]: //
  			//     [6]: jblas:password@mycompany.com:8080
  			//     [7]: jblas:password
  			//     [8]: jblas
  			//     [9]: password
  			//    [10]: mycompany.com:8080
  			//    [11]: mycompany.com
  			//    [12]: 8080
  			//    [13]: /mail/inbox
  			//    [14]: /mail/
  			//    [15]: inbox
  			//    [16]: ?msg=1234&type=unread
  			//    [17]: #msg-content
  			//
  			urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
  
  			// Abstraction to address xss (Issue #4787) by removing the authority in
  			// browsers that auto	decode it. All references to location.href should be
  			// replaced with a call to this method so that it can be dealt with properly here
  			getLocation: function( url ) {
  				var uri = url ? this.parseUrl( url ) : location,
  					hash = this.parseUrl( url || location.href ).hash;
  
  				// mimic the browser with an empty string when the hash is empty
  				hash = hash === "#" ? "" : hash;
  
  				// Make sure to parse the url or the location object for the hash because using location.hash
  				// is autodecoded in firefox, the rest of the url should be from the object (location unless
  				// we're testing) to avoid the inclusion of the authority
  				return uri.protocol + "//" + uri.host + uri.pathname + uri.search + hash;
  			},
  
  			parseLocation: function() {
  				return this.parseUrl( this.getLocation() );
  			},
  
  			//Parse a URL into a structure that allows easy access to
  			//all of the URL components by name.
  			parseUrl: function( url ) {
  				// If we're passed an object, we'll assume that it is
  				// a parsed url object and just return it back to the caller.
  				if ( $.type( url ) === "object" ) {
  					return url;
  				}
  
  				var matches = path.urlParseRE.exec( url || "" ) || [];
  
  					// Create an object that allows the caller to access the sub-matches
  					// by name. Note that IE returns an empty string instead of undefined,
  					// like all other browsers do, so we normalize everything so its consistent
  					// no matter what browser we're running on.
  					return {
  						href:         matches[  0 ] || "",
  						hrefNoHash:   matches[  1 ] || "",
  						hrefNoSearch: matches[  2 ] || "",
  						domain:       matches[  3 ] || "",
  						protocol:     matches[  4 ] || "",
  						doubleSlash:  matches[  5 ] || "",
  						authority:    matches[  6 ] || "",
  						username:     matches[  8 ] || "",
  						password:     matches[  9 ] || "",
  						host:         matches[ 10 ] || "",
  						hostname:     matches[ 11 ] || "",
  						port:         matches[ 12 ] || "",
  						pathname:     matches[ 13 ] || "",
  						directory:    matches[ 14 ] || "",
  						filename:     matches[ 15 ] || "",
  						search:       matches[ 16 ] || "",
  						hash:         matches[ 17 ] || ""
  					};
  			},
  
  			//Turn relPath into an asbolute path. absPath is
  			//an optional absolute path which describes what
  			//relPath is relative to.
  			makePathAbsolute: function( relPath, absPath ) {
  				if ( relPath && relPath.charAt( 0 ) === "/" ) {
  					return relPath;
  				}
  
  				relPath = relPath || "";
  				absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
  
  				var absStack = absPath ? absPath.split( "/" ) : [],
  					relStack = relPath.split( "/" );
  				for ( var i = 0; i < relStack.length; i++ ) {
  					var d = relStack[ i ];
  					switch ( d ) {
  						case ".":
  							break;
  						case "..":
  							if ( absStack.length ) {
  								absStack.pop();
  							}
  							break;
  						default:
  							absStack.push( d );
  							break;
  					}
  				}
  				return "/" + absStack.join( "/" );
  			},
  
  			//Returns true if both urls have the same domain.
  			isSameDomain: function( absUrl1, absUrl2 ) {
  				return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
  			},
  
  			//Returns true for any relative variant.
  			isRelativeUrl: function( url ) {
  				// All relative Url variants have one thing in common, no protocol.
  				return path.parseUrl( url ).protocol === "";
  			},
  
  			//Returns true for an absolute url.
  			isAbsoluteUrl: function( url ) {
  				return path.parseUrl( url ).protocol !== "";
  			},
  
  			//Turn the specified realtive URL into an absolute one. This function
  			//can handle all relative variants (protocol, site, document, query, fragment).
  			makeUrlAbsolute: function( relUrl, absUrl ) {
  				if ( !path.isRelativeUrl( relUrl ) ) {
  					return relUrl;
  				}
  
  				if ( absUrl === undefined ) {
  					absUrl = documentBase;
  				}
  
  				var relObj = path.parseUrl( relUrl ),
  					absObj = path.parseUrl( absUrl ),
  					protocol = relObj.protocol || absObj.protocol,
  					doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ),
  					authority = relObj.authority || absObj.authority,
  					hasPath = relObj.pathname !== "",
  					pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
  					search = relObj.search || ( !hasPath && absObj.search ) || "",
  					hash = relObj.hash;
  
  				return protocol + doubleSlash + authority + pathname + search + hash;
  			},
  
  			//Add search (aka query) params to the specified url.
  			addSearchParams: function( url, params ) {
  				var u = path.parseUrl( url ),
  					p = ( typeof params === "object" ) ? $.param( params ) : params,
  					s = u.search || "?";
  				return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" );
  			},
  
  			convertUrlToDataUrl: function( absUrl ) {
  				var u = path.parseUrl( absUrl );
  				if ( path.isEmbeddedPage( u ) ) {
  					// For embedded pages, remove the dialog hash key as in getFilePath(),
  					// and remove otherwise the Data Url won't match the id of the embedded Page.
  					return u.hash
  						.split( dialogHashKey )[0]
  						.replace( /^#/, "" )
  						.replace( /\?.*$/, "" );
  				} else if ( path.isSameDomain( u, documentBase ) ) {
  					return u.hrefNoHash.replace( documentBase.domain, "" ).split( dialogHashKey )[0];
  				}
  
  				return window.decodeURIComponent(absUrl);
  			},
  
  			//get path from current hash, or from a file path
  			get: function( newPath ) {
  				if ( newPath === undefined ) {
  					newPath = path.parseLocation().hash;
  				}
  				return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
  			},
  
  			//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.split( splitkey )[0].split( dialogHashKey )[0];
  			},
  
  			//set location hash to path
  			set: function( path ) {
  				location.hash = path;
  			},
  
  			//test if a given url (string) is a path
  			//NOTE might be exceptionally naive
  			isPath: function( url ) {
  				return ( /\// ).test( url );
  			},
  
  			//return a url path with the window's location protocol/hostname/pathname removed
  			clean: function( url ) {
  				return url.replace( documentBase.domain, "" );
  			},
  
  			//just return the url without an initial #
  			stripHash: function( url ) {
  				return url.replace( /^#/, "" );
  			},
  
  			//remove the preceding hash, any query params, and dialog notations
  			cleanHash: function( hash ) {
  				return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
  			},
  
  			isHashValid: function( hash ) {
  				return ( /^#[^#]+$/ ).test( hash );
  			},
  
  			//check whether a url is referencing the same domain, or an external domain or different protocol
  			//could be mailto, etc
  			isExternal: function( url ) {
  				var u = path.parseUrl( url );
  				return u.protocol && u.domain !== documentUrl.domain ? true : false;
  			},
  
  			hasProtocol: function( url ) {
  				return ( /^(:?\w+:)/ ).test( url );
  			},
  
  			//check if the specified url refers to the first page in the main application document.
  			isFirstPageUrl: function( url ) {
  				// We only deal with absolute paths.
  				var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ),
  
  					// Does the url have the same path as the document?
  					samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
  
  					// Get the first page element.
  					fp = $.mobile.firstPage,
  
  					// Get the id of the first page element if it has one.
  					fpId = fp && fp[0] ? fp[0].id : undefined;
  
  					// The url refers to the first page if the path matches the document and
  					// it either has no hash value, or the hash is exactly equal to the id of the
  					// first page element.
  					return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
  			},
  
  			isEmbeddedPage: function( url ) {
  				var u = path.parseUrl( url );
  
  				//if the path is absolute, then we need to compare the url against
  				//both the documentUrl and the documentBase. The main reason for this
  				//is that links embedded within external documents will refer to the
  				//application document, whereas links embedded within the application
  				//document will be resolved against the document base.
  				if ( u.protocol !== "" ) {
  					return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) );
  				}
  				return ( /^#/ ).test( u.href );
  			},
  
  
  			// 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.
  			isPermittedCrossDomainRequest: function( docUrl, reqUrl ) {
  				return $.mobile.allowCrossDomainPages &&
  					docUrl.protocol === "file:" &&
  					reqUrl.search( /^https?:/ ) !== -1;
  			}
  		},
  
  		//will be defined when a link is clicked and given an active class
  		$activeClickedLink = null,
  
  		//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, 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 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, title, pageUrl, role ) {
  				//if there's forward history, wipe it
  				if ( urlHistory.getNext() ) {
  					urlHistory.clearForward();
  				}
  
  				urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
  
  				urlHistory.activeIndex = urlHistory.stack.length - 1;
  			},
  
  			//wipe urls ahead of active index
  			clearForward: function() {
  				urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
  			},
  
  			directHashChange: function( opts ) {
  				var back , forward, newActiveIndex, prev = this.getActive();
  
  				// check if url is in history and if it's ahead or behind current page
  				$.each( urlHistory.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 < urlHistory.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 )( true );
  				} else if ( forward ) {
  					( opts.either || opts.isForward )( false );
  				}
  			},
  
  			//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
  		},
  
  		//define first selector to receive focus when a page is shown
  		focusable = "[tabindex],a,button:visible,select:visible,input",
  
  		//queue to hold simultanious page transitions
  		pageTransitionQueue = [],
  
  		//indicates whether or not page is in process of transitioning
  		isPageTransitioning = false,
  
  		//nonsense hash change key for dialogs, so they create a history entry
  		dialogHashKey = "&ui-state=dialog",
  
  		//existing base tag?
  		$base = $head.children( "base" ),
  
  		//tuck away the original document URL minus any fragment.
  		documentUrl = path.parseLocation(),
  
  		//if the document has an embedded base tag, documentBase is set to its
  		//initial value. If a base tag does not exist, then we default to the documentUrl.
  		documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl,
  
  		//cache the comparison once.
  		documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash ),
  
  		getScreenHeight = $.mobile.getScreenHeight;
  
  		//base element management, defined depending on dynamic base tag support
  		var base = {
  			//define base element, for use in routing asset urls that are referenced in Ajax-requested markup
  			element: ( $base.length ? $base : $( "<base>", { href: documentBase.hrefNoHash } ).prependTo( $head ) ),
  
  			linkSelector: "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]",
  
  			//set the generated BASE element's href attribute to a new page's base path
  			set: function( href, page ) {
  				// we should do nothing if the user wants to manage their url base manually
  				if ( !$.mobile.dynamicBaseEnabled ){
  					return;
  				}
  
  				// we should use the base tag if we can manipulate it dynamically
  				if ( $.support.dynamicBaseTag ){
  					base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) );
  				}
  
  				// otherwise rewrite src and href attrs to use a base url
  				if ( !$.support.dynamicBaseTag && page ) {
  					var newPath = path.get( href );
  					page.find( base.linkSelector ).each(function( i, link ) {
  						var thisAttr = $( link ).is( '[href]' ) ? 'href' : $( link ).is( '[src]' ) ? 'src' : 'action',
  						thisUrl = $( link ).attr( thisAttr );
  
  						// XXX_jblas: We need to fix this so that it removes the document
  						//            base URL, and then prepends with the new page URL.
  						//if full path exists and is same, chop it - helps IE out
  						thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
  
  						if ( !/^(\w+:|#|\/)/.test( thisUrl ) ) {
  							$( link ).attr( thisAttr, newPath + thisUrl );
  						}
  					});
  				}
  			},
  
  			//set the generated BASE element's href attribute to a new page's base path
  			reset: function() {
  				base.element.attr( "href", documentBase.hrefNoHash );
  			}
  		};
  
  	/* internal utility functions */
  
  	// NOTE Issue #4950 Android phonegap doesn't navigate back properly
  	//      when a full page refresh has taken place. It appears that hashchange
  	//      and replacestate history alterations work fine but we need to support
  	//      both forms of history traversal in our code that uses backward history
  	//      movement
  	$.mobile.back = function() {
  		var nav = window.navigator;
  
  		// if the setting is on and the navigator object is
  		// available use the phonegap navigation capability
  		if( this.phonegapNavigationEnabled &&
  			nav &&
  			nav.app &&
  			nav.app.backHistory ){
  			nav.app.backHistory();
  		} else {
  			window.history.back();
  		}
  	};
  
  	//direct focus to the page title, or otherwise first focusable element
  	$.mobile.focusPage = function ( page ) {
  		var autofocus = page.find( "[autofocus]" ),
  			pageTitle = page.find( ".ui-title:eq(0)" );
  
  		if ( autofocus.length ) {
  			autofocus.focus();
  			return;
  		}
  
  		if ( pageTitle.length ) {
  			pageTitle.focus();
  		} else{
  			page.focus();
  		}
  	};
  
  	//remove active classes after page transition or error
  	function removeActiveLinkClass( forceRemoval ) {
  		if ( !!$activeClickedLink && ( !$activeClickedLink.closest( "." + $.mobile.activePageClass ).length || forceRemoval ) ) {
  			$activeClickedLink.removeClass( $.mobile.activeBtnClass );
  		}
  		$activeClickedLink = null;
  	}
  
  	function releasePageTransitionLock() {
  		isPageTransitioning = false;
  		if ( pageTransitionQueue.length > 0 ) {
  			$.mobile.changePage.apply( null, pageTransitionQueue.pop() );
  		}
  	}
  
  	// Save the last scroll distance per page, before it is hidden
  	var setLastScrollEnabled = true,
  		setLastScroll, delayedSetLastScroll;
  
  	setLastScroll = function() {
  		// this barrier prevents setting the scroll value based on the browser
  		// scrolling the window based on a hashchange
  		if ( !setLastScrollEnabled ) {
  			return;
  		}
  
  		var active = $.mobile.urlHistory.getActive();
  
  		if ( active ) {
  			var lastScroll = $window.scrollTop();
  
  			// Set active page's lastScroll prop.
  			// If the location we're scrolling to is less than minScrollBack, let it go.
  			active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
  		}
  	};
  
  	// bind to scrollstop to gather scroll position. The delay allows for the hashchange
  	// event to fire and disable scroll recording in the case where the browser scrolls
  	// to the hash targets location (sometimes the top of the page). once pagechange fires
  	// getLastScroll is again permitted to operate
  	delayedSetLastScroll = function() {
  		setTimeout( setLastScroll, 100 );
  	};
  
  	// disable an scroll setting when a hashchange has been fired, this only works
  	// because the recording of the scroll position is delayed for 100ms after
  	// the browser might have changed the position because of the hashchange
  	$window.bind( $.support.pushState ? "popstate" : "hashchange", function() {
  		setLastScrollEnabled = false;
  	});
  
  	// handle initial hashchange from chrome :(
  	$window.one( $.support.pushState ? "popstate" : "hashchange", function() {
  		setLastScrollEnabled = true;
  	});
  
  	// wait until the mobile page container has been determined to bind to pagechange
  	$window.one( "pagecontainercreate", function() {
  		// once the page has changed, re-enable the scroll recording
  		$.mobile.pageContainer.bind( "pagechange", function() {
  
  			setLastScrollEnabled = true;
  
  			// remove any binding that previously existed on the get scroll
  			// which may or may not be different than the scroll element determined for
  			// this page previously
  			$window.unbind( "scrollstop", delayedSetLastScroll );
  
  			// determine and bind to the current scoll element which may be the window
  			// or in the case of touch overflow the element with touch overflow
  			$window.bind( "scrollstop", delayedSetLastScroll );
  		});
  	});
  
  	// bind to scrollstop for the first page as "pagechange" won't be fired in that case
  	$window.bind( "scrollstop", delayedSetLastScroll );
  
  	// No-op implementation of transition degradation
  	$.mobile._maybeDegradeTransition = $.mobile._maybeDegradeTransition || function( transition ) {
  		return transition;
  	};
  
  	//function for transitioning between two existing pages
  	function transitionPages( toPage, fromPage, transition, reverse ) {
  
  		if ( fromPage ) {
  			//trigger before show/hide events
  			fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
  		}
  
  		toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
  
  		//clear page loader
  		$.mobile.hidePageLoadingMsg();
  
  		transition = $.mobile._maybeDegradeTransition( transition );
  
  		//find the transition handler for the specified transition. If there
  		//isn't one in our transitionHandlers dictionary, use the default one.
  		//call the handler immediately to kick-off the transition.
  		var th = $.mobile.transitionHandlers[ transition || "default" ] || $.mobile.defaultTransitionHandler,
  			promise = th( transition, reverse, toPage, fromPage );
  
  		promise.done(function() {
  
  			//trigger show/hide events
  			if ( fromPage ) {
  				fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
  			}
  
  			//trigger pageshow, define prevPage as either fromPage or empty jQuery obj
  			toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } );
  		});
  
  		return promise;
  	}
  
  	//simply set the active page's minimum height to screen height, depending on orientation
  	function resetActivePageHeight() {
  		var aPage = $( "." + $.mobile.activePageClass ),
  			aPagePadT = parseFloat( aPage.css( "padding-top" ) ),
  			aPagePadB = parseFloat( aPage.css( "padding-bottom" ) ),
  			aPageBorderT = parseFloat( aPage.css( "border-top-width" ) ),
  			aPageBorderB = parseFloat( aPage.css( "border-bottom-width" ) );
  
  		aPage.css( "min-height", getScreenHeight() - aPagePadT - aPagePadB - aPageBorderT - aPageBorderB );
  	}
  
  	//shared page enhancements
  	function enhancePage( $page, role ) {
  		// If a role was specified, make sure the data-role attribute
  		// on the page element is in sync.
  		if ( role ) {
  			$page.attr( "data-" + $.mobile.ns + "role", role );
  		}
  
  		//run page plugin
  		$page.page();
  	}
  
  	// determine the current base url
  	function findBaseWithDefault() {
  		var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) );
  		return closestBase || documentBase.hrefNoHash;
  	}
  
  	/* exposed $.mobile methods */
  
  	//animation complete callback
  	$.fn.animationComplete = function( callback ) {
  		if ( $.support.cssTransitions ) {
  			return $( this ).one( 'webkitAnimationEnd animationend', callback );
  		}
  		else{
  			// defer execution for consistency between webkit/non webkit
  			setTimeout( callback, 0 );
  			return $( this );
  		}
  	};
  
  	//expose path object on $.mobile
  	$.mobile.path = path;
  
  	//expose base object on $.mobile
  	$.mobile.base = base;
  
  	//history stack
  	$.mobile.urlHistory = urlHistory;
  
  	$.mobile.dialogHashKey = dialogHashKey;
  
  	//enable cross-domain page support
  	$.mobile.allowCrossDomainPages = false;
  
  	//return the original document url
  	$.mobile.getDocumentUrl = function( asParsedObject ) {
  		return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href;
  	};
  
  	//return the original document base url
  	$.mobile.getDocumentBase = function( asParsedObject ) {
  		return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href;
  	};
  
  	$.mobile._bindPageRemove = function() {
  		var page = $( this );
  
  		// when dom caching is not enabled or the page is embedded bind to remove the page on hide
  		if ( !page.data( "page" ).options.domCache &&
  				page.is( ":jqmData(external-page='true')" ) ) {
  
  			page.bind( 'pagehide.remove', function() {
  				var $this = $( this ),
  					prEvent = new $.Event( "pageremove" );
  
  				$this.trigger( prEvent );
  
  				if ( !prEvent.isDefaultPrevented() ) {
  					$this.removeWithDependents();
  				}
  			});
  		}
  	};
  
  	// Load a page into the DOM.
  	$.mobile.loadPage = function( url, options ) {
  		// This function uses deferred notifications to let callers
  		// know when the page is done loading, or if an error has occurred.
  		var deferred = $.Deferred(),
  
  			// The default loadPage options with overrides specified by
  			// the caller.
  			settings = $.extend( {}, $.mobile.loadPage.defaults, options ),
  
  			// The DOM element for the page after it has been loaded.
  			page = null,
  
  			// If the reloadPage option is true, and the page is already
  			// in the DOM, dupCachedPage will be set to the page element
  			// so that it can be removed after the new version of the
  			// page is loaded off the network.
  			dupCachedPage = null,
  
  			// The absolute version of the URL passed into the function. This
  			// version of the URL may contain dialog/subpage params in it.
  			absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() );
  
  
  		// If the caller provided data, and we're using "get" request,
  		// append the data to the URL.
  		if ( settings.data && settings.type === "get" ) {
  			absUrl = path.addSearchParams( absUrl, settings.data );
  			settings.data = undefined;
  		}
  
  		// If the caller is using a "post" request, reloadPage must be true
  		if ( settings.data && settings.type === "post" ) {
  			settings.reloadPage = true;
  		}
  
  		// The absolute version of the URL minus any dialog/subpage params.
  		// In otherwords the real URL of the page to be loaded.
  		var fileUrl = path.getFilePath( absUrl ),
  
  			// The version of the Url actually stored in the data-url attribute of
  			// the page. For embedded pages, it is just the id of the page. For pages
  			// 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.
  		settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
  
  		// Check to see if the page already exists in the DOM.
  		// NOTE do _not_ use the :jqmData psuedo selector because parenthesis
  		//      are a valid url char and it breaks on the first occurence
  		page = settings.pageContainer.children( "[data-" + $.mobile.ns +"url='" + dataUrl + "']" );
  
  		// If we failed to find the page, check to see if the url is a
  		// reference to an embedded page. If so, it may have been dynamically
  		// injected by a developer, in which case it would be lacking a data-url
  		// attribute and in need of enhancement.
  		if ( page.length === 0 && dataUrl && !path.isPath( dataUrl ) ) {
  			page = settings.pageContainer.children( "#" + dataUrl )
  				.attr( "data-" + $.mobile.ns + "url", dataUrl )
  				.jqmData( "url", dataUrl );
  		}
  
  		// If we failed to find a page in the DOM, check the URL to see if it
  		// refers to the first page in the application. If it isn't a reference
  		// to the first page and refers to non-existent embedded page, error out.
  		if ( page.length === 0 ) {
  			if ( $.mobile.firstPage && path.isFirstPageUrl( fileUrl ) ) {
  				// Check to make sure our cached-first-page is actually
  				// in the DOM. Some user deployed apps are pruning the first
  				// page from the DOM for various reasons, we check for this
  				// case here because we don't want a first-page with an id
  				// falling through to the non-existent embedded page error
  				// case. If the first-page is not in the DOM, then we let
  				// things fall through to the ajax loading code below so
  				// that it gets reloaded.
  				if ( $.mobile.firstPage.parent().length ) {
  					page = $( $.mobile.firstPage );
  				}
  			} else if ( path.isEmbeddedPage( fileUrl )  ) {
  				deferred.reject( absUrl, options );
  				return deferred.promise();
  			}
  		}
  
  		// If the page we are interested in is already in the DOM,
  		// and the caller did not indicate that we should force a
  		// reload of the file, we are done. Otherwise, track the
  		// existing page as a duplicated.
  		if ( page.length ) {
  			if ( !settings.reloadPage ) {
  				enhancePage( page, settings.role );
  				deferred.resolve( absUrl, options, page );
  				return deferred.promise();
  			}
  			dupCachedPage = page;
  		}
  
  		var mpc = settings.pageContainer,
  			pblEvent = new $.Event( "pagebeforeload" ),
  			triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings };
  
  		// Let listeners know we're about to load a page.
  		mpc.trigger( pblEvent, triggerData );
  
  		// If the default behavior is prevented, stop here!
  		if ( pblEvent.isDefaultPrevented() ) {
  			return deferred.promise();
  		}
  
  		if ( settings.showLoadMsg ) {
  
  			// This configurable timeout allows cached pages a brief delay to load without showing a message
  			var loadMsgDelay = setTimeout(function() {
  					$.mobile.showPageLoadingMsg();
  				}, settings.loadMsgDelay ),
  
  				// Shared logic for clearing timeout and removing message.
  				hideMsg = function() {
  
  					// Stop message show timer
  					clearTimeout( loadMsgDelay );
  
  					// Hide loading message
  					$.mobile.hidePageLoadingMsg();
  				};
  		}
  
  		// Reset base to the default document base.
  		if ( base ) {
  			base.reset();
  		}
  
  		if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) {
  			deferred.reject( absUrl, options );
  		} else {
  			// Load the new page.
  			$.ajax({
  				url: fileUrl,
  				type: settings.type,
  				data: settings.data,
  				dataType: "html",
  				success: function( html, textStatus, xhr ) {
  					//pre-parse html to check for a data-url,
  					//use it as the new fileUrl, base path, etc
  					var all = $( "<div></div>" ),
  
  						//page title regexp
  						newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1,
  
  						// TODO handle dialogs again
  						pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)" ),
  						dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" );
  
  
  					// data-url must be provided for the base tag so resource requests can be directed to the
  					// correct url. loading into a temprorary element makes these requests immediately
  					if ( pageElemRegex.test( html ) &&
  							RegExp.$1 &&
  							dataUrlRegex.test( RegExp.$1 ) &&
  							RegExp.$1 ) {
  						url = fileUrl = path.getFilePath( $( "<div>" + RegExp.$1 + "</div>" ).text() );
  					}
  
  					//workaround to allow scripts to execute when included in page divs
  					all.get( 0 ).innerHTML = html;
  					page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first();
  
  					//if page elem couldn't be found, create one and insert the body element's contents
  					if ( !page.length ) {
  						page = $( "<div data-" + $.mobile.ns + "role='page'>" + html.split( /<\/?body[^>]*>/gmi )[1] + "</div>" );
  					}
  
  					if ( newPageTitle && !page.jqmData( "title" ) ) {
  						if ( ~newPageTitle.indexOf( "&" ) ) {
  							newPageTitle = $( "<div>" + newPageTitle + "</div>" ).text();
  						}
  						page.jqmData( "title", newPageTitle );
  					}
  
  					base.set( fileUrl, page );
  
  					//append to page and enhance
  					// TODO taging a page with external to make sure that embedded pages aren't removed
  					//      by the various page handling code is bad. Having page handling code in many
  					//      places is bad. Solutions post 1.0
  					page
  						.attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) )
  						.attr( "data-" + $.mobile.ns + "external-page", true )
  						.appendTo( settings.pageContainer );
  
  					// wait for page creation to leverage options defined on widget
  					page.one( 'pagecreate', $.mobile._bindPageRemove );
  
  					enhancePage( page, settings.role );
  
  					// Enhancing the page may result in new dialogs/sub pages being inserted
  					// into the DOM. If the original absUrl refers to a sub-page, that is the
  					// real page we are interested in.
  					if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) {
  						page = settings.pageContainer.children( "[data-" + $.mobile.ns +"url='" + dataUrl + "']" );
  					}
  
  					//bind pageHide to removePage after it's hidden, if the page options specify to do so
  					// Remove loading message.
  					if ( settings.showLoadMsg ) {
  						hideMsg();
  					}
  
  					// Add the page reference and xhr to our triggerData.
  					triggerData.xhr = xhr;
  					triggerData.textStatus = textStatus;
  					triggerData.page = page;
  
  					// Let listeners know the page loaded successfully.
  					settings.pageContainer.trigger( "pageload", triggerData );
  
  					deferred.resolve( absUrl, options, page, dupCachedPage );
  				},
  				error: function( xhr, textStatus, errorThrown ) {
  					//set base back to current path
  					base.set( path.get() );
  
  					// Add error info to our triggerData.
  					triggerData.xhr = xhr;
  					triggerData.textStatus = textStatus;
  					triggerData.errorThrown = errorThrown;
  
  					var plfEvent = new $.Event( "pageloadfailed" );
  
  					// Let listeners know the page load failed.
  					settings.pageContainer.trigger( plfEvent, triggerData );
  
  					// If the default behavior is prevented, stop here!
  					// Note that it is the responsibility of the listener/handler
  					// that called preventDefault(), to resolve/reject the
  					// deferred object within the triggerData.
  					if ( plfEvent.isDefaultPrevented() ) {
  						return;
  					}
  
  					// Remove loading message.
  					if ( settings.showLoadMsg ) {
  
  						// Remove loading message.
  						hideMsg();
  
  						// show error message
  						$.mobile.showPageLoadingMsg( $.mobile.pageLoadErrorMessageTheme, $.mobile.pageLoadErrorMessage, true );
  
  						// hide after delay
  						setTimeout( $.mobile.hidePageLoadingMsg, 1500 );
  					}
  
  					deferred.reject( absUrl, options );
  				}
  			});
  		}
  
  		return deferred.promise();
  	};
  
  	$.mobile.loadPage.defaults = {
  		type: "get",
  		data: undefined,
  		reloadPage: false,
  		role: undefined, // By default we rely on the role defined by the @data-role attribute.
  		showLoadMsg: false,
  		pageContainer: undefined,
  		loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message.
  	};
  
  	// Show a specific page in the page container.
  	$.mobile.changePage = function( toPage, options ) {
  		// If we are in the midst of a transition, queue the current request.
  		// We'll call changePage() once we're done with the current transition to
  		// service the request.
  		if ( isPageTransitioning ) {
  			pageTransitionQueue.unshift( arguments );
  			return;
  		}
  
  		var settings = $.extend( {}, $.mobile.changePage.defaults, options ), isString;
  
  		// Make sure we have a pageContainer to work with.
  		settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
  
  		// Make sure we have a fromPage.
  		settings.fromPage = settings.fromPage || $.mobile.activePage;
  
  		isString = (typeof toPage === "string");
  
  		var mpc = settings.pageContainer,
  			pbcEvent = new $.Event( "pagebeforechange" ),
  			triggerData = { toPage: toPage, options: settings };
  
  		// NOTE: preserve the original target as the dataUrl value will be simplified
  		//       eg, removing ui-state, and removing query params from the hash
  		//       this is so that users who want to use query params have access to them
  		//       in the event bindings for the page life cycle See issue #5085
  		if ( isString ) {
  			// if the toPage is a string simply convert it
  			triggerData.absUrl = path.makeUrlAbsolute( toPage, findBaseWithDefault() );
  		} else {
  			// if the toPage is a jQuery object grab the absolute url stored
  			// in the loadPage callback where it exists
  			triggerData.absUrl = toPage.data( 'absUrl' );
  		}
  
  		// Let listeners know we're about to change the current page.
  		mpc.trigger( pbcEvent, triggerData );
  
  		// If the default behavior is prevented, stop here!
  		if ( pbcEvent.isDefaultPrevented() ) {
  			return;
  		}
  
  		// We allow "pagebeforechange" observers to modify the toPage in the trigger
  		// data to allow for redirects. Make sure our toPage is updated.
  
  		toPage = triggerData.toPage;
  
  		// Set the isPageTransitioning flag to prevent any requests from
  		// entering this method while we are in the midst of loading a page
  		// or transitioning.
  
  		isPageTransitioning = true;
  
  		// If the caller passed us a url, call loadPage()
  		// to make sure it is loaded into the DOM. We'll listen
  		// to the promise object it returns so we know when
  		// it is done loading or if an error ocurred.
  		if ( isString ) {
  			$.mobile.loadPage( toPage, settings )
  				.done(function( url, options, newPage, dupCachedPage ) {
  					isPageTransitioning = false;
  					options.duplicateCachedPage = dupCachedPage;
  
  					// store the original absolute url so that it can be provided
  					// to events in the triggerData of the subsequent changePage call
  					newPage.data( 'absUrl', triggerData.absUrl );
  					$.mobile.changePage( newPage, options );
  				})
  				.fail(function( url, options ) {
  					isPageTransitioning = false;
  
  					//clear out the active button state
  					removeActiveLinkClass( true );
  
  					//release transition lock so navigation is free again
  					releasePageTransitionLock();
  					settings.pageContainer.trigger( "pagechangefailed", triggerData );
  				});
  			return;
  		}
  
  		// If we are going to the first-page of the application, we need to make
  		// sure settings.dataUrl is set to the application document url. This allows
  		// us to avoid generating a document url with an id hash in the case where the
  		// first-page of the document has an id attribute specified.
  		if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) {
  			settings.dataUrl = documentUrl.hrefNoHash;
  		}
  
  		// The caller passed us a real page DOM element. Update our
  		// internal state and then trigger a transition to the page.
  		var fromPage = settings.fromPage,
  			url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ),
  			// The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
  			pageUrl = url,
  			fileUrl = path.getFilePath( url ),
  			active = urlHistory.getActive(),
  			activeIsInitialPage = urlHistory.activeIndex === 0,
  			historyDir = 0,
  			pageTitle = document.title,
  			isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
  
  		// By default, we prevent changePage requests when the fromPage and toPage
  		// are the same element, but folks that generate content manually/dynamically
  		// and reuse pages want to be able to transition to the same page. To allow
  		// this, they will need to change the default value of allowSamePageTransition
  		// to true, *OR*, pass it in as an option when they manually call changePage().
  		// It should be noted that our default transition animations assume that the
  		// formPage and toPage are different elements, so they may behave unexpectedly.
  		// It is up to the developer that turns on the allowSamePageTransitiona option
  		// to either turn off transition animations, or make sure that an appropriate
  		// animation transition is used.
  		if ( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) {
  			isPageTransitioning = false;
  			mpc.trigger( "pagechange", triggerData );
  
  			// Even if there is no page change to be done, we should keep the urlHistory in sync with the hash changes
  			if ( settings.fromHashChange ) {
  				urlHistory.directHashChange({
  					currentUrl:	url,
  					isBack:		function() {},
  					isForward:	function() {}
  				});
  			}
  
  			return;
  		}
  
  		// We need to make sure the page we are given has already been enhanced.
  		enhancePage( toPage, settings.role );
  
  		// If the changePage request was sent from a hashChange event, check to see if the
  		// page is already within the urlHistory stack. If so, we'll assume the user hit
  		// the forward/back button and will try to match the transition accordingly.
  		if ( settings.fromHashChange ) {
  			urlHistory.directHashChange({
  				currentUrl:	url,
  				isBack:		function() { historyDir = -1; },
  				isForward:	function() { historyDir = 1; }
  			});
  		}
  
  		// Kill the keyboard.
  		// XXX_jblas: We need to stop crawling the entire document to kill focus. Instead,
  		//            we should be tracking focus with a delegate() handler so we already have
  		//            the element in hand at this point.
  		// Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement
  		// is undefined when we are in an IFrame.
  		try {
  			if ( document.activeElement && document.activeElement.nodeName.toLowerCase() !== 'body' ) {
  				$( document.activeElement ).blur();
  			} else {
  				$( "input:focus, textarea:focus, select:focus" ).blur();
  			}
  		} catch( e ) {}
  
  		// Record whether we are at a place in history where a dialog used to be - if so, do not add a new history entry and do not change the hash either
  		var alreadyThere = false;
  
  		// If we're displaying the page as a dialog, we don't want the url
  		// for the dialog content to be used in the hash. Instead, we want
  		// to append the dialogHashKey to the url of the current page.
  		if ( isDialog && active ) {
  			// on the initial page load active.url is undefined and in that case should
  			// be an empty string. Moving the undefined -> empty string back into
  			// urlHistory.addNew seemed imprudent given undefined better represents
  			// the url state
  
  			// If we are at a place in history that once belonged to a dialog, reuse
  			// this state without adding to urlHistory and without modifying the hash.
  			// However, if a dialog is already displayed at this point, and we're
  			// about to display another dialog, then we must add another hash and
  			// history entry on top so that one may navigate back to the original dialog
  			if ( active.url.indexOf( dialogHashKey ) > -1 && !$.mobile.activePage.is( ".ui-dialog" ) ) {
  				settings.changeHash = false;
  				alreadyThere = true;
  			}
  
  			// Normally, we tack on a dialog hash key, but if this is the location of a stale dialog,
  			// we reuse the URL from the entry
  			url = ( active.url || "" ) + ( alreadyThere ? "" : dialogHashKey );
  
  			// tack on another dialogHashKey if this is the same as the initial hash
  			// this makes sure that a history entry is created for this dialog
  			if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
  				url += dialogHashKey;
  			}
  		}
  
  		// Set the location hash.
  		if ( settings.changeHash !== false && url ) {
  			//disable hash listening temporarily
  			urlHistory.ignoreNextHashChange = true;
  			//update hash and history
  			path.set( url );
  		}
  
  		// if title element wasn't found, try the page div data attr too
  		// If this is a deep-link or a reload ( active === undefined ) then just use pageTitle
  		var newPageTitle = ( !active )? pageTitle : toPage.jqmData( "title" ) || toPage.children( ":jqmData(role='header')" ).find( ".ui-title" ).getEncodedText();
  		if ( !!newPageTitle && pageTitle === document.title ) {
  			pageTitle = newPageTitle;
  		}
  		if ( !toPage.jqmData( "title" ) ) {
  			toPage.jqmData( "title", pageTitle );
  		}
  
  		// Make sure we have a transition defined.
  		settings.transition = settings.transition ||
  			( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined ) ||
  			( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition );
  
  		//add page to history stack if it's not back or forward
  		if ( !historyDir ) {
  			// Overwrite the current entry if it's a leftover from a dialog
  			if ( alreadyThere ) {
  				urlHistory.activeIndex = Math.max( 0, urlHistory.activeIndex - 1 );
  			}
  
  			urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
  		}
  
  		//set page title
  		document.title = urlHistory.getActive().title;
  
  		//set "toPage" as activePage
  		$.mobile.activePage = toPage;
  
  		// If we're navigating back in the URL history, set reverse accordingly.
  		settings.reverse = settings.reverse || historyDir < 0;
  
  		transitionPages( toPage, fromPage, settings.transition, settings.reverse )
  			.done(function( name, reverse, $to, $from, alreadyFocused ) {
  				removeActiveLinkClass();
  
  				//if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
  				if ( settings.duplicateCachedPage ) {
  					settings.duplicateCachedPage.remove();
  				}
  
  				// Send focus to the newly shown page. Moved from promise .done binding in transitionPages
  				// itself to avoid ie bug that reports offsetWidth as > 0 (core check for visibility)
  				// despite visibility: hidden addresses issue #2965
  				// https://github.com/jquery/jquery-mobile/issues/2965
  				if ( !alreadyFocused ) {
  					$.mobile.focusPage( toPage );
  				}
  
  				releasePageTransitionLock();
  
  				// Let listeners know we're all done changing the current page.
  				mpc.trigger( "pagechange", triggerData );
  			});
  	};
  
  	$.mobile.changePage.defaults = {
  		transition: undefined,
  		reverse: false,
  		changeHash: true,
  		fromHashChange: false,
  		role: undefined, // By default we rely on the role defined by the @data-role attribute.
  		duplicateCachedPage: undefined,
  		pageContainer: undefined,
  		showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
  		dataUrl: undefined,
  		fromPage: undefined,
  		allowSamePageTransition: false
  	};
  
  	/* Event Bindings - hashchange, submit, and click */
  	function findClosestLink( ele )	{
  		while ( ele ) {
  			// Look for the closest element with a nodeName of "a".
  			// Note that we are checking if we have a valid nodeName
  			// before attempting to access it. This is because the
  			// node we get called with could have originated from within
  			// an embedded SVG document where some symbol instance elements
  			// don't have nodeName defined on them, or strings are of type
  			// SVGAnimatedString.
  			if ( ( typeof ele.nodeName === "string" ) && ele.nodeName.toLowerCase() === "a" ) {
  				break;
  			}
  			ele = ele.parentNode;
  		}
  		return ele;
  	}
  
  	// The base URL for any given element depends on the page it resides in.
  	function getClosestBaseUrl( ele )	{
  		// Find the closest page and extract out its url.
  		var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ),
  			base = documentBase.hrefNoHash;
  
  		if ( !$.mobile.dynamicBaseEnabled || !url || !path.isPath( url ) ) {
  			url = base;
  		}
  
  		return path.makeUrlAbsolute( url, base );
  	}
  
  	//The following event bindings should be bound after mobileinit has been triggered
  	//the following deferred is resolved in the init file
  	$.mobile.navreadyDeferred = $.Deferred();
  	$.mobile._registerInternalEvents = function() {
  		//bind to form submit events, handle with Ajax
  		$( document ).delegate( "form", "submit", function( event ) {
  			var $this = $( this );
  
  			if ( !$.mobile.ajaxEnabled ||
  					// test that the form is, itself, ajax false
  					$this.is( ":jqmData(ajax='false')" ) ||
  					// test that $.mobile.ignoreContentEnabled is set and
  					// the form or one of it's parents is ajax=false
  					!$this.jqmHijackable().length ) {
  				return;
  			}
  
  			var type = $this.attr( "method" ),
  				target = $this.attr( "target" ),
  				url = $this.attr( "action" );
  
  			// If no action is specified, browsers default to using the
  			// URL of the document containing the form. Since we dynamically
  			// pull in pages from external documents, the form should submit
  			// to the URL for the source document of the page containing
  			// the form.
  			if ( !url ) {
  				// Get the @data-url for the page containing the form.
  				url = getClosestBaseUrl( $this );
  				if ( url === documentBase.hrefNoHash ) {
  					// The url we got back matches the document base,
  					// which means the page must be an internal/embedded page,
  					// so default to using the actual document url as a browser
  					// would.
  					url = documentUrl.hrefNoSearch;
  				}
  			}
  
  			url = path.makeUrlAbsolute(  url, getClosestBaseUrl( $this ) );
  
  			if ( ( path.isExternal( url ) && !path.isPermittedCrossDomainRequest( documentUrl, url ) ) || target ) {
  				return;
  			}
  
  			$.mobile.changePage(
  				url,
  				{
  					type:		type && type.length && type.toLowerCase() || "get",
  					data:		$this.serialize(),
  					transition:	$this.jqmData( "transition" ),
  					reverse:	$this.jqmData( "direction" ) === "reverse",
  					reloadPage:	true
  				}
  			);
  			event.preventDefault();
  		});
  
  		//add active state on vclick
  		$( document ).bind( "vclick", function( event ) {
  			// if this isn't a left click we don't care. Its important to note
  			// that when the virtual event is generated it will create the which attr
  			if ( event.which > 1 || !$.mobile.linkBindingEnabled ) {
  				return;
  			}
  
  			var link = findClosestLink( event.target );
  
  			// split from the previous return logic to avoid find closest where possible
  			// TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
  			// can be avoided
  			if ( !$( link ).jqmHijackable().length ) {
  				return;
  			}
  
  			if ( link ) {
  				if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
  					removeActiveLinkClass( true );
  					$activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
  					$activeClickedLink.addClass( $.mobile.activeBtnClass );
  				}
  			}
  		});
  
  		// click routing - direct to HTTP or Ajax, accordingly
  		$( document ).bind( "click", function( event ) {
  			if ( !$.mobile.linkBindingEnabled ) {
  				return;
  			}
  
  			var link = findClosestLink( event.target ), $link = $( link ), httpCleanup;
  
  			// If there is no link associated with the click or its not a left
  			// click we want to ignore the click
  			// TODO teach $.mobile.hijackable to operate on raw dom elements so the link wrapping
  			// can be avoided
  			if ( !link || event.which > 1 || !$link.jqmHijackable().length ) {
  				return;
  			}
  
  			//remove active link class if external (then it won't be there if you come back)
  			httpCleanup = function() {
  				window.setTimeout(function() { removeActiveLinkClass( true ); }, 200 );
  			};
  
  			//if there's a data-rel=back attr, go back in history
  			if ( $link.is( ":jqmData(rel='back')" ) ) {
  				$.mobile.back();
  				return false;
  			}
  
  			var baseUrl = getClosestBaseUrl( $link ),
  
  				//get href, if defined, otherwise default to empty hash
  				href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
  
  			//if ajax is disabled, exit early
  			if ( !$.mobile.ajaxEnabled && !path.isEmbeddedPage( href ) ) {
  				httpCleanup();
  				//use default click handling
  				return;
  			}
  
  			// 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 ) {
  					//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.
  
  				//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 ) && !path.isPermittedCrossDomainRequest( documentUrl, href ) );
  
  			if ( isExternal ) {
  				httpCleanup();
  				//use default click handling
  				return;
  			}
  
  			//use ajax
  			var transition = $link.jqmData( "transition" ),
  				reverse = $link.jqmData( "direction" ) === "reverse" ||
  							// deprecated - remove by 1.0
  							$link.jqmData( "back" ),
  
  				//this may need to be more specific as we use data-rel more
  				role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
  
  			$.mobile.changePage( href, { transition: transition, reverse: reverse, role: role, link: $link } );
  			event.preventDefault();
  		});
  
  		//prefetch pages when anchors with data-prefetch are encountered
  		$( document ).delegate( ".ui-page", "pageshow.prefetch", function() {
  			var urls = [];
  			$( this ).find( "a:jqmData(prefetch)" ).each(function() {
  				var $link = $( this ),
  					url = $link.attr( "href" );
  
  				if ( url && $.inArray( url, urls ) === -1 ) {
  					urls.push( url );
  
  					$.mobile.loadPage( url, { role: $link.attr( "data-" + $.mobile.ns + "rel" ) } );
  				}
  			});
  		});
  
  		$.mobile._handleHashChange = function( hash ) {
  			//find first page via hash
  			var to = path.stripHash( hash ),
  				//transition is false if it's the first page, undefined otherwise (and may be overridden by default)
  				transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
  
  				// "navigate" event fired to allow others to take advantage of the more robust hashchange handling
  				navEvent = new $.Event( "navigate" ),
  
  				// default options for the changPage calls made after examining the current state
  				// of the page and the hash
  				changePageOptions = {
  					transition: transition,
  					changeHash: false,
  					fromHashChange: true
  				};
  
  			if ( 0 === urlHistory.stack.length ) {
  				urlHistory.initialDst = to;
  			}
  
  			// We should probably fire the "navigate" event from those places that make calls to _handleHashChange,
  			// and have _handleHashChange hook into the "navigate" event instead of triggering it here
  			$.mobile.pageContainer.trigger( navEvent );
  			if ( navEvent.isDefaultPrevented() ) {
  				return;
  			}
  
  			//if listening is disabled (either globally or temporarily), or it's a dialog hash
  			if ( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
  				urlHistory.ignoreNextHashChange = false;
  				return;
  			}
  
  			// special case for dialogs
  			if ( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 && urlHistory.initialDst !== to ) {
  
  				// If current active page is not a dialog skip the dialog and continue
  				// in the same direction
  				if ( !$.mobile.activePage.is( ".ui-dialog" ) ) {
  					//determine if we're heading forward or backward and continue accordingly past
  					//the current dialog
  					urlHistory.directHashChange({
  						currentUrl: to,
  						isBack: function() { $.mobile.back(); },
  						isForward: function() { window.history.forward(); }
  					});
  
  					// prevent changePage()
  					return;
  				} else {
  					// if the current active page is a dialog and we're navigating
  					// to a dialog use the dialog objected saved in the stack
  					urlHistory.directHashChange({
  						currentUrl: to,
  
  						// regardless of the direction of the history change
  						// do the following
  						either: function( isBack ) {
  							var active = $.mobile.urlHistory.getActive();
  
  							to = active.pageUrl;
  
  							// make sure to set the role, transition and reversal
  							// as most of this is lost by the domCache cleaning
  							$.extend( changePageOptions, {
  								role: active.role,
  								transition: active.transition,
  								reverse: isBack
  							});
  						}
  					});
  				}
  			}
  
  			//if to is defined, load it
  			if ( to ) {
  				// At this point, 'to' can be one of 3 things, a cached page element from
  				// a history stack entry, an id, or site-relative/absolute URL. If 'to' is
  				// an id, we need to resolve it against the documentBase, not the location.href,
  				// since the hashchange could've been the result of a forward/backward navigation
  				// that crosses from an external page/dialog to an internal page/dialog.
  				to = ( typeof to === "string" && !path.isPath( to ) ) ? ( path.makeUrlAbsolute( '#' + to, documentBase ) ) : to;
  
  				// If we're about to go to an initial URL that contains a reference to a non-existent
  				// internal page, go to the first page instead. We know that the initial hash refers to a
  				// non-existent page, because the initial hash did not end up in the initial urlHistory entry
  				if ( to === path.makeUrlAbsolute( '#' + urlHistory.initialDst, documentBase ) &&
  					urlHistory.stack.length && urlHistory.stack[0].url !== urlHistory.initialDst.replace( dialogHashKey, "" ) ) {
  					to = $.mobile.firstPage;
  				}
  				$.mobile.changePage( to, changePageOptions );
  			}	else {
  				//there's no hash, go to the first page in the dom
  				$.mobile.changePage( $.mobile.firstPage, changePageOptions );
  			}
  		};
  
  		//hashchange event handler
  		$window.bind( "hashchange", function( e, triggered ) {
  			// Firefox auto-escapes the location.hash as for v13 but
  			// leaves the href untouched
  			$.mobile._handleHashChange( path.parseLocation().hash );
  		});
  
  		//set page min-heights to be device specific
  		$( document ).bind( "pageshow", resetActivePageHeight );
  		$( window ).bind( "throttledresize", resetActivePageHeight );
  
  	};//navreadyDeferred done callback
  	$.mobile.navreadyDeferred.done( function() { $.mobile._registerInternalEvents(); } );
  
  })( jQuery );
  //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
  });
  //>>excludeEnd("jqmBuildExclude");
  