Merge branch 'master' of github.com:maxious/ACTBus-ui
[busui.git] / js / jquery.mobile-1.0b3.js
blob:a/js/jquery.mobile-1.0b3.js -> blob:b/js/jquery.mobile-1.0b3.js
--- a/js/jquery.mobile-1.0b3.js
+++ b/js/jquery.mobile-1.0b3.js
@@ -1,5 +1,5 @@
 /*!
- * jQuery Mobile v1.0b2
+ * jQuery Mobile v1.0b3
  * http://jquerymobile.com/
  *
  * Copyright 2010, jQuery Project
@@ -356,7 +356,7 @@
 
 var fakeBody = $( "<body>" ).prependTo( "html" ),
 	fbCSS = fakeBody[ 0 ].style,
-	vendors = [ "webkit", "moz", "o" ],
+	vendors = [ "Webkit", "Moz", "O" ],
 	webos = "palmGetResource" in window, //only used to rule out scrollTop
 	bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
 
@@ -366,7 +366,7 @@
 		props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
 
 	for ( var v in props ){
-		if ( fbCSS[ v ] !== undefined ) {
+		if ( fbCSS[ props[ v ] ] !== undefined ) {
 			return true;
 		}
 	}
@@ -415,9 +415,10 @@
 	orientation: "orientation" in window,
 	touch: "ontouchend" in document,
 	cssTransitions: "WebKitTransitionEvent" in window,
-	pushState: !!history.pushState,
+	pushState: "pushState" in history && "replaceState" in history,
 	mediaquery: $.mobile.media( "only all" ),
 	cssPseudoElement: !!propExists( "content" ),
+	touchOverflow: !!propExists( "overflowScrolling" ),
 	boxShadow: !!propExists( "boxShadow" ) && !bb,
 	scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
 	dynamicBaseTag: baseTagTest(),
@@ -631,8 +632,7 @@
 }
 
 function triggerVirtualEvent( eventType, event, flags ) {
-	var defaultPrevented = false,
-		ve;
+	var ve;
 
 	if ( ( flags && flags[ eventType ] ) ||
 				( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
@@ -640,18 +640,27 @@
 		ve = createVirtualEvent( event, eventType );
 
 		$( event.target).trigger( ve );
-
-		defaultPrevented = ve.isDefaultPrevented();
 	}
 
-	return defaultPrevented;
+	return ve;
 }
 
 function mouseEventCallback( event ) {
 	var touchID = $.data(event.target, touchTargetPropertyName);
 
 	if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
-		triggerVirtualEvent( "v" + event.type, event );
+		var ve = triggerVirtualEvent( "v" + event.type, event );
+		if ( ve ) {
+			if ( ve.isDefaultPrevented() ) {
+				event.preventDefault();
+			}
+			if ( ve.isPropagationStopped() ) {
+				event.stopPropagation();
+			}
+			if ( ve.isImmediatePropagationStopped() ) {
+				event.stopImmediatePropagation();
+			}
+		}
 	}
 }
 
@@ -731,7 +740,8 @@
 	triggerVirtualEvent( "vmouseup", event, flags );
 
 	if ( !didScroll ) {
-		if ( triggerVirtualEvent( "vclick", event, flags ) ) {
+		var ve = triggerVirtualEvent( "vclick", event, flags );
+		if ( ve && ve.isDefaultPrevented() ) {
 			// The target of the mouse events that follow the touchend
 			// event don't necessarily match the target used during the
 			// touch. This means we need to rely on coordinates for blocking
@@ -1028,16 +1038,19 @@
 				return false;
 			}
 
-			var touching = true,
-				origTarget = event.target,
+			var origTarget = event.target,
 				origEvent = event.originalEvent,
 				timer;
 
+			function clearTapTimer() {
+				clearTimeout( timer );
+			}
+
 			function clearTapHandlers() {
-				touching = false;
-				clearTimeout(timer);
+				clearTapTimer();
 
 				$this.unbind( "vclick", clickHandler )
+					.unbind( "vmouseup", clearTapTimer )
 					.unbind( "vmousecancel", clearTapHandlers );
 			}
 
@@ -1052,12 +1065,11 @@
 			}
 
 			$this.bind( "vmousecancel", clearTapHandlers )
+				.bind( "vmouseup", clearTapTimer )
 				.bind( "vclick", clickHandler );
 
 			timer = setTimeout(function() {
-				if ( touching ) {
-					triggerCustomEvent( thisObject, "taphold", event );
-				}
+					triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
 			}, 750 );
 		});
 	}
@@ -1657,14 +1669,12 @@
 	},
 
 	_create: function() {
-		var $elem = this.element,
-			o = this.options;
-
-		if ( this._trigger( "beforeCreate" ) === false ) {
-			return;
-		}
-
-		$elem.addClass( "ui-page ui-body-" + o.theme );
+
+		this._trigger( "beforecreate" );
+
+		this.element
+			.attr( "tabindex", "0" )
+			.addClass( "ui-page ui-body-" + this.options.theme );
 	}
 });
 
@@ -1707,7 +1717,7 @@
 		defaultPageTransition: "slide",
 
 		// Minimum scroll distance that will be remembered when returning to a page
-		minScrollBack: screen.height / 2,
+		minScrollBack: 250,
 
 		// Set default dialog transition - 'none' for no transitions
 		defaultDialogTransition: "pop",
@@ -1718,9 +1728,11 @@
 
 		// Error response message - appears when an Ajax page request fails
 		pageLoadErrorMessage: "Error Loading Page",
-		
+
 		//automatically initialize the DOM when it's ready
 		autoInitializePage: true,
+
+		pushStateEnabled: true,
 
 		// Support conditions that must be met in order to proceed
 		// default enhanced qualifications are media query support OR IE 7+
@@ -1835,6 +1847,7 @@
 		return $.find( expr, null, null, [ node ] ).length > 0;
 	};
 })( jQuery, this );
+
 /*
 * jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt,
 * Copyright (c) jQuery Project
@@ -1875,7 +1888,7 @@
 			//    [15]: ?msg=1234&type=unread
 			//    [16]: #msg-content
 			//
-			urlParseRE: /^(((([^:\/#\?]+:)?(?:\/\/((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?]+)(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
+			urlParseRE: /^(((([^:\/#\?]+:)?(?:\/\/((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
 
 			//Parse a URL into a structure that allows easy access to
 			//all of the URL components by name.
@@ -2053,6 +2066,26 @@
 				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 );
 
@@ -2074,7 +2107,7 @@
 		//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. 
+			// 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: [],
 
@@ -2095,13 +2128,13 @@
 			},
 
 			// addNew is used whenever a new page is added
-			addNew: function( url, transition, title, pageUrl ) {
+			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 } );
+				urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
 
 				urlHistory.activeIndex = urlHistory.stack.length - 1;
 			},
@@ -2112,7 +2145,7 @@
 			},
 
 			directHashChange: function( opts ) {
-				var back , forward, newActiveIndex;
+				var back , forward, newActiveIndex, prev = this.getActive();
 
 				// check if url isp in history and if it's ahead or behind current page
 				$.each( urlHistory.stack, function( i, historyEntry ) {
@@ -2130,9 +2163,9 @@
 				this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;
 
 				if( back ) {
-					opts.isBack();
+					( opts.either || opts.isBack )( true );
 				} else if( forward ) {
-					opts.isForward();
+					( opts.either || opts.isForward )( false );
 				}
 			},
 
@@ -2191,20 +2224,13 @@
 
 	//direct focus to the page title, or otherwise first focusable element
 	function reFocus( page ) {
-		var lastClicked = page.jqmData( "lastClicked" );
-
-		if( lastClicked && lastClicked.length ) {
-			lastClicked.focus();
-		}
-		else {
-			var pageTitle = page.find( ".ui-title:eq(0)" );
-
-			if( pageTitle.length ) {
-				pageTitle.focus();
-			}
-			else{
-				page.find( focusable ).eq( 0 ).focus();
-			}
+		var pageTitle = page.find( ".ui-title:eq(0)" );
+
+		if( pageTitle.length ) {
+			pageTitle.focus();
+		}
+		else{
+			page.focus();
 		}
 	}
 
@@ -2222,41 +2248,94 @@
 			$.mobile.changePage.apply( null, pageTransitionQueue.pop() );
 		}
 	}
+	
+	// Save the last scroll distance per page, before it is hidden
+	var getLastScroll = (function( lastScrollEnabled ){
+		return function(){
+			if( !lastScrollEnabled ){
+				lastScrollEnabled = true;
+				return;
+			}
+			
+			lastScrollEnabled = false;
+		
+			var active = $.mobile.urlHistory.getActive(),
+				activePage = $( ".ui-page-active" ),
+				scrollElem = $( window ),
+				touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled;
+			
+			if( touchOverflow ){
+				scrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage;
+			}	
+			
+			if( active ){
+				var lastScroll = scrollElem.scrollTop();
+
+				// Set active page's lastScroll prop.
+				// If the Y location we're scrolling to is less than minScrollBack, let it go.
+				active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
+			}
+		};
+	})( true );
+	
+	// to get last scroll, we need to get scrolltop before the page change
+	// using beforechangepage or popstate/hashchange (whichever comes first)
+	$( document ).bind( "beforechangepage", getLastScroll );
+	$( window ).bind( $.support.pushState ? "popstate" : "hashchange", getLastScroll );
+	
+	// Make the iOS clock quick-scroll work again if we're using native overflow scrolling
+	/*
+	if( $.support.touchOverflow ){
+		if( $.mobile.touchOverflowEnabled ){
+			$( window ).bind( "scrollstop", function(){
+				if( $( this ).scrollTop() === 0 ){
+					$.mobile.activePage.scrollTop( 0 );
+				}
+			});
+		}
+	}
+	*/
 
 	//function for transitioning between two existing pages
 	function transitionPages( toPage, fromPage, transition, reverse ) {
 
 		//get current scroll distance
-		var currScroll = $.support.scrollTop ? $window.scrollTop() : true,
-			toScroll	= toPage.data( "lastScroll" ) || $.mobile.defaultHomeScroll,
+		var active	= $.mobile.urlHistory.getActive(),
+			touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
+			toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ),
 			screenHeight = getScreenHeight();
