Upgrade jQuery mobile to beta1
[busui.git] / js / jquery.mobile-1.0b1.js
blob:a/js/jquery.mobile-1.0b1.js -> blob:b/js/jquery.mobile-1.0b1.js
--- a/js/jquery.mobile-1.0b1.js
+++ b/js/jquery.mobile-1.0b1.js
@@ -1,5 +1,5 @@
 /*!
- * jQuery Mobile v1.0a4
+ * jQuery Mobile v1.0b1
  * http://jquerymobile.com/
  *
  * Copyright 2010, jQuery Project
@@ -396,7 +396,8 @@
 $(document).bind("mobileinit.htmlclass", function(){
 	/* bind to orientationchange and resize
 	to add classes to HTML element for min/max breakpoints and orientation */
-	$window.bind("orientationchange.htmlclass resize.htmlclass", function(event){
+	var ev = $.support.orientation;
+	$window.bind("orientationchange.htmlclass throttledResize.htmlclass", function(event){
 		//add orientation class to HTML element on flip/resize.
 		if(event.orientation){
 			$html.removeClass( "portrait landscape" ).addClass( event.orientation );
@@ -422,77 +423,78 @@
 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
 * Note: Code is in draft form and is subject to change 
 */
-(function($, undefined ) {
-
-
-
-var fakeBody = $( "<body>" ).prependTo( "html" ),
-	fbCSS = fakeBody[0].style,
-	vendors = ['webkit','moz','o'],
-	webos = window.palmGetResource || window.PalmServiceBridge, //only used to rule out scrollTop 
+( function( $, undefined  ) {
+
+var fakeBody = $( "<body>"  ).prependTo( "html" ),
+	fbCSS = fakeBody[ 0 ].style,
+	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
 
-//thx Modernizr
-function propExists( prop ){
-	var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1),
-		props   = (prop + ' ' + vendors.join(uc_prop + ' ') + uc_prop).split(' ');
-	for(var v in props){
-		if( fbCSS[ v ] !== undefined ){
+// thx Modernizr
+function propExists( prop  ){
+	var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
+		props   = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
+	for( var v in props ){
+		if( fbCSS[ v ] !== undefined  ){
 			return true;
 		}
 	}
-};
-
-//test for dynamic-updating base tag support (allows us to avoid href,src attr rewriting)
+}
+
+// test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
 function baseTagTest(){
-	var fauxBase = location.protocol + '//' + location.host + location.pathname + "ui-dir/",
-		base = $("head base"),
+	var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
+		base = $( "head base" ),
 		fauxEle = null,
-		href = '';
-	if (!base.length) {
-		base = fauxEle = $("<base>", {"href": fauxBase}).appendTo("head");
+		href = "";
+	if ( !base.length ) {
+		base = fauxEle = $( "<base>", { "href": fauxBase} ).appendTo( "head" );
 	}
 	else {
-		href = base.attr("href");
+		href = base.attr( "href" );
 	}
-	var link = $( "<a href='testurl'></a>" ).prependTo( fakeBody ),
-		rebase = link[0].href;
-	base[0].href = href ? href : location.pathname;
-	if (fauxEle) {
+	var link = $( "<a href='testurl'></a>"  ).prependTo( fakeBody  ),
+		rebase = link[ 0 ].href;
+	base[ 0 ].href = href ? href : location.pathname;
+	if ( fauxEle ) {
 		fauxEle.remove();
 	}
-	return rebase.indexOf(fauxBase) === 0;
-};
-
-
-//non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
-//allows for inclusion of IE 6+, including Windows Mobile 7
+	return rebase.indexOf( fauxBase ) === 0;
+}
+
+
+// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
+// allows for inclusion of IE 6+, including Windows Mobile 7
 $.mobile.browser = {};
-$.mobile.browser.ie = (function() {
-    var v = 3, div = document.createElement('div'), a = div.all || [];
-    while (div.innerHTML = '<!--[if gt IE '+(++v)+']><br><![endif]-->', a[0]); 
+$.mobile.browser.ie = ( function() {
+    var v = 3, 
+	div = document.createElement( "div" ), 
+	a = div.all || [];
+    while ( div.innerHTML = "<!--[if gt IE " + ( ++v ) + "]><br><![endif]-->", a[ 0 ] ); 
     return v > 4 ? v : !v;
-}());
+}() );
+
 
 $.extend( $.support, {
 	orientation: "orientation" in window,
 	touch: "ontouchend" in document,
 	cssTransitions: "WebKitTransitionEvent" in window,
 	pushState: !!history.pushState,
-	mediaquery: $.mobile.media('only all'),
-	cssPseudoElement: !!propExists('content'),
-	boxShadow: !!propExists('boxShadow') && !bb,
-	scrollTop: ("pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[0]) && !webos,
+	mediaquery: $.mobile.media( "only all" ),
+	cssPseudoElement: !!propExists( "content" ),
+	boxShadow: !!propExists( "boxShadow" ) && !bb,
+	scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
 	dynamicBaseTag: baseTagTest(),
-	eventCapture: ("addEventListener" in document) // This is a weak test. We may want to beef this up later.
-});
+	eventCapture: ( "addEventListener" in document ) // This is a weak test. We may want to beef this up later.
+} );
 
 fakeBody.remove();
 
-//for ruling out shadows via css
-if( !$.support.boxShadow ){ $('html').addClass('ui-mobile-nosupport-boxshadow'); }
-
-})( jQuery );/*
+// for ruling out shadows via css
+if( !$.support.boxShadow  ){ $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" ); }
+
+} )( jQuery  );/*
 * jQuery Mobile Framework : "mouse" plugin
 * Copyright (c) jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
@@ -524,12 +526,10 @@
 	resetTimerID = 0,
 	startX = 0,
 	startY = 0,
-	startScrollX = 0,
-	startScrollY = 0,
 	didScroll = false,
 	clickBlockList = [],
 	blockMouseTriggers = false,
-	scrollTopSupported = $.support.scrollTop,
+	blockTouchTriggers = false,
 	eventCaptureSupported = $.support.eventCapture,
 	$document = $(document),
 	nextTouchID = 1,
@@ -569,10 +569,12 @@
 	}
 	
 	if (t.search(/^touch/) !== -1){
-		var ne = getNativeEvent(oe);
-		if (typeof ne.touches !== "undefined" && ne.touches[0]){
-			var touch = ne.touches[0];
-			for (var i = 0; i < touchEventProps.length; i++){
+		var ne = getNativeEvent(oe),
+			t = ne.touches,
+			ct = ne.changedTouches,
+			touch = (t && t.length) ? t[0] : ((ct && ct.length) ? ct[0] : undefined);
+		if (touch){
+			for (var i = 0, len = touchEventProps.length; i < len; i++){
 				var prop = touchEventProps[i];
 				event[prop] = touch[prop];
 			}
@@ -585,62 +587,38 @@
 function getVirtualBindingFlags(element)
 {
 	var flags = {};
-	var $ele = $(element);
-	while ($ele && $ele.length){
-		var b = $ele.data(dataPropertyName);
+	while (element){
+		var b = $.data(element, dataPropertyName);
 		for (var k in b) {
 			if (b[k]){
 				flags[k] = flags.hasVirtualBinding = true;
 			}
 		}
-		$ele = $ele.parent();
+		element = element.parentNode;
 	}
 	return flags;
 }
 
 function getClosestElementWithVirtualBinding(element, eventType)
 {
-	var $ele = $(element);
-	while ($ele && $ele.length){
-		var b = $ele.data(dataPropertyName);
+	while (element){
+		var b = $.data(element, dataPropertyName);
 		if (b && (!eventType || b[eventType])) {
-			return $ele;
-		}
-		$ele = $ele.parent();
+			return element;
+		}
+		element = element.parentNode;
 	}
 	return null;
 }
 
 function enableTouchBindings()
 {
-	if (!activeDocHandlers["touchbindings"]){
-		$document.bind("touchend", handleTouchEnd)
-		
-			// On touch platforms, touching the screen and then dragging your finger
-			// causes the window content to scroll after some distance threshold is
-			// exceeded. On these platforms, a scroll prevents a click event from being
-			// dispatched, and on some platforms, even the touchend is suppressed. To
-			// mimic the suppression of the click event, we need to watch for a scroll
-			// event. Unfortunately, some platforms like iOS don't dispatch scroll
-			// events until *AFTER* the user lifts their finger (touchend). This means
-			// we need to watch both scroll and touchmove events to figure out whether
-			// or not a scroll happenens before the touchend event is fired.
-		
-			.bind("touchmove", handleTouchMove)
-			.bind("scroll", handleScroll);
-
-		activeDocHandlers["touchbindings"] = 1;
-	}
+	blockTouchTriggers = false;
 }
 
 function disableTouchBindings()
 {
-	if (activeDocHandlers["touchbindings"]){
-		$document.unbind("touchmove", handleTouchMove)
-			.unbind("touchend", handleTouchEnd)
-			.unbind("scroll", handleScroll);
-		activeDocHandlers["touchbindings"] = 0;
-	}
+	blockTouchTriggers = true;
 }
 
 function enableMouseBindings()
@@ -693,7 +671,7 @@
 
 function mouseEventCallback(event)
 {
-	var touchID = $(event.target).data(touchTargetPropertyName);
+	var touchID = $.data(event.target, touchTargetPropertyName);
 	if (!blockMouseTriggers && (!lastTouchID || lastTouchID !== touchID)){
 		triggerVirtualEvent("v" + event.type, event);
 	}
@@ -708,7 +686,7 @@
 	
 		if (flags.hasVirtualBinding){
 			lastTouchID = nextTouchID++;
-			$(target).data(touchTargetPropertyName, lastTouchID);
+			$.data(target, touchTargetPropertyName, lastTouchID);
 	
 			clearResetTimer();
 			
@@ -719,11 +697,6 @@
 			startX = t.pageX;
 			startY = t.pageY;
 		
-			if (scrollTopSupported){
-				startScrollX = window.pageXOffset;
-				startScrollY = window.pageYOffset;
-			}
-		
 			triggerVirtualEvent("vmouseover", event, flags);
 			triggerVirtualEvent("vmousedown", event, flags);
 		}
@@ -732,6 +705,10 @@
 
 function handleScroll(event)
 {
+	if (blockTouchTriggers){
+		return;
+	}
+
 	if (!didScroll){
 		triggerVirtualEvent("vmousecancel", event, getVirtualBindingFlags(event.target));
 	}
@@ -742,12 +719,15 @@
 
 function handleTouchMove(event)
 {
+	if (blockTouchTriggers){
+		return;
+	}
+
 	var t = getNativeEvent(event).touches[0];
 
 	var didCancel = didScroll,
 		moveThreshold = $.vmouse.moveDistanceThreshold;
 	didScroll = didScroll
-		|| (scrollTopSupported && (startScrollX !== window.pageXOffset || startScrollY !== window.pageYOffset))
 		|| (Math.abs(t.pageX - startX) > moveThreshold || Math.abs(t.pageY - startY) > moveThreshold);
 
 	var flags = getVirtualBindingFlags(event.target);
@@ -760,6 +740,10 @@
 
 function handleTouchEnd(event)
 {
+	if (blockTouchTriggers){
+		return;
+	}
+
 	disableTouchBindings();
 
 	var flags = getVirtualBindingFlags(event.target);
@@ -784,9 +768,9 @@
 	startResetTimer();
 }
 
-function hasVirtualBindings($ele)
+function hasVirtualBindings(ele)
 {
-	var bindings = $ele.data(dataPropertyName), k;
+	var bindings = $.data(ele, dataPropertyName), k;
 	if (bindings){
 		for (k in bindings){
 			if (bindings[k]){
@@ -807,16 +791,14 @@
 			// If this is the first virtual mouse binding for this element,
 			// add a bindings object to its data.
 
-			var $this = $(this);
-
-			if (!hasVirtualBindings($this)){
-				$this.data(dataPropertyName, {});
+			if (!hasVirtualBindings(this)){
+				$.data(this, dataPropertyName, {});
 			}
 
 			// If setup is called, we know it is the first binding for this
 			// eventType, so initialize the count for the eventType to zero.
 
-			var bindings = $this.data(dataPropertyName);
+			var bindings = $.data(this, dataPropertyName);
 			bindings[eventType] = true;
 
 			// If this is the first virtual mouse event for this type,
@@ -831,7 +813,7 @@
 			// for elements unless they actually have handlers registered on them.
 			// To get around this, we register dummy handlers on the elements.
 
-			$this.bind(realType, dummyMouseHandler);
+			$(this).bind(realType, dummyMouseHandler);
 
 			// For now, if event capture is not supported, we rely on mouse handlers.
 			if (eventCaptureSupported){
@@ -840,7 +822,22 @@
 	
 				activeDocHandlers["touchstart"] = (activeDocHandlers["touchstart"] || 0) + 1;
 				if (activeDocHandlers["touchstart"] === 1) {
-					$document.bind("touchstart", handleTouchStart);
+					$document.bind("touchstart", handleTouchStart)
+
+						.bind("touchend", handleTouchEnd)
+					
+						// On touch platforms, touching the screen and then dragging your finger
+						// causes the window content to scroll after some distance threshold is
+						// exceeded. On these platforms, a scroll prevents a click event from being
+						// dispatched, and on some platforms, even the touchend is suppressed. To
+						// mimic the suppression of the click event, we need to watch for a scroll
+						// event. Unfortunately, some platforms like iOS don't dispatch scroll
+						// events until *AFTER* the user lifts their finger (touchend). This means
+						// we need to watch both scroll and touchmove events to figure out whether
+						// or not a scroll happenens before the touchend event is fired.
+					
+						.bind("touchmove", handleTouchMove)
+						.bind("scroll", handleScroll);			
 				}
 			}
 		},
@@ -860,13 +857,24 @@
 	
 				--activeDocHandlers["touchstart"];
 				if (!activeDocHandlers["touchstart"]) {
-					$document.unbind("touchstart", handleTouchStart);
+					$document.unbind("touchstart", handleTouchStart)
+						.unbind("touchmove", handleTouchMove)
+						.unbind("touchend", handleTouchEnd)
+						.unbind("scroll", handleScroll);
 				}
 			}
 
 			var $this = $(this),
-				bindings = $this.data(dataPropertyName);
-			bindings[eventType] = false;
+				bindings = $.data(this, dataPropertyName);
+
+			// teardown may be called when an element was
+			// removed from the DOM. If this is the case,
+			// jQuery core may have already stripped the element
+			// of any data bindings so we need to check it before
+			// using it.
+			if (bindings){
+				bindings[eventType] = false;
+			}
 
 			// Unregister the dummy event handler.
 
@@ -875,7 +883,7 @@
 			// If this is the last virtual mouse binding on the
 			// element, remove the binding data from the element.
 
-			if (!hasVirtualBindings($this)){
+			if (!hasVirtualBindings(this)){
 				$this.removeData(dataPropertyName);
 			}
 		}
@@ -932,7 +940,7 @@
 				for (var i = 0; i < cnt; i++) {
 					var o = clickBlockList[i],
 						touchID = 0;
-					if ((ele === target && Math.abs(o.x - x) < threshold && Math.abs(o.y - y) < threshold) || $(ele).data(touchTargetPropertyName) === o.touchID){
+					if ((ele === target && Math.abs(o.x - x) < threshold && Math.abs(o.y - y) < threshold) || $.data(ele, touchTargetPropertyName) === o.touchID){
 						// XXX: We may want to consider removing matches from the block list
 						//      instead of waiting for the reset timer to fire.
 						e.preventDefault();
@@ -945,7 +953,8 @@
 		}
 	}, true);
 }
-})(jQuery, window, document);/*
+})(jQuery, window, document);
+/*
 * jQuery Mobile Framework : events
 * Copyright (c) jQuery Project
 * Dual licensed under the MIT or GPL Version 2 licenses.
@@ -954,7 +963,7 @@
 (function($, undefined ) {
 
 // add new event shortcuts
-$.each( "touchstart touchmove touchend orientationchange tap taphold swipe swipeleft swiperight scrollstart scrollstop".split( " " ), function( i, name ) {
+$.each( "touchstart touchmove touchend orientationchange throttledresize tap taphold swipe swipeleft swiperight scrollstart scrollstop".split( " " ), function( i, name ) {
 	$.fn[ name ] = function( fn ) {
 		return fn ? this.bind( name, fn ) : this.trigger( name );
 	};
@@ -1028,7 +1037,7 @@
 				function clearTapHandlers() {
 					touching = false;
 					clearTimeout(timer);
-					$(this).unbind("vmouseclick", clickHandler).unbind("vmousecancel", clearTapHandlers);
+					$this.unbind("vclick", clickHandler).unbind("vmousecancel", clearTapHandlers);
 				}
 				
 				function clickHandler(event) {
@@ -1129,7 +1138,7 @@
 			
 			// Because the orientationchange event doesn't exist, simulate the
 			// event by testing window dimensions on resize.
-			win.bind( "resize", handler );
+			win.bind( "throttledresize", handler );
 		},
 		teardown: function(){
 			// If the event is not supported natively, return false so that
@@ -1138,7 +1147,7 @@
 			
 			// Because the orientationchange event doesn't exist, unbind the
 			// resize event handler.
-			win.unbind( "resize", handler );
+			win.unbind( "throttledresize", handler );
 		},
 		add: function( handleObj ) {
 			// Save a reference to the bound event handler.
@@ -1169,12 +1178,47 @@
 	
 	// Get the current page orientation. This method is exposed publicly, should it
 	// be needed, as jQuery.event.special.orientationchange.orientation()
-	special_event.orientation = get_orientation = function() {
+	$.event.special.orientationchange.orientation = get_orientation = function() {
 		var elem = document.documentElement;
 		return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape";
 	};
 	
 })(jQuery);
+
+
+// throttled resize event
+(function(){
+	$.event.special.throttledresize = {
+		setup: function() {
+			$( this ).bind( "resize", handler );	
+		},
+		teardown: function(){
+			$( this ).unbind( "resize", handler );
+		}
+	};
+
+	var throttle = 250,
+		handler = function(){
+			curr = ( new Date() ).getTime();
+			diff = curr - lastCall;
+			if( diff >= throttle ){
+				lastCall = curr;
+				$( this ).trigger( "throttledresize" );
+			}
+			else{
+				if( heldCall ){
+					clearTimeout( heldCall );
+				}
+				//promise a held call will still execute
+				heldCall = setTimeout( handler, throttle - diff );
+			}
+		},
+		lastCall = 0,
+		heldCall,
+		curr,
+		diff;
+})();
+
 
 $.each({
 	scrollstop: "scrollstart",
@@ -1591,7 +1635,7 @@
 $.widget( "mobile.page", $.mobile.widget, {
 	options: {
 		backBtnText: "Back",
-		addBackBtn: true,
+		addBackBtn: false,
 		backBtnTheme: null,
 		degradeInputs: {
 			color: false,
@@ -1814,7 +1858,7 @@
 		//hash segment before &ui-page= is used to make Ajax request
 		subPageUrlKey: "ui-page",
 
-		//anchor links with a data-rel, or pages with a  data-role, that match these selectors will be untrackable in history
+		//anchor links with a data-rel, or pages with a	 data-role, that match these selectors will be untrackable in history
 		//(no change in URL, not bookmarkable)
 		nonHistorySelectors: "dialog",
 
@@ -1826,32 +1870,30 @@
 
 		//automatically handle clicks and form submissions through Ajax, when same-domain
 		ajaxEnabled: true,
+		
+		//When enabled, clicks and taps that result in Ajax page changes will happen slightly sooner on touch devices.
+		//Also, it will prevent the address bar from appearing on platforms like iOS during page transitions.
+		//This option has no effect on non-touch devices, but enabling it may interfere with jQuery plugins that bind to click events
+		useFastClick: true,
 
 		//automatically load and show pages based on location.hash
 		hashListeningEnabled: true,
 
-		// TODO: deprecated - remove at 1.0
-		//automatically handle link clicks through Ajax, when possible
-		ajaxLinksEnabled: true,
-
-		// TODO: deprecated - remove at 1.0
-		//automatically handle form submissions through Ajax, when possible
-		ajaxFormsEnabled: true,
-
-		//set default transition - 'none' for no transitions
-		defaultTransition: "slide",
+		//set default page transition - 'none' for no transitions
+		defaultPageTransition: "slide",
+		
+		//minimum scroll distance that will be remembered when returning to a page
+		minScrollBack: screen.height / 2,
+
+		//set default dialog transition - 'none' for no transitions
+		defaultDialogTransition: "pop",
 
 		//show loading message during Ajax requests
 		//if false, message will not appear, but loading classes will still be toggled on html el
 		loadingMessage: "loading",
-		
+
 		//error response message - appears when an Ajax page request fails
 		pageLoadErrorMessage: "Error Loading Page",
-
-		//configure meta viewport tag's content attr:
-		//note: this feature is deprecated in A4 in favor of adding
-		//the meta viewport element directly in the markup
-		metaViewportContent: "width=device-width, minimum-scale=1, maximum-scale=1",
 
 		//support conditions that must be met in order to proceed
 		//default enhanced qualifications are media query support OR IE 7+
@@ -1897,7 +1939,10 @@
 
 		//scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
 		silentScroll: function( ypos ) {
-			ypos = ypos || 0;
+			if( $.type( ypos ) !== "number" ){
+				ypos = $.mobile.defaultHomeScroll;
+			}
+
 			// prevent scrollstart and scrollstop events
 			$.event.special.scrollstart.enabled = false;
 
@@ -1909,31 +1954,41 @@
 			setTimeout(function() {
 				$.event.special.scrollstart.enabled = true;
 			}, 150 );
+		},
+
+		// compile the namespace normalization regex once
+		normalizeRegex: /-([a-z])/g,
+
+		// take a data attribute property, prepend the namespace
+		// and then camel case the attribute string
+		nsNormalize: function(prop){
+			if(!prop) return;
+
+			return $.camelCase( $.mobile.ns + prop );
 		}
 	});
 
 	//mobile version of data and removeData and hasData methods
 	//ensures all data is set and retrieved using jQuery Mobile's data namespace
-    $.fn.jqmData = function( prop, value ){
-    	return this.data( prop ? $.mobile.ns + prop : prop, value );
-    };
-    
-    $.jqmData = function( elem, prop, value ){
-    	return $.data( elem, prop && $.mobile.ns + prop, value );
-    };
-    
-    $.fn.jqmRemoveData = function( prop ){
-    	return this.removeData( $.mobile.ns + prop );
-    };
-    
-    $.jqmRemoveData = function( elem, prop ){
-    	return $.removeData( elem, prop && $.mobile.ns + prop );
-    };
-    
-    $.jqmHasData = function( elem, prop ){
-    	return $.hasData( elem, prop && $.mobile.ns + prop );
-    };
-    
+	$.fn.jqmData = function( prop, value ){
+		return this.data( prop ? $.mobile.nsNormalize(prop) : prop, value );
+	};
+
+	$.jqmData = function( elem, prop, value ){
+		return $.data( elem, $.mobile.nsNormalize(prop), value );
+	};
+
+	$.fn.jqmRemoveData = function( prop ){
+		return this.removeData( $.mobile.nsNormalize(prop) );
+	};
+
+	$.jqmRemoveData = function( elem, prop ){
+		return $.removeData( elem, $.mobile.nsNormalize(prop) );
+	};
+
+	$.jqmHasData = function( elem, prop ){
+		return $.hasData( elem, $.mobile.nsNormalize(prop) );
+	};
 
 	// Monkey-patching Sizzle to filter the :jqmData selector
 	var oldFind = $.find;
@@ -1960,86 +2015,228 @@
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */
-(function($, undefined ) {
+( function( $, undefined ) {
 
 	//define vars for interal use
-	var $window = $(window),
-		$html = $('html'),
-		$head = $('head'),
+	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]: jblas:password@mycompany.com:8080
+			//     [6]: jblas:password
+			//     [7]: jblas
+			//     [8]: password
+			//     [9]: mycompany.com:8080
+			//    [10]: mycompany.com
+			//    [11]: 8080
+			//    [12]: /mail/inbox
+			//    [13]: /mail/
+			//    [14]: inbox
+			//    [15]: ?msg=1234&type=unread
+			//    [16]: #msg-content
+			//
+			urlParseRE: /^(((([^:\/#\?]+:)?(?:\/\/((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?]+)(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
+
+			//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 ( typeof url === "object" ) {
+					return url;
+				}
+
+				var u = url || "",
+					matches = path.urlParseRE.exec( url ),
+					results;
+				if ( matches ) {
+					// 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.
+					results = {
+						href:         matches[0] || "",
+						hrefNoHash:   matches[1] || "",
+						hrefNoSearch: matches[2] || "",
+						domain:       matches[3] || "",
+						protocol:     matches[4] || "",
+						authority:    matches[5] || "",
+						username:     matches[7] || "",
+						password:     matches[8] || "",
+						host:         matches[9] || "",
+						hostname:     matches[10] || "",
+						port:         matches[11] || "",
+						pathname:     matches[12] || "",
+						directory:    matches[13] || "",
+						filename:     matches[14] || "",
+						search:       matches[15] || "",
+						hash:         matches[16] || ""
+					};
+				}
+				return results || {};
+			},
+
+			//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;
+				}
+		
+				var relObj = path.parseUrl( relUrl ),
+					absObj = path.parseUrl( absUrl ),
+					protocol = relObj.protocol || absObj.protocol,
+					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 + "//" + 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 ) ) {
+					return u.hash.replace( /^#/, "" );
+				} else if ( path.isSameDomain( u, documentBase ) ) {
+					return u.hrefNoHash.replace( documentBase.domain, "" );
+				}
+				return absUrl;
+			},
+
 			//get path from current hash, or from a file path
-			get: function( newPath ){
-				if( newPath === undefined ){
+			get: function( newPath ) {
+				if( newPath === undefined ) {
 					newPath = location.hash;
 				}
-				return path.stripHash( newPath ).replace(/[^\/]*\.[^\/*]+$/, '');
+				return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
 			},
 
 			//return the substring of a filepath before the sub-page key, for making a server request
-			getFilePath: function( path ){
+			getFilePath: function( path ) {
 				var splitkey = '&' + $.mobile.subPageUrlKey;
 				return path && path.split( splitkey )[0].split( dialogHashKey )[0];
 			},
 
 			//set location hash to path
-			set: function( path ){
+			set: function( path ) {
 				location.hash = path;
 			},
 
-			//location pathname from intial directory request
-			origin: '',
-
-			setOrigin: function(){
-				path.origin = path.get( location.protocol + '//' + location.host + location.pathname );
+			//test if a given url (string) is a path
+			//NOTE might be exceptionally naive
+			isPath: function( url ) {
+				return ( /\// ).test( url );
 			},
 
-			//prefix a relative url with the current path
-			// TODO rename to reflect conditional functionality
-			makeAbsolute: function( url ){
-				// only create an absolute path when the hash can be used as one
-				return path.isPath(window.location.hash) ? path.get() + url : url;
+			//return a url path with the window's location protocol/hostname/pathname removed
+			clean: function( url ) {
+				return url.replace( documentBase.domain, "" );
 			},
 
-			// 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 ){
-				// Replace the protocol, host, and pathname only once at the beginning of the url to avoid
-				// problems when it's included as a part of a param
-				// Also, since all urls are absolute in IE, we need to remove the pathname as well.
-				var leadingUrlRootRegex = new RegExp("^" + location.protocol + "//" + location.host + location.pathname);
-				return url.replace(leadingUrlRootRegex, "");
-			},
-
 			//just return the url without an initial #
-			stripHash: function( url ){
+			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, "" ) );
+			},
+
 			//check whether a url is referencing the same domain, or an external domain or different protocol
 			//could be mailto, etc
-			isExternal: function( url ){
-				return path.hasProtocol( path.clean( url ) );
+			isExternal: function( url ) {
+				var u = path.parseUrl( url );
+				return u.protocol && u.domain !== documentUrl.domain ? true : false;
 			},
 
-			hasProtocol: function( url ){
-				return (/^(:?\w+:)/).test( url );
+			hasProtocol: function( url ) {
+				return ( /^(:?\w+:)/ ).test( url );
 			},
 
-			//check if the url is relative
-			isRelative: function( url ){
-				return  (/^[^\/|#]/).test( url ) && !path.hasProtocol( url );
-			},
-
-			isEmbeddedPage: function( url ){
-				return (/^#/).test( url );
+			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 );
 			}
 		},
 
@@ -2056,43 +2253,43 @@
 			activeIndex: 0,
 
 			//get active
-			getActive: function(){
+			getActive: function() {
 				return urlHistory.stack[ urlHistory.activeIndex ];
 			},
 
-			getPrev: function(){
+			getPrev: function() {
 				return urlHistory.stack[ urlHistory.activeIndex - 1 ];
 			},
 
-			getNext: function(){
+			getNext: function() {
 				return urlHistory.stack[ urlHistory.activeIndex + 1 ];
 			},
 
 			// addNew is used whenever a new page is added
-			addNew: function( url, transition, title, storedTo ){
+			addNew: function( url, transition, title, storedTo ) {
 				//if there's forward history, wipe it
-				if( urlHistory.getNext() ){
+				if( urlHistory.getNext() ) {
 					urlHistory.clearForward();
 				}
 
 				urlHistory.stack.push( {url : url, transition: transition, title: title, page: storedTo } );