-
-		//if scrolled down, scroll to top
-		if( currScroll ){
-			window.scrollTo( 0, $.mobile.defaultHomeScroll );
-		}
-
-		//if the Y location we're scrolling to is less than 10px, let it go for sake of smoothness
-		if( toScroll < $.mobile.minScrollBack ){
-			toScroll = 0;
-		}
+			
+		// Scroll to top, hide addr bar
+		window.scrollTo( 0, $.mobile.defaultHomeScroll );
 
 		if( fromPage ) {
-			//set as data for returning to that spot
-			fromPage
-				.height( screenHeight + currScroll )
-				.jqmData( "lastScroll", currScroll )
-				.jqmData( "lastClicked", $activeClickedLink );
-
 			//trigger before show/hide events
 			fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
 		}
-		toPage
-			.height( screenHeight + toScroll )
-			.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
+		
+		if( !touchOverflow){
+			toPage.height( screenHeight + toScroll );
+		}	
+		
+		toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
 
 		//clear page loader
 		$.mobile.hidePageLoadingMsg();
+		
+		if( touchOverflow && toScroll ){
+			
+			toPage.addClass( "ui-mobile-pre-transition" );
+			// Send focus to page as it is now display: block
+			reFocus( toPage );
+			
+			//set page's scrollTop to remembered distance
+			if( toPage.is( ".ui-native-fixed" ) ){
+				toPage.find( ".ui-content" ).scrollTop( toScroll );
+			}
+			else{
+				toPage.scrollTop( toScroll );
+			}
+		}
 
 		//find the transition handler for the specified transition. If there
 		//isn't one in our transitionHandlers dictionary, use the default one.
@@ -2265,21 +2344,25 @@
 			promise = th( transition, reverse, toPage, fromPage );
 
 		promise.done(function() {
-			//reset toPage height bac
-			toPage.height( "" );
-
-			//jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
-			if( toScroll ){
+			//reset toPage height back
+			if( !touchOverflow ){
+				toPage.height( "" );
+				// Send focus to the newly shown page
+				reFocus( toPage );
+			}
+			
+			// Jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
+			if( !touchOverflow ){
 				$.mobile.silentScroll( toScroll );
-				$( document ).one( "silentscroll", function() { reFocus( toPage ); } );
-			}
-			else{
-				reFocus( toPage );
 			}
 
 			//trigger show/hide events
 			if( fromPage ) {
-				fromPage.height("").data( "page" )._trigger( "hide", null, { nextPage: toPage } );
+				if( !touchOverflow ){
+					fromPage.height( "" );
+				}
+				
+				fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
 			}
 
 			//trigger pageshow, define prevPage as either fromPage or empty jQuery obj
@@ -2300,6 +2383,8 @@
 
 		return pageMin;
 	}
+	
+	$.mobile.getScreenHeight = getScreenHeight;
 
 	//simply set the active page's minimum height to screen height, depending on orientation
 	function resetActivePageHeight(){
@@ -2349,6 +2434,8 @@
 	//history stack
 	$.mobile.urlHistory = urlHistory;
 
+	$.mobile.dialogHashKey = dialogHashKey;
+
 	//default non-animation transition handler
 	$.mobile.noneTransitionHandler = function( name, reverse, $toPage, $fromPage ) {
 		if ( $fromPage ) {
@@ -2433,6 +2520,12 @@
 
 		// Check to see if the page already exists in the DOM.
 		page = settings.pageContainer.children( ":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 ( page.length === 0 && $.mobile.firstPage && path.isFirstPageUrl( absUrl ) ) {
+			page = $( $.mobile.firstPage );
+		}
 
 		// Reset base to the default document base.
 		if ( base ) {
@@ -2453,18 +2546,18 @@
 		}
 
 		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();
 				};
@@ -2582,7 +2675,7 @@
 
 					// Remove loading message.
 					if ( settings.showLoadMsg ) {
-						
+
 						// Remove loading message.
 						hideMsg();
 
@@ -2661,16 +2754,38 @@
 			return;
 		}
 
+		var settings = $.extend( {}, $.mobile.changePage.defaults, options );
+
+		// 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;
+
+		var mpc = settings.pageContainer,
+			pbcEvent = new $.Event( "pagebeforechange" ),
+			triggerData = { toPage: toPage, options: settings };
+
+		// Let listeners know we're about to change the current page.
+		mpc.trigger( pbcEvent, triggerData );
+
+		mpc.trigger( "beforechangepage", triggerData ); // XXX: DEPRECATED for 1.0
+		
+		// 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;
-
-		var settings = $.extend( {}, $.mobile.changePage.defaults, options );
-
-		// Make sure we have a pageContainer to work with.
-		settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
 
 		// If the caller passed us a url, call loadPage()
 		// to make sure it is loaded into the DOM. We'll listen
@@ -2692,16 +2807,16 @@
 
 					//release transition lock so navigation is free again
 					releasePageTransitionLock();
-					settings.pageContainer.trigger("changepagefailed");
+					settings.pageContainer.trigger( "pagechangefailed", triggerData );
+					settings.pageContainer.trigger( "changepagefailed", triggerData ); // XXX: DEPRECATED for 1.0
 				});
 			return;
 		}
 
 		// The caller passed us a real page DOM element. Update our
 		// internal state and then trigger a transition to the page.
-		var mpc = settings.pageContainer,
-			fromPage = $.mobile.activePage,
-			url = toPage.jqmData( "url" ),
+		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 ),
@@ -2711,9 +2826,6 @@
 			pageTitle = document.title,
 			isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
 
-		// Let listeners know we're about to change the current page.
-		mpc.trigger( "beforechangepage" );
-
 		// 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 toPage is not an array or object (those are allowed to be "same")
@@ -2722,7 +2834,8 @@
 		//            to the same page.
 		if( fromPage && fromPage[0] === toPage[0] ) {
 			isPageTransitioning = false;
-			mpc.trigger( "changepage" );
+			mpc.trigger( "pagechange", triggerData );
+			mpc.trigger( "changepage", triggerData ); // XXX: DEPRECATED for 1.0
 			return;
 		}
 
@@ -2754,7 +2867,11 @@
 		// 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 ) {
-			url = active.url + dialogHashKey;
+			// 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
+			url = ( active.url || "" ) + dialogHashKey;
 		}
 
 		// Set the location hash.
@@ -2773,7 +2890,7 @@
 
 		//add page to history stack if it's not back or forward
 		if( !historyDir ) {
-			urlHistory.addNew( url, settings.transition, pageTitle, pageUrl );
+			urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
 		}
 
 		//set page title
@@ -2805,7 +2922,9 @@
 				releasePageTransitionLock();
 
 				// Let listeners know we're all done changing the current page.
-				mpc.trigger( "changepage" );
+				mpc.trigger( "pagechange", triggerData );
+
+				mpc.trigger( "changepage", triggerData ); // XXX: DEPRECATED for 1.0
 			});
 	};
 
@@ -2817,7 +2936,9 @@
 		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
+		showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
+		dataUrl: undefined,
+		fromPage: undefined
 	};
 
 /* Event Bindings - hashchange, submit, and click */
@@ -2905,7 +3026,9 @@
 			var link = findClosestLink( event.target );
 			if ( link ) {
 				if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
-					$( link ).closest( ".ui-btn" ).not( ".ui-disabled" ).addClass( $.mobile.activeBtnClass );
+					removeActiveLinkClass( true );
+					$activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
+					$activeClickedLink.addClass( $.mobile.activeBtnClass );
 					$( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
 				}
 			}
@@ -2983,8 +3106,6 @@
 				//     moved into more comprehensive isExternalLink
 				isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
 
-			$activeClickedLink = $link.closest( ".ui-btn" );
-
 			if( isExternal ) {
 				httpCleanup();
 				//use default click handling
@@ -3017,12 +3138,19 @@
 			});
 		} );
 
-		//hashchange event handler
-		$window.bind( "hashchange", function( e, triggered ) {
+		$.mobile._handleHashChange = function( hash ) {
 			//find first page via hash
-			var to = path.stripHash( location.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;
+				transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
+
+				// 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 listening is disabled (either globally or temporarily), or it's a dialog hash
 			if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
@@ -3031,8 +3159,7 @@
 			}
 
 			// special case for dialogs
-			if( urlHistory.stack.length > 1 &&
-					to.indexOf( dialogHashKey ) > -1 ) {
+			if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) {
 
 				// If current active page is not a dialog skip the dialog and continue
 				// in the same direction
@@ -3048,22 +3175,43 @@
 					// prevent changepage
 					return;
 				} else {
-					var setTo = function() { to = $.mobile.urlHistory.getActive().pageUrl; };
 					// 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, isBack: setTo, isForward: setTo	});
-				}
-			}
-			
+					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 ) {
 				to = ( typeof to === "string" && !path.isPath( to ) ) ? ( '#' + to ) : to;
-				$.mobile.changePage( to, { transition: transition, changeHash: false, fromHashChange: true } );
-			}
-			//there's no hash, go to the first page in the dom
-			else {
-				$.mobile.changePage( $.mobile.firstPage, { transition: transition, changeHash: false, fromHashChange: true } );
-			}
+				$.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 ) {
+			$.mobile._handleHashChange( location.hash );
 		});
 
 		//set page min-heights to be device specific
@@ -3073,7 +3221,122 @@
 	};//_registerInternalEvents callback
 
 })( jQuery );
-/*!
+/*
+* jQuery Mobile Framework : history.pushState support, layered on top of hashchange
+* Copyright (c) jQuery Project
+* Dual licensed under the MIT or GPL Version 2 licenses.
+* http://jquery.org/license
+*/
+( function( $, window ) {
+	// For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
+	// Scope self to pushStateHandler so we can reference it sanely within the
+	// methods handed off as event handlers
+	var	pushStateHandler = {},
+		self = pushStateHandler,
+		$win = $( window ),
+		url = $.mobile.path.parseUrl( location.href );
+
+	$.extend( pushStateHandler, {
+		// TODO move to a path helper, this is rather common functionality
+		initialFilePath: (function() {
+			return url.pathname + url.search;
+		})(),
+
+		initialHref: url.hrefNoHash,
+
+		// Flag for tracking if a Hashchange naturally occurs after each popstate + replace
+		hashchangeFired: false,
+
+		state: function() {
+			return {
+				hash: location.hash || "#" + self.initialFilePath,
+				title: document.title,
+
+				// persist across refresh
+				initialHref: self.initialHref
+			};
+		},
+
+		resetUIKeys: function( url ) {
+			var dialog = $.mobile.dialogHashKey,
+				subkey = "&" + $.mobile.subPageUrlKey,
+				dialogIndex = url.indexOf( dialog );
+
+			if( dialogIndex > -1 ) {
+				url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
+			} else if( url.indexOf( subkey ) > -1 ) {
+				url = url.split( subkey ).join( "#" + subkey );
+			}
+
+			return url;
+		},
+
+		// on hash change we want to clean up the url
+		// NOTE this takes place *after* the vanilla navigation hash change
+		// handling has taken place and set the state of the DOM
+		onHashChange: function( e ) {
+			var href, state;
+
+			self.hashchangeFired = true;
+
+			// only replaceState when the hash doesn't represent an embeded page
+			if( $.mobile.path.isPath(location.hash) ) {
+
+				// propulate the hash when its not available
+				state = self.state();
+
+				// make the hash abolute with the current href
+				href = $.mobile.path.makeUrlAbsolute( state.hash.replace("#", ""), location.href );
+
+				href = self.resetUIKeys( href );
+
+				// replace the current url with the new href and store the state
+				history.replaceState( state, document.title, href );
+			}
+		},
+
+		// on popstate (ie back or forward) we need to replace the hash that was there previously
+		// cleaned up by the additional hash handling
+		onPopState: function( e ) {
+			var poppedState = e.originalEvent.state, holdnexthashchange = false;
+
+			// if there's no state its not a popstate we care about, ie chrome's initial popstate
+			// or forward popstate
+			if( poppedState ) {
+				// disable any hashchange triggered by the browser
+				$.mobile.urlHistory.ignoreNextHashChange = true;
+
+				// defer our manual hashchange until after the browser fired
+				// version has come and gone
+				setTimeout(function() {
+					// make sure that the manual hash handling takes place
+					$.mobile.urlHistory.ignoreNextHashChange = false;
+					
+					// change the page based on the hash
+					$.mobile._handleHashChange( poppedState.hash );
+				}, 100);
+			}
+		},
+
+		init: function() {
+			$win.bind( "hashchange", self.onHashChange );
+
+			// Handle popstate events the occur through history changes
+			$win.bind( "popstate", self.onPopState );
+
+			// if there's no hash, we need to replacestate for returning to home
+			if ( location.hash === "" ) {
+				history.replaceState( self.state(), document.title, location.href );
+			}
+		}
+	});
+
+	$( function() {
+		if( $.mobile.pushStateEnabled && $.support.pushState ){
+			pushStateHandler.init();
+		}
+	});
+})( jQuery, this );/*!
  * jQuery Mobile v@VERSION
  * http://jquerymobile.com/
  *
@@ -3141,7 +3404,7 @@
 	month: false,
 	number: false,
 	range: "number",
-	search: true,
+	search: "text",
 	tel: false,
 	time: false,
 	url: false,
@@ -3324,7 +3587,9 @@
 
 		} else if ( role === "content" ) {
 
-			$this.addClass( "ui-body-" + ( theme || pageTheme || o.contentTheme ) );
+			if (theme || o.contentTheme) {
+			    $this.addClass( "ui-body-" + ( theme || o.contentTheme ) );
+			}
 
 			// Add ARIA role
 			$this.attr( "role", "main" );
@@ -3345,7 +3610,7 @@
 	options: {
 		expandCueText: " click to expand contents",
 		collapseCueText: " click to collapse contents",
-		collapsed: false,
+		collapsed: true,
 		heading: ">:header,>legend",
 		theme: null,
 		iconTheme: "d",
@@ -3653,22 +3918,26 @@
 			return orig + " ui-listview " + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
 		});
 
-		t.refresh();
+		t.refresh( true );
 	},
 
 	_itemApply: function( $list, item ) {
+		var $countli = item.find( ".ui-li-count" );
+		if ( $countli.length ) {
+			item.addClass( "ui-li-has-count" );
+		}
+		$countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
+
 		// TODO class has to be defined in markup
-		item.find( ".ui-li-count" )
-			.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" ).end()
-		.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
-		.find( "p, dl" ).addClass( "ui-li-desc" ).end()
-		.find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() {
-			item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
-		}).end()
-		.find( ".ui-li-aside" ).each(function() {
-			var $this = $(this);
-			$this.prependTo( $this.parent() ); //shift aside to front for css float
-		});
+		item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
+			.find( "p, dl" ).addClass( "ui-li-desc" ).end()
+			.find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() {
+				item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
+			}).end()
+			.find( ".ui-li-aside" ).each(function() {
+				var $this = $(this);
+				$this.prependTo( $this.parent() ); //shift aside to front for css float
+			});
 	},
 
 	_removeCorners: function( li, which ) {
@@ -3683,6 +3952,43 @@
 			li.removeClass( bot );
 		} else {
 			li.removeClass( top + " " + bot );
+		}
+	},
+
+	_refreshCorners: function( create ) {
+		var $li,
+			$visibleli,
+			$topli,
+			$bottomli;
+
+		if ( this.options.inset ) {
+			$li = this.element.children( "li" );
+			// at create time the li are not visible yet so we need to rely on .ui-screen-hidden
+			$visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
+			
+			this._removeCorners( $li );
+
+			// Select the first visible li element
+			$topli = $visibleli.first()
+				.addClass( "ui-corner-top" );
+
+			$topli.add( $topli.find( ".ui-btn-inner" ) )
+				.find( ".ui-li-link-alt" )
+					.addClass( "ui-corner-tr" )
+				.end()
+				.find( ".ui-li-thumb" )
+					.addClass( "ui-corner-tl" );
+
+			// Select the last visible li element
+			$bottomli = $visibleli.last()
+				.addClass( "ui-corner-bottom" );
+
+			$bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
+				.find( ".ui-li-link-alt" )
+					.addClass( "ui-corner-br" )
+				.end()
+				.find( ".ui-li-thumb" )
+					.addClass( "ui-corner-bl" );
 		}
 	},
 
@@ -3725,6 +4031,10 @@
 						icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
 						theme: itemTheme
 					});
+
+					if ( ( icon != false ) && ( a.length == 1 ) ) {
+						item.addClass( "ui-li-has-arrow" );
+					}
 
 					a.first().addClass( "ui-link-inherit" );
 
@@ -3771,40 +4081,6 @@
 				}
 			}
 
-			if ( o.inset ) {
-				if ( pos === 0 ) {
-					itemClass += " ui-corner-top";
-
-					item.add( item.find( ".ui-btn-inner" ) )
-						.find( ".ui-li-link-alt" )
-							.addClass( "ui-corner-tr" )
-						.end()
-						.find( ".ui-li-thumb" )
-							.addClass( "