PageSourceSearch

https://www.duaexplorer.com/Scripts/jquery.mobile-1.0rc3.js

js duaexplorer.com collected 2026-10-02 00:29:15 UTC 214,139 bytes, 6,937 lines download raw bytes

1/*
2* jQuery Mobile Framework Git Build: SHA1: 59099abd314c790a083a211574c05bc83129e16a <> Date: Mon Nov 14 16:39:50 2011 -0500
3* http://jquerymobile.com
4*
5* Copyright 2011 (c) jQuery Project
6* Dual licensed under the MIT or GPL Version 2 licenses.
7* http://jquery.org/license
8*
9*/
10/*!
11 * jQuery UI Widget @VERSION
12 *
13 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
14 * Dual licensed under the MIT or GPL Version 2 licenses.
15 * http://jquery.org/license
16 *
17 * http://docs.jquery.com/UI/Widget
18 */
19
20(function( $, undefined ) {
21
22// jQuery 1.4+
23if ( $.cleanData ) {
24	var _cleanData = $.cleanData;
25	$.cleanData = function( elems ) {
26		for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
27			$( elem ).triggerHandler( "remove" );
28		}
29		_cleanData( elems );
30	};
31} else {
32	var _remove = $.fn.remove;
33	$.fn.remove = function( selector, keepData ) {
34		return this.each(function() {
35			if ( !keepData ) {
36				if ( !selector || $.filter( selector, [ this ] ).length ) {
37					$( "*", this ).add( [ this ] ).each(function() {
38						$( this ).triggerHandler( "remove" );
39					});
40				}
41			}
42			return _remove.call( $(this), selector, keepData );
43		});
44	};
45}
46
47$.widget = function( name, base, prototype ) {
48	var namespace = name.split( "." )[ 0 ],
49		fullName;
50	name = name.split( "." )[ 1 ];
51	fullName = namespace + "-" + name;
52
53	if ( !prototype ) {
54		prototype = base;
55		base = $.Widget;
56	}
57
58	// create selector for plugin
59	$.expr[ ":" ][ fullName ] = function( elem ) {
60		return !!$.data( elem, name );
61	};
62
63	$[ namespace ] = $[ namespace ] || {};
64	$[ namespace ][ name ] = function( options, element ) {
65		// allow instantiation without initializing for simple inheritance
66		if ( arguments.length ) {
67			this._createWidget( options, element );
68		}
69	};
70
71	var basePrototype = new base();
72	// we need to make the options hash a property directly on the new instance
73	// otherwise we'll modify the options hash on the prototype that we're
74	// inheriting from
75//	$.each( basePrototype, function( key, val ) {
76//		if ( $.isPlainObject(val) ) {
77//			basePrototype[ key ] = $.extend( {}, val );
78//		}
79//	});
80	basePrototype.options = $.extend( true, {}, basePrototype.options );
81	$[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
82		namespace: namespace,
83		widgetName: name,
84		widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
85		widgetBaseClass: fullName
86	}, prototype );
87
88	$.widget.bridge( name, $[ namespace ][ name ] );
89};
90
91$.widget.bridge = function( name, object ) {
92	$.fn[ name ] = function( options ) {
93		var isMethodCall = typeof options === "string",
94			args = Array.prototype.slice.call( arguments, 1 ),
95			returnValue = this;
96
97		// allow multiple hashes to be passed on init
98		options = !isMethodCall && args.length ?
99			$.extend.apply( null, [ true, options ].concat(args) ) :
100			options;
101
102		// prevent calls to internal methods
103		if ( isMethodCall && options.charAt( 0 ) === "_" ) {
104			return returnValue;
105		}
106
107		if ( isMethodCall ) {
108			this.each(function() {
109				var instance = $.data( this, name );
110				if ( !instance ) {
111					throw "cannot call methods on " + name + " prior to initialization; " +
112						"attempted to call method '" + options + "'";
113				}
114				if ( !$.isFunction( instance[options] ) ) {
115					throw "no such method '" + options + "' for " + name + " widget instance";
116				}
117				var methodValue = instance[ options ].apply( instance, args );
118				if ( methodValue !== instance && methodValue !== undefined ) {
119					returnValue = methodValue;
120					return false;
121				}
122			});
123		} else {
124			this.each(function() {
125				var instance = $.data( this, name );
126				if ( instance ) {
127					instance.option( options || {} )._init();
128				} else {
129					$.data( this, name, new object( options, this ) );
130				}
131			});
132		}
133
134		return returnValue;
135	};
136};
137
138$.Widget = function( options, element ) {
139	// allow instantiation without initializing for simple inheritance
140	if ( arguments.length ) {
141		this._createWidget( options, element );
142	}
143};
144
145$.Widget.prototype = {
146	widgetName: "widget",
147	widgetEventPrefix: "",
148	options: {
149		disabled: false
150	},
151	_createWidget: function( options, element ) {
152		// $.widget.bridge stores the plugin instance, but we do it anyway
153		// so that it's stored even before the _create function runs
154		$.data( element, this.widgetName, this );
155		this.element = $( element );
156		this.options = $.extend( true, {},
157			this.options,
158			this._getCreateOptions(),
159			options );
160
161		var self = this;
162		this.element.bind( "remove." + this.widgetName, function() {
163			self.destroy();
164		});
165
166		this._create();
167		this._trigger( "create" );
168		this._init();
169	},
170	_getCreateOptions: function() {
171		var options = {};
172		if ( $.metadata ) {
173			options = $.metadata.get( element )[ this.widgetName ];
174		}
175		return options;
176	},
177	_create: function() {},
178	_init: function() {},
179
180	destroy: function() {
181		this.element
182			.unbind( "." + this.widgetName )
183			.removeData( this.widgetName );
184		this.widget()
185			.unbind( "." + this.widgetName )
186			.removeAttr( "aria-disabled" )
187			.removeClass(
188				this.widgetBaseClass + "-disabled " +
189				"ui-state-disabled" );
190	},
191
192	widget: function() {
193		return this.element;
194	},
195
196	option: function( key, value ) {
197		var options = key;
198
199		if ( arguments.length === 0 ) {
200			// don't return a reference to the internal hash
201			return $.extend( {}, this.options );
202		}
203
204		if  (typeof key === "string" ) {
205			if ( value === undefined ) {
206				return this.options[ key ];
207			}
208			options = {};
209			options[ key ] = value;
210		}
211
212		this._setOptions( options );
213
214		return this;
215	},
216	_setOptions: function( options ) {
217		var self = this;
218		$.each( options, function( key, value ) {
219			self._setOption( key, value );
220		});
221
222		return this;
223	},
224	_setOption: function( key, value ) {
225		this.options[ key ] = value;
226
227		if ( key === "disabled" ) {
228			this.widget()
229				[ value ? "addClass" : "removeClass"](
230					this.widgetBaseClass + "-disabled" + " " +
231					"ui-state-disabled" )
232				.attr( "aria-disabled", value );
233		}
234
235		return this;
236	},
237
238	enable: function() {
239		return this._setOption( "disabled", false );
240	},
241	disable: function() {
242		return this._setOption( "disabled", true );
243	},
244
245	_trigger: function( type, event, data ) {
246		var callback = this.options[ type ];
247
248		event = $.Event( event );
249		event.type = ( type === this.widgetEventPrefix ?
250			type :
251			this.widgetEventPrefix + type ).toLowerCase();
252		data = data || {};
253
254		// copy original event properties over to the new event
255		// this would happen if we could call $.event.fix instead of $.Event
256		// but we don't have a way to force an event to be fixed multiple times
257		if ( event.originalEvent ) {
258			for ( var i = $.event.props.length, prop; i; ) {
259				prop = $.event.props[ --i ];
260				event[ prop ] = event.originalEvent[ prop ];
261			}
262		}
263
264		this.element.trigger( event, data );
265
266		return !( $.isFunction(callback) &&
267			callback.call( this.element[0], event, data ) === false ||
268			event.isDefaultPrevented() );
269	}
270};
271
272}
272)( jQuery );
273/*
274* widget factory extentions for mobile
275*/
276
277(function( $, undefined ) {
278
279$.widget( "mobile.widget", {
280	// decorate the parent _createWidget to trigger `widgetinit` for users
281	// who wish to do post post `widgetcreate` alterations/additions
282	//
283	// TODO create a pull request for jquery ui to trigger this event
284	// in the original _createWidget
285	_createWidget: function() {
286		$.Widget.prototype._createWidget.apply( this, arguments );
287		this._trigger( 'init' );
288	},
289
290	_getCreateOptions: function() {
291
292		var elem = this.element,
293			options = {};
294
295		$.each( this.options, function( option ) {
296
297			var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) {
298							return "-" + c.toLowerCase();
299						})
300					);
301
302			if ( value !== undefined ) {
303				options[ option ] = value;
304			}
305		});
306
307		return options;
308	},
309
310	enhanceWithin: function( target ) {
311		// TODO remove dependency on the page widget for the keepNative.
312		// Currently the keepNative value is defined on the page prototype so
313		// the method is as well
314		var page = $(target).closest(":jqmData(role='page')").data( "page" ),
315			keepNative = (page && page.keepNativeSelector()) || "";
316
317		$( this.options.initSelector, target ).not( keepNative )[ this.widgetName ]();
318	}
319});
320
321})( jQuery );
322/*
323* a workaround for window.matchMedia
324*/
325
326(function( $, undefined ) {
327
328var $window = $( window ),
329	$html = $( "html" );
330
331/* $.mobile.media method: pass a CSS media type or query and get a bool return
332	note: this feature relies on actual media query support for media queries, though types will work most anywhere
333	examples:
334		$.mobile.media('screen') //>> tests for screen media type
335		$.mobile.media('screen and (min-width: 480px)') //>> tests for screen media type with window width > 480px
336		$.mobile.media('@media screen and (-webkit-min-device-pixel-ratio: 2)') //>> tests for webkit 2x pixel ratio (iPhone 4)
337*/
338$.mobile.media = (function() {
339	// TODO: use window.matchMedia once at least one UA implements it
340	var cache = {},
341		testDiv = $( "<div id='jquery-mediatest'>" ),
342		fakeBody = $( "<body>" ).append( testDiv );
343
344	return function( query ) {
345		if ( !( query in cache ) ) {
346			var styleBlock = document.createElement( "style" ),
347				cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }";
348
349			//must set type for IE!
350			styleBlock.type = "text/css";
351
352			if ( styleBlock.styleSheet  ){
353				styleBlock.styleSheet.cssText = cssrule;
354			} else {
355				styleBlock.appendChild( document.createTextNode(cssrule) );
356			}
357
358			$html.prepend( fakeBody ).prepend( styleBlock );
359			cache[ query ] = testDiv.css( "position" ) === "absolute";
360			fakeBody.add( styleBlock ).remove();
361		}
362		return cache[ query ];
363	};
364})();
365
366})(jQuery);
367/*
368* support tests
369*/
370
371(function( $, undefined ) {
372
373var fakeBody = $( "<body>" ).prependTo( "html" ),
374	fbCSS = fakeBody[ 0 ].style,
375	vendors = [ "Webkit", "Moz", "O" ],
376	webos = "palmGetResource" in window, //only used to rule out scrollTop
377	bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
378
379// thx Modernizr
380function propExists( prop ) {
381	var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
382		props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
383
384	for ( var v in props ){
385		if ( fbCSS[ props[ v ] ] !== undefined ) {
386			return true;
387		}
388	}
389}
390
391// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting )
392function baseTagTest() {
393	var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/",
394		base = $( "head base" ),
395		fauxEle = null,
396		href = "",
397		link, rebase;
398
399	if ( !base.length ) {
400		base = fauxEle = $( "<base>", { "href": fauxBase }).appendTo( "head" );
401	} else {
402		href = base.attr( "href" );
403	}
404
405	link = $( "<a href='testurl' />" ).prependTo( fakeBody );
406	rebase = link[ 0 ].href;
407	base[ 0 ].href = href || location.pathname;
408
409	if ( fauxEle ) {
410		fauxEle.remove();
411	}
412	return rebase.indexOf( fauxBase ) === 0;
413}
414
415
416// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
417// allows for inclusion of IE 6+, including Windows Mobile 7
418$.mobile.browser = {};
419$.mobile.browser.ie = (function() {
420	var v = 3,
421	div = document.createElement( "div" ),
422	a = div.all || [];
423
424	while ( div.innerHTML = "<!--[if gt IE " + ( ++v ) + "]><br><![endif]-->", a[ 0 ] );
425
426	return v > 4 ? v : !v;
427})();
428
429
430$.extend( $.support, {
431	orientation: "orientation" in window && "onorientationchange" in window,
432	touch: "ontouchend" in document,
433	cssTransitions: "WebKitTransitionEvent" in window,
434	pushState: "pushState" in history && "replaceState" in history,
435	mediaquery: $.mobile.media( "only all" ),
436	cssPseudoElement: !!propExists( "content" ),
437	touchOverflow: !!propExists( "overflowScrolling" ),
438	boxShadow: !!propExists( "boxShadow" ) && !bb,
439	scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
440	dynamicBaseTag: baseTagTest()
441});
442
443fakeBody.remove();
444
445
446// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian)
447// or that generally work better browsing in regular http for full page refreshes (Opera Mini)
448// Note: This detection below is used as a last resort.
449// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible
450var nokiaLTE7_3 = (function(){
451
452	var ua = window.navigator.userAgent;
453
454	//The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older
455	return ua.indexOf( "Nokia" ) > -1 &&
456			( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) &&
457			ua.indexOf( "AppleWebKit" ) > -1 &&
458			ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ );
459})();
460
461$.mobile.ajaxBlacklist =
462			// BlackBerry browsers, pre-webkit
463			window.blackberry && !window.WebKitPoint ||
464			// Opera Mini
465			window.operamini && Object.prototype.toString.call( window.operamini ) === "[object OperaMini]" ||
466			// Symbian webkits pre 7.3
467			nokiaLTE7_3;
468
469// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices
470// to render the stylesheets when they're referenced before this script, as we'd recommend doing.
471// This simply reappends the CSS in place, which for some reason makes it apply
472if ( nokiaLTE7_3 ) {
473	$(function() {
474		$( "head link[rel='stylesheet']" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" );
475	});
476}
477
478// For ruling out shadows via css
479if ( !$.support.boxShadow ) {
480	$( "html" ).addClass( "ui-mobile-nosupport-boxshadow" );
481}
482
483})( jQuery );
484/*
485* "mouse" plugin
486*/
487
488// This plugin is an experiment for abstracting away the touch and mouse
489// events so that developers don't have to worry about which method of input
490// the device their document is loaded on supports.
491//
492// The idea here is to allow the developer to register listeners for the
493// basic mouse events, such as mousedown, mousemove, mouseup, and click,
494// and the plugin will take care of registering the correct listeners
495// behind the scenes to invoke the listener at the fastest possible time
496// for that device, while still retaining the order of event firing in
497// the traditional mouse environment, should multiple handlers be registered
498// on the same element for different events.
499//
500// The current version exposes the following virtual events to jQuery bind methods:
501// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
502
503(function( $, window, document, undefined ) {
504
505var dataPropertyName = "virtualMouseBindings",
506	touchTargetPropertyName = "virtualTouchID",
507	virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
508	touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
509	activeDocHandlers = {},
510	resetTimerID = 0,
511	startX = 0,
512	startY = 0,
513	didScroll = false,
514	clickBlockList = [],
515	blockMouseTriggers = false,
516	blockTouchTriggers = false,
517	eventCaptureSupported = "addEventListener" in document,
518	$document = $( document ),
519	nextTouchID = 1,
520	lastTouchID = 0;
521
522$.vmouse = {
523	moveDistanceThreshold: 10,
524	clickDistanceThreshold: 10,
525	resetTimerDuration: 1500
526};
527
528function getNativeEvent( event ) {
529
530	while ( event && typeof event.originalEvent !== "undefined" ) {
531		event = event.originalEvent;
532	}
533	return event;
534}
535
536function createVirtualEvent( event, eventType ) {
537
538	var t = event.type,
539		oe, props, ne, prop, ct, touch, i, j;
540
541	event = $.Event(event);
542	event.type = eventType;
543
544	oe = event.originalEvent;
545	props = $.event.props;
546
547	// copy original event properties over to the new event
548	// this would happen if we could call $.event.fix instead of $.Event
549	// but we don't have a way to force an event to be fixed multiple times
550	if ( oe ) {
551		for ( i = props.length, prop; i; ) {
552			prop = props[ --i ];
553			event[ prop ] = oe[ prop ];
554		}
555	}
556
557	// make sure that if the mouse and click virtual events are generated
558	// without a .which one is defined
559	if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ){
560		event.which = 1;
561	}
562
563	if ( t.search(/^touch/) !== -1 ) {
564		ne = getNativeEvent( oe );
565		t = ne.touches;
566		ct = ne.changedTouches;
567		touch = ( t && t.length ) ? t[0] : ( (ct && ct.length) ? ct[ 0 ] : undefined );
568
569		if ( touch ) {
570			for ( j = 0, len = touchEventProps.length; j < len; j++){
571				prop = touchEventProps[ j ];
572				event[ prop ] = touch[ prop ];
573			}
574		}
575	}
576
577	return event;
578}
579
580function getVirtualBindingFlags( element ) {
581
582	var flags = {},
583		b, k;
584
585	while ( element ) {
586
587		b = $.data( element, dataPropertyName );
588
589		for (  k in b ) {
590			if ( b[ k ] ) {
591				flags[ k ] = flags.hasVirtualBinding = true;
592			}
593		}
594		element = element.parentNode;
595	}
596	return flags;
597}
598
599function getClosestElementWithVirtualBinding( element, eventType ) {
600	var b;
601	while ( element ) {
602
603		b = $.data( element, dataPropertyName );
604
605		if ( b && ( !eventType || b[ eventType ] ) ) {
606			return element;
607		}
608		element = element.parentNode;
609	}
610	return null;
611}
612
613function enableTouchBindings() {
614	blockTouchTriggers = false;
615}
616
617function disableTouchBindings() {
618	blockTouchTriggers = true;
619}
620
621function enableMouseBindings() {
622	lastTouchID = 0;
623	clickBlockList.length = 0;
624	blockMouseTriggers = false;
625
626	// When mouse bindings are enabled, our
627	// touch bindings are disabled.
628	disableTouchBindings();
629}
630
631function disableMouseBindings() {
632	// When mouse bindings are disabled, our
633	// touch bindings are enabled.
634	enableTouchBindings();
635}
636
637function startResetTimer() {
638	clearResetTimer();
639	resetTimerID = setTimeout(function(){
640		resetTimerID = 0;
641		enableMouseBindings();
642	}, $.vmouse.resetTimerDuration );
643}
644
645function clearResetTimer() {
646	if ( resetTimerID ){
647		clearTimeout( resetTimerID );
648		resetTimerID = 0;
649	}
650}
651
652function triggerVirtualEvent( eventType, event, flags ) {
653	var ve;
654
655	if ( ( flags && flags[ eventType ] ) ||
656				( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
657
658		ve = createVirtualEvent( event, eventType );
659
660		$( event.target).trigger( ve );
661	}
662
663	return ve;
664}
665
666function mouseEventCallback( event ) {
667	var touchID = $.data(event.target, touchTargetPropertyName);
668
669	if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
670		var ve = triggerVirtualEvent( "v" + event.type, event );
671		if ( ve ) {
672			if ( ve.isDefaultPrevented() ) {
673				event.preventDefault();
674			}
675			if ( ve.isPropagationStopped() ) {
676				event.stopPropagation();
677			}
678			if ( ve.isImmediatePropagationStopped() ) {
679				event.stopImmediatePropagation();
680			}
681		}
682	}
683}
684
685function handleTouchStart( event ) {
686
687	var touches = getNativeEvent( event ).touches,
688		target, flags;
689
690	if ( touches && touches.length === 1 ) {
691
692		target = event.target;
693		flags = getVirtualBindingFlags( target );
694
695		if ( flags.hasVirtualBinding ) {
696
697			lastTouchID = nextTouchID++;
698			$.data( target, touchTargetPropertyName, lastTouchID );
699
700			clearResetTimer();
701
702			disableMouseBindings();
703			didScroll = false;
704
705			var t = getNativeEvent( event ).touches[ 0 ];
706			startX = t.pageX;
707			startY = t.pageY;
708
709			triggerVirtualEvent( "vmouseover", event, flags );
710			triggerVirtualEvent( "vmousedown", event, flags );
711		}
712	}
713}
714
715function handleScroll( event ) {
716	if ( blockTouchTriggers ) {
717		return;
718	}
719
720	if ( !didScroll ) {
721		triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
722	}
723
724	didScroll = true;
725	startResetTimer();
726}
727
728function handleTouchMove( event ) {
729	if ( blockTouchTriggers ) {
730		return;
731	}
732
733	var t = getNativeEvent( event ).touches[ 0 ],
734		didCancel = didScroll,
735		moveThreshold = $.vmouse.moveDistanceThreshold;
736		didScroll = didScroll ||
737			( Math.abs(t.pageX - startX) > moveThreshold ||
738				Math.abs(t.pageY - startY) > moveThreshold ),
739		flags = getVirtualBindingFlags( event.target );
740
741	if ( didScroll && !didCancel ) {
742		triggerVirtualEvent( "vmousecancel", event, flags );
743	}
744
745	triggerVirtualEvent( "vmousemove", event, flags );
746	startResetTimer();
747}
748
749function handleTouchEnd( event ) {
750	if ( blockTouchTriggers ) {
751		return;
752	}
753
754	disableTouchBindings();
755
756	var flags = getVirtualBindingFlags( event.target ),
757		t;
758	triggerVirtualEvent( "vmouseup", event, flags );
759
760	if ( !didScroll ) {
761		var ve = triggerVirtualEvent( "vclick", event, flags );
762		if ( ve && ve.isDefaultPrevented() ) {
763			// The target of the mouse events that follow the touchend
764			// event don't necessarily match the target used during the
765			// touch. This means we need to rely on coordinates for blocking
766			// any click that is generated.
767			t = getNativeEvent( event ).changedTouches[ 0 ];
768			clickBlockList.push({
769				touchID: lastTouchID,
770				x: t.clientX,
771				y: t.clientY
772			});
773
774			// Prevent any mouse events that follow from triggering
775			// virtual event notifications.
776			blockMouseTriggers = true;
777		}
778	}
779	triggerVirtualEvent( "vmouseout", event, flags);
780	didScroll = false;
781
782	startResetTimer();
783}
784
785function hasVirtualBindings( ele ) {
786	var bindings = $.data( ele, dataPropertyName ),
787		k;
788
789	if ( bindings ) {
790		for ( k in bindings ) {
791			if ( bindings[ k ] ) {
792				return true;
793			}
794		}
795	}
796	return false;
797}
798
799function dummyMouseHandler(){}
800
801function getSpecialEventObject( eventType ) {
802	var realType = eventType.substr( 1 );
803
804	return {
805		setup: function( data, namespace ) {
806			// If this is the first virtual mouse binding for this element,
807			// add a bindings object to its data.
808
809			if ( !hasVirtualBindings( this ) ) {
810				$.data( this, dataPropertyName, {});
811			}
812
813			// If setup is called, we know it is the first binding for this
814			// eventType, so initialize the count for the eventType to zero.
815			var bindings = $.data( this, dataPropertyName );
816			bindings[ eventType ] = true;
817
818			// If this is the first virtual mouse event for this type,
819			// register a global handler on the document.
820
821			activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
822
823			if ( activeDocHandlers[ eventType ] === 1 ) {
824				$document.bind( realType, mouseEventCallback );
825			}
826
827			// Some browsers, like Opera Mini, won't dispatch mouse/click events
828			// for elements unless they actually have handlers registered on them.
829			// To get around this, we register dummy handlers on the elements.
830
831			$( this ).bind( realType, dummyMouseHandler );
832
833			// For now, if event capture is not supported, we rely on mouse handlers.
834			if ( eventCaptureSupported ) {
835				// If this is the first virtual mouse binding for the document,
836				// register our touchstart handler on the document.
837
838				activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
839
840				if (activeDocHandlers[ "touchstart" ] === 1) {
841					$document.bind( "touchstart", handleTouchStart )
842						.bind( "touchend", handleTouchEnd )
843
844						// On touch platforms, touching the screen and then dragging your finger
845						// causes the window content to scroll after some distance threshold is
846						// exceeded. On these platforms, a scroll prevents a click event from being
847						// dispatched, and on some platforms, even the touchend is suppressed. To
848						// mimic the suppression of the click event, we need to watch for a scroll
849						// event. Unfortunately, some platforms like iOS don't dispatch scroll
850						// events until *AFTER* the user lifts their finger (touchend). This means
851						// we need to watch both scroll and touchmove events to figure out whether
852						// or not a scroll happenens before the touchend event is fired.
853
854						.bind( "touchmove", handleTouchMove )
855						.bind( "scroll", handleScroll );
856				}
857			}
858		},
859
860		teardown: function( data, namespace ) {
861			// If this is the last virtual binding for this eventType,
862			// remove its global handler from the document.
863
864			--activeDocHandlers[ eventType ];
865
866			if ( !activeDocHandlers[ eventType ] ) {
867				$document.unbind( realType, mouseEventCallback );
868			}
869
870			if ( eventCaptureSupported ) {
871				// If this is the last virtual mouse binding in existence,
872				// remove our document touchstart listener.
873
874				--activeDocHandlers[ "touchstart" ];
875
876				if ( !activeDocHandlers[ "touchstart" ] ) {
877					$document.unbind( "touchstart", handleTouchStart )
878						.unbind( "touchmove", handleTouchMove )
879						.unbind( "touchend", handleTouchEnd )
880						.unbind( "scroll", handleScroll );
881				}
882			}
883
884			var $this = $( this ),
885				bindings = $.data( this, dataPropertyName );
886
887			// teardown may be called when an element was
888			// removed from the DOM. If this is the case,
889			// jQuery core may have already stripped the element
890			// of any data bindings so we need to check it before
891			// using it.
892			if ( bindings ) {
893				bindings[ eventType ] = false;
894			}
895
896			// Unregister the dummy event handler.
897
898			$this.unbind( realType, dummyMouseHandler );
899
900			// If this is the last virtual mouse binding on the
901			// element, remove the binding data from the element.
902
903			if ( !hasVirtualBindings( this ) ) {
904				$this.removeData( dataPropertyName );
905			}
906		}
907	};
908}
909
910// Expose our custom events to the jQuery bind/unbind mechanism.
911
912for ( var i = 0; i < virtualEventNames.length; i++ ){
913	$.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
914}
915
916// Add a capture click handler to block clicks.
917// Note that we require event capture support for this so if the device
918// doesn't support it, we punt for now and rely solely on mouse events.
919if ( eventCaptureSupported ) {
920	document.addEventListener( "click", function( e ){
921		var cnt = clickBlockList.length,
922			target = e.target,
923			x, y, ele, i, o, touchID;
924
925		if ( cnt ) {
926			x = e.clientX;
927			y = e.clientY;
928			threshold = $.vmouse.clickDistanceThreshold;
929
930			// The idea here is to run through the clickBlockList to see if
931			// the current click event is in the proximity of one of our
932			// vclick events that had preventDefault() called on it. If we find
933			// one, then we block the click.
934			//
935			// Why do we have to rely on proximity?
936			//
937			// Because the target of the touch event that triggered the vclick
938			// can be different from the target of the click event synthesized
939			// by the browser. The target of a mouse/click event that is syntehsized
940			// from a touch event seems to be implementation specific. For example,
941			// some browsers will fire mouse/click events for a link that is near
942			// a touch event, even though the target of the touchstart/touchend event
943			// says the user touched outside the link. Also, it seems that with most
944			// browsers, the target of the mouse/click event is not calculated until the
945			// time it is dispatched, so if you replace an element that you touched
946			// with another element, the target of the mouse/click will be the new
947			// element underneath that point.
948			//
949			// Aside from proximity, we also check to see if the target and any
950			// of its ancestors were the ones that blocked a click. This is necessary
951			// because of the strange mouse/click target calculation done in the
952			// Android 2.1 browser, where if you click on an element, and there is a
953			// mouse/click handler on one of its ancestors, the target will be the
954			// innermost child of the touched element, even if that child is no where
955			// near the point of touch.
956
957			ele = target;
958
959			while ( ele ) {
960				for ( i = 0; i < cnt; i++ ) {
961					o = clickBlockList[ i ];
962					touchID = 0;
963
964					if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
965								$.data( ele, touchTargetPropertyName ) === o.touchID ) {
966						// XXX: We may want to consider removing matches from the block list
967						//      instead of waiting for the reset timer to fire.
968						e.preventDefault();
969						e.stopPropagation();
970						return;
971					}
972				}
973				ele = ele.parentNode;
974			}
975		}
976	}, true);
977}
978}
978)( jQuery, window, document );
979/* 
980* "events" plugin - Handles events
981*/
982
983(function( $, window, undefined ) {
984
985// add new event shortcuts
986$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
987					"tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {
988
989	$.fn[ name ] = function( fn ) {
990		return fn ? this.bind( name, fn ) : this.trigger( name );
991	};
992
993	$.attrFn[ name ] = true;
994});
995
996var supportTouch = $.support.touch,
997	scrollEvent = "touchmove scroll",
998	touchStartEvent = supportTouch ? "touchstart" : "mousedown",
999	touchStopEvent = supportTouch ? "touchend" : "mouseup",
1000	touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
1001
1002function triggerCustomEvent( obj, eventType, event ) {
1003	var originalType = event.type;
1004	event.type = eventType;
1005	$.event.handle.call( obj, event );
1006	event.type = originalType;
1007}
1008
1009// also handles scrollstop
1010$.event.special.scrollstart = {
1011
1012	enabled: true,
1013
1014	setup: function() {
1015
1016		var thisObject = this,
1017			$this = $( thisObject ),
1018			scrolling,
1019			timer;
1020
1021		function trigger( event, state ) {
1022			scrolling = state;
1023			triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
1024		}
1025
1026		// iPhone triggers scroll after a small delay; use touchmove instead
1027		$this.bind( scrollEvent, function( event ) {
1028
1029			if ( !$.event.special.scrollstart.enabled ) {
1030				return;
1031			}
1032
1033			if ( !scrolling ) {
1034				trigger( event, true );
1035			}
1036
1037			clearTimeout( timer );
1038			timer = setTimeout(function() {
1039				trigger( event, false );
1040			}, 50 );
1041		});
1042	}
1043};
1044
1045// also handles taphold
1046$.event.special.tap = {
1047	setup: function() {
1048		var thisObject = this,
1049			$this = $( thisObject );
1050
1051		$this.bind( "vmousedown", function( event ) {
1052
1053			if ( event.which && event.which !== 1 ) {
1054				return false;
1055			}
1056
1057			var origTarget = event.target,
1058				origEvent = event.originalEvent,
1059				timer;
1060
1061			function clearTapTimer() {
1062				clearTimeout( timer );
1063			}
1064
1065			function clearTapHandlers() {
1066				clearTapTimer();
1067
1068				$this.unbind( "vclick", clickHandler )
1069					.unbind( "vmouseup", clearTapTimer )
1070					.unbind( "vmousecancel", clearTapHandlers );
1071			}
1072
1073			function clickHandler(event) {
1074				clearTapHandlers();
1075
1076				// ONLY trigger a 'tap' event if the start target is
1077				// the same as the stop target.
1078				if ( origTarget == event.target ) {
1079					triggerCustomEvent( thisObject, "tap", event );
1080				}
1081			}
1082
1083			$this.bind( "vmousecancel", clearTapHandlers )
1084				.bind( "vmouseup", clearTapTimer )
1085				.bind( "vclick", clickHandler );
1086
1087			timer = setTimeout(function() {
1088					triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
1089			}, 750 );
1090		});
1091	}
1092};
1093
1094// also handles swipeleft, swiperight
1095$.event.special.swipe = {
1096	scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.
1097
1098	durationThreshold: 1000, // More time than this, and it isn't a swipe.
1099
1100	horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.
1101
1102	verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.
1103
1104	setup: function() {
1105		var thisObject = this,
1106			$this = $( thisObject );
1107
1108		$this.bind( touchStartEvent, function( event ) {
1109			var data = event.originalEvent.touches ?
1110								event.originalEvent.touches[ 0 ] : event,
1111				start = {
1112					time: ( new Date() ).getTime(),
1113					coords: [ data.pageX, data.pageY ],
1114					origin: $( event.target )
1115				},
1116				stop;
1117
1118			function moveHandler( event ) {
1119
1120				if ( !start ) {
1121					return;
1122				}
1123
1124				var data = event.originalEvent.touches ?
1125						event.originalEvent.touches[ 0 ] : event;
1126
1127				stop = {
1128					time: ( new Date() ).getTime(),
1129					coords: [ data.pageX, data.pageY ]
1130				};
1131
1132				// prevent scrolling
1133				if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
1134					event.preventDefault();
1135				}
1136			}
1137
1138			$this.bind( touchMoveEvent, moveHandler )
1139				.one( touchStopEvent, function( event ) {
1140					$this.unbind( touchMoveEvent, moveHandler );
1141
1142					if ( start && stop ) {
1143						if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
1144								Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
1145								Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
1146
1147							start.origin.trigger( "swipe" )
1148								.trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
1149						}
1150					}
1151					start = stop = undefined;
1152				});
1153		});
1154	}
1155};
1156
1157(function( $, window ) {
1158	// "Cowboy" Ben Alman
1159
1160	var win = $( window ),
1161		special_event,
1162		get_orientation,
1163		last_orientation;
1164
1165	$.event.special.orientationchange = special_event = {
1166		setup: function() {
1167			// If the event is supported natively, return false so that jQuery
1168			// will bind to the event using DOM methods.
1169			if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
1170				return false;
1171			}
1172
1173			// Get the current orientation to avoid initial double-triggering.
1174			last_orientation = get_orientation();
1175
1176			// Because the orientationchange event doesn't exist, simulate the
1177			// event by testing window dimensions on resize.
1178			win.bind( "throttledresize", handler );
1179		},
1180		teardown: function(){
1181			// If the event is not supported natively, return false so that
1182			// jQuery will unbind the event using DOM methods.
1183			if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
1184				return false;
1185			}
1186
1187			// Because the orientationchange event doesn't exist, unbind the
1188			// resize event handler.
1189			win.unbind( "throttledresize", handler );
1190		},
1191		add: function( handleObj ) {
1192			// Save a reference to the bound event handler.
1193			var old_handler = handleObj.handler;
1194
1195
1196			handleObj.handler = function( event ) {
1197				// Modify event object, adding the .orientation property.
1198				event.orientation = get_orientation();
1199
1200				// Call the originally-bound event handler and return its result.
1201				return old_handler.apply( this, arguments );
1202			};
1203		}
1204	};
1205
1206	// If the event is not supported natively, this handler will be bound to
1207	// the window resize event to simulate the orientationchange event.
1208	function handler() {
1209		// Get the current orientation.
1210		var orientation = get_orientation();
1211
1212		if ( orientation !== last_orientation ) {
1213			// The orientation has changed, so trigger the orientationchange event.
1214			last_orientation = orientation;
1215			win.trigger( "orientationchange" );
1216		}
1217	}
1218
1219	// Get the current page orientation. This method is exposed publicly, should it
1220	// be needed, as jQuery.event.special.orientationchange.orientation()
1221	$.event.special.orientationchange.orientation = get_orientation = function() {
1222		var isPortrait = true, elem = document.documentElement;
1223
1224		// prefer window orientation to the calculation based on screensize as
1225		// the actual screen resize takes place before or after the orientation change event
1226		// has been fired depending on implementation (eg android 2.3 is before, iphone after).
1227		// More testing is required to determine if a more reliable method of determining the new screensize
1228		// is possible when orientationchange is fired. (eg, use media queries + element + opacity)
1229		if ( $.support.orientation ) {
1230			// if the window orientation registers as 0 or 180 degrees report
1231			// portrait, otherwise landscape
1232			isPortrait = window.orientation % 180 == 0;
1233		} else {
1234			isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
1235		}
1236
1237		return isPortrait ? "portrait" : "landscape";
1238	};
1239
1240})( jQuery, window );
1241
1242
1243// throttled resize event
1244(function() {
1245
1246	$.event.special.throttledresize = {
1247		setup: function() {
1248			$( this ).bind( "resize", handler );
1249		},
1250		teardown: function(){
1251			$( this ).unbind( "resize", handler );
1252		}
1253	};
1254
1255	var throttle = 250,
1256		handler = function() {
1257			curr = ( new Date() ).getTime();
1258			diff = curr - lastCall;
1259
1260			if ( diff >= throttle ) {
1261
1262				lastCall = curr;
1263				$( this ).trigger( "throttledresize" );
1264
1265			} else {
1266
1267				if ( heldCall ) {
1268					clearTimeout( heldCall );
1269				}
1270
1271				// Promise a held call will still execute
1272				heldCall = setTimeout( handler, throttle - diff );
1273			}
1274		},
1275		lastCall = 0,
1276		heldCall,
1277		curr,
1278		diff;
1279})();
1280
1281
1282$.each({
1283	scrollstop: "scrollstart",
1284	taphold: "tap",
1285	swipeleft: "swipe",
1286	swiperight: "swipe"
1287}, function( event, sourceEvent ) {
1288
1289	$.event.special[ event ] = {
1290		setup: function() {
1291			$( this ).bind( sourceEvent, $.noop );
1292		}
1293	};
1294});
1295
1296})( jQuery, this );
1297// Script: jQuery hashchange event
1298// 
1299// *Version: 1.3, Last updated: 7/21/2010*
1300// 
1301// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
1302// GitHub       - http://github.com/cowboy/jquery-hashchange/
1303// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
1304// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8
1304kb gzipped)
1305// 
1306// About: License
1307// 
1308// Copyright (c) 2010 "Cowboy" Ben Alman,
1309// Dual licensed under the MIT and GPL licenses.
1310// http://benalman.com/about/license/
1311// 
1312// About: Examples
1313// 
1314// These working examples, complete with fully commented code, illustrate a few
1315// ways in which this plugin can be used.
1316// 
1317// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
1318// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
1319// 
1320// About: Support and Testing
1321// 
1322// Information about what version or versions of jQuery this plugin has been
1323// tested with, what browsers it has been tested in, and where the unit tests
1324// reside (so you can test it yourself).
1325// 
1326// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
1327// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
1328//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
1329// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
1330// 
1331// About: Known issues
1332// 
1333// While this jQuery hashchange event implementation is quite stable and
1334// robust, there are a few unfortunate browser bugs surrounding expected
1335// hashchange event-based behaviors, independent of any JavaScript
1336// window.onhashchange abstraction. See the following examples for more
1337// information:
1338// 
1339// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
1340// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
1341// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
1342// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
1343// 
1344// Also note that should a browser natively support the window.onhashchange 
1345// event, but not report that it does, the fallback polling loop will be used.
1346// 
1347// About: Release History
1348// 
1349// 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
1350//         "removable" for mobile-only development. Added IE6/7 document.title
1351//         support. Attempted to make Iframe as hidden as possible by using
1352//         techniques from http://www.paciellogroup.com/blog/?p=604. Added 
1353//         support for the "shortcut" format $(window).hashchange( fn ) and
1354//         $(window).hashchange() like jQuery provides for built-in events.
1355//         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
1356//         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
1357//         and <jQuery.fn.hashchange.src> properties plus document-domain.html
1358//         file to address access denied issues when setting document.domain in
1359//         IE6/7.
1360// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
1361//         from a page on another domain would cause an error in Safari 4. Also,
1362//         IE6/7 Iframe is now inserted after the body (this actually works),
1363//         which prevents the page from scrolling when the event is first bound.
1364//         Event can also now be bound before DOM ready, but it won't be usable
1365//         before then in IE6/7.
1366// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
1367//         where browser version is incorrectly reported as 8.0, despite
1368//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
1369// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
1370//         window.onhashchange functionality into a separate plugin for users
1371//         who want just the basic event & back button support, without all the
1372//         extra awesomeness that BBQ provides. This plugin will be included as
1373//         part of jQuery BBQ, but also be available separately.
1374
1375(function($,window,undefined){
1376  '$:nomunge'; // Used by YUI compressor.
1377  
1378  // Reused string.
1379  var str_hashchange = 'hashchange',
1380    
1381    // Method / object references.
1382    doc = document,
1383    fake_onhashchange,
1384    special = $.event.special,
1385    
1386    // Does the browser support window.onhashchange? Note that IE8 running in
1387    // IE7 compatibility mode reports true for 'onhashchange' in window, even
1388    // though the event isn't supported, so also test document.documentMode.
1389    doc_mode = doc.documentMode,
1390    supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
1391  
1392  // Get location.hash (or what you'd expect location.hash to be) sans any
1393  // leading #. Thanks for making this necessary, Firefox!
1394  function get_fragment( url ) {
1395    url = url || location.href;
1396    return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
1397  };
1398  
1399  // Method: jQuery.fn.hashchange
1400  // 
1401  // Bind a handler to the window.onhashchange event or trigger all bound
1402  // window.onhashchange event handlers. This behavior is consistent with
1403  // jQuery's built-in event handlers.
1404  // 
1405  // Usage:
1406  // 
1407  // > jQuery(window).hashchange( [ handler ] );
1408  // 
1409  // Arguments:
1410  // 
1411  //  handler - (Function) Optional handler to be bound to the hashchange
1412  //    event. This is a "shortcut" for the more verbose form:
1413  //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
1414  //    all bound window.onhashchange event handlers will be triggered. This
1415  //    is a shortcut for the more verbose
1416  //    jQuery(window).trigger( 'hashchange' ). These forms are described in
1417  //    the <hashchange event> section.
1418  // 
1419  // Returns:
1420  // 
1421  //  (jQuery) The initial jQuery collection of elements.
1422  
1423  // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
1424  // $(elem).hashchange() for triggering, like jQuery does for built-in events.
1425  $.fn[ str_hashchange ] = function( fn ) {
1426    return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
1427  };
1428  
1429  // Property: jQuery.fn.hashchange.delay
1430  // 
1431  // The numeric interval (in milliseconds) at which the <hashchange event>
1432  // polling loop executes. Defaults to 50.
1433  
1434  // Property: jQuery.fn.hashchange.domain
1435  // 
1436  // If you're setting document.domain in your JavaScript, and you want hash
1437  // history to work in IE6/7, not only must this property be set, but you must
1438  // also set document.domain BEFORE jQuery is loaded into the page. This
1439  // property is only applicable if you are supporting IE6/7 (or IE8 operating
1440  // in "IE7 compatibility" mode).
1441  // 
1442  // In addition, the <jQuery.fn.hashchange.src> property must be set to the
1443  // path of the included "document-domain.html" file, which can be renamed or
1444  // modified if necessary (note that the document.domain specified must be the
1445  // same in both your main JavaScript as well as in this file).
1446  // 
1447  // Usage:
1448  // 
1449  // jQuery.fn.hashchange.domain = document.domain;
1450  
1451  // Property: jQuery.fn.hashchange.src
1452  // 
1453  // If, for some reason, you need to specify an Iframe src file (for example,
1454  // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
1455  // do so using this property. Note that when using this property, history
1456  // won't be recorded in IE6/7 until the Iframe src file loads. This property
1457  // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
1458  // compatibility" mode).
1459  // 
1460  // Usage:
1461  // 
1462  // jQuery.fn.hashchange.src = 'path/to/file.html';
1463  
1464  $.fn[ str_hashchange ].delay = 50;
1465  /*
1466  $.fn[ str_hashchange ].domain = null;
1467  $.fn[ str_hashchange ].src = null;
1468  */
1469  
1470  // Event: hashchange event
1471  // 
1472  // Fired when location.hash changes. In browsers that support it, the native
1473  // HTML5 window.onhashchange event is used, otherwise a polling loop is
1474  // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
1475  // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
1476  // compatibility" mode), a hidden Iframe is created to allow the back button
1477  // and hash-based history to work.
1478  // 
1479  // Usage as described in <jQuery.fn.hashchange>:
1480  // 
1481  // > // Bind an event handler.
1482  // > jQuery(window).hashchange( function(e) {
1483  // >   var hash = location.hash;
1484  // >   ...
1485  // > });
1486  // > 
1487  // > // Manually trigger the event handler.
1488  // > jQuery(window).hashchange();
1489  // 
1490  // A more verbose usage that allows for event namespacing:
1491  // 
1492  // > // Bind an event handler.
1493  // > jQuery(window).bind( 'hashchange', function(e) {
1494  // >   var hash = location.hash;
1495  // >   ...
1496  // > });
1497  // > 
1498  // > // Manually trigger the event handler.
1499  // > jQuery(window).trigger( 'hashchange' );
1500  // 
1501  // Additional Notes:
1502  // 
1503  // * The polling loop and Iframe are not created until at least one handler
1504  //   is actually bound to the 'hashchange' event.
1505  // * If you need the bound handler(s) to execute immediately, in cases where
1506  //   a location.hash exists on page load, via bookmark or page refresh for
1507  //   example, use jQuery(window).hashchange() or the more verbose 
1508  //   jQuery(window).trigger( 'hashchange' ).
1509  // * The event can be bound before DOM ready, but since it won't be usable
1510  //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
1511  //   to bind it inside a DOM ready handler.
1512  
1513  // Override existing $.event.special.hashchange methods (allowing this plugin
1514  // to be defined after jQuery BBQ in BBQ's source code).
1515  special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
1516    
1517    // Called only when the first 'hashchange' event is bound to window.
1518    setup: function() {
1519      // If window.onhashchange is supported natively, there's nothing to do..
1520      if ( supports_onhashchange ) { return false; }
1521      
1522      // Otherwise, we need to create our own. And we don't want to call this
1523      // until the user binds to the event, just in case they never do, since it
1524      // will create a polling loop and possibly even a hidden Iframe.
1525      $( fake_onhashchange.start );
1526    },
1527    
1528    // Called only when the last 'hashchange' event is unbound from window.
1529    teardown: function() {
1530      // If window.onhashchange is supported natively, there's nothing to do..
1531      if ( supports_onhashchange ) { return false; }
1532      
1533      // Otherwise, we need to stop ours (if possible).
1534      $( fake_onhashchange.stop );
1535    }
1536    
1537  });
1538  
1539  // fake_onhashchange does all the work of triggering the window.onhashchange
1540  // event for browsers that don't natively support it, including creating a
1541  // polling loop to watch for hash changes and in IE 6/7 creating a hidden
1542  // Iframe to enable back and forward.
1543  fake_onhashchange = (function(){
1544    var self = {},
1545      timeout_id,
1546      
1547      // Remember the initial hash so it doesn't get triggered immediately.
1548      last_hash = get_fragment(),
1549      
1550      fn_retval = function(val){ return val; },
1551      history_set = fn_retval,
1552      history_get = fn_retval;
1553    
1554    // Start the polling loop.
1555    self.start = function() {
1556      timeout_id || poll();
1557    };
1558    
1559    // Stop the polling loop.
1560    self.stop = function() {
1561      timeout_id && clearTimeout( timeout_id );
1562      timeout_id = undefined;
1563    };
1564    
1565    // This polling loop checks every $.fn.hashchange.delay milliseconds to see
1566    // if location.hash has changed, and triggers the 'hashchange' event on
1567    // window when necessary.
1568    function poll() {
1569      var hash = get_fragment(),
1570        history_hash = history_get( last_hash );
1571      
1572      if ( hash !== last_hash ) {
1573        history_set( last_hash = hash, history_hash );
1574        
1575        $(window).trigger( str_hashchange );
1576        
1577      } else if ( history_hash !== last_hash ) {
1578        location.href = location.href.replace( /#.*/, '' ) + history_hash;
1579      }
1580      
1581      timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
1582    };
1583    
1584    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1585    // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
1586    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1587    $.browser.msie && !supports_onhashchange && (function(){
1588      // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
1589      // when running in "IE7 compatibility" mode.
1590      
1591      var iframe,
1592        iframe_src;
1593      
1594      // When the event is bound and polling starts in IE 6/7, create a hidden
1595      // Iframe for history handling.
1596      self.start = function(){
1597        if ( !iframe ) {
1598          iframe_src = $.fn[ str_hashchange ].src;
1599          iframe_src = iframe_src && iframe_src + get_fragment();
1600          
1601          // Create hidden Iframe. Attempt to make Iframe as hidden as possible
1602          // by using techniques from http://www.paciellogroup.com/blog/?p=604.
1603          iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
1604            
1605            // When Iframe has completely loaded, initialize the history and
1606            // start polling.
1607            .one( 'load', function(){
1608              iframe_src || history_set( get_fragment() );
1609              poll();
1610            })
1611            
1612            // Load Iframe src if specified, otherwise nothing.
1613            .attr( 'src', iframe_src || 'javascript:0' )
1614            
1615            // Append Iframe after the end of the body to prevent unnecessary
1616            // initial page scrolling (yes, this works).
1617            .insertAfter( 'body' )[0].contentWindow;
1618          
1619          // Whenever `document.title` changes, update the Iframe's title to
1620          // prettify the back/next history menu entries. Since IE sometimes
1621          // errors with "Unspecified error" the very first time this is set
1622          // (yes, very useful) wrap this with a try/catch block.
1623          doc.onpropertychange = function(){
1624            try {
1625              if ( event.propertyName === 'title' ) {
1626                iframe.document.title = doc.title;
1627              }
1628            } catch(e) {}
1629          };
1630          
1631        }
1632      };
1633      
1634      // Override the "stop" method since an IE6/7 Iframe was created. Even
1635      // if there are no longer any bound event handlers, the polling loop
1636      // is still necessary for back/next to work at all!
1637      self.stop = fn_retval;
1638      
1639      // Get history by looking at the hidden Iframe's location.hash.
1640      history_get = function() {
1641        return get_fragment( iframe.location.href );
1642      };
1643      
1644      // Set a new history item by opening and then closing the Iframe
1645      // document, *then* setting its location.hash. If document.domain has
1646      // been set, update that as well.
1647      history_set = function( hash, history_hash ) {
1648        var iframe_doc = iframe.document,
1649          domain = $.fn[ str_hashchange ].domain;
1650        
1651        if ( hash !== history_hash ) {
1652          // Update Iframe with any initial `document.title` that might be set.
1653          iframe_doc.title = doc.title;
1654          
1655          // Opening the Iframe's document after it has been closed is what
1656          // actually adds a history entry.
1657          iframe_doc.open();
1658          
1659          // Set document.domain for the Iframe document as well, if necessary.
1660          domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
1661          
1662          iframe_doc.close();
1663          
1664          // Update the Iframe's hash, for great justice.
1665          iframe.location.hash = hash;
1666        }
1667      };
1668      
1669    })();
1670    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1671    // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
1672    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1673    
1674    return self;
1675  })();
1676  
1677})(jQuery,this);
1678/*
1679* "page" plugin
1680*/
1681
1682(function( $, undefined ) {
1683
1684$.widget( "mobile.page", $.mobile.widget, {
1685	options: {
1686		theme: "c",
1687		domCache: false,
1688		keepNativeDefault: ":jqmData(role='none'), :jqmData(role='nojs')"
1689	},
1690
1691	_create: function() {
1692
1693		this._trigger( "beforecreate" );
1694
1695		this.element
1696			.attr( "tabindex", "0" )
1697			.addClass( "ui-page ui-body-" + this.options.theme );
1698	},
1699
1700	keepNativeSelector: function() {
1701		var options = this.options,
1702			keepNativeDefined = options.keepNative && $.trim(options.keepNative);
1703
1704		if( keepNativeDefined && options.keepNative !== options.keepNativeDefault ){
1705			return [options.keepNative, options.keepNativeDefault].join(", ");
1706		}
1707
1708		return options.keepNativeDefault;
1709	}
1710});
1711})( jQuery );
1712/* 
1713* "core" - The base file for jQm
1714*/
1715
1716(function( $, window, undefined ) {
1717
1718	var nsNormalizeDict = {};
1719
1720	// jQuery.mobile configurable options
1721	$.extend( $.mobile, {
1722
1723		// Namespace used framework-wide for data-attrs. Default is no namespace
1724		ns: "",
1725
1726		// Define the url parameter used for referencing widget-generated sub-pages.
1727		// Translates to to example.html&ui-page=subpageIdentifier
1728		// hash segment before &ui-page= is used to make Ajax request
1729		subPageUrlKey: "ui-page",
1730
1731		// Class assigned to page currently in view, and during transitions
1732		activePageClass: "ui-page-active",
1733
1734		// Class used for "active" button state, from CSS framework
1735		activeBtnClass: "ui-btn-active",
1736
1737		// Automatically handle clicks and form submissions through Ajax, when same-domain
1738		ajaxEnabled: true,
1739
1740		// Automatically load and show pages based on location.hash
1741		hashListeningEnabled: true,
1742
1743		// disable to prevent jquery from bothering with links
1744		linkBindingEnabled: true,
1745
1746		// Set default page transition - 'none' for no transitions
1747		defaultPageTransition: "slide",
1748
1749		// Minimum scroll distance that will be remembered when returning to a page
1750		minScrollBack: 250,
1751
1752		// Set default dialog transition - 'none' for no transitions
1753		defaultDialogTransition: "pop",
1754
1755		// Show loading message during Ajax requests
1756		// if false, message will not appear, but loading classes will still be toggled on html el
1757		loadingMessage: "loading",
1758
1759		// Error response message - appears when an Ajax page request fails
1760		pageLoadErrorMessage: "Error Loading Page",
1761
1762		//automatically initialize the DOM when it's ready
1763		autoInitializePage: true,
1764
1765		pushStateEnabled: true,
1766
1767		// turn of binding to the native orientationchange due to android orientation behavior
1768		orientationChangeEnabled: true,
1769
1770		// Support conditions that must be met in order to proceed
1771		// default enhanced qualifications are media query support OR IE 7+
1772		gradeA: function(){
1773			return $.support.mediaquery || $.mobile.browser.ie && $.mobile.browser.ie >= 7;
1774		},
1775
1776		// TODO might be useful upstream in jquery itself ?
1777		keyCode: {
1778			ALT: 18,
1779			BACKSPACE: 8,
1780			CAPS_LOCK: 20,
1781			COMMA: 188,
1782			COMMAND: 91,
1783			COMMAND_LEFT: 91, // COMMAND
1784			COMMAND_RIGHT: 93,
1785			CONTROL: 17,
1786			DELETE: 46,
1787			DOWN: 40,
1788			END: 35,
1789			ENTER: 13,
1790			ESCAPE: 27,
1791			HOME: 36,
1792			INSERT: 45,
1793			LEFT: 37,
1794			MENU: 93, // COMMAND_RIGHT
1795			NUMPAD_ADD: 107,
1796			NUMPAD_DECIMAL: 110,
1797			NUMPAD_DIVIDE: 111,
1798			NUMPAD_ENTER: 108,
1799			NUMPAD_MULTIPLY: 106,
1800			NUMPAD_SUBTRACT: 109,
1801			PAGE_DOWN: 34,
1802			PAGE_UP: 33,
1803			PERIOD: 190,
1804			RIGHT: 39,
1805			SHIFT: 16,
1806			SPACE: 32,
1807			TAB: 9,
1808			UP: 38,
1809			WINDOWS: 91 // COMMAND
1810		},
1811
1812		// Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
1813		silentScroll: function( ypos ) {
1814			if ( $.type( ypos ) !== "number" ) {
1815				ypos = $.mobile.defaultHomeScroll;
1816			}
1817
1818			// prevent scrollstart and scrollstop events
1819			$.event.special.scrollstart.enabled = false;
1820
1821			setTimeout(function() {
1822				window.scrollTo( 0, ypos );
1823				$( document ).trigger( "silentscroll", { x: 0, y: ypos });
1824			}, 20 );
1825
1826			setTimeout(function() {
1827				$.event.special.scrollstart.enabled = true;
1828			}, 150 );
1829		},
1830
1831		// Expose our cache for testing purposes.
1832		nsNormalizeDict: nsNormalizeDict,
1833
1834		// Take a data attribute property, prepend the namespace
1835		// and then camel case the attribute string. Add the result
1836		// to our nsNormalizeDict so we don't have to do this again.
1837		nsNormalize: function( prop ) {
1838			if ( !prop ) {
1839				return;
1840			}
1841
1842			return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) );
1843		},
1844
1845		getInheritedTheme: function( el, defaultTheme ) {
1846
1847			// Find the closest parent with a theme class on it. Note that
1848			// we are not using $.fn.closest() on purpose here because this
1849			// method gets called quite a bit and we need it to be as fast
1850			// as possible.
1851
1852			var e = el[ 0 ],
1853				ltr = "",
1854				re = /ui-(bar|body)-([a-z])\b/,
1855				c, m;
1856
1857			while ( e ) {
1858				var c = e.className || "";
1859				if ( ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) {
1860					// We found a parent with a theme class
1861					// on it so bail from this loop.
1862					break;
1863				}
1864				e = e.parentNode;
1865			}
1866			
1867			// Return the theme letter we found, if none, return the
1868			// specified default.
1869
1870			return ltr || defaultTheme || "a";
1871		}
1872	});
1873
1874	// Mobile version of data and removeData and hasData methods
1875	// ensures all data is set and retrieved using jQuery Mobile's data namespace
1876	$.fn.jqmData = function( prop, value ) {
1877		var result;
1878		if ( typeof prop != "undefined" ) {
1879			result = this.data( prop ? $.mobile.nsNormalize( prop ) : prop, value );
1880		}
1881		return result;
1882	};
1883
1884	$.jqmData = function( elem, prop, value ) {
1885		var result;
1886		if ( typeof prop != "undefined" ) {
1887			result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value );
1888		}
1889		return result;
1890	};
1891
1892	$.fn.jqmRemoveData = function( prop ) {
1893		return this.removeData( $.mobile.nsNormalize( prop ) );
1894	};
1895
1896	$.jqmRemoveData = function( elem, prop ) {
1897		return $.removeData( elem, $.mobile.nsNormalize( prop ) );
1898	};
1899
1900	$.fn.removeWithDependents = function() {
1901		$.removeWithDependents( this );
1902	};
1903
1904	$.removeWithDependents = function( elem ) {
1905		var $elem = $( elem );
1906
1907		( $elem.jqmData('dependents') || $() ).remove();
1908		$elem.remove();
1909	};
1910
1911	$.fn.addDependents = function( newDependents ) {
1912		$.addDependents( $(this), newDependents );
1913	};
1914
1915	$.addDependents = function( elem, newDependents ) {
1916		var dependents = $(elem).jqmData( 'dependents' ) || $();
1917
1918		$(elem).jqmData( 'dependents', $.merge(dependents, newDependents) );
1919	};
1920
1921	// note that this helper doesn't attempt to handle the callback
1922	// or setting of an html elements text, its only purpose is
1923	// to return the html encoded version of the text in all cases. (thus the name)
1924	$.fn.getEncodedText = function() {
1925		return $( "<div/>" ).text( $(this).text() ).html();
1926	};
1927
1928	// Monkey-patching Sizzle to filter the :jqmData selector
1929	var oldFind = $.find,
1930		jqmDataRE = /:jqmData\(([^)]*)\)/g;
1931
1932	$.find = function( selector, context, ret, extra ) {
1933		selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" );
1934
1935		return oldFind.call( this, selector, context, ret, extra );
1936	};
1937
1938	$.extend( $.find, oldFind );
1939
1940	$.find.matches = function( expr, set ) {
1941		return $.find( expr, null, null, set );
1942	};
1943
1944	$.find.matchesSelector = function( node, expr ) {
1945		return $.find( expr, null, null, [ node ] ).length > 0;
1946	};
1947})( jQuery, this );
1948
1949/*
1950* core utilities for auto ajax navigation, base tag mgmt,
1951*/
1952
1953( function( $, undefined ) {
1954
1955	//define vars for interal use
1956	var $window = $( window ),
1957		$html = $( 'html' ),
1958		$head = $( 'head' ),
1959
1960		//url path helpers for use in relative url management
1961		path = {
1962
1963			// This scary looking regular expression parses an absolute URL or its relative
1964			// variants (protocol, site, document, query, and hash), into the various
1965			// components (protocol, host, path, query, fragment, etc that make up the
1966			// URL as well as some other commonly used sub-parts. When used with RegExp.exec()
1967			// or String.match, it parses the URL into a results array that looks like this:
1968			//
1969			//     [0]: http://jblas:[email protected]:8080/mail/inbox?msg=1234&type=unread#msg-content
1970			//     [1]: http://jblas:[email protected]:8080/mail/inbox?msg=1234&type=unread
1971			//     [2]: http://jblas:[email protected]:8080/mail/inbox
1972			//     [3]: http://jblas:[email protected]:8080
1973			//     [4]: http:
1974			//     [5]: //
1975			//     [6]: jblas:[email protected]:8080
1976			//     [7]: jblas:password
1977			//     [8]: jblas
1978			//     [9]: password
1979			//    [10]: mycompany.com:8080
1980			//    [11]: mycompany.com
1981			//    [12]: 8080
1982			//    [13]: /mail/inbox
1983			//    [14]: /mail/
1984			//    [15]: inbox
1985			//    [16]: ?msg=1234&type=unread
1986			//    [17]: #msg-content
1987			//
1988			urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
1989
1990			//Parse a URL into a structure that allows easy access to
1991			//all of the URL components by name.
1992			parseUrl: function( url ) {
1993				// If we're passed an object, we'll assume that it is
1994				// a parsed url object and just return it back to the caller.
1995				if ( $.type( url ) === "object" ) {
1996					return url;
1997				}
1998
1999				var matches = path.urlParseRE.exec( url || "" ) || [];
2000
2001					// Create an object that allows the caller to access the sub-matches
2002					// by name. Note that IE returns an empty string instead of undefined,
2003					// like all other browsers do, so we normalize everything so its consistent
2004					// no matter what browser we're running on.
2005					return {
2006						href:         matches[  0 ] || "",
2007						hrefNoHash:   matches[  1 ] || "",
2008						hrefNoSearch: matches[  2 ] || "",
2009						domain:       matches[  3 ] || "",
2010						protocol:     matches[  4 ] || "",
2011						doubleSlash:  matches[  5 ] || "",
2012						authority:    matches[  6 ] || "",
2013						username:     matches[  8 ] || "",
2014						password:     matches[  9 ] || "",
2015						host:         matches[ 10 ] || "",
2016						hostname:     matches[ 11 ] || "",
2017						port:         matches[ 12 ] || "",
2018						pathname:     matches[ 13 ] || "",
2019						directory:    matches[ 14 ] || "",
2020						filename:     matches[ 15 ] || "",
2021						search:       matches[ 16 ] || "",
2022						hash:         matches[ 17 ] || ""
2023					};
2024			},
2025
2026			//Turn relPath into an asbolute path. absPath is
2027			//an optional absolute path which describes what
2028			//relPath is relative to.
2029			makePathAbsolute: function( relPath, absPath ) {
2030				if ( relPath && relPath.charAt( 0 ) === "/" ) {
2031					return relPath;
2032				}
2033
2034				relPath = relPath || "";
2035				absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : "";
2036
2037				var absStack = absPath ? absPath.split( "/" ) : [],
2038					relStack = relPath.split( "/" );
2039				for ( var i = 0; i < relStack.length; i++ ) {
2040					var d = relStack[ i ];
2041					switch ( d ) {
2042						case ".":
2043							break;
2044						case "..":
2045							if ( absStack.length ) {
2046								absStack.pop();
2047							}
2048							break;
2049						default:
2050							absStack.push( d );
2051							break;
2052					}
2053				}
2054				return "/" + absStack.join( "/" );
2055			},
2056
2057			//Returns true if both urls have the same domain.
2058			isSameDomain: function( absUrl1, absUrl2 ) {
2059				return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
2060			},
2061
2062			//Returns true for any relative variant.
2063			isRelativeUrl: function( url ) {
2064				// All relative Url variants have one thing in common, no protocol.
2065				return path.parseUrl( url ).protocol === "";
2066			},
2067
2068			//Returns true for an absolute url.
2069			isAbsoluteUrl: function( url ) {
2070				return path.parseUrl( url ).protocol !== "";
2071			},
2072
2073			//Turn the specified realtive URL into an absolute one. This function
2074			//can handle all relative variants (protocol, site, document, query, fragment).
2075			makeUrlAbsolute: function( relUrl, absUrl ) {
2076				if ( !path.isRelativeUrl( relUrl ) ) {
2077					return relUrl;
2078				}
2079
2080				var relObj = path.parseUrl( relUrl ),
2081					absObj = path.parseUrl( absUrl ),
2082					protocol = relObj.protocol || absObj.protocol,
2083					doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ),
2084					authority = relObj.authority || absObj.authority,
2085					hasPath = relObj.pathname !== "",
2086					pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ),
2087					search = relObj.search || ( !hasPath && absObj.search ) || "",
2088					hash = relObj.hash;
2089
2090				return protocol + doubleSlash + authority + pathname + search + hash;
2091			},
2092
2093			//Add search (aka query) params to the specified url.
2094			addSearchParams: function( url, params ) {
2095				var u = path.parseUrl( url ),
2096					p = ( typeof params === "object" ) ? $.param( params ) : params,
2097					s = u.search || "?";
2098				return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" );
2099			},
2100
2101			convertUrlToDataUrl: function( absUrl ) {
2102				var u = path.parseUrl( absUrl );
2103				if ( path.isEmbeddedPage( u ) ) {
2104				    // For embedded pages, remove the dialog hash key as in getFilePath(),
2105				    // otherwise the Data Url won't match the id of the embedded Page.
2106					return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
2107				} else if ( path.isSameDomain( u, documentBase ) ) {
2108					return u.hrefNoHash.replace( documentBase.domain, "" );
2109				}
2110				return absUrl;
2111			},
2112
2113			//get path from current hash, or from a file path
2114			get: function( newPath ) {
2115				if( newPath === undefined ) {
2116					newPath = location.hash;
2117				}
2118				return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
2119			},
2120
2121			//return the substring of a filepath before the sub-page key, for making a server request
2122			getFilePath: function( path ) {
2123				var splitkey = '&' + $.mobile.subPageUrlKey;
2124				return path && path.split( splitkey )[0].split( dialogHashKey )[0];
2125			},
2126
2127			//set location hash to path
2128			set: function( path ) {
2129				location.hash = path;
2130			},
2131
2132			//test if a given url (string) is a path
2133			//NOTE might be exceptionally naive
2134			isPath: function( url ) {
2135				return ( /\// ).test( url );
2136			},
2137
2138			//return a url path with the window's location protocol/hostname/pathname removed
2139			clean: function( url ) {
2140				return url.replace( documentBase.domain, "" );
2141			},
2142
2143			//just return the url without an initial #
2144			stripHash: function( url ) {
2145				return url.replace( /^#/, "" );
2146			},
2147
2148			//remove the preceding hash, any query params, and dialog notations
2149			cleanHash: function( hash ) {
2150				return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
2151			},
2152
2153			//check whether a url is referencing the same domain, or an external domain or different protocol
2154			//could be mailto, etc
2155			isExternal: function( url ) {
2156				var u = path.parseUrl( url );
2157				return u.protocol && u.domain !== documentUrl.domain ? true : false;
2158			},
2159
2160			hasProtocol: function( url ) {
2161				return ( /^(:?\w+:)/ ).test( url );
2162			},
2163
2164			//check if the specified url refers to the first page in the main application document.
2165			isFirstPageUrl: function( url ) {
2166				// We only deal with absolute paths.
2167				var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ),
2168
2169					// Does the url have the same path as the document?
2170					samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
2171
2172					// Get the first page element.
2173					fp = $.mobile.firstPage,
2174
2175					// Get the id of the first page element if it has one.
2176					fpId = fp && fp[0] ? fp[0].id : undefined;
2177
2178					// The url refers to the first page if the path matches the document and
2179					// it either has no hash value, or the hash is exactly equal to the id of the
2180					// first page element.
2181					return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
2182			},
2183
2184			isEmbeddedPage: function( url ) {
2185				var u = path.parseUrl( url );
2186
2187				//if the path is absolute, then we need to compare the url against
2188				//both the documentUrl and the documentBase. The main reason for this
2189				//is that links embedded within external documents will refer to the
2190				//application document, whereas links embedded within the application
2191				//document will be resolved against the document base.
2192				if ( u.protocol !== "" ) {
2193					return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) );
2194				}
2195				return (/^#/).test( u.href );
2196			}
2197		},
2198
2199		//will be defined when a link is clicked and given an active class
2200		$activeClickedLink = null,
2201
2202		//urlHistory is purely here to make guesses at whether the back or forward button was clicked
2203		//and provide an appropriate transition
2204		urlHistory = {
2205			// Array of pages that are visited during a single page load.
2206			// Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs)
2207			stack: [],
2208
2209			//maintain an index number for the active page in the stack
2210			activeIndex: 0,
2211
2212			//get active
2213			getActive: function() {
2214				return urlHistory.stack[ urlHistory.activeIndex ];
2215			},
2216
2217			getPrev: function() {
2218				return urlHistory.stack[ urlHistory.activeIndex - 1 ];
2219			},
2220
2221			getNext: function() {
2222				return urlHistory.stack[ urlHistory.activeIndex + 1 ];
2223			},
2224
2225			// addNew is used whenever a new page is added
2226			addNew: function( url, transition, title, pageUrl, role ) {
2227				//if there's forward history, wipe it
2228				if( urlHistory.getNext() ) {
2229					urlHistory.clearForward();
2230				}
2231
2232				urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
2233
2234				urlHistory.activeIndex = urlHistory.stack.length - 1;
2235			},
2236
2237			//wipe urls ahead of active index
2238			clearForward: function() {
2239				urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
2240			},
2241
2242			directHashChange: function( opts ) {
2243				var back , forward, newActiveIndex, prev = this.getActive();
2244
2245				// check if url isp in history and if it's ahead or behind current page
2246				$.each( urlHistory.stack, function( i, historyEntry ) {
2247
2248					//if the url is in the stack, it's a forward or a back
2249					if( opts.currentUrl === historyEntry.url ) {
2250						//define back and forward by whether url is older or newer than current page
2251						back = i < urlHistory.activeIndex;
2252						forward = !back;
2253						newActiveIndex = i;
2254					}
2255				});
2256
2257				// save new page index, null check to prevent falsey 0 result
2258				this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;
2259
2260				if( back ) {
2261					( opts.either || opts.isBack )( true );
2262				} else if( forward ) {
2263					( opts.either || opts.isForward )( false );
2264				}
2265			},
2266
2267			//disable hashchange event listener internally to ignore one change
2268			//toggled internally when location.hash is updated to match the url of a successful page load
2269			ignoreNextHashChange: false
2270		},
2271
2272		//define first selector to receive focus when a page is shown
2273		focusable = "[tabindex],a,button:visible,select:visible,input",
2274
2275		//queue to hold simultanious page transitions
2276		pageTransitionQueue = [],
2277
2278		//indicates whether or not page is in process of transitioning
2279		isPageTransitioning = false,
2280
2281		//nonsense hash change key for dialogs, so they create a history entry
2282		dialogHashKey = "&ui-state=dialog",
2283
2284		//existing base tag?
2285		$base = $head.children( "base" ),
2286
2287		//tuck away the original document URL minus any fragment.
2288		documentUrl = path.parseUrl( location.href ),
2289
2290		//if the document has an embedded base tag, documentBase is set to its
2291		//initial value. If a base tag does not exist, then we default to the documentUrl.
2292		documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl,
2293
2294		//cache the comparison once.
2295		documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash );
2296
2297		//base element management, defined depending on dynamic base tag support
2298		var base = $.support.dynamicBaseTag ? {
2299
2300			//define base element, for use in routing asset urls that are referenced in Ajax-requested markup
2301			element: ( $base.length ? $base : $( "<base>", { href: documentBase.hrefNoHash } ).prependTo( $head ) ),
2302
2303			//set the generated BASE element's href attribute to a new page's base path
2304			set: function( href ) {
2305				base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) );
2306			},
2307
2308			//set the generated BASE element's href attribute to a new page's base path
2309			reset: function() {
2310				base.element.attr( "href", documentBase.hrefNoHash );
2311			}
2312
2313		} : undefined;
2314
2315/*
2316	internal utility functions
2317--------------------------------------*/
2318
2319
2320	//direct focus to the page title, or otherwise first focusable element
2321	function reFocus( page ) {
2322		var pageTitle = page.find( ".ui-title:eq(0)" );
2323
2324		if( pageTitle.length ) {
2325			pageTitle.focus();
2326		}
2327		else{
2328			page.focus();
2329		}
2330	}
2331
2332	//remove active classes after page transition or error
2333	function removeActiveLinkClass( forceRemoval ) {
2334		if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) {
2335			$activeClickedLink.removeClass( $.mobile.activeBtnClass );
2336		}
2337		$activeClickedLink = null;
2338	}
2339
2340	function releasePageTransitionLock() {
2341		isPageTransitioning = false;
2342		if( pageTransitionQueue.length > 0 ) {
2343			$.mobile.changePage.apply( null, pageTransitionQueue.pop() );
2344		}
2345	}
2346
2347	// Save the last scroll distance per page, before it is hidden
2348	var setLastScrollEnabled = true,
2349		firstScrollElem, getScrollElem, setLastScroll, delayedSetLastScroll;
2350
2351	getScrollElem = function() {
2352		var scrollElem = $window, activePage,
2353			touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled;
2354
2355		if( touchOverflow ){
2356			activePage = $( ".ui-page-active" );
2357			scrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage;
2358		}
2359
2360		return scrollElem;
2361	};
2362
2363	setLastScroll = function( scrollElem ) {
2364		// this barrier prevents setting the scroll value based on the browser
2365		// scrolling the window based on a hashchange
2366		if( !setLastScrollEnabled ) {
2367			return;
2368		}
2369
2370		var active = $.mobile.urlHistory.getActive();
2371
2372		if( active ) {
2373			var lastScroll = scrollElem && scrollElem.scrollTop();
2374
2375			// Set active page's lastScroll prop.
2376			// If the location we're scrolling to is less than minScrollBack, let it go.
2377			active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
2378		}
2379	};
2380
2381	// bind to scrollstop to gather scroll position. The delay allows for the hashchange
2382	// event to fire and disable scroll recording in the case where the browser scrolls
2383	// to the hash targets location (sometimes the top of the page). once pagechange fires
2384	// getLastScroll is again permitted to operate
2385	delayedSetLastScroll = function() {
2386		setTimeout( setLastScroll, 100, $(this) );
2387	};
2388
2389	// disable an scroll setting when a hashchange has been fired, this only works
2390	// because the recording of the scroll position is delayed for 100ms after
2391	// the browser might have changed the position because of the hashchange
2392	$window.bind( $.support.pushState ? "popstate" : "hashchange", function() {
2393	 	setLastScrollEnabled = false;
2394	});
2395
2396	// handle initial hashchange from chrome :(
2397	$window.one( $.support.pushState ? "popstate" : "hashchange", function() {
2398		setLastScrollEnabled = true;
2399	});
2400
2401	// wait until the mobile page container has been determined to bind to pagechange
2402	$window.one( "pagecontainercreate", function(){
2403		// once the page has changed, re-enable the scroll recording
2404		$.mobile.pageContainer.bind( "pagechange", function() {
2405			var scrollElem = getScrollElem();
2406
2407	 		setLastScrollEnabled = true;
2408
2409			// remove any binding that previously existed on the get scroll
2410			// which may or may not be different than the scroll element determined for
2411			// this page previously
2412			scrollElem.unbind( "scrollstop", delayedSetLastScroll );
2413
2414			// determine and bind to the current scoll element which may be the window
2415			// or in the case of touch overflow the element with touch overflow
2416			scrollElem.bind( "scrollstop", delayedSetLastScroll );
2417		});
2418	});
2419
2420	// bind to scrollstop for the first page as "pagechange" won't be fired in that case
2421	getScrollElem().bind( "scrollstop", delayedSetLastScroll );
2422
2423	// Make the iOS clock quick-scroll work again if we're using native overflow scrolling
2424	/*
2425	if( $.support.touchOverflow ){
2426		if( $.mobile.touchOverflowEnabled ){
2427			$( window ).bind( "scrollstop", function(){
2428				if( $( this ).scrollTop() === 0 ){
2429					$.mobile.activePage.scrollTop( 0 );
2430				}
2431			});
2432		}
2433	}
2434	*/
2435
2436	//function for transitioning between two existing pages
2437	function transitionPages( toPage, fromPage, transition, reverse ) {
2438
2439		//get current scroll distance
2440		var active	= $.mobile.urlHistory.getActive(),
2441			touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
2442			toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ),
2443			screenHeight = getScreenHeight();
2444
2445		// Scroll to top, hide addr bar
2446		window.scrollTo( 0, $.mobile.defaultHomeScroll );
2447
2448		if( fromPage ) {
2449			//trigger before show/hide events
2450			fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
2451		}
2452
2453		if( !touchOverflow){
2454			toPage.height( screenHeight + toScroll );
2455		}
2456
2457		toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
2458
2459		//clear page loader
2460		$.mobile.hidePageLoadingMsg();
2461
2462		if( touchOverflow && toScroll ){
2463
2464			toPage.addClass( "ui-mobile-pre-transition" );
2465			// Send focus to page as it is now display: block
2466			reFocus( toPage );
2467
2468			//set page's scrollTop to remembered distance
2469			if( toPage.is( ".ui-native-fixed" ) ){
2470				toPage.find( ".ui-content" ).scrollTop( toScroll );
2471			}
2472			else{
2473				toPage.scrollTop( toScroll );
2474			}
2475		}
2476
2477		//find the transition handler for the specified transition. If there
2478		//isn't one in our transitionHandlers dictionary, use the default one.
2479		//call the handler immediately to kick-off the transition.
2480		var th = $.mobile.transitionHandlers[transition || "none"] || $.mobile.defaultTransitionHandler,
2481			promise = th( transition, reverse, toPage, fromPage );
2482
2483		promise.done(function() {
2484			//reset toPage height back
2485			if( !touchOverflow ){
2486				toPage.height( "" );
2487				// Send focus to the newly shown page
2488				reFocus( toPage );
2489			}
2490
2491			// Jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
2492			if( !touchOverflow ){
2493				$.mobile.silentScroll( toScroll );
2494			}
2495
2496			//trigger show/hide events
2497			if( fromPage ) {
2498				if( !touchOverflow ){
2499					fromPage.height( "" );
2500				}
2501
2502				fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
2503			}
2504
2505			//trigger pageshow, define prevPage as either fromPage or empty jQuery obj
2506			toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } );
2507		});
2508
2509		return promise;
2510	}
2511
2512	//simply set the active page's minimum height to screen height, depending on orientation
2513	function getScreenHeight(){
2514		var orientation 	= $.event.special.orientationchange.orientation(),
2515			port			= orientation === "portrait",
2516			winMin			= port ? 480 : 320,
2517			screenHeight	= port ? screen.availHeight : screen.availWidth,
2518			winHeight		= Math.max( winMin, $( window ).height() ),
2519			pageMin			= Math.min( screenHeight, winHeight );
2520
2521		return pageMin;
2522	}
2523
2524	$.mobile.getScreenHeight = getScreenHeight;
2525
2526	//simply set the active page's minimum height to screen height, depending on orientation
2527	function resetActivePageHeight(){
2528		// Don't apply this height in touch overflow enabled mode
2529		if( $.support.touchOverflow && $.mobile.touchOverflowEnabled ){
2530			return;
2531		}
2532		$( "." + $.mobile.activePageClass ).css( "min-height", getScreenHeight() );
2533	}
2534
2535	//shared page enhancements
2536	function enhancePage( $page, role ) {
2537		// If a role was specified, make sure the data-role attribute
2538		// on the page element is in sync.
2539		if( role ) {
2540			$page.attr( "data-" + $.mobile.ns + "role", role );
2541		}
2542
2543		//run page plugin
2544		$page.page();
2545	}
2546
2547/* exposed $.mobile methods	 */
2548
2549	//animation complete callback
2550	$.fn.animationComplete = function( callback ) {
2551		if( $.support.cssTransitions ) {
2552			return $( this ).one( 'webkitAnimationEnd', callback );
2553		}
2554		else{
2555			// defer execution for consistency between webkit/non webkit
2556			setTimeout( callback, 0 );
2557			return $( this );
2558		}
2559	};
2560
2561	//expose path object on $.mobile
2562	$.mobile.path = path;
2563
2564	//expose base object on $.mobile
2565	$.mobile.base = base;
2566
2567	//history stack
2568	$.mobile.urlHistory = urlHistory;
2569
2570	$.mobile.dialogHashKey = dialogHashKey;
2571
2572	//default non-animation transition handler
2573	$.mobile.noneTransitionHandler = function( name, reverse, $toPage, $fromPage ) {
2574		if ( $fromPage ) {
2575			$fromPage.removeClass( $.mobile.activePageClass );
2576		}
2577		$toPage.addClass( $.mobile.activePageClass );
2578
2579		return $.Deferred().resolve( name, reverse, $toPage, $fromPage ).promise();
2580	};
2581
2582	//default handler for unknown transitions
2583	$.mobile.defaultTransitionHandler = $.mobile.noneTransitionHandler;
2584
2585	//transition handler dictionary for 3rd party transitions
2586	$.mobile.transitionHandlers = {
2587		none: $.mobile.defaultTransitionHandler
2588	};
2589
2590	//enable cross-domain page support
2591	$.mobile.allowCrossDomainPages = false;
2592
2593	//return the original document url
2594	$.mobile.getDocumentUrl = function(asParsedObject) {
2595		return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href;
2596	};
2597
2598	//return the original document base url
2599	$.mobile.getDocumentBase = function(asParsedObject) {
2600		return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href;
2601	};
2602
2603	$.mobile._bindPageRemove = function() {
2604		var page = $(this);
2605
2606		// when dom caching is not enabled or the page is embedded bind to remove the page on hide
2607		if( !page.data("page").options.domCache
2608				&& page.is(":jqmData(external-page='true')") ) {
2609
2610			page.bind( 'pagehide.remove', function() {
2611				var $this = $( this ),
2612					prEvent = new $.Event( "pageremove" );
2613
2614				$this.trigger( prEvent );
2615
2616				if( !prEvent.isDefaultPrevented() ){
2617					$this.removeWithDependents();
2618				}
2619			});
2620		}
2621	};
2622
2623	// Load a page into the DOM.
2624	$.mobile.loadPage = function( url, options ) {
2625		// This function uses deferred notifications to let callers
2626		// know when the page is done loading, or if an error has occurred.
2627		var deferred = $.Deferred(),
2628
2629			// The default loadPage options with overrides specified by
2630			// the caller.
2631			settings = $.extend( {}, $.mobile.loadPage.defaults, options ),
2632
2633			// The DOM element for the page after it has been loaded.
2634			page = null,
2635
2636			// If the reloadPage option is true, and the page is already
2637			// in the DOM, dupCachedPage will be set to the page element
2638			// so that it can be removed after the new version of the
2639			// page is loaded off the network.
2640			dupCachedPage = null,
2641
2642			// determine the current base url
2643			findBaseWithDefault = function(){
2644				var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) );
2645				return closestBase || documentBase.hrefNoHash;
2646			},
2647
2648			// The absolute version of the URL passed into the function. This
2649			// version of the URL may contain dialog/subpage params in it.
2650			absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() );
2651
2652
2653		// If the caller provided data, and we're using "get" request,
2654		// append the data to the URL.
2655		if ( settings.data && settings.type === "get" ) {
2656			absUrl = path.addSearchParams( absUrl, settings.data );
2657			settings.data = undefined;
2658		}
2659
2660		// If the caller is using a "post" request, reloadPage must be true
2661		if(  settings.data && settings.type === "post" ){
2662			settings.reloadPage = true;
2663		}
2664
2665			// The absolute version of the URL minus any dialog/subpage params.
2666			// In otherwords the real URL of the page to be loaded.
2667		var fileUrl = path.getFilePath( absUrl ),
2668
2669			// The version of the Url actually stored in the data-url attribute of
2670			// the page. For embedded pages, it is just the id of the page. For pages
2671			// within the same domain as the document base, it is the site relative
2672			// path. For cross-domain pages (Phone Gap only) the entire absolute Url
2673			// used to load the page.
2674			dataUrl = path.convertUrlToDataUrl( absUrl );
2675
2676		// Make sure we have a pageContainer to work with.
2677		settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
2678
2679		// Check to see if the page already exists in the DOM.
2680		page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
2681
2682		// If we failed to find the page, check to see if the url is a
2683		// reference to an embedded page. If so, it may have been dynamically
2684		// injected by a developer, in which case it would be lacking a data-url
2685		// attribute and in need of enhancement.
2686		if ( page.length === 0 && dataUrl && !path.isPath( dataUrl ) ) {
2687			page = settings.pageContainer.children( "#" + dataUrl )
2688				.attr( "data-" + $.mobile.ns + "url", dataUrl );
2689		}
2690
2691		// If we failed to find a page in the DOM, check the URL to see if it
2692		// refers to the first page in the application. If it isn't a reference
2693		// to the first page and refers to non-existent embedded page, error out.
2694		if ( page.length === 0 ) {
2695			if ( $.mobile.firstPage && path.isFirstPageUrl( fileUrl ) ) {
2696				// Check to make sure our cached-first-page is actually
2697				// in the DOM. Some user deployed apps are pruning the first
2698				// page from the DOM for various reasons, we check for this
2699				// case here because we don't want a first-page with an id
2700				// falling through to the non-existent embedded page error
2701				// case. If the first-page is not in the DOM, then we let
2702				// things fall through to the ajax loading code below so
2703				// that it gets reloaded.
2704				if ( $.mobile.firstPage.parent().length ) {
2705					page = $( $.mobile.firstPage );
2706				}
2707			} else if ( path.isEmbeddedPage( fileUrl )  ) {
2708				deferred.reject( absUrl, options );
2709				return deferred.promise();
2710			}
2711		}
2712
2713		// Reset base to the default document base.
2714		if ( base ) {
2715			base.reset();
2716		}
2717
2718		// If the page we are interested in is already in the DOM,
2719		// and the caller did not indicate that we should force a
2720		// reload of the file, we are done. Otherwise, track the
2721		// existing page as a duplicated.
2722		if ( page.length ) {
2723			if ( !settings.reloadPage ) {
2724				enhancePage( page, settings.role );
2725				deferred.resolve( absUrl, options, page );
2726				return deferred.promise();
2727			}
2728			dupCachedPage = page;
2729		}
2730
2731		var mpc = settings.pageContainer,
2732			pblEvent = new $.Event( "pagebeforeload" ),
2733			triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings };
2734
2735		// Let listeners know we're about to load a page.
2736		mpc.trigger( pblEvent, triggerData );
2737
2738		// If the default behavior is prevented, stop here!
2739		if( pblEvent.isDefaultPrevented() ){
2740			return deferred.promise();
2741		}
2742
2743		if ( settings.showLoadMsg ) {
2744
2745			// This configurable timeout allows cached pages a brief delay to load without showing a message
2746			var loadMsgDelay = setTimeout(function(){
2747					$.mobile.showPageLoadingMsg();
2748				}, settings.loadMsgDelay ),
2749
2750				// Shared logic for clearing timeout and removing message.
2751				hideMsg = function(){
2752
2753					// Stop message show timer
2754					clearTimeout( loadMsgDelay );
2755
2756					// Hide loading message
2757					$.mobile.hidePageLoadingMsg();
2758				};
2759		}
2760
2761		if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) {
2762			deferred.reject( absUrl, options );
2763		} else {
2764			// Load the new page.
2765			$.ajax({
2766				url: fileUrl,
2767				type: settings.type,
2768				data: settings.data,
2769				dataType: "html",
2770				success: function( html, textStatus, xhr ) {
2771					//pre-parse html to check for a data-url,
2772					//use it as the new fileUrl, base path, etc
2773					var all = $( "<div></div>" ),
2774
2775						//page title regexp
2776						newPageTitle = html.match( /<title[^>]*>([^<]*)/ ) && RegExp.$1,
2777
2778						// TODO handle dialogs again
2779						pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)" ),
2780						dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" );
2781
2782
2783					// data-url must be provided for the base tag so resource requests can be directed to the
2784					// correct url. loading into a temprorary element makes these requests immediately
2785					if( pageElemRegex.test( html )
2786							&& RegExp.$1
2787							&& dataUrlRegex.test( RegExp.$1 )
2788							&& RegExp.$1 ) {
2789						url = fileUrl = path.getFilePath( RegExp.$1 );
2790					}
2791
2792					if ( base ) {
2793						base.set( fileUrl );
2794					}
2795
2796					//workaround to allow scripts to execute when included in page divs
2797					all.get( 0 ).innerHTML = html;
2798					page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first();
2799
2800					//if page elem couldn't be found, create one and insert the body element's contents
2801					if( !page.length ){
2802						page = $( "<div data-" + $.mobile.ns + "role='page'>" + html.split( /<\/?body[^>]*>/gmi )[1] + "</div>" );
2803					}
2804
2805					if ( newPageTitle && !page.jqmData( "title" ) ) {
2806						if ( ~newPageTitle.indexOf( "&" ) ) {
2807							newPageTitle = $( "<div>" + newPageTitle + "</div>" ).text();
2808						}
2809						page.jqmData( "title", newPageTitle );
2810					}
2811
2812					//rewrite src and href attrs to use a base url
2813					if( !$.support.dynamicBaseTag ) {
2814						var newPath = path.get( fileUrl );
2815						page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() {
2816							var thisAttr = $( this ).is( '[href]' ) ? 'href' :
2817									$(this).is('[src]') ? 'src' : 'action',
2818								thisUrl = $( this ).attr( thisAttr );
2819
2820							// XXX_jblas: We need to fix this so that it removes the document
2821							//            base URL, and then prepends with the new page URL.
2822							//if full path exists and is same, chop it - helps IE out
2823							thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
2824
2825							if( !/^(\w+:|#|\/)/.test( thisUrl ) ) {
2826								$( this ).attr( thisAttr, newPath + thisUrl );
2827							}
2828						});
2829					}
2830
2831					//append to page and enhance
2832					// TODO taging a page with external to make sure that embedded pages aren't removed
2833					//      by the various page handling code is bad. Having page handling code in many
2834					//      places is bad. Solutions post 1.0
2835					page
2836						.attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) )
2837						.attr( "data-" + $.mobile.ns + "external-page", true )
2838						.appendTo( settings.pageContainer );
2839
2840					// wait for page creation to leverage options defined on widget
2841					page.one( 'pagecreate', $.mobile._bindPageRemove );
2842
2843					enhancePage( page, settings.role );
2844
2845					// Enhancing the page may result in new dialogs/sub pages being inserted
2846					// into the DOM. If the original absUrl refers to a sub-page, that is the
2847					// real page we are interested in.
2848					if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) {
2849						page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
2850					}
2851
2852					//bind pageHide to removePage after it's hidden, if the page options specify to do so
2853
2854					// Remove loading message.
2855					if ( settings.showLoadMsg ) {
2856						hideMsg();
2857					}
2858
2859					// Add the page reference and xhr to our triggerData.
2860					triggerData.xhr = xhr;
2861					triggerData.textStatus = textStatus;
2862					triggerData.page = page;
2863
2864					// Let listeners know the page loaded successfully.
2865					settings.pageContainer.trigger( "pageload", triggerData );
2866
2867					deferred.resolve( absUrl, options, page, dupCachedPage );
2868				},
2869				error: function( xhr, textStatus, errorThrown ) {
2870					//set base back to current path
2871					if( base ) {
2872						base.set( path.get() );
2873					}
2874
2875					// Add error info to our triggerData.
2876					triggerData.xhr = xhr;
2877					triggerData.textStatus = textStatus;
2878					triggerData.errorThrown = errorThrown;
2879
2880					var plfEvent = new $.Event( "pageloadfailed" );
2881
2882					// Let listeners know the page load failed.
2883					settings.pageContainer.trigger( plfEvent, triggerData );
2884
2885					// If the default behavior is prevented, stop here!
2886					// Note that it is the responsibility of the listener/handler
2887					// that called preventDefault(), to resolve/reject the
2888					// deferred object within the triggerData.
2889					if( plfEvent.isDefaultPrevented() ){
2890						return;
2891					}
2892
2893					// Remove loading message.
2894					if ( settings.showLoadMsg ) {
2895
2896						// Remove loading message.
2897						hideMsg();
2898
2899						//show error message
2900						$( "<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>"+ $.mobile.pageLoadErrorMessage +"</h1></div>" )
2901							.css({ "display": "block", "opacity": 0.96, "top": $window.scrollTop() + 100 })
2902							.appendTo( settings.pageContainer )
2903							.delay( 800 )
2904							.fadeOut( 400, function() {
2905								$( this ).remove();
2906							});
2907					}
2908
2909					deferred.reject( absUrl, options );
2910				}
2911			});
2912		}
2913
2914		return deferred.promise();
2915	};
2916
2917	$.mobile.loadPage.defaults = {
2918		type: "get",
2919		data: undefined,
2920		reloadPage: false,
2921		role: undefined, // By default we rely on the role defined by the @data-role attribute.
2922		showLoadMsg: false,
2923		pageContainer: undefined,
2924		loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message.
2925	};
2926
2927	// Show a specific page in the page container.
2928	$.mobile.changePage = function( toPage, options ) {
2929		// If we are in the midst of a transition, queue the current request.
2930		// We'll call changePage() once we're done with the current transition to
2931		// service the request.
2932		if( isPageTransitioning ) {
2933			pageTransitionQueue.unshift( arguments );
2934			return;
2935		}
2936
2937		var settings = $.extend( {}, $.mobile.changePage.defaults, options );
2938
2939		// Make sure we have a pageContainer to work with.
2940		settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
2941
2942		// Make sure we have a fromPage.
2943		settings.fromPage = settings.fromPage || $.mobile.activePage;
2944
2945		var mpc = settings.pageContainer,
2946			pbcEvent = new $.Event( "pagebeforechange" ),
2947			triggerData = { toPage: toPage, options: settings };
2948
2949		// Let listeners know we're about to change the current page.
2950		mpc.trigger( pbcEvent, triggerData );
2951
2952		// If the default behavior is prevented, stop here!
2953		if( pbcEvent.isDefaultPrevented() ){
2954			return;
2955		}
2956
2957		// We allow "pagebeforechange" observers to modify the toPage in the trigger
2958		// data to allow for redirects. Make sure our toPage is updated.
2959
2960		toPage = triggerData.toPage;
2961
2962		// Set the isPageTransitioning flag to prevent any requests from
2963		// entering this method while we are in the midst of loading a page
2964		// or transitioning.
2965
2966		isPageTransitioning = true;
2967
2968		// If the caller passed us a url, call loadPage()
2969		// to make sure it is loaded into the DOM. We'll listen
2970		// to the promise object it returns so we know when
2971		// it is done loading or if an error ocurred.
2972		if ( typeof toPage == "string" ) {
2973			$.mobile.loadPage( toPage, settings )
2974				.done(function( url, options, newPage, dupCachedPage ) {
2975					isPageTransitioning = false;
2976					options.duplicateCachedPage = dupCachedPage;
2977					$.mobile.changePage( newPage, options );
2978				})
2979				.fail(function( url, options ) {
2980					isPageTransitioning = false;
2981
2982					//clear out the active button state
2983					removeActiveLinkClass( true );
2984
2985					//release transition lock so navigation is free again
2986					releasePageTransitionLock();
2987					settings.pageContainer.trigger( "pagechangefailed", triggerData );
2988				});
2989			return;
2990		}
2991
2992		// If we are going to the first-page of the application, we need to make
2993		// sure settings.dataUrl is set to the application document url. This allows
2994		// us to avoid generating a document url with an id hash in the case where the
2995		// first-page of the document has an id attribute specified.
2996		if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) {
2997			settings.dataUrl = documentUrl.hrefNoHash;
2998		}
2999
3000		// The caller passed us a real page DOM element. Update our
3001		// internal state and then trigger a transition to the page.
3002		var fromPage = settings.fromPage,
3003			url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ),
3004			// The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
3005			pageUrl = url,
3006			fileUrl = path.getFilePath( url ),
3007			active = urlHistory.getActive(),
3008			activeIsInitialPage = urlHistory.activeIndex === 0,
3009			historyDir = 0,
3010			pageTitle = document.title,
3011			isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
3012
3013		// By default, we prevent changePage requests when the fromPage and toPage
3014		// are the same element, but folks that generate content manually/dynamically
3015		// and reuse pages want to be able to transition to the same page. To allow
3016		// this, they will need to change the default value of allowSamePageTransition
3017		// to true, *OR*, pass it in as an option when they manually call changePage().
3018		// It should be noted that our default transition animations assume that the
3019		// formPage and toPage are different elements, so they may behave unexpectedly.
3020		// It is up to the developer that turns on the allowSamePageTransitiona option
3021		// to either turn off transition animations, or make sure that an appropriate
3022		// animation transition is used.
3023		if( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) {
3024			isPageTransitioning = false;
3025			mpc.trigger( "pagechange", triggerData );
3026			return;
3027		}
3028
3029		// We need to make sure the page we are given has already been enhanced.
3030		enhancePage( toPage, settings.role );
3031
3032		// If the changePage request was sent from a hashChange event, check to see if the
3033		// page is already within the urlHistory stack. If so, we'll assume the user hit
3034		// the forward/back button and will try to match the transition accordingly.
3035		if( settings.fromHashChange ) {
3036			urlHistory.directHashChange({
3037				currentUrl:	url,
3038				isBack:		function() { historyDir = -1; },
3039				isForward:	function() { historyDir = 1; }
3040			});
3041		}
3042
3043		// Kill the keyboard.
3044		// XXX_jblas: We need to stop crawling the entire document to kill focus. Instead,
3045		//            we should be tracking focus with a live() handler so we already have
3046		//            the element in hand at this point.
3047		// Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement
3048		// is undefined when we are in an IFrame.
3049		try {
3050			if(document.activeElement && document.activeElement.nodeName.toLowerCase() != 'body') {
3051				$(document.activeElement).blur();
3052			} else {
3053				$( "input:focus, textarea:focus, select:focus" ).blur();
3054			}
3055		} catch(e) {}
3056
3057		// If we're displaying the page as a dialog, we don't want the url
3058		// for the dialog content to be used in the hash. Instead, we want
3059		// to append the dialogHashKey to the url of the current page.
3060		if ( isDialog && active ) {
3061			// on the initial page load active.url is undefined and in that case should
3062			// be an empty string. Moving the undefined -> empty string back into
3063			// urlHistory.addNew seemed imprudent given undefined better represents
3064			// the url state
3065			url = ( active.url || "" ) + dialogHashKey;
3066		}
3067
3068		// Set the location hash.
3069		if( settings.changeHash !== false && url ) {
3070			//disable hash listening temporarily
3071			urlHistory.ignoreNextHashChange = true;
3072			//update hash and history
3073			path.set( url );
3074		}
3075
3076		// if title element wasn't found, try the page div data attr too
3077		// If this is a deep-link or a reload ( active === undefined ) then just use pageTitle
3078		var newPageTitle = ( !active )? pageTitle : toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).getEncodedText();
3079		if( !!newPageTitle && pageTitle == document.title ) {
3080			pageTitle = newPageTitle;
3081		}
3082		if ( !toPage.jqmData( "title" ) ) {
3083			toPage.jqmData( "title", pageTitle );
3084		}
3085
3086		// Make sure we have a transition defined.
3087		settings.transition = settings.transition
3088			|| ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined )
3089			|| ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition );
3090
3091		//add page to history stack if it's not back or forward
3092		if( !historyDir ) {
3093			urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
3094		}
3095
3096		//set page title
3097		document.title = urlHistory.getActive().title;
3098
3099		//set "toPage" as activePage
3100		$.mobile.activePage = toPage;
3101
3102		// If we're navigating back in the URL history, set reverse accordingly.
3103		settings.reverse = settings.reverse || historyDir < 0;
3104
3105		transitionPages( toPage, fromPage, settings.transition, settings.reverse )
3106			.done(function() {
3107				removeActiveLinkClass();
3108
3109				//if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
3110				if ( settings.duplicateCachedPage ) {
3111					settings.duplicateCachedPage.remove();
3112				}
3113
3114				//remove initial build class (only present on first pageshow)
3115				$html.removeClass( "ui-mobile-rendering" );
3116
3117				releasePageTransitionLock();
3118
3119				// Let listeners know we're all done changing the current page.
3120				mpc.trigger( "pagechange", triggerData );
3121			});
3122	};
3123
3124	$.mobile.changePage.defaults = {
3125		transition: undefined,
3126		reverse: false,
3127		changeHash: true,
3128		fromHashChange: false,
3129		role: undefined, // By default we rely on the role defined by the @data-role attribute.
3130		duplicateCachedPage: undefined,
3131		pageContainer: undefined,
3132		showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
3133		dataUrl: undefined,
3134		fromPage: undefined,
3135		allowSamePageTransition: false
3136	};
3137
3138/* Event Bindings - hashchange, submit, and click */
3139	function findClosestLink( ele )
3140	{
3141		while ( ele ) {
3142			if ( ele.nodeName.toLowerCase() == "a" ) {
3143				break;
3144			}
3145			ele = ele.parentNode;
3146		}
3147		return ele;
3148	}
3149
3150	// The base URL for any given element depends on the page it resides in.
3151	function getClosestBaseUrl( ele )
3152	{
3153		// Find the closest page and extract out its url.
3154		var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ),
3155			base = documentBase.hrefNoHash;
3156
3157		if ( !url || !path.isPath( url ) ) {
3158			url = base;
3159		}
3160
3161		return path.makeUrlAbsolute( url, base);
3162	}
3163
3164
3165	//The following event bindings should be bound after mobileinit has been triggered
3166	//the following function is called in the init file
3167	$.mobile._registerInternalEvents = function(){
3168
3169		//bind to form submit events, handle with Ajax
3170		$( "form" ).live('submit', function( event ) {
3171			var $this = $( this );
3172			if( !$.mobile.ajaxEnabled ||
3173				$this.is( ":jqmData(ajax='false')" ) ) {
3174					return;
3175				}
3176
3177			var type = $this.attr( "method" ),
3178				target = $this.attr( "target" ),
3179				url = $this.attr( "action" );
3180
3181			// If no action is specified, browsers default to using the
3182			// URL of the document containing the form. Since we dynamically
3183			// pull in pages from external documents, the form should submit
3184			// to the URL for the source document of the page containing
3185			// the form.
3186			if ( !url ) {
3187				// Get the @data-url for the page containing the form.
3188				url = getClosestBaseUrl( $this );
3189				if ( url === documentBase.hrefNoHash ) {
3190					// The url we got back matches the document base,
3191					// which means the page must be an internal/embedded page,
3192					// so default to using the actual document url as a browser
3193					// would.
3194					url = documentUrl.hrefNoSearch;
3195				}
3196			}
3197
3198			url = path.makeUrlAbsolute(  url, getClosestBaseUrl($this) );
3199
3200			//external submits use regular HTTP
3201			if( path.isExternal( url ) || target ) {
3202				return;
3203			}
3204
3205			$.mobile.changePage(
3206				url,
3207				{
3208					type:		type && type.length && type.toLowerCase() || "get",
3209					data:		$this.serialize(),
3210					transition:	$this.jqmData( "transition" ),
3211					direction:	$this.jqmData( "direction" ),
3212					reloadPage:	true
3213				}
3214			);
3215			event.preventDefault();
3216		});
3217
3218		//add active state on vclick
3219		$( document ).bind( "vclick", function( event ) {
3220			// if this isn't a left click we don't care. Its important to note
3221			// that when the virtual event is generated it will create
3222			if ( event.which > 1 || !$.mobile.linkBindingEnabled ){
3223				return;
3224			}
3225
3226			var link = findClosestLink( event.target );
3227			if ( link ) {
3228				if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
3229					removeActiveLinkClass( true );
3230					$activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
3231					$activeClickedLink.addClass( $.mobile.activeBtnClass );
3232					$( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
3233				}
3234			}
3235		});
3236
3237		// click routing - direct to HTTP or Ajax, accordingly
3238		$( document ).bind( "click", function( event ) {
3239			if( !$.mobile.linkBindingEnabled ){
3240				return;
3241			}
3242
3243			var link = findClosestLink( event.target );
3244
3245			// If there is no link associated with the click or its not a left
3246			// click we want to ignore the click
3247			if ( !link || event.which > 1) {
3248				return;
3249			}
3250
3251			var $link = $( link ),
3252				//remove active link class if external (then it won't be there if you come back)
3253				httpCleanup = function(){
3254					window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
3255				};
3256
3257			//if there's a data-rel=back attr, go back in history
3258			if( $link.is( ":jqmData(rel='back')" ) ) {
3259				window.history.back();
3260				return false;
3261			}
3262
3263			var baseUrl = getClosestBaseUrl( $link ),
3264
3265				//get href, if defined, otherwise default to empty hash
3266				href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl );
3267
3268			//if ajax is disabled, exit early
3269			if( !$.mobile.ajaxEnabled && !path.isEmbeddedPage( href ) ){
3270				httpCleanup();
3271				//use default click handling
3272				return;
3273			}
3274
3275			// XXX_jblas: Ideally links to application pages should be specified as
3276			//            an url to the application document with a hash that is either
3277			//            the site relative path or id to the page. But some of the
3278			//            internal code that dynamically generates sub-pages for nested
3279			//            lists and select dialogs, just write a hash in the link they
3280			//            create. This means the actual URL path is based on whatever
3281			//            the current value of the base tag is at the time this code
3282			//            is called. For now we are just assuming that any url with a
3283			//            hash in it is an application page reference.
3284			if ( href.search( "#" ) != -1 ) {
3285				href = href.replace( /[^#]*#/, "" );
3286				if ( !href ) {
3287					//link was an empty hash meant purely
3288					//for interaction, so we ignore it.
3289					event.preventDefault();
3290					return;
3291				} else if ( path.isPath( href ) ) {
3292					//we have apath so make it the href we want to load.
3293					href = path.makeUrlAbsolute( href, baseUrl );
3294				} else {
3295					//we have a simple id so use the documentUrl as its base.
3296					href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash );
3297				}
3298			}
3299
3300				// Should we handle this link, or let the browser deal with it?
3301			var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ),
3302
3303				// Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
3304				// requests if the document doing the request was loaded via the file:// protocol.
3305				// This is usually to allow the application to "phone home" and fetch app specific
3306				// data. We normally let the browser handle external/cross-domain urls, but if the
3307				// allowCrossDomainPages option is true, we will allow cross-domain http/https
3308				// requests to go through our page loading logic.
3309				isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ),
3310
3311				//check for protocol or rel and its not an embedded page
3312				//TODO overlap in logic from isExternal, rel=external check should be
3313				//     moved into more comprehensive isExternalLink
3314				isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
3315
3316			if( isExternal ) {
3317				httpCleanup();
3318				//use default click handling
3319				return;
3320			}
3321
3322			//use ajax
3323			var transition = $link.jqmData( "transition" ),
3324				direction = $link.jqmData( "direction" ),
3325				reverse = ( direction && direction === "reverse" ) ||
3326							// deprecated - remove by 1.0
3327							$link.jqmData( "back" ),
3328
3329				//this may need to be more specific as we use data-rel more
3330				role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined;
3331
3332			$.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } );
3333			event.preventDefault();
3334		});
3335
3336		//prefetch pages when anchors with data-prefetch are encountered
3337		$( ".ui-page" ).live( "pageshow.prefetch", function() {
3338			var urls = [];
3339			$( this ).find( "a:jqmData(prefetch)" ).each(function(){
3340				var $link = $(this),
3341					url = $link.attr( "href" );
3342
3343				if ( url && $.inArray( url, urls ) === -1 ) {
3344					urls.push( url );
3345
3346					$.mobile.loadPage( url, {role: $link.attr("data-" + $.mobile.ns + "rel")} );
3347				}
3348			});
3349		});
3350
3351		$.mobile._handleHashChange = function( hash ) {
3352			//find first page via hash
3353			var to = path.stripHash( hash ),
3354				//transition is false if it's the first page, undefined otherwise (and may be overridden by default)
3355				transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
3356
3357				// default options for the changPage calls made after examining the current state
3358				// of the page and the hash
3359				changePageOptions = {
3360					transition: transition,
3361					changeHash: false,
3362					fromHashChange: true
3363				};
3364
3365			//if listening is disabled (either globally or temporarily), or it's a dialog hash
3366			if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
3367				urlHistory.ignoreNextHashChange = false;
3368				return;
3369			}
3370
3371			// special case for dialogs
3372			if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) {
3373
3374				// If current active page is not a dialog skip the dialog and continue
3375				// in the same direction
3376				if(!$.mobile.activePage.is( ".ui-dialog" )) {
3377					//determine if we're heading forward or backward and continue accordingly past
3378					//the current dialog
3379					urlHistory.directHashChange({
3380						currentUrl: to,
3381						isBack: function() { window.history.back(); },
3382						isForward: function() { window.history.forward(); }
3383					});
3384
3385					// prevent changePage()
3386					return;
3387				} else {
3388					// if the current active page is a dialog and we're navigating
3389					// to a dialog use the dialog objected saved in the stack
3390					urlHistory.directHashChange({
3391						currentUrl: to,
3392
3393						// regardless of the direction of the history change
3394						// do the following
3395						either: function( isBack ) {
3396							var active = $.mobile.urlHistory.getActive();
3397
3398							to = active.pageUrl;
3399
3400							// make sure to set the role, transition and reversal
3401							// as most of this is lost by the domCache cleaning
3402							$.extend( changePageOptions, {
3403								role: active.role,
3404								transition:	 active.transition,
3405								reverse: isBack
3406							});
3407						}
3408					});
3409				}
3410			}
3411
3412			//if to is defined, load it
3413			if ( to ) {
3414				// At this point, 'to' can be one of 3 things, a cached page element from
3415				// a history stack entry, an id, or site-relative/absolute URL. If 'to' is
3416				// an id, we need to resolve it against the documentBase, not the location.href,
3417				// since the hashchange could've been the result of a forward/backward navigation
3418				// that crosses from an external page/dialog to an internal page/dialog.
3419				to = ( typeof to === "string" && !path.isPath( to ) ) ? ( path.makeUrlAbsolute( '#' + to, documentBase ) ) : to;
3420				$.mobile.changePage( to, changePageOptions );
3421			}	else {
3422				//there's no hash, go to the first page in the dom
3423				$.mobile.changePage( $.mobile.firstPage, changePageOptions );
3424			}
3425		};
3426
3427		//hashchange event handler
3428		$window.bind( "hashchange", function( e, triggered ) {
3429			$.mobile._handleHashChange( location.hash );
3430		});
3431
3432		//set page min-heights to be device specific
3433		$( document ).bind( "pageshow", resetActivePageHeight );
3434		$( window ).bind( "throttledresize", resetActivePageHeight );
3435
3436	};//_registerInternalEvents callback
3437
3438})( jQuery );
3439/*
3440* history.pushState support, layered on top of hashchange
3441*/
3442
3443( function( $, window ) {
3444	// For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
3445	// Scope self to pushStateHandler so we can reference it sanely within the
3446	// methods handed off as event handlers
3447	var	pushStateHandler = {},
3448		self = pushStateHandler,
3449		$win = $( window ),
3450		url = $.mobile.path.parseUrl( location.href );
3451
3452	$.extend( pushStateHandler, {
3453		// TODO move to a path helper, this is rather common functionality
3454		initialFilePath: (function() {
3455			return url.pathname + url.search;
3456		})(),
3457
3458		initialHref: url.hrefNoHash,
3459
3460		// Flag for tracking if a Hashchange naturally occurs after each popstate + replace
3461		hashchangeFired: false,
3462
3463		state: function() {
3464			return {
3465				hash: location.hash || "#" + self.initialFilePath,
3466				title: document.title,
3467
3468				// persist across refresh
3469				initialHref: self.initialHref
3470			};
3471		},
3472
3473		resetUIKeys: function( url ) {
3474			var dialog = $.mobile.dialogHashKey,
3475				subkey = "&" + $.mobile.subPageUrlKey,
3476				dialogIndex = url.indexOf( dialog );
3477
3478			if( dialogIndex > -1 ) {
3479				url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
3480			} else if( url.indexOf( subkey ) > -1 ) {
3481				url = url.split( subkey ).join( "#" + subkey );
3482			}
3483
3484			return url;
3485		},
3486
3487		// TODO sort out a single barrier to hashchange functionality
3488		nextHashChangePrevented: function( value ) {
3489			$.mobile.urlHistory.ignoreNextHashChange = value;
3490			self.onHashChangeDisabled = value;
3491		},
3492
3493		// on hash change we want to clean up the url
3494		// NOTE this takes place *after* the vanilla navigation hash change
3495		// handling has taken place and set the state of the DOM
3496		onHashChange: function( e ) {
3497			// disable this hash change
3498			if( self.onHashChangeDisabled ){
3499				return;
3500			}
3501			
3502			var href, state,
3503				hash = location.hash,
3504				isPath = $.mobile.path.isPath( hash ),
3505				resolutionUrl = isPath ? location.href : $.mobile.getDocumentUrl();
3506			hash = isPath ? hash.replace( "#", "" ) : hash;
3507
3508			// propulate the hash when its not available
3509			state = self.state();
3510
3511			// make the hash abolute with the current href
3512			href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl );
3513
3514			if ( isPath ) {
3515				href = self.resetUIKeys( href );
3516			}
3517
3518			// replace the current url with the new href and store the state
3519			// Note that in some cases we might be replacing an url with the
3520			// same url. We do this anyways because we need to make sure that
3521			// all of our history entries have a state object associated with
3522			// them. This allows us to work around the case where window.history.back()
3523			// is called to transition from an external page to an embedded page.
3524			// In that particular case, a hashchange event is *NOT* generated by the browser.
3525			// Ensuring each history entry has a state object means that onPopState()
3526			// will always trigger our hashchange callback even when a hashchange event
3527			// is not fired.
3528			history.replaceState( state, document.title, href );
3529		},
3530
3531		// on popstate (ie back or forward) we need to replace the hash that was there previously
3532		// cleaned up by the additional hash handling
3533		onPopState: function( e ) {
3534			var poppedState = e.originalEvent.state, holdnexthashchange = false;
3535
3536			// if there's no state its not a popstate we care about, ie chrome's initial popstate
3537			// or forward popstate
3538			if( poppedState ) {
3539				// disable any hashchange triggered by the browser
3540				self.nextHashChangePrevented( true );
3541
3542				// defer our manual hashchange until after the browser fired
3543				// version has come and gone
3544				setTimeout(function() {
3545					// make sure that the manual hash handling takes place
3546					self.nextHashChangePrevented( false );
3547
3548					// change the page based on the hash
3549					$.mobile._handleHashChange( poppedState.hash );
3550				}, 100);
3551			}
3552		},
3553
3554		init: function() {
3555			$win.bind( "hashchange", self.onHashChange );
3556
3557			// Handle popstate events the occur through history changes
3558			$win.bind( "popstate", self.onPopState );
3559
3560			// if there's no hash, we need to replacestate for returning to home
3561			if ( location.hash === "" ) {
3562				history.replaceState( self.state(), document.title, location.href );
3563			}
3564		}
3565	});
3566
3567	$( function() {
3568		if( $.mobile.pushStateEnabled && $.support.pushState ){
3569			pushStateHandler.init();
3570		}
3571	});
3572})( jQuery, this );
3573/*
3574* "transitions" plugin - Page change tranistions
3575*/
3576
3577(function( $, window, undefined ) {
3578
3579function css3TransitionHandler( name, reverse, $to, $from ) {
3580
3581	var deferred = new $.Deferred(),
3582		reverseClass = reverse ? " reverse" : "",
3583		viewportClass = "ui-mobile-viewport-transitioning viewport-" + name,
3584		doneFunc = function() {
3585
3586			$to.add( $from ).removeClass( "out in reverse " + name );
3587
3588			if ( $from && $from[ 0 ] !== $to[ 0 ] ) {
3589				$from.removeClass( $.mobile.activePageClass );
3590			}
3591
3592			$to.parent().removeClass( viewportClass );
3593
3594			deferred.resolve( name, reverse, $to, $from );
3595		};
3596
3597	$to.animationComplete( doneFunc );
3598
3599	$to.parent().addClass( viewportClass );
3600
3601	if ( $from ) {
3602		$from.addClass( name + " out" + reverseClass );
3603	}
3604	$to.addClass( $.mobile.activePageClass + " " + name + " in" + reverseClass );
3605
3606	return deferred.promise();
3607}
3608
3609// Make our transition handler public.
3610$.mobile.css3TransitionHandler = css3TransitionHandler;
3611
3612// If the default transition handler is the 'none' handler, replace it with our handler.
3613if ( $.mobile.defaultTransitionHandler === $.mobile.noneTransitionHandler ) {
3614	$.mobile.defaultTransitionHandler = css3TransitionHandler;
3615}
3616
3617})( jQuery, this );
3618/*
3619* "degradeInputs" plugin - degrades inputs to another type after custom enhancements are made.
3620*/
3621
3622(function( $, undefined ) {
3623
3624$.mobile.page.prototype.options.degradeInputs = {
3625	color: false,
3626	date: false,
3627	datetime: false,
3628	"datetime-local": false,
3629	email: false,
3630	month: false,
3631	number: false,
3632	range: "number",
3633	search: "text",
3634	tel: false,
3635	time: false,
3636	url: false,
3637	week: false
3638};
3639
3640
3641//auto self-init widgets
3642$( document ).bind( "pagecreate create", function( e ){
3643
3644	var page = $(e.target).closest(':jqmData(role="page")').data("page"), options;
3645
3646	if( !page ) {
3647		return;
3648	}
3649
3650	options = page.options;
3651
3652	// degrade inputs to avoid poorly implemented native functionality
3653	$( e.target ).find( "input" ).not( page.keepNativeSelector() ).each(function() {
3654		var $this = $( this ),
3655			type = this.getAttribute( "type" ),
3656			optType = options.degradeInputs[ type ] || "text";
3657
3658		if ( options.degradeInputs[ type ] ) {
3659			var html = $( "<div>" ).html( $this.clone() ).html(),
3660				// In IE browsers, the type sometimes doesn't exist in the cloned markup, so we replace the closing tag instead
3661				hasType = html.indexOf( " type=" ) > -1,
3662				findstr = hasType ? /\s+type=["']?\w+['"]?/ : /\/?>/,
3663				repstr = " type=\"" + optType + "\" data-" + $.mobile.ns + "type=\"" + type + "\"" + ( hasType ? "" : ">" );
3664
3665			$this.replaceWith( html.replace( findstr, repstr ) );
3666		}
3667	});
3668
3669});
3670
3671}
3671)( jQuery );/*
3672* "dialog" plugin.
3673*/
3674
3675(function( $, window, undefined ) {
3676
3677$.widget( "mobile.dialog", $.mobile.widget, {
3678	options: {
3679		closeBtnText 	: "Close",
3680		overlayTheme	: "a",
3681		initSelector	: ":jqmData(role='dialog')"
3682	},
3683	_create: function() {
3684		var self = this,
3685			$el = this.element,
3686			headerCloseButton = $( "<a href='#' data-" + $.mobile.ns + "icon='delete' data-" + $.mobile.ns + "iconpos='notext'>"+ this.options.closeBtnText + "</a>" );
3687
3688		$el.addClass( "ui-overlay-" + this.options.overlayTheme );
3689
3690		// Class the markup for dialog styling
3691		// Set aria role
3692		$el.attr( "role", "dialog" )
3693			.addClass( "ui-dialog" )
3694			.find( ":jqmData(role='header')" )
3695			.addClass( "ui-corner-top ui-overlay-shadow" )
3696				.prepend( headerCloseButton )
3697			.end()
3698			.find( ":jqmData(role='content'),:jqmData(role='footer')" )
3699				.addClass( "ui-overlay-shadow" )
3700				.last()
3701				.addClass( "ui-corner-bottom" );
3702
3703		// this must be an anonymous function so that select menu dialogs can replace
3704		// the close method. This is a change from previously just defining data-rel=back
3705		// on the button and letting nav handle it
3706		headerCloseButton.bind( "vclick", function() {
3707			self.close();
3708		});
3709
3710		/* bind events
3711			- clicks and submits should use the closing transition that the dialog opened with
3712			  unless a data-transition is specified on the link/form
3713			- if the click was on the close button, or the link has a data-rel="back" it'll go back in history naturally
3714		*/
3715		$el.bind( "vclick submit", function( event ) {
3716			var $target = $( event.target ).closest( event.type === "vclick" ? "a" : "form" ),
3717				active;
3718
3719			if ( $target.length && !$target.jqmData( "transition" ) ) {
3720
3721				active = $.mobile.urlHistory.getActive() || {};
3722
3723				$target.attr( "data-" + $.mobile.ns + "transition", ( active.transition || $.mobile.defaultDialogTransition ) )
3724					.attr( "data-" + $.mobile.ns + "direction", "reverse" );
3725			}
3726		})
3727		.bind( "pagehide", function() {
3728			$( this ).find( "." + $.mobile.activeBtnClass ).removeClass( $.mobile.activeBtnClass );
3729		});
3730	},
3731
3732	// Close method goes back in history
3733	close: function() {
3734		window.history.back();
3735	}
3736});
3737
3738//auto self-init widgets
3739$( $.mobile.dialog.prototype.options.initSelector ).live( "pagecreate", function(){
3740	$( this ).dialog();
3741});
3742
3743})( jQuery, this );
3744/*
3745* This plugin handles theming and layout of headers, footers, and content areas
3746*/
3747
3748(function( $, undefined ) {
3749
3750$.mobile.page.prototype.options.backBtnText  = "Back";
3751$.mobile.page.prototype.options.addBackBtn   = false;
3752$.mobile.page.prototype.options.backBtnTheme = null;
3753$.mobile.page.prototype.options.headerTheme  = "a";
3754$.mobile.page.prototype.options.footerTheme  = "a";
3755$.mobile.page.prototype.options.contentTheme = null;
3756
3757$( ":jqmData(role='page'), :jqmData(role='dialog')" ).live( "pagecreate", function( e ) {
3758	
3759	var $page = $( this ),
3760		o = $page.data( "page" ).options,
3761		pageRole = $page.jqmData( "role" ),
3762		pageTheme = o.theme;
3763	
3764	$( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this ).each(function() {
3765		var $this = $( this ),
3766			role = $this.jqmData( "role" ),
3767			theme = $this.jqmData( "theme" ),
3768			contentTheme = theme || o.contentTheme || pageTheme,
3769			$headeranchors,
3770			leftbtn,
3771			rightbtn,
3772			backBtn;
3773			
3774		$this.addClass( "ui-" + role );	
3775
3776		//apply theming and markup modifications to page,header,content,footer
3777		if ( role === "header" || role === "footer" ) {
3778			
3779			var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
3780
3781			$this
3782				//add theme class
3783				.addClass( "ui-bar-" + thisTheme )
3784				// Add ARIA role
3785				.attr( "role", role === "header" ? "banner" : "contentinfo" );
3786
3787			// Right,left buttons
3788			$headeranchors	= $this.children( "a" );
3789			leftbtn	= $headeranchors.hasClass( "ui-btn-left" );
3790			rightbtn = $headeranchors.hasClass( "ui-btn-right" );
3791
3792			leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
3793			
3794			rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
3795			
3796			// Auto-add back btn on pages beyond first view
3797			if ( o.addBackBtn && 
3798				role === "header" &&
3799				$( ".ui-page" ).length > 1 &&
3800				$this.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
3801				!leftbtn ) {
3802
3803				backBtn = $( "<a href='#' class='ui-btn-left' data-"+ $.mobile.ns +"rel='back' data-"+ $.mobile.ns +"icon='arrow-l'>"+ o.backBtnText +"</a>" )
3804					// If theme is provided, override default inheritance
3805					.attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme )
3806					.prependTo( $this );				
3807			}
3808
3809			// Page title
3810			$this.children( "h1, h2, h3, h4, h5, h6" )
3811				.addClass( "ui-title" )
3812				// Regardless of h element number in src, it becomes h1 for the enhanced page
3813				.attr({
3814					"tabindex": "0",
3815					"role": "heading",
3816					"aria-level": "1"
3817				});
3818
3819		} else if ( role === "content" ) {
3820			if ( contentTheme && pageRole === "dialog" ) {
3821			    $this.addClass( "ui-body-" + ( contentTheme ) );
3822			}
3823
3824			// Add ARIA role
3825			$this.attr( "role", "main" );
3826		}
3827	});
3828});
3829
3830})( jQuery );/*
3831* "collapsible" plugin
3832*/
3833
3834(function( $, undefined ) {
3835
3836$.widget( "mobile.collapsible", $.mobile.widget, {
3837	options: {
3838		expandCueText: " click to expand contents",
3839		collapseCueText: " click to collapse contents",
3840		collapsed: true,
3841		heading: "h1,h2,h3,h4,h5,h6,legend",
3842		theme: null,
3843		contentTheme: null,
3844		iconTheme: "d",
3845		initSelector: ":jqmData(role='collapsible')"
3846	},
3847	_create: function() {
3848
3849		var $el = this.element,
3850			o = this.options,
3851			collapsible = $el.addClass( "ui-collapsible" ),
3852			collapsibleHeading = $el.children( o.heading ).first(),
3853			collapsibleContent = collapsible.wrapInner( "<div class='ui-collapsible-content'></div>" ).find( ".ui-collapsible-content" ),
3854			collapsibleSet = $el.closest( ":jqmData(role='collapsible-set')" ).addClass( "ui-collapsible-set" ),
3855			collapsiblesInSet = collapsibleSet.children( ":jqmData(role='collapsible')" );
3856
3857		// Replace collapsibleHeading if it's a legend
3858		if ( collapsibleHeading.is( "legend" ) ) {
3859			collapsibleHeading = $( "<div role='heading'>"+ collapsibleHeading.html() +"</div>" ).insertBefore( collapsibleHeading );
3860			collapsibleHeading.next().remove();
3861		}
3862
3863		// If we are in a collapsible set
3864		if ( collapsibleSet.length ) {
3865			// Inherit the theme from collapsible-set
3866			if ( !o.theme ) {
3867				o.theme = collapsibleSet.jqmData( "theme" );
3868			}
3869			// Inherit the content-theme from collapsible-set
3870			if ( !o.contentTheme ) {
3871				o.contentTheme = collapsibleSet.jqmData( "content-theme" );
3872			}
3873		}
3874
3875		collapsibleContent.addClass( ( o.contentTheme ) ? ( "ui-body-" + o.contentTheme ) : "");
3876
3877		collapsibleHeading
3878			//drop heading in before content
3879			.insertBefore( collapsibleContent )
3880			//modify markup & attributes
3881			.addClass( "ui-collapsible-heading" )
3882			.append( "<span class='ui-collapsible-heading-status'></span>" )
3883			.wrapInner( "<a href='#' class='ui-collapsible-heading-toggle'></a>" )
3884			.find( "a" )
3885				.first()
3886				.buttonMarkup({
3887					shadow: false,
3888					corners: false,
3889					iconPos: "left",
3890					icon: "plus",
3891					theme: o.theme
3892				});
3893
3894		if ( !collapsibleSet.length ) {
3895			collapsibleHeading
3896				.find( "a" ).first().add( collapsibleHeading.find( ".ui-btn-inner" ) )
3897					.addClass( "ui-corner-top ui-corner-bottom" );
3898		} else {
3899			// If we are in a collapsible set
3900
3901			// Initialize the collapsible set if it's not already initialized
3902			if ( !collapsibleSet.jqmData( "collapsiblebound" ) ) {
3903
3904				collapsibleSet
3905					.jqmData( "collapsiblebound", true )
3906					.bind( "expand", function( event ) {
3907
3908						$( event.target )
3909							.closest( ".ui-collapsible" )
3910							.siblings( ".ui-collapsible" )
3911							.trigger( "collapse" );
3912
3913					});
3914			}
3915
3916			collapsiblesInSet.first()
3917				.find( "a" )
3918					.first()
3919					.addClass( "ui-corner-top" )
3920						.find( ".ui-btn-inner" )
3921							.addClass( "ui-corner-top" );
3922
3923			collapsiblesInSet.last()
3924				.jqmData( "collapsible-last", true )
3925				.find( "a" )
3926					.first()
3927					.addClass( "ui-corner-bottom" )
3928						.find( ".ui-btn-inner" )
3929							.addClass( "ui-corner-bottom" );
3930
3931
3932			if ( collapsible.jqmData( "collapsible-last" ) ) {
3933				collapsibleHeading
3934					.find( "a" ).first().add ( collapsibleHeading.find( ".ui-btn-inner" ) )
3935						.addClass( "ui-corner-bottom" );
3936			}
3937		}
3938
3939		//events
3940		collapsible
3941			.bind( "expand collapse", function( event ) {
3942				if ( !event.isDefaultPrevented() ) {
3943
3944					event.preventDefault();
3945
3946					var $this = $( this ),
3947						isCollapse = ( event.type === "collapse" ),
3948					    contentTheme = o.contentTheme;
3949
3950					collapsibleHeading
3951						.toggleClass( "ui-collapsible-heading-collapsed", isCollapse)
3952						.find( ".ui-collapsible-heading-status" )
3953							.text( isCollapse ? o.expandCueText : o.collapseCueText )
3954						.end()
3955						.find( ".ui-icon" )
3956							.toggleClass( "ui-icon-minus", !isCollapse )
3957							.toggleClass( "ui-icon-plus", isCollapse );
3958
3959					$this.toggleClass( "ui-collapsible-collapsed", isCollapse );
3960					collapsibleContent.toggleClass( "ui-collapsible-content-collapsed", isCollapse ).attr( "aria-hidden", isCollapse );
3961
3962					if ( contentTheme && ( !collapsibleSet.length || collapsible.jqmData( "collapsible-last" ) ) ) {
3963						collapsibleHeading
3964							.find( "a" ).first().add( collapsibleHeading.find( ".ui-btn-inner" ) )
3965							.toggleClass( "ui-corner-bottom", isCollapse );
3966						collapsibleContent.toggleClass( "ui-corner-bottom", !isCollapse );
3967					}
3968					collapsibleContent.trigger( "updatelayout" );
3969				}
3970			})
3971			.trigger( o.collapsed ? "collapse" : "expand" );
3972
3973		collapsibleHeading
3974			.bind( "click", function( event ) {
3975
3976				var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ?
3977										"expand" : "collapse";
3978
3979				collapsible.trigger( type );
3980
3981				event.preventDefault();
3982			});
3983	}
3984});
3985
3986//auto self-init widgets
3987$( document ).bind( "pagecreate create", function( e ){
3988	$( $.mobile.collapsible.prototype.options.initSelector, e.target ).collapsible();
3989});
3990
3991})( jQuery );
3992/*
3993* "fieldcontain" plugin - simple class additions to make form row 
3993separators
3994*/
3995
3996(function( $, undefined ) {
3997
3998$.fn.fieldcontain = function( options ) {
3999	return this.addClass( "ui-field-contain ui-body ui-br" );
4000};
4001
4002//auto self-init widgets
4003$( document ).bind( "pagecreate create", function( e ){
4004	$( ":jqmData(role='fieldcontain')", e.target ).fieldcontain();
4005});
4006
4007})( jQuery );/*
4008* plugin for creating CSS grids
4009*/
4010
4011(function( $, undefined ) {
4012
4013$.fn.grid = function( options ) {
4014	return this.each(function() {
4015
4016		var $this = $( this ),
4017			o = $.extend({
4018				grid: null
4019			},options),
4020			$kids = $this.children(),
4021			gridCols = {solo:1, a:2, b:3, c:4, d:5},
4022			grid = o.grid,
4023			iterator;
4024
4025			if ( !grid ) {
4026				if ( $kids.length <= 5 ) {
4027					for ( var letter in gridCols ) {
4028						if ( gridCols[ letter ] === $kids.length ) {
4029							grid = letter;
4030						}
4031					}
4032				} else {
4033					grid = "a";
4034				}
4035			}
4036			iterator = gridCols[grid];
4037
4038		$this.addClass( "ui-grid-" + grid );
4039
4040		$kids.filter( ":nth-child(" + iterator + "n+1)" ).addClass( "ui-block-a" );
4041
4042		if ( iterator > 1 ) {
4043			$kids.filter( ":nth-child(" + iterator + "n+2)" ).addClass( "ui-block-b" );
4044		}
4045		if ( iterator > 2 ) {
4046			$kids.filter( ":nth-child(3n+3)" ).addClass( "ui-block-c" );
4047		}
4048		if ( iterator > 3 ) {
4049			$kids.filter( ":nth-child(4n+4)" ).addClass( "ui-block-d" );
4050		}
4051		if ( iterator > 4 ) {
4052			$kids.filter( ":nth-child(5n+5)" ).addClass( "ui-block-e" );
4053		}
4054	});
4055};
4056})( jQuery );/*
4057* "navbar" plugin
4058*/
4059
4060(function( $, undefined ) {
4061
4062$.widget( "mobile.navbar", $.mobile.widget, {
4063	options: {
4064		iconpos: "top",
4065		grid: null,
4066		initSelector: ":jqmData(role='navbar')"
4067	},
4068
4069	_create: function(){
4070
4071		var $navbar = this.element,
4072			$navbtns = $navbar.find( "a" ),
4073			iconpos = $navbtns.filter( ":jqmData(icon)" ).length ?
4074									this.options.iconpos : undefined;
4075
4076		$navbar.addClass( "ui-navbar" )
4077			.attr( "role","navigation" )
4078			.find( "ul" )
4079				.grid({ grid: this.options.grid });
4080
4081		if ( !iconpos ) {
4082			$navbar.addClass( "ui-navbar-noicons" );
4083		}
4084
4085		$navbtns.buttonMarkup({
4086			corners:	false,
4087			shadow:		false,
4088			iconpos:	iconpos
4089		});
4090
4091		$navbar.delegate( "a", "vclick", function( event ) {
4092			$navbtns.not( ".ui-state-persist" ).removeClass( $.mobile.activeBtnClass );
4093			$( this ).addClass( $.mobile.activeBtnClass );
4094		});
4095	}
4096});
4097
4098//auto self-init widgets
4099$( document ).bind( "pagecreate create", function( e ){
4100	$( $.mobile.navbar.prototype.options.initSelector, e.target ).navbar();
4101});
4102
4103})( jQuery );
4104/*
4105* "listview" plugin
4106*/
4107
4108(function( $, undefined ) {
4109
4110//Keeps track of the number of lists per page UID
4111//This allows support for multiple nested list in the same page
4112//https://github.com/jquery/jquery-mobile/issues/1617
4113var listCountPerPage = {};
4114
4115$.widget( "mobile.listview", $.mobile.widget, {
4116	options: {
4117		theme: null,
4118		countTheme: "c",
4119		headerTheme: "b",
4120		dividerTheme: "b",
4121		splitIcon: "arrow-r",
4122		splitTheme: "b",
4123		inset: false,
4124		initSelector: ":jqmData(role='listview')"
4125	},
4126
4127	_create: function() {
4128		var t = this;
4129
4130		// create listview markup
4131		t.element.addClass(function( i, orig ) {
4132			return orig + " ui-listview " + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
4133		});
4134
4135		t.refresh( true );
4136	},
4137
4138	_removeCorners: function( li, which ) {
4139		var top = "ui-corner-top ui-corner-tr ui-corner-tl",
4140			bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
4141
4142		li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
4143
4144		if ( which === "top" ) {
4145			li.removeClass( top );
4146		} else if ( which === "bottom" ) {
4147			li.removeClass( bot );
4148		} else {
4149			li.removeClass( top + " " + bot );
4150		}
4151	},
4152
4153	_refreshCorners: function( create ) {
4154		var $li,
4155			$visibleli,
4156			$topli,
4157			$bottomli;
4158
4159		if ( this.options.inset ) {
4160			$li = this.element.children( "li" );
4161			// at create time the li are not visible yet so we need to rely on .ui-screen-hidden
4162			$visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
4163
4164			this._removeCorners( $li );
4165
4166			// Select the first visible li element
4167			$topli = $visibleli.first()
4168				.addClass( "ui-corner-top" );
4169
4170			$topli.add( $topli.find( ".ui-btn-inner" )
4171					.not( ".ui-li-link-alt span:first-child" ) )
4172                                .addClass( "ui-corner-top" )
4173                                .end()
4174				.find( ".ui-li-link-alt, .ui-li-link-alt span:first-child" )
4175					.addClass( "ui-corner-tr" )
4176				.end()
4177				.find( ".ui-li-thumb" )
4178					.not(".ui-li-icon")
4179					.addClass( "ui-corner-tl" );
4180
4181			// Select the last visible li element
4182			$bottomli = $visibleli.last()
4183				.addClass( "ui-corner-bottom" );
4184
4185			$bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
4186				.find( ".ui-li-link-alt" )
4187					.addClass( "ui-corner-br" )
4188				.end()
4189				.find( ".ui-li-thumb" )
4190					.not(".ui-li-icon")
4191					.addClass( "ui-corner-bl" );
4192		}
4193		if ( !create ) {
4194			this.element.trigger( "updatelayout" );
4195		}
4196	},
4197
4198	// This is a generic utility method for finding the first
4199	// node with a given nodeName. It uses basic DOM traversal
4200	// to be fast and is meant to be a substitute for simple
4201	// $.fn.closest() and $.fn.children() calls on a single
4202	// element. Note that callers must pass both the lowerCase
4203	// and upperCase version of the nodeName they are looking for.
4204	// The main reason for this is that this function will be
4205	// called many times and we want to avoid having to lowercase
4206	// the nodeName from the element every time to ensure we have
4207	// a match. Note that this function lives here for now, but may
4208	// be moved into $.mobile if other components need a similar method.
4209	_findFirstElementByTagName: function( ele, nextProp, lcName, ucName )
4210	{
4211		var dict = {};
4212		dict[ lcName ] = dict[ ucName ] = true;
4213		while ( ele ) {
4214			if ( dict[ ele.nodeName ] ) {
4215				return ele;
4216			}
4217			ele = ele[ nextProp ];
4218		}
4219		return null;
4220	},
4221	_getChildrenByTagName: function( ele, lcName, ucName )
4222	{
4223		var results = [],
4224			dict = {};
4225		dict[ lcName ] = dict[ ucName ] = true;
4226		ele = ele.firstChild;
4227		while ( ele ) {
4228			if ( dict[ ele.nodeName ] ) {
4229				results.push( ele );
4230			}
4231			ele = ele.nextSibling;
4232		}
4233		return $( results );
4234	},
4235
4236	_addThumbClasses: function( containers )
4237	{
4238		var i, img, len = containers.length;
4239		for ( i = 0; i < len; i++ ) {
4240			img = $( this._findFirstElementByTagName( containers[ i ].firstChild, "nextSibling", "img", "IMG" ) );
4241			if ( img.length ) {
4242				img.addClass( "ui-li-thumb" );
4243				$( this._findFirstElementByTagName( img[ 0 ].parentNode, "parentNode", "li", "LI" ) ).addClass( img.is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
4244			}
4245		}
4246	},
4247
4248	refresh: function( create ) {
4249		this.parentPage = this.element.closest( ".ui-page" );
4250		this._createSubPages();
4251
4252		var o = this.options,
4253			$list = this.element,
4254			self = this,
4255			dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
4256			listsplittheme = $list.jqmData( "splittheme" ),
4257			listspliticon = $list.jqmData( "spliticon" ),
4258			li = this._getChildrenByTagName( $list[ 0 ], "li", "LI" ),
4259			counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
4260			itemClassDict = {},
4261			item, itemClass, itemTheme,
4262			a, last, splittheme, countParent, icon, imgParents, img;
4263
4264		if ( counter ) {
4265			$list.find( ".ui-li-dec" ).remove();
4266		}
4267		
4268		if ( !o.theme ) {
4269			o.theme = $.mobile.getInheritedTheme( this.element, "c" );
4270		}
4271
4272		for ( var pos = 0, numli = li.length; pos < numli; pos++ ) {
4273			item = li.eq( pos );
4274			itemClass = "ui-li";
4275
4276			// If we're creating the element, we update it regardless
4277			if ( create || !item.hasClass( "ui-li" ) ) {
4278				itemTheme = item.jqmData("theme") || o.theme;
4279				a = this._getChildrenByTagName( item[ 0 ], "a", "A" );
4280
4281				if ( a.length ) {
4282					icon = item.jqmData("icon");
4283
4284					item.buttonMarkup({
4285						wrapperEls: "div",
4286						shadow: false,
4287						corners: false,
4288						iconpos: "right",
4289						icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
4290						theme: itemTheme
4291					});
4292
4293					if ( ( icon != false ) && ( a.length == 1 ) ) {
4294						item.addClass( "ui-li-has-arrow" );
4295					}
4296
4297					a.first().addClass( "ui-link-inherit" );
4298
4299					if ( a.length > 1 ) {
4300						itemClass += " ui-li-has-alt";
4301
4302						last = a.last();
4303						splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
4304
4305						last.appendTo(item)
4306							.attr( "title", last.getEncodedText() )
4307							.addClass( "ui-li-link-alt" )
4308							.empty()
4309							.buttonMarkup({
4310								shadow: false,
4311								corners: false,
4312								theme: itemTheme,
4313								icon: false,
4314								iconpos: false
4315							})
4316							.find( ".ui-btn-inner" )
4317								.append(
4318									$( document.createElement( "span" ) ).buttonMarkup({
4319										shadow: true,
4320										corners: true,
4321										theme: splittheme,
4322										iconpos: "notext",
4323										icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
4324									})
4325								);
4326					}
4327				} else if ( item.jqmData( "role" ) === "list-divider" ) {
4328
4329					itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
4330					item.attr( "role", "heading" );
4331
4332					//reset counter when a divider heading is encountered
4333					if ( counter ) {
4334						counter = 1;
4335					}
4336
4337				} else {
4338					itemClass += " ui-li-static ui-body-" + itemTheme;
4339				}
4340			}
4341
4342			if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
4343				countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
4344
4345				countParent.addClass( "ui-li-jsnumbering" )
4346					.prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
4347			}
4348
4349			// Instead of setting item class directly on the list item and its
4350			// btn-inner at this point in time, push the item into a dictionary
4351			// that tells us what class to set on it so we can do this after this
4352			// processing loop is finished.
4353
4354			if ( !itemClassDict[ itemClass ] ) {
4355				itemClassDict[ itemClass ] = [];
4356			}
4357
4358			itemClassDict[ itemClass ].push( item[ 0 ] );
4359		}
4360
4361		// Set the appropriate listview item classes on each list item
4362		// and their btn-inner elements. The main reason we didn't do this
4363		// in the for-loop above is because we can eliminate per-item function overhead
4364		// by calling addClass() and children() once or twice afterwards. This
4365		// can give us a significant boost on platforms like WP7.5.
4366
4367		for ( itemClass in itemClassDict ) {
4368			$( itemClassDict[ itemClass ] ).addClass( itemClass ).children( ".ui-btn-inner" ).addClass( itemClass );
4369		}
4370
4371		$list.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" )
4372			.end()
4373
4374			.find( "p, dl" ).addClass( "ui-li-desc" )
4375			.end()
4376
4377			.find( ".ui-li-aside" ).each(function() {
4378					var $this = $(this);
4379					$this.prependTo( $this.parent() ); //shift aside to front for css float
4380				})
4381			.end()
4382
4383			.find( ".ui-li-count" ).each( function() {
4384					$( this ).closest( "li" ).addClass( "ui-li-has-count" );
4385				}).addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme) + " ui-btn-corner-all" );
4386
4387		// The idea here is to look at the first image in the list item
4388		// itself, and any .ui-link-inherit element it may contain, so we
4389		// can place the appropriate classes on the image and list item.
4390		// Note that we used to use something like:
4391		//
4392		//    li.find(">img:eq(0), .ui-link-inherit>img:eq(0)").each( ... );
4393		//
4394		// But executing a find() like that on Windows Phone 7.5 took a
4395		// really long time. Walking things manually with the code below
4396		// allows the 400 listview item page to load in about 3 seconds as
4397		// opposed to 30 seconds.
4398
4399		this._addThumbClasses( li );
4400		this._addThumbClasses( $list.find( ".ui-link-inherit" ) );
4401
4402		this._refreshCorners( create );
4403	},
4404
4405	//create a string for ID/subpage url creation
4406	_idStringEscape: function( str ) {
4407		return str.replace(/[^a-zA-Z0-9]/g, '-');
4408	},
4409
4410	_createSubPages: function() {
4411		var parentList = this.element,
4412			parentPage = parentList.closest( ".ui-page" ),
4413			parentUrl = parentPage.jqmData( "url" ),
4414			parentId = parentUrl || parentPage[ 0 ][ $.expando ],
4415			parentListId = parentList.attr( "id" ),
4416			o = this.options,
4417			dns = "data-" + $.mobile.ns,
4418			self = this,
4419			persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
4420			hasSubPages;
4421
4422		if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
4423			listCountPerPage[ parentId ] = -1;
4424		}
4425
4426		parentListId = parentListId || ++listCountPerPage[ parentId ];
4427
4428		$( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
4429			var self = this,
4430				list = $( this ),
4431				listId = list.attr( "id" ) || parentListId + "-" + i,
4432				parent = list.parent(),
4433				nodeEls = $( list.prevAll().toArray().reverse() ),
4434				nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" ),
4435				title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
4436				id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
4437				theme = list.jqmData( "theme" ) || o.theme,
4438				countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
4439				newPage, anchor;
4440
4441			//define hasSubPages for use in later removal
4442			hasSubPages = true;
4443
4444			newPage = list.detach()
4445						.wrap( "<div " + dns + "role='page' " +	dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
4446						.parent()
4447							.before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
4448							.after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='"+ persistentFooterID +"'>") : "" )
4449							.parent()
4450								.appendTo( $.mobile.pageContainer );
4451
4452			newPage.page();
4453
4454			anchor = parent.find('a:first');
4455
4456			if ( !anchor.length ) {
4457				anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
4458			}
4459
4460			anchor.attr( "href", "#" + id );
4461
4462		}).listview();
4463
4464		// on pagehide, remove any nested pages along with the parent page, as long as they aren't active
4465		// and aren't embedded
4466		if( hasSubPages &&
4467			parentPage.is( ":jqmData(external-page='true')" ) &&
4468			parentPage.data("page").options.domCache === false ) {
4469
4470			var newRemove = function( e, ui ){
4471				var nextPage = ui.nextPage, npURL;
4472
4473				if( ui.nextPage ){
4474					npURL = nextPage.jqmData( "url" );
4475					if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){
4476						self.childPages().remove();
4477						parentPage.remove();
4478					}
4479				}
4480			};
4481
4482			// unbind the original page remove and replace with our specialized version
4483			parentPage
4484				.unbind( "pagehide.remove" )
4485				.bind( "pagehide.remove", newRemove);
4486		}
4487	},
4488
4489	// TODO sort out a better way to track sub pages of the listview this is brittle
4490	childPages: function(){
4491		var parentUrl = this.parentPage.jqmData( "url" );
4492
4493		return $( ":jqmData(url^='"+  parentUrl + "&" + $.mobile.subPageUrlKey +"')");
4494	}
4495});
4496
4497//auto self-init widgets
4498$( document ).bind( "pagecreate create", function( e ){
4499	$( $.mobile.listview.prototype.options.initSelector, e.target ).listview();
4500});
4501
4502})( jQuery );
4503/*
4504* "listview" filter extension
4505*/
4506
4507(function( $, undefined ) {
4508
4509$.mobile.listview.prototype.options.filter = false;
4510$.mobile.listview.prototype.options.filterPlaceholder = "Filter items...";
4511$.mobile.listview.prototype.options.filterTheme = "c";
4512$.mobile.listview.prototype.options.filterCallback = function( text, searchValue ){
4513	return text.toLowerCase().indexOf( searchValue ) === -1;
4514};
4515
4516$( ":jqmData(role='listview')" ).live( "listviewcreate", function() {
4517
4518	var list = $( this ),
4519		listview = list.data( "listview" );
4520
4521	if ( !listview.options.filter ) {
4522		return;
4523	}
4524
4525	var wrapper = $( "<form>", {
4526			"class": "ui-listview-filter ui-bar-" + listview.options.filterTheme,
4527			"role": "search"
4528		}),
4529		search = $( "<input>", {
4530			placeholder: listview.options.filterPlaceholder
4531		})
4532		.attr( "data-" + $.mobile.ns + "type", "search" )
4533		.jqmData( "lastval", "" )
4534		.bind( "keyup change", function() {
4535
4536			var $this = $(this),
4537				val = this.value.toLowerCase(),
4538				listItems = null,
4539				lastval = $this.jqmData( "lastval" ) + "",
4540				childItems = false,
4541				itemtext = "",
4542				item, change;
4543
4544			// Change val as lastval for next execution
4545			$this.jqmData( "lastval" , val );
4546			change = val.substr( 0 , lastval.length - 1 ).replace( lastval , "" );
4547
4548			if ( val.length < lastval.length || change.length != ( val.length - lastval.length ) ) {
4549
4550				// Removed chars or pasted something totally different, check all items
4551				listItems = list.children();
4552			} else {
4553
4554				// Only chars added, not removed, only use visible subset
4555				listItems = list.children( ":not(.ui-screen-hidden)" );
4556			}
4557
4558			if ( val ) {
4559
4560				// This handles hiding regular rows without the text we search for
4561				// and any list dividers without regular rows shown under it
4562
4563				for ( var i = listItems.length - 1; i >= 0; i-- ) {
4564					item = $( listItems[ i ] );
4565					itemtext = item.jqmData( "filtertext" ) || item.text();
4566
4567					if ( item.is( "li:jqmData(role=list-divider)" ) ) {
4568
4569						item.toggleClass( "ui-filter-hidequeue" , !childItems );
4570
4571						// New bucket!
4572						childItems = false;
4573
4574					} else if ( listview.options.filterCallback( itemtext, val ) ) {
4575
4576						//mark to be hidden
4577						item.toggleClass( "ui-filter-hidequeue" , true );
4578					} else {
4579
4580						// There's a shown item in the bucket
4581						childItems = true;
4582					}
4583				}
4584
4585				// Show items, not marked to be hidden
4586				listItems
4587					.filter( ":not(.ui-filter-hidequeue)" )
4588					.toggleClass( "ui-screen-hidden", false );
4589
4590				// Hide items, marked to be hidden
4591				listItems
4592					.filter( ".ui-filter-hidequeue" )
4593					.toggleClass( "ui-screen-hidden", true )
4594					.toggleClass( "ui-filter-hidequeue", false );
4595
4596			} else {
4597
4598				//filtervalue is empty => show all
4599				listItems.toggleClass( "ui-screen-hidden", false );
4600			}
4601			listview._refreshCorners();
4602		})
4603		.appendTo( wrapper )
4604		.textinput();
4605
4606	if ( $( this ).jqmData( "inset" ) ) {
4607		wrapper.addClass( "ui-listview-filter-inset" );
4608	}
4609
4610	wrapper.bind( "submit", function() {
4611		return false;
4612	})
4613	.insertBefore( list );
4614});
4615
4616})( jQuery );/*
4617* "nojs" plugin - class to make elements hidden to A grade browsers
4618*/
4619
4620(function( $, undefined ) {
4621
4622$( document ).bind( "pagecreate create", function( e ){
4623	$( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" );
4624	
4625});
4626
4627})( jQuery );/*
4628* "checkboxradio" plugin
4629*/
4630
4631(function( $, undefined ) {
4632
4633$.widget( "mobile.checkboxradio", $.mobile.widget, {
4634	options: {
4635		theme: null,
4636		initSelector: "input[type='checkbox'],input[type='radio']"
4637	},
4638	_create: function() {
4639		var self = this,
4640			input = this.element,
4641			// NOTE: Windows Phone could not find the label through a selector
4642			// filter works though.
4643			label = input.closest( "form,fieldset,:jqmData(role='page')" ).find( "label[for='" + input[ 0 ].id + "']"),
4644			inputtype = input.attr( "type" ),
4645			checkedState = inputtype + "-on",
4646			uncheckedState = inputtype + "-off",
4647			icon = input.parents( ":jqmData(type='horizontal')" ).length ? undefined : uncheckedState,
4648			activeBtn = icon ? "" : " " + $.mobile.activeBtnClass,
4649			checkedClass = "ui-" + checkedState + activeBtn,
4650			uncheckedClass = "ui-" + uncheckedState,
4651			checkedicon = "ui-icon-" + checkedState,
4652			uncheckedicon = "ui-icon-" + uncheckedState;
4653
4654		if ( inputtype !== "checkbox" && inputtype !== "radio" ) {
4655			return;
4656		}
4657
4658		// Expose for other methods
4659		$.extend( this, {
4660			label: label,
4661			inputtype: inputtype,
4662			checkedClass: checkedClass,
4663			uncheckedClass: uncheckedClass,
4664			checkedicon: checkedicon,
4665			uncheckedicon: uncheckedicon
4666		});
4667
4668		// If there's no selected theme...
4669		if( !this.options.theme ) {
4670			this.options.theme = this.element.jqmData( "theme" );
4671		}
4672
4673		label.buttonMarkup({
4674			theme: this.options.theme,
4675			icon: icon,
4676			shadow: false
4677		});
4678
4679		// Wrap the input + label in a div
4680		input.add( label )
4681			.wrapAll( "<div class='ui-" + inputtype + "'></div>" );
4682
4683		label.bind({
4684			vmouseover: function( event ) {
4685				if ( $( this ).parent().is( ".ui-disabled" ) ) {
4686					event.stopPropagation();
4687				}
4688			},
4689
4690			vclick: function( event ) {
4691				if ( input.is( ":disabled" ) ) {
4692					event.preventDefault();
4693					return;
4694				}
4695
4696				self._cacheVals();
4697
4698				input.prop( "checked", inputtype === "radio" && true || !input.prop( "checked" ) );
4699
4700				// trigger click handler's bound directly to the input as a substitute for
4701				// how label clicks behave normally in the browsers
4702				// TODO: it would be nice to let the browser's handle the clicks and pass them
4703				//       through to the associate input. we can swallow that click at the parent
4704				//       wrapper element level
4705				input.triggerHandler( 'click' );
4706
4707				// Input set for common radio buttons will contain all the radio
4708				// buttons, but will not for checkboxes. clearing the checked status
4709				// of other radios ensures the active button state is applied properly
4710				self._getInputSet().not( input ).prop( "checked", false );
4711
4712				self._updateAll();
4713				return false;
4714			}
4715
4716		});
4717
4718		input
4719			.bind({
4720				vmousedown: function() {
4721					self._cacheVals();
4722				},
4723
4724				vclick: function() {
4725					var $this = $(this);
4726
4727					// Adds checked attribute to checked input when keyboard is used
4728					if ( $this.is( ":checked" ) ) {
4729
4730						$this.prop( "checked", true);
4731						self._getInputSet().not($this).prop( "checked", false );
4732					} else {
4733
4734						$this.prop( "checked", false );
4735					}
4736
4737					self._updateAll();
4738				},
4739
4740				focus: function() {
4741					label.addClass( "ui-focus" );
4742				},
4743
4744				blur: function() {
4745					label.removeClass( "ui-focus" );
4746				}
4747			});
4748
4749		this.refresh();
4750	},
4751
4752	_cacheVals: function() {
4753		this._getInputSet().each(function() {
4754			var $this = $(this);
4755
4756			$this.jqmData( "cacheVal", $this.is( ":checked" ) );
4757		});
4758	},
4759
4760	//returns either a set of radios with the same name attribute, or a single checkbox
4761	_getInputSet: function(){
4762		if(this.inputtype == "checkbox") {
4763			return this.element;
4764		}
4765
4766		return this.element.closest( "form,fieldset,:jqmData(role='page')" )
4767			.find( "input[name='"+ this.element.attr( "name" ) +"'][type='"+ this.inputtype +"']" );
4768	},
4769
4770	_updateAll: function() {
4771		var self = this;
4772
4773		this._getInputSet().each(function() {
4774			var $this = $(this);
4775
4776			if ( $this.is( ":checked" ) || self.inputtype === "checkbox" ) {
4777				$this.trigger( "change" );
4778			}
4779		})
4780		.checkboxradio( "refresh" );
4781	},
4782
4783	refresh: function() {
4784		var input = this.element,
4785			label = this.label,
4786			icon = label.find( ".ui-icon" );
4787
4788		// input[0].checked expando doesn't always report the proper value
4789		// for checked='checked'
4790		if ( $( input[ 0 ] ).prop( "checked" ) ) {
4791
4792			label.addClass( this.checkedClass ).removeClass( this.uncheckedClass );
4793			icon.addClass( this.checkedicon ).removeClass( this.uncheckedicon );
4794
4795		} else {
4796
4797			label.removeClass( this.checkedClass ).addClass( this.uncheckedClass );
4798			icon.removeClass( this.checkedicon ).addClass( this.uncheckedicon );
4799		}
4800
4801		if ( input.is( ":disabled" ) ) {
4802			this.disable();
4803		} else {
4804			this.enable();
4805		}
4806	},
4807
4808	disable: function() {
4809		this.element.prop( "disabled", true ).parent().addClass( "ui-disabled" );
4810	},
4811
4812	enable: function() {
4813		this.element.prop( "disabled", false ).parent().removeClass( "ui-disabled" );
4814	}
4815});
4816
4817//auto self-init widgets
4818$( document ).bind( "pagecreate create", function( e ){
4819	$.mobile.checkboxradio.prototype.enhanceWithin( e.target );
4820});
4821
4822})( jQuery );
4823/*
4824* "button" plugin - links that proxy to native input/buttons
4825*/
4826
4827(function( $, undefined ) {
4828
4829$.widget( "mobile.button", $.mobile.widget, {
4830	options: {
4831		theme: null,
4832		icon: null,
4833		iconpos: null,
4834		inline: null,
4835		corners: true,
4836		shadow: true,
4837		iconshadow: true,
4838		initSelector: "button, [type='button'], [type='submit'], [type='reset'], [type='image']"
4839	},
4840	_create: function() {
4841		var $el = this.element,
4842			o = this.options,
4843			type,
4844			name,
4845			$buttonPlaceholder;
4846
4847		// Add ARIA role
4848		this.button = $( "<div></div>" )
4849			.text( $el.text() || $el.val() )
4850			.insertBefore( $el )
4851			.buttonMarkup({
4852				theme: o.theme,
4853				icon: o.icon,
4854				iconpos: o.iconpos,
4855				inline: o.inline,
4856				corners: o.corners,
4857				shadow: o.shadow,
4858				iconshadow: o.iconshadow
4859			})
4860			.append( $el.addClass( "ui-btn-hidden" ) );
4861
4862		type = $el.attr( "type" );
4863		name = $el.attr( "name" );
4864
4865		// Add hidden input during submit if input type="submit" has a name.
4866		if ( type !== "button" && type !== "reset" && name ) {
4867				$el.bind( "vclick", function() {
4868					// Add hidden input if it doesn’t already exist.
4869					if( $buttonPlaceholder === undefined ) {
4870						$buttonPlaceholder = $( "<input>", {
4871							type: "hidden",
4872							name: $el.attr( "name" ),
4873							value: $el.attr( "value" )
4874						}).insertBefore( $el );
4875
4876						// Bind to doc to remove after submit handling
4877						$( document ).one("submit", function(){
4878							$buttonPlaceholder.remove();
4879
4880							// reset the local var so that the hidden input
4881							// will be re-added on subsequent clicks
4882							$buttonPlaceholder = undefined;
4883						});
4884					}
4885				});
4886		}
4887
4888		this.refresh();
4889	},
4890
4891	enable: function() {
4892		this.element.attr( "disabled", false );
4893		this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
4894		return this._setOption( "disabled", false );
4895	},
4896
4897	disable: function() {
4898		this.element.attr( "disabled", true );
4899		this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true );
4900		return this._setOption( "disabled", true );
4901	},
4902
4903	refresh: function() {
4904		var $el = this.element;
4905
4906		if ( $el.prop("disabled") ) {
4907			this.disable();
4908		} else {
4909			this.enable();
4910		}
4911
4912		// the textWrapper is stored as a data element on the button object
4913		// to prevent referencing by it's implementation details (eg 'class')
4914		this.button.data( 'textWrapper' ).text( $el.text() || $el.val() );
4915	}
4916});
4917
4918//auto self-init widgets
4919$( document ).bind( "pagecreate create", function( e ){
4920	$.mobile.button.prototype.enhanceWithin( e.target );
4921});
4922
4923})( jQuery );/*
4924* "slider" plugin
4925*/
4926
4927( function( $, undefined ) {
4928
4929$.widget( "mobile.slider", $.mobile.widget, {
4930	options: {
4931		theme: null,
4932		trackTheme: null,
4933		disabled: false,
4934		initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')"
4935	},
4936
4937	_create: function() {
4938
4939		// TODO: Each of these should have comments explain what they're for
4940		var self = this,
4941
4942			control = this.element,
4943
4944			parentTheme = $.mobile.getInheritedTheme( control, "c" ),
4945
4946			theme = this.options.theme || parentTheme,
4947
4948			trackTheme = this.options.trackTheme || parentTheme,
4949
4950			cType = control[ 0 ].nodeName.toLowerCase(),
4951
4952			selectClass = ( cType == "select" ) ? "ui-slider-switch" : "",
4953
4954			controlID = control.attr( "id" ),
4955
4956			labelID = controlID + "-label",
4957
4958			label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ),
4959
4960			val = function() {
4961				return  cType == "input"  ? parseFloat( control.val() ) : control[0].selectedIndex;
4962			},
4963
4964			min =  cType == "input" ? parseFloat( control.attr( "min" ) ) : 0,
4965
4966			max =  cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1,
4967
4968			step = window.parseFloat( control.attr( "step" ) || 1 ),
4969
4970			slider = $( "<div class='ui-slider " + selectClass + " ui-btn-down-" + trackTheme +
4971									" ui-btn-corner-all' role='application'></div>" ),
4972
4973			handle = $( "<a href='#' class='ui-slider-handle'></a>" )
4974				.appendTo( slider )
4975				.buttonMarkup({ corners: true, theme: theme, shadow: true })
4976				.attr({
4977					"role": "slider",
4978					"aria-valuemin": min,
4979					"aria-valuemax": max,
4980					"aria-valuenow": val(),
4981					"aria-valuetext": val(),
4982					"title": val(),
4983					"aria-labelledby": labelID
4984				}),
4985			options;
4986
4987		$.extend( this, {
4988			slider: slider,
4989			handle: handle,
4990			dragging: false,
4991			beforeStart: null,
4992			userModified: false,
4993			mouseMoved: false
4994		});
4995
4996		if ( cType == "select" ) {
4997
4998			slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );
4999			
5000			// make the handle move with a smooth transition
5001			handle.addClass( "ui-slider-handle-snapping" );
5002
5003			options = control.find( "option" );
5004
5005			control.find( "option" ).each(function( i ) {
5006
5007				var side = !i ? "b":"a",
5008					corners = !i ? "right" :"left",
5009					theme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass );
5010
5011				$( "<div class='ui-slider-labelbg ui-slider-labelbg-" + side + theme + " ui-btn-corner-" + corners + "'></div>" )
5012					.prependTo( slider );
5013
5014				$( "<span class='ui-slider-label ui-slider-label-" + side + theme + " ui-btn-corner-" + corners + "' role='img'>" + $( this ).getEncodedText() + "</span>" )
5015					.prependTo( handle );
5016			});
5017
5018		}
5019
5020		label.addClass( "ui-slider" );
5021
5022		// monitor the input for updated values
5023		control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
5024			.change( function() {
5025				// if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
5026				if (!self.mouseMoved) {
5027					self.refresh( val(), true );
5028				}
5029			})
5030			.keyup( function() { // necessary?
5031				self.refresh( val(), true, true );
5032			})
5033			.blur( function() {
5034				self.refresh( val(), true );
5035			});
5036
5037		// prevent screen drag when slider activated
5038		$( document ).bind( "vmousemove", function( event ) {
5039			if ( self.dragging ) {
5040				// self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
5041				self.mouseMoved = true;
5042				
5043				if ( cType === "select" ) {
5044					// make the handle move in sync with the mouse
5045					handle.removeClass( "ui-slider-handle-snapping" );
5046				}
5047				
5048				self.refresh( event );
5049				
5050				// only after refresh() you can calculate self.userModified
5051				self.userModified = self.beforeStart !== control[0].selectedIndex;
5052				return false;
5053			}
5054		});
5055
5056		slider.bind( "vmousedown", function( event ) {
5057			self.dragging = true;
5058			self.userModified = false;
5059			self.mouseMoved = false;
5060
5061			if ( cType === "select" ) {
5062				self.beforeStart = control[0].selectedIndex;
5063			}
5064			
5065			self.refresh( event );
5066			return false;
5067		});
5068
5069		slider.add( document )
5070			.bind( "vmouseup", function() {
5071				if ( self.dragging ) {
5072
5073					self.dragging = false;
5074
5075					if ( cType === "select") {
5076					
5077						// make the handle move with a smooth transition
5078						handle.addClass( "ui-slider-handle-snapping" );
5079					
5080						if ( self.mouseMoved ) {
5081						
5082							// this is a drag, change the value only if user dragged enough
5083							if ( self.userModified ) {
5084								self.refresh( self.beforeStart == 0 ? 1 : 0 );
5085							}
5086							else {
5087								self.refresh( self.beforeStart );
5088							}
5089							
5090						}
5091						else {
5092							// this is just a click, change the value
5093							self.refresh( self.beforeStart == 0 ? 1 : 0 );
5094						}
5095						
5096					}
5097					
5098					self.mouseMoved = false;
5099					
5100					return false;
5101				}
5102			});
5103
5104		slider.insertAfter( control );
5105
5106		// NOTE force focus on handle
5107		this.handle
5108			.bind( "vmousedown", function() {
5109				$( this ).focus();
5110			})
5111			.bind( "vclick", false );
5112
5113		this.handle
5114			.bind( "keydown", function( event ) {
5115				var index = val();
5116
5117				if ( self.options.disabled ) {
5118					return;
5119				}
5120
5121				// In all cases prevent the default and mark the handle as active
5122				switch ( event.keyCode ) {
5123				 case $.mobile.keyCode.HOME:
5124				 case $.mobile.keyCode.END:
5125				 case $.mobile.keyCode.PAGE_UP:
5126				 case $.mobile.keyCode.PAGE_DOWN:
5127				 case $.mobile.keyCode.UP:
5128				 case $.mobile.keyCode.RIGHT:
5129				 case $.mobile.keyCode.DOWN:
5130				 case $.mobile.keyCode.LEFT:
5131					event.preventDefault();
5132
5133					if ( !self._keySliding ) {
5134						self._keySliding = true;
5135						$( this ).addClass( "ui-state-active" );
5136					}
5137					break;
5138				}
5139
5140				// move the slider according to the keypress
5141				switch ( event.keyCode ) {
5142				 case $.mobile.keyCode.HOME:
5143					self.refresh( min );
5144					break;
5145				 case $.mobile.keyCode.END:
5146					self.refresh( max );
5147					break;
5148				 case $.mobile.keyCode.PAGE_UP:
5149				 case $.mobile.keyCode.UP:
5150				 case $.mobile.keyCode.RIGHT:
5151					self.refresh( index + step );
5152					break;
5153				 case $.mobile.keyCode.PAGE_DOWN:
5154				 case $.mobile.keyCode.DOWN:
5155				 case $.mobile.keyCode.LEFT:
5156					self.refresh( index - step );
5157					break;
5158				}
5159			}) // remove active mark
5160			.keyup( function( event ) {
5161				if ( self._keySliding ) {
5162					self._keySliding = false;
5163					$( this ).removeClass( "ui-state-active" );
5164				}
5165			});
5166
5167		this.refresh(undefined, undefined, true);
5168	},
5169
5170	refresh: function( val, isfromControl, preventInputUpdate ) {
5171
5172		if ( this.options.disabled || this.element.attr('disabled')) { 
5173			this.disable();
5174		}
5175
5176		var control = this.element, percent,
5177			cType = control[0].nodeName.toLowerCase(),
5178			min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0,
5179			max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1;
5180
5181		if ( typeof val === "object" ) {
5182			var data = val,
5183				// a slight tolerance helped get to the ends of the slider
5184				tol = 8;
5185			if ( !this.dragging ||
5186					data.pageX < this.slider.offset().left - tol ||
5187					data.pageX > this.slider.offset().left + this.slider.width() + tol ) {
5188				return;
5189			}
5190			percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 );
5191		} else {
5192			if ( val == null ) {
5193				val = cType === "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
5194			}
5195			percent = ( parseFloat( val ) - min ) / ( max - min ) * 100;
5196		}
5197
5198		if ( isNaN( percent ) ) {
5199			return;
5200		}
5201
5202		if ( percent < 0 ) {
5203			percent = 0;
5204		}
5205
5206		if ( percent > 100 ) {
5207			percent = 100;
5208		}
5209
5210		var newval = Math.round( ( percent / 100 ) * ( max - min ) ) + min;
5211
5212		if ( newval < min ) {
5213			newval = min;
5214		}
5215
5216		if ( newval > max ) {
5217			newval = max;
5218		}
5219
5220		// Flip the stack of the bg colors
5221		if ( percent > 60 && cType === "select" ) {
5222			// TODO: Dead path?
5223		}
5224		this.handle.css( "left", percent + "%" );
5225		this.handle.attr( {
5226				"aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ),
5227				"aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText(),
5228				title: newval
5229			});
5230
5231		// add/remove classes for flip toggle switch
5232		if ( cType === "select" ) {
5233			if ( newval === 0 ) {
5234				this.slider.addClass( "ui-slider-switch-a" )
5235					.removeClass( "ui-slider-switch-b" );
5236			} else {
5237				this.slider.addClass( "ui-slider-switch-b" )
5238					.removeClass( "ui-slider-switch-a" );
5239			}
5240		}
5241
5242		if ( !preventInputUpdate ) {
5243			var valueChanged = false;
5244
5245			// update control"s value
5246			if ( cType === "input" ) {
5247				valueChanged = control.val() !== newval;
5248				control.val( newval );
5249			} else {
5250				valueChanged = control[ 0 ].selectedIndex !== newval;
5251				control[ 0 ].selectedIndex = newval;
5252			}
5253			if ( !isfromControl && valueChanged ) {
5254				control.trigger( "change" );
5255			}
5256		}
5257	},
5258
5259	enable: function() {
5260		this.element.attr( "disabled", false );
5261		this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
5262		return this._setOption( "disabled", false );
5263	},
5264
5265	disable: function() {
5266		this.element.attr( "disabled", true );
5267		this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true );
5268		return this._setOption( "disabled", true );
5269	}
5270
5271});
5272
5273//auto self-init widgets
5274$( document ).bind( "pagecreate create", function( e ){
5275	$.mobile.slider.prototype.enhanceWithin( e.target );
5276});
5277
5278})( jQuery );
5279/*
5280* "textinput" plugin for text inputs, textareas
5281*/
5282
5283(function( $, undefined ) {
5284
5285$.widget( "mobile.textinput", $.mobile.widget, {
5286	options: {
5287		theme: null,
5288		initSelector: "input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])"
5289	},
5290
5291	_create: function() {
5292
5293		var input = this.element,
5294			o = this.options,
5295			theme = o.theme || $.mobile.getInheritedTheme( this.element, "c" ),
5296			themeclass  = " ui-body-" + theme,
5297			focusedEl, clearbtn;
5298
5299		$( "label[for='" + input.attr( "id" ) + "']" ).addClass( "ui-input-text" );
5300
5301		focusedEl = input.addClass("ui-input-text ui-body-"+ theme );
5302
5303		// XXX: Temporary workaround for issue 785 (Apple bug 8910589).
5304		//      Turn off autocorrect and autocomplete on non-iOS 5 devices
5305		//      since the popup they use can't be dismissed by the user. Note
5306		//      that we test for the presence of the feature by looking for
5307		//      the autocorrect property on the input element. We currently
5308		//      have no test for iOS 5 or newer so we're temporarily using
5309		//      the touchOverflow support flag for jQM 1.0. Yes, I feel dirty. - jblas
5310		if ( typeof input[0].autocorrect !== "undefined" && !$.support.touchOverflow ) {
5311			// Set the attribute instead of the property just in case there
5312			// is code that attempts to make modifications via HTML.
5313			input[0].setAttribute( "autocorrect", "off" );
5314			input[0].setAttribute( "autocomplete", "off" );
5315		}
5316
5317
5318		//"search" input widget
5319		if ( input.is( "[type='search'],:jqmData(type='search')" ) ) {
5320
5321			focusedEl = input.wrap( "<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield" + themeclass + "'></div>" ).parent();
5322			clearbtn = $( "<a href='#' class='ui-input-clear' title='clear text'>clear text</a>" )
5323				.tap(function( event ) {
5324					input.val( "" ).focus();
5325					input.trigger( "change" );
5326					clearbtn.addClass( "ui-input-clear-hidden" );
5327					event.preventDefault();
5328				})
5329				.appendTo( focusedEl )
5330				.buttonMarkup({
5331					icon: "delete",
5332					iconpos: "notext",
5333					corners: true,
5334					shadow: true
5335				});
5336
5337			function toggleClear() {
5338				setTimeout(function() {
5339					clearbtn.toggleClass( "ui-input-clear-hidden", !input.val() );
5340				}, 0);
5341			}
5342
5343			toggleClear();
5344
5345			input.bind('paste cut keyup focus change blur', toggleClear);
5346
5347		} else {
5348			input.addClass( "ui-corner-all ui-shadow-inset" + themeclass );
5349		}
5350
5351		input.focus(function() {
5352				focusedEl.addClass( "ui-focus" );
5353			})
5354			.blur(function(){
5355				focusedEl.removeClass( "ui-focus" );
5356			});
5357
5358		// Autogrow
5359		if ( input.is( "textarea" ) ) {
5360			var extraLineHeight = 15,
5361				keyupTimeoutBuffer = 100,
5362				keyup = function() {
5363					var scrollHeight = input[ 0 ].scrollHeight,
5364						clientHeight = input[ 0 ].clientHeight;
5365
5366					if ( clientHeight < scrollHeight ) {
5367						input.height(scrollHeight + extraLineHeight);
5368					}
5369				},
5370				keyupTimeout;
5371
5372			input.keyup(function() {
5373				clearTimeout( keyupTimeout );
5374				keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer );
5375			});
5376
5377			// Issue 509: the browser is not providing scrollHeight properly until the styles load
5378			if ( $.trim( input.val() ) ) {
5379				// bind to the window load to make sure the height is calculated based on BOTH
5380				// the DOM and CSS
5381				$( window ).load( keyup );
5382
5383				// binding to pagechange here ensures that for pages loaded via
5384				// ajax the height is recalculated without user input
5385				$( document ).one( "pagechange", keyup );
5386			}
5387		}
5388	},
5389
5390	disable: function(){
5391		( this.element.attr( "disabled", true ).is( "[type='search'],:jqmData(type='search')" ) ?
5392			this.element.parent() : this.element ).addClass( "ui-disabled" );
5393	},
5394
5395	enable: function(){
5396		( this.element.attr( "disabled", false).is( "[type='search'],:jqmData(type='search')" ) ?
5397			this.element.parent() : this.element ).removeClass( "ui-disabled" );
5398	}
5399});
5400
5401//auto self-init widgets
5402$( document ).bind( "pagecreate create", function( e ){
5403	$.mobile.textinput.prototype.enhanceWithin( e.target );
5404});
5405
5406})( jQuery );
5407/*
5408* custom "selectmenu" plugin
5409*/
5410
5411(function( $, undefined ) {
5412	var extendSelect = function( widget ){
5413
5414		var select = widget.select,
5415			selectID  = widget.selectID,
5416			label = widget.label,
5417			thisPage = widget.select.closest( ".ui-page" ),
5418			screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"} ).appendTo( thisPage ),
5419			selectOptions = widget._selectOptions(),
5420			isMultiple = widget.isMultiple = widget.select[ 0 ].multiple,
5421			buttonId = selectID + "-button",
5422			menuId = selectID + "-menu",
5423			menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' data-" +$.mobile.ns + "theme='"+ widget.options.theme +"' data-" +$.mobile.ns + "overlay-theme='"+ widget.options.overlayTheme +"'>" +
5424				"<div data-" + $.mobile.ns + "role='header'>" +
5425				"<div class='ui-title'>" + label.getEncodedText() + "</div>"+
5426				"</div>"+
5427				"<div data-" + $.mobile.ns + "role='content'></div>"+
5428				"</div>" ).appendTo( $.mobile.pageContainer ).page(),
5429
5430			listbox =  $("<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + widget.options.overlayTheme + " " + $.mobile.defaultDialogTransition } ).insertAfter(screen),
5431
5432			list = $( "<ul>", {
5433				"class": "ui-selectmenu-list",
5434				"id": menuId,
5435				"role": "listbox",
5436				"aria-labelledby": buttonId
5437			}).attr( "data-" + $.mobile.ns + "theme", widget.options.theme ).appendTo( listbox ),
5438
5439			header = $( "<div>", {
5440				"class": "ui-header ui-bar-" + widget.options.theme
5441			}).prependTo( listbox ),
5442
5443			headerTitle = $( "<h1>", {
5444				"class": "ui-title"
5445			}).appendTo( header ),
5446
5447			headerClose = $( "<a>", {
5448				"text": widget.options.closeText,
5449				"href": "#",
5450				"class": "ui-btn-left"
5451			}).attr( "data-" + $.mobile.ns + "iconpos", "notext" ).attr( "data-" + $.mobile.ns + "icon", "delete" ).appendTo( header ).buttonMarkup(),
5452
5453			menuPageContent = menuPage.find( ".ui-content" ),
5454
5455			menuPageClose = menuPage.find( ".ui-header a" );
5456
5457
5458		$.extend( widget, {
5459			select: widget.select,
5460			selectID: selectID,
5461			buttonId: buttonId,
5462			menuId: menuId,
5463			thisPage: thisPage,
5464			menuPage: menuPage,
5465			label: label,
5466			screen: screen,
5467			selectOptions: selectOptions,
5468			isMultiple: isMultiple,
5469			theme: widget.options.theme,
5470			listbox: listbox,
5471			list: list,
5472			header: header,
5473			headerTitle: headerTitle,
5474			headerClose: headerClose,
5475			menuPageContent: menuPageContent,
5476			menuPageClose: menuPageClose,
5477			placeholder: "",
5478
5479			build: function() {
5480				var self = this;
5481
5482				// Create list from select, update state
5483				self.refresh();
5484
5485				self.select.attr( "tabindex", "-1" ).focus(function() {
5486					$( this ).blur();
5487					self.button.focus();
5488				});
5489
5490				// Button events
5491				self.button.bind( "vclick keydown" , function( event ) {
5492					if ( event.type == "vclick" ||
5493							 event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
5494																	event.keyCode === $.mobile.keyCode.SPACE ) ) {
5495
5496						self.open();
5497						event.preventDefault();
5498					}
5499				});
5500
5501				// Events for list items
5502				self.list.attr( "role", "listbox" )
5503					.delegate( ".ui-li>a", "focusin", function() {
5504						$( this ).attr( "tabindex", "0" );
5505					})
5506					.delegate( ".ui-li>a", "focusout", function() {
5507						$( this ).attr( "tabindex", "-1" );
5508					})
5509					.delegate( "li:not(.ui-disabled, .ui-li-divider)", "click", function( event ) {
5510
5511						// index of option tag to be selected
5512						var oldIndex = self.select[ 0 ].selectedIndex,
5513							newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
5514							option = self._selectOptions().eq( newIndex )[ 0 ];
5515
5516						// toggle selected status on the tag for multi selects
5517						option.selected = self.isMultiple ? !option.selected : true;
5518
5519						// toggle checkbox class for multiple selects
5520						if ( self.isMultiple ) {
5521							$( this ).find( ".ui-icon" )
5522								.toggleClass( "ui-icon-checkbox-on", option.selected )
5523								.toggleClass( "ui-icon-checkbox-off", !option.selected );
5524						}
5525
5526						// trigger change if value changed
5527						if ( self.isMultiple || oldIndex !== newIndex ) {
5528							self.select.trigger( "change" );
5529						}
5530
5531						//hide custom select for single selects only
5532						if ( !self.isMultiple ) {
5533							self.close();
5534						}
5535
5536						event.preventDefault();
5537					})
5538					.keydown(function( event ) {  //keyboard events for menu items
5539						var target = $( event.target ),
5540							li = target.closest( "li" ),
5541							prev, next;
5542
5543						// switch logic based on which key was pressed
5544						switch ( event.keyCode ) {
5545							// up or left arrow keys
5546						 case 38:
5547							prev = li.prev();
5548
5549							// if there's a previous option, focus it
5550							if ( prev.length ) {
5551								target
5552									.blur()
5553									.attr( "tabindex", "-1" );
5554
5555								prev.find( "a" ).first().focus();
5556							}
5557
5558							return false;
5559							break;
5560
5561							// down or right arrow keys
5562						 case 40:
5563							next = li.next();
5564
5565							// if there's a next option, focus it
5566							if ( next.length ) {
5567								target
5568									.blur()
5569									.attr( "tabindex", "-1" );
5570
5571								next.find( "a" ).first().focus();
5572							}
5573
5574							return false;
5575							break;
5576
5577							// If enter or space is pressed, trigger click
5578						 case 13:
5579						 case 32:
5580							target.trigger( "click" );
5581
5582							return false;
5583							break;
5584						}
5585					});
5586
5587				// button refocus ensures proper height calculation
5588				// by removing the inline style and ensuring page inclusion
5589				self.menuPage.bind( "pagehide", function() {
5590					self.list.appendTo( self.listbox );
5591					self._focusButton();
5592
5593					// TODO centralize page removal binding / handling in the page plugin.
5594					// Suggestion from @jblas to do refcounting
5595					//
5596					// TODO extremely confusing dependency on the open method where the pagehide.remove
5597					// bindings are stripped to prevent the parent page from disappearing. The way
5598					// we're keeping pages in the DOM right now sucks
5599					//
5600					// rebind the page remove that was unbound in the open function
5601					// to allow for the parent page removal from actions other than the use
5602					// of a dialog sized custom select
5603					//
5604					// doing this here provides for the back button on the custom select dialog
5605					$.mobile._bindPageRemove.call( self.thisPage );
5606				});
5607
5608				// Events on "screen" overlay
5609				self.screen.bind( "vclick", function( event ) {
5610					self.close();
5611				});
5612
5613				// Close button on small overlays
5614				self.headerClose.click( function() {
5615					if ( self.menuType == "overlay" ) {
5616						self.close();
5617						return false;
5618					}
5619				});
5620
5621				// track this dependency so that when the parent page
5622				// is removed on pagehide it will also remove the menupage
5623				self.thisPage.addDependents( this.menuPage );
5624			},
5625
5626			_isRebuildRequired: function() {
5627				var list = this.list.find( "li" ),
5628					options = this._selectOptions();
5629
5630				// TODO exceedingly naive method to determine difference
5631				// ignores value changes etc in favor of a forcedRebuild
5632				// from the user in the refresh method
5633				return options.text() !== list.text();
5634			},
5635
5636			refresh: function( forceRebuild , foo ){
5637				var self = this,
5638				select = this.element,
5639				isMultiple = this.isMultiple,
5640				options = this._selectOptions(),
5641				selected = this.selected(),
5642				// return an array of all selected index's
5643				indicies = this.selectedIndices();
5644
5645				if (  forceRebuild || this._isRebuildRequired() ) {
5646					self._buildList();
5647				}
5648
5649				self.setButtonText();
5650				self.setButtonCount();
5651
5652				self.list.find( "li:not(.ui-li-divider)" )
5653					.removeClass( $.mobile.activeBtnClass )
5654					.attr( "aria-selected", false )
5655					.each(function( i ) {
5656
5657						if ( $.inArray( i, indicies ) > -1 ) {
5658							var item = $( this );
5659
5660							// Aria selected attr
5661							item.attr( "aria-selected", true );
5662
5663							// Multiple selects: add the "on" checkbox state to the icon
5664							if ( self.isMultiple ) {
5665								item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
5666							} else {
5667								item.addClass( $.mobile.activeBtnClass );
5668							}
5669						}
5670					});
5671			},
5672
5673			close: function() {
5674				if ( this.options.disabled || !this.isOpen ) {
5675					return;
5676				}
5677
5678				var self = this;
5679
5680				if ( self.menuType == "page" ) {
5681					// doesn't solve the possible issue with calling change page
5682					// where the objects don't define data urls which prevents dialog key
5683					// stripping - changePage has incoming refactor
5684					window.history.back();
5685				} else {
5686					self.screen.addClass( "ui-screen-hidden" );
5687					self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
5688					self.list.appendTo( self.listbox );
5689					self._focusButton();
5690				}
5691
5692				// allow the dialog to be closed again
5693				self.isOpen = false;
5694			},
5695
5696			open: function() {
5697				if ( this.options.disabled ) {
5698					return;
5699				}
5700
5701				var self = this,
5702					menuHeight = self.list.parent().outerHeight(),
5703					menuWidth = self.list.parent().outerWidth(),
5704					activePage = $( ".ui-page-active" ),
5705					tOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
5706					tScrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage;
5707					scrollTop = tOverflow ? tScrollElem.scrollTop() : $( window ).scrollTop(),
5708					btnOffset = self.button.offset().top,
5709					screenHeight = window.innerHeight,
5710					screenWidth = window.innerWidth;
5711
5712				//add active class to button
5713				self.button.addClass( $.mobile.activeBtnClass );
5714
5715				//remove after delay
5716				setTimeout( function() {
5717					self.button.removeClass( $.mobile.activeBtnClass );
5718				}, 300);
5719
5720				function focusMenuItem() {
5721					self.list.find( $.mobile.activeBtnClass ).focus();
5722				}
5723
5724				if ( menuHeight >
5724 screenHeight - 80 || !$.support.scrollTop ) {
5725					// prevent the parent page from being removed from the DOM,
5726					// otherwise the results of selecting a list item in the dialog
5727					// fall into a black hole
5728					self.thisPage.unbind( "pagehide.remove" );
5729
5730					//for webos (set lastscroll using button offset)
5731					if ( scrollTop == 0 && btnOffset > screenHeight ) {
5732						self.thisPage.one( "pagehide", function() {
5733							$( this ).jqmData( "lastScroll", btnOffset );
5734						});
5735					}
5736
5737					self.menuPage.one( "pageshow", function() {
5738						// silentScroll() is called whenever a page is shown to restore
5739						// any previous scroll position the page may have had. We need to
5740						// wait for the "silentscroll" event before setting focus to avoid
5741						// the browser"s "feature" which offsets rendering to make sure
5742						// whatever has focus is in view.
5743						$( window ).one( "silentscroll", function() {
5744							focusMenuItem();
5745						});
5746
5747						self.isOpen = true;
5748					});
5749
5750					self.menuType = "page";
5751					self.menuPageContent.append( self.list );
5752					self.menuPage.find("div .ui-title").text(self.label.text());
5753					$.mobile.changePage( self.menuPage, {
5754						transition: $.mobile.defaultDialogTransition
5755					});
5756				} else {
5757					self.menuType = "overlay";
5758
5759					self.screen.height( $(document).height() )
5760						.removeClass( "ui-screen-hidden" );
5761
5762					// Try and center the overlay over the button
5763					var roomtop = btnOffset - scrollTop,
5764						roombot = scrollTop + screenHeight - btnOffset,
5765						halfheight = menuHeight / 2,
5766						maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
5767						newtop, newleft;
5768
5769					if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
5770						newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
5771					} else {
5772						// 30px tolerance off the edges
5773						newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
5774					}
5775
5776					// If the menuwidth is smaller than the screen center is
5777					if ( menuWidth < maxwidth ) {
5778						newleft = ( screenWidth - menuWidth ) / 2;
5779					} else {
5780
5781						//otherwise insure a >= 30px offset from the left
5782						newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
5783
5784						// 30px tolerance off the edges
5785						if ( newleft < 30 ) {
5786							newleft = 30;
5787						} else if ( (newleft + menuWidth) > screenWidth ) {
5788							newleft = screenWidth - menuWidth - 30;
5789						}
5790					}
5791
5792					self.listbox.append( self.list )
5793						.removeClass( "ui-selectmenu-hidden" )
5794						.css({
5795							top: newtop,
5796							left: newleft
5797						})
5798						.addClass( "in" );
5799
5800					focusMenuItem();
5801
5802					// duplicate with value set in page show for dialog sized selects
5803					self.isOpen = true;
5804				}
5805			},
5806
5807			_buildList: function() {
5808				var self = this,
5809					o = this.options,
5810					placeholder = this.placeholder,
5811					optgroups = [],
5812					lis = [],
5813					dataIcon = self.isMultiple ? "checkbox-off" : "false";
5814
5815				self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
5816
5817				// Populate menu with options from select element
5818				self.select.find( "option" ).each( function( i ) {
5819					var $this = $( this ),
5820						$parent = $this.parent(),
5821						text = $this.getEncodedText(),
5822						anchor = "<a href='#'>"+ text +"</a>",
5823						classes = [],
5824						extraAttrs = [];
5825
5826					// Are we inside an optgroup?
5827					if ( $parent.is( "optgroup" ) ) {
5828						var optLabel = $parent.attr( "label" );
5829
5830						// has this optgroup already been built yet?
5831						if ( $.inArray( optLabel, optgroups ) === -1 ) {
5832							lis.push( "<li data-" + $.mobile.ns + "role='list-divider'>"+ optLabel +"</li>" );
5833							optgroups.push( optLabel );
5834						}
5835					}
5836
5837					// Find placeholder text
5838					// TODO: Are you sure you want to use getAttribute? ^RW
5839					if ( !this.getAttribute( "value" ) || text.length == 0 || $this.jqmData( "placeholder" ) ) {
5840						if ( o.hidePlaceholderMenuItems ) {
5841							classes.push( "ui-selectmenu-placeholder" );
5842						}
5843						placeholder = self.placeholder = text;
5844					}
5845
5846					// support disabled option tags
5847					if ( this.disabled ) {
5848						classes.push( "ui-disabled" );
5849						extraAttrs.push( "aria-disabled='true'" );
5850					}
5851
5852					lis.push( "<li data-" + $.mobile.ns + "option-index='" + i + "' data-" + $.mobile.ns + "icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" );
5853				});
5854
5855				self.list.html( lis.join(" ") );
5856
5857				self.list.find( "li" )
5858					.attr({ "role": "option", "tabindex": "-1" })
5859					.first().attr( "tabindex", "0" );
5860
5861				// Hide header close link for single selects
5862				if ( !this.isMultiple ) {
5863					this.headerClose.hide();
5864				}
5865
5866				// Hide header if it's not a multiselect and there's no placeholder
5867				if ( !this.isMultiple && !placeholder.length ) {
5868					this.header.hide();
5869				} else {
5870					this.headerTitle.text( this.placeholder );
5871				}
5872
5873				// Now populated, create listview
5874				self.list.listview();
5875			},
5876
5877			_button: function(){
5878				return $( "<a>", {
5879					"href": "#",
5880					"role": "button",
5881					// TODO value is undefined at creation
5882					"id": this.buttonId,
5883					"aria-haspopup": "true",
5884
5885					// TODO value is undefined at creation
5886					"aria-owns": this.menuId
5887				});
5888			}
5889		});
5890	};
5891
5892	$( "select" ).live( "selectmenubeforecreate", function(){
5893		var selectmenuWidget = $( this ).data( "selectmenu" );
5894
5895		if( !selectmenuWidget.options.nativeMenu ){
5896			extendSelect( selectmenuWidget );
5897		}
5898	});
5899})( jQuery );
5900/*
5901* "selectmenu" plugin
5902*/
5903
5904(function( $, undefined ) {
5905
5906$.widget( "mobile.selectmenu", $.mobile.widget, {
5907	options: {
5908		theme: null,
5909		disabled: false,
5910		icon: "arrow-d",
5911		iconpos: "right",
5912		inline: null,
5913		corners: true,
5914		shadow: true,
5915		iconshadow: true,
5916		menuPageTheme: "b",
5917		overlayTheme: "a",
5918		hidePlaceholderMenuItems: true,
5919		closeText: "Close",
5920		nativeMenu: true,
5921		initSelector: "select:not(:jqmData(role='slider'))"
5922	},
5923
5924	_button: function(){
5925		return $( "<div/>" );
5926	},
5927
5928	_setDisabled: function( value ) {
5929		this.element.attr( "disabled", value );
5930		this.button.attr( "aria-disabled", value );
5931		return this._setOption( "disabled", value );
5932	},
5933
5934	_focusButton : function() {
5935		var self = this;
5936
5937		setTimeout( function() {
5938			self.button.focus();
5939		}, 40);
5940	},
5941
5942  _selectOptions: function() {
5943    return this.select.find( "option" );
5944  },
5945
5946	// setup items that are generally necessary for select menu extension
5947	_preExtension: function(){
5948		this.select = this.element.wrap( "<div class='ui-select'>" );
5949		this.selectID  = this.select.attr( "id" );
5950		this.label = $( "label[for='"+ this.selectID +"']" ).addClass( "ui-select" );
5951		this.isMultiple = this.select[ 0 ].multiple;
5952		if ( !this.options.theme ) {
5953			this.options.theme = $.mobile.getInheritedTheme( this.select, "c" );
5954		}
5955	},
5956
5957	_create: function() {
5958		this._preExtension();
5959
5960 		// Allows for extension of the native select for custom selects and other plugins
5961		// see select.custom for example extension
5962		// TODO explore plugin registration
5963		this._trigger( "beforeCreate" );
5964
5965		this.button = this._button();
5966
5967		var self = this,
5968
5969			options = this.options,
5970
5971			// IE throws an exception at options.item() function when
5972			// there is no selected item
5973			// select first in this case
5974			selectedIndex = this.select[ 0 ].selectedIndex == -1 ? 0 : this.select[ 0 ].selectedIndex,
5975
5976			// TODO values buttonId and menuId are undefined here
5977			button = this.button
5978				.text( $( this.select[ 0 ].options.item( selectedIndex ) ).text() )
5979				.insertBefore( this.select )
5980				.buttonMarkup( {
5981					theme: options.theme,
5982					icon: options.icon,
5983					iconpos: options.iconpos,
5984					inline: options.inline,
5985					corners: options.corners,
5986					shadow: options.shadow,
5987					iconshadow: options.iconshadow
5988				});
5989
5990		// Opera does not properly support opacity on select elements
5991		// In Mini, it hides the element, but not its text
5992		// On the desktop,it seems to do the opposite
5993		// for these reasons, using the nativeMenu option results in a full native select in Opera
5994		if ( options.nativeMenu && window.opera && window.opera.version ) {
5995			this.select.addClass( "ui-select-nativeonly" );
5996		}
5997
5998		// Add counter for multi selects
5999		if ( this.isMultiple ) {
6000			this.buttonCount = $( "<span>" )
6001				.addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" )
6002				.hide()
6003				.appendTo( button.addClass('ui-li-has-count') );
6004		}
6005
6006		// Disable if specified
6007		if ( options.disabled || this.element.attr('disabled')) {
6008			this.disable();
6009		}
6010
6011		// Events on native select
6012		this.select.change( function() {
6013			self.refresh();
6014		});
6015
6016		this.build();
6017	},
6018
6019	build: function() {
6020		var self = this;
6021
6022		this.select
6023			.appendTo( self.button )
6024			.bind( "vmousedown", function() {
6025				// Add active class to button
6026				self.button.addClass( $.mobile.activeBtnClass );
6027			})
6028			.bind( "focus vmouseover", function() {
6029				self.button.trigger( "vmouseover" );
6030			})
6031			.bind( "vmousemove", function() {
6032				// Remove active class on scroll/touchmove
6033				self.button.removeClass( $.mobile.activeBtnClass );
6034			})
6035			.bind( "change blur vmouseout", function() {
6036				self.button.trigger( "vmouseout" )
6037					.removeClass( $.mobile.activeBtnClass );
6038			})
6039			.bind( "change blur", function() {
6040				self.button.removeClass( "ui-btn-down-" + self.options.theme );
6041			});
6042	},
6043
6044	selected: function() {
6045		return this._selectOptions().filter( ":selected" );
6046	},
6047
6048	selectedIndices: function() {
6049		var self = this;
6050
6051		return this.selected().map( function() {
6052			return self._selectOptions().index( this );
6053		}).get();
6054	},
6055
6056	setButtonText: function() {
6057		var self = this, selected = this.selected();
6058
6059		this.button.find( ".ui-btn-text" ).text( function() {
6060			if ( !self.isMultiple ) {
6061				return selected.text();
6062			}
6063
6064			return selected.length ? selected.map( function() {
6065				return $( this ).text();
6066			}).get().join( ", " ) : self.placeholder;
6067		});
6068	},
6069
6070	setButtonCount: function() {
6071		var selected = this.selected();
6072
6073		// multiple count inside button
6074		if ( this.isMultiple ) {
6075			this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
6076		}
6077	},
6078
6079	refresh: function() {
6080		this.setButtonText();
6081		this.setButtonCount();
6082	},
6083
6084	// open and close preserved in native selects
6085	// to simplify users code when looping over selects
6086	open: $.noop,
6087	close: $.noop,
6088
6089	disable: function() {
6090		this._setDisabled( true );
6091		this.button.addClass( "ui-disabled" );
6092	},
6093
6094	enable: function() {
6095		this._setDisabled( false );
6096		this.button.removeClass( "ui-disabled" );
6097	}
6098});
6099
6100//auto self-init widgets
6101$( document ).bind( "pagecreate create", function( e ){
6102	$.mobile.selectmenu.prototype.enhanceWithin( e.target );
6103});
6104})( jQuery );
6105/*
6106* "buttons" plugin - for making button-like links
6107*/
6108
6109( function( $, undefined ) {
6110
6111$.fn.buttonMarkup = function( options ) {
6112	options = options || {};
6113
6114	for ( var i = 0; i < this.length; i++ ) {
6115		var el = this.eq( i ),
6116			e = el[ 0 ],
6117			o = $.extend( {}, $.fn.buttonMarkup.defaults, {
6118				icon:       options.icon       !== undefined ? options.icon       : el.jqmData( "icon" ),
6119				iconpos:    options.iconpos    !== undefined ? options.iconpos    : el.jqmData( "iconpos" ),
6120				theme:      options.theme      !== undefined ? options.theme      : el.jqmData( "theme" ),
6121				inline:     options.inline     !== undefined ? options.inline     : el.jqmData( "inline" ),
6122				shadow:     options.shadow     !== undefined ? options.shadow     : el.jqmData( "shadow" ),
6123				corners:    options.corners    !== undefined ? options.corners    : el.jqmData( "corners" ),
6124				iconshadow: options.iconshadow !== undefined ? options.iconshadow : el.jqmData( "iconshadow" )
6125			}, options ),
6126
6127			// Classes Defined
6128			innerClass = "ui-btn-inner",
6129			textClass = "ui-btn-text",
6130			buttonClass, iconClass,
6131
6132			// Button inner markup
6133			buttonInner = document.createElement( o.wrapperEls ),
6134			buttonText = document.createElement( o.wrapperEls ),
6135			buttonIcon = o.icon ? document.createElement( "span" ) : null;
6136
6137		if ( attachEvents ) {
6138			attachEvents();
6139		}
6140
6141		// if not, try to find closest theme container
6142		if ( !o.theme ) {
6143			o.theme = $.mobile.getInheritedTheme( el, "c" );
6144		}
6145
6146		buttonClass = "ui-btn ui-btn-up-" + o.theme;
6147
6148		if ( o.inline ) {
6149			buttonClass += " ui-btn-inline";
6150		}
6151
6152		if ( o.icon ) {
6153			o.icon = "ui-icon-" + o.icon;
6154			o.iconpos = o.iconpos || "left";
6155
6156			iconClass = "ui-icon " + o.icon;
6157
6158			if ( o.iconshadow ) {
6159				iconClass += " ui-icon-shadow";
6160			}
6161		}
6162
6163		if ( o.iconpos ) {
6164			buttonClass += " ui-btn-icon-" + o.iconpos;
6165
6166			if ( o.iconpos == "notext" && !el.attr( "title" ) ) {
6167				el.attr( "title", el.getEncodedText() );
6168			}
6169		}
6170
6171		if ( o.corners ) {
6172			buttonClass += " ui-btn-corner-all";
6173			innerClass += " ui-btn-corner-all";
6174		}
6175
6176		if ( o.shadow ) {
6177			buttonClass += " ui-shadow";
6178		}
6179
6180		e.setAttribute( "data-" + $.mobile.ns + "theme", o.theme );
6181		el.addClass( buttonClass );
6182
6183		buttonInner.className = innerClass;
6184		buttonInner.setAttribute("aria-hidden", "true");
6185
6186		buttonText.className = textClass;
6187		buttonInner.appendChild( buttonText );
6188
6189		if ( buttonIcon ) {
6190			buttonIcon.className = iconClass;
6191			buttonInner.appendChild( buttonIcon );
6192		}
6193
6194		while ( e.firstChild ) {
6195			buttonText.appendChild( e.firstChild );
6196		}
6197
6198		e.appendChild( buttonInner );
6199		
6200		// TODO obviously it would be nice to pull this element out instead of
6201		// retrieving it from the DOM again, but this change is much less obtrusive
6202		// and 1.0 draws nigh
6203		$.data( e, 'textWrapper', $( buttonText ) );
6204	}
6205
6206	return this;
6207};
6208
6209$.fn.buttonMarkup.defaults = {
6210	corners: true,
6211	shadow: true,
6212	iconshadow: true,
6213	inline: false,
6214	wrapperEls: "span"
6215};
6216
6217function closestEnabledButton( element ) {
6218    var cname;
6219
6220    while ( element ) {
6221        cname = element.className && element.className.split(' ');
6222        if ( cname && $.inArray( "ui-btn", cname ) > -1 && $.inArray( "ui-disabled", cname ) < 0 ) {
6223            break;
6224        }
6225        element = element.parentNode;
6226    }
6227
6228    return element;
6229}
6230
6231var attachEvents = function() {
6232	$( document ).bind( {
6233		"vmousedown": function( event ) {
6234			var btn = closestEnabledButton( event.target ),
6235				$btn, theme;
6236
6237			if ( btn ) {
6238				$btn = $( btn );
6239				theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
6240				$btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
6241			}
6242		},
6243		"vmousecancel vmouseup": function( event ) {
6244			var btn = closestEnabledButton( event.target ),
6245				$btn, theme;
6246
6247			if ( btn ) {
6248				$btn = $( btn );
6249				theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
6250				$btn.removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
6251			}
6252		},
6253		"vmouseover focus": function( event ) {
6254			var btn = closestEnabledButton( event.target ),
6255				$btn, theme;
6256
6257			if ( btn ) {
6258				$btn = $( btn );
6259				theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
6260				$btn.removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme );
6261			}
6262		},
6263		"vmouseout blur": function( event ) {
6264			var btn = closestEnabledButton( event.target ),
6265				$btn, theme;
6266
6267			if ( btn ) {
6268				$btn = $( btn );
6269				theme = $btn.attr( "data-" + $.mobile.ns + "theme" );
6270				$btn.removeClass( "ui-btn-hover-" + theme  + " ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
6271			}
6272		}
6273	});
6274
6275	attachEvents = null;
6276};
6277
6278//links in bars, or those with  data-role become buttons
6279//auto self-init widgets
6280$( document ).bind( "pagecreate create", function( e ){
6281
6282	$( ":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a", e.target )
6283		.not( ".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')" )
6284		.buttonMarkup();
6285});
6286
6287})( jQuery );
6288/* 
6289* "controlgroup" plugin - corner-rounding for groups of buttons, checks, radios, etc
6290*/
6291
6292(function( $, undefined ) {
6293
6294$.fn.controlgroup = function( options ) {
6295
6296	return this.each(function() {
6297
6298		var $el = $( this ),
6299			o = $.extend({
6300						direction: $el.jqmData( "type" ) || "vertical",
6301						shadow: false,
6302						excludeInvisible: true
6303					}, options ),
6304			groupheading = $el.children( "legend" ),
6305			flCorners = o.direction == "horizontal" ? [ "ui-corner-left", "ui-corner-right" ] : [ "ui-corner-top", "ui-corner-bottom" ],
6306			type = $el.find( "input" ).first().attr( "type" );
6307
6308		// Replace legend with more stylable replacement div
6309		if ( groupheading.length ) {
6310			$el.wrapInner( "<div class='ui-controlgroup-controls'></div>" );
6311			$( "<div role='heading' class='ui-controlgroup-label'>" + groupheading.html() + "</div>" ).insertBefore( $el.children(0) );
6312			groupheading.remove();
6313		}
6314
6315		$el.addClass( "ui-corner-all ui-controlgroup ui-controlgroup-" + o.direction );
6316
6317		// TODO: This should be moved out to the closure
6318		// otherwise it is redefined each time controlgroup() is called
6319		function flipClasses( els ) {
6320			els.removeClass( "ui-btn-corner-all ui-shadow" )
6321				.eq( 0 ).addClass( flCorners[ 0 ] )
6322				.end()
6323				.last().addClass( flCorners[ 1 ] ).addClass( "ui-controlgroup-last" );
6324		}
6325
6326		flipClasses( $el.find( ".ui-btn" + ( o.excludeInvisible ? ":visible" : "" ) ) );
6327		flipClasses( $el.find( ".ui-btn-inner" ) );
6328
6329		if ( o.shadow ) {
6330			$el.addClass( "ui-shadow" );
6331		}
6332	});
6333};
6334
6335//auto self-init widgets
6336$( document ).bind( "pagecreate create", function( e ){
6337	$( ":jqmData(role='controlgroup')", e.target ).controlgroup({ excludeInvisible: false });
6338});
6339
6340})(jQuery);/*
6341* "links" plugin - simple class additions for links
6342*/
6343
6344(function( $, undefined ) {
6345
6346$( document ).bind( "pagecreate create", function( e ){
6347	
6348	//links within content areas
6349	$( e.target )
6350		.find( "a" )
6351		.not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
6352		.addClass( "ui-link" );
6353
6354});
6355
6356}
6356)( jQuery );/*
6357* "fixHeaderFooter" plugin - on-demand positioning for headers,footers
6358*/
6359
6360(function( $, undefined ) {
6361
6362var slideDownClass = "ui-header-fixed ui-fixed-inline fade",
6363	slideUpClass = "ui-footer-fixed ui-fixed-inline fade",
6364
6365	slideDownSelector = ".ui-header:jqmData(position='fixed')",
6366	slideUpSelector = ".ui-footer:jqmData(position='fixed')";
6367
6368$.fn.fixHeaderFooter = function( options ) {
6369
6370	if ( !$.support.scrollTop || ( $.support.touchOverflow && $.mobile.touchOverflowEnabled ) ) {
6371		return this;
6372	}
6373
6374	return this.each(function() {
6375		var $this = $( this );
6376
6377		if ( $this.jqmData( "fullscreen" ) ) {
6378			$this.addClass( "ui-page-fullscreen" );
6379		}
6380
6381		// Should be slidedown
6382		$this.find( slideDownSelector ).addClass( slideDownClass );
6383
6384		// Should be slideup
6385		$this.find( slideUpSelector ).addClass( slideUpClass );
6386	});
6387};
6388
6389// single controller for all showing,hiding,toggling
6390$.mobile.fixedToolbars = (function() {
6391
6392	if ( !$.support.scrollTop || ( $.support.touchOverflow && $.mobile.touchOverflowEnabled ) ) {
6393		return;
6394	}
6395
6396	var stickyFooter, delayTimer,
6397		currentstate = "inline",
6398		autoHideMode = false,
6399		showDelay = 100,
6400		ignoreTargets = "a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed",
6401		toolbarSelector = ".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last",
6402		// for storing quick references to duplicate footers
6403		supportTouch = $.support.touch,
6404		touchStartEvent = supportTouch ? "touchstart" : "mousedown",
6405		touchStopEvent = supportTouch ? "touchend" : "mouseup",
6406		stateBefore = null,
6407		scrollTriggered = false,
6408		touchToggleEnabled = true;
6409
6410	function showEventCallback( event ) {
6411		// An event that affects the dimensions of the visual viewport has
6412		// been triggered. If the header and/or footer for the current page are in overlay
6413		// mode, we want to hide them, and then fire off a timer to show them at a later
6414		// point. Events like a resize can be triggered continuously during a scroll, on
6415		// some platforms, so the timer is used to delay the actual positioning until the
6416		// flood of events have subsided.
6417		//
6418		// If we are in autoHideMode, we don't do anything because we know the scroll
6419		// callbacks for the plugin will fire off a show when the scrolling has stopped.
6420		if ( !autoHideMode && currentstate === "overlay" ) {
6421			if ( !delayTimer ) {
6422				$.mobile.fixedToolbars.hide( true );
6423			}
6424
6425			$.mobile.fixedToolbars.startShowTimer();
6426		}
6427	}
6428
6429	$(function() {
6430		var $document = $( document ),
6431			$window = $( window );
6432
6433		$document
6434			.bind( "vmousedown", function( event ) {
6435				if ( touchToggleEnabled ) {
6436					stateBefore = currentstate;
6437				}
6438			})
6439			.bind( "vclick", function( event ) {
6440				if ( touchToggleEnabled ) {
6441
6442					if ( $(event.target).closest( ignoreTargets ).length ) {
6443						return;
6444					}
6445
6446					if ( !scrollTriggered ) {
6447						$.mobile.fixedToolbars.toggle( stateBefore );
6448						stateBefore = null;
6449					}
6450				}
6451			})
6452			.bind( "silentscroll", showEventCallback );
6453
6454
6455		// The below checks first for a $(document).scrollTop() value, and if zero, binds scroll events to $(window) instead.
6456		// If the scrollTop value is actually zero, both will return zero anyway.
6457		//
6458		// Works with $(document), not $(window) : Opera Mobile (WinMO phone; kinda broken anyway)
6459		// Works with $(window), not $(document) : IE 7/8
6460		// Works with either $(window) or $(document) : Chrome, FF 3.6/4, Android 1.6/2.1, iOS
6461		// Needs work either way : BB5, Opera Mobile (iOS)
6462
6463		( ( $document.scrollTop() === 0 ) ? $window : $document )
6464			.bind( "scrollstart", function( event ) {
6465
6466				scrollTriggered = true;
6467
6468				if ( stateBefore === null ) {
6469					stateBefore = currentstate;
6470				}
6471
6472				// We only enter autoHideMode if the headers/footers are in
6473				// an overlay state or the show timer was started. If the
6474				// show timer is set, clear it so the headers/footers don't
6475				// show up until after we're done scrolling.
6476				var isOverlayState = stateBefore == "overlay";
6477
6478				autoHideMode = isOverlayState || !!delayTimer;
6479
6480				if ( autoHideMode ) {
6481					$.mobile.fixedToolbars.clearShowTimer();
6482
6483					if ( isOverlayState ) {
6484						$.mobile.fixedToolbars.hide( true );
6485					}
6486				}
6487			})
6488			.bind( "scrollstop", function( event ) {
6489
6490				if ( $( event.target ).closest( ignoreTargets ).length ) {
6491					return;
6492				}
6493
6494				scrollTriggered = false;
6495
6496				if ( autoHideMode ) {
6497					$.mobile.fixedToolbars.startShowTimer();
6498					autoHideMode = false;
6499				}
6500				stateBefore = null;
6501			});
6502
6503			$window.bind( "resize updatelayout", showEventCallback );
6504	});
6505
6506	// 1. Before page is shown, check for duplicate footer
6507	// 2. After page is shown, append footer to new page
6508	$( ".ui-page" )
6509		.live( "pagebeforeshow", function( event, ui ) {
6510
6511			var page = $( event.target ),
6512				footer = page.find( ":jqmData(role='footer')" ),
6513				id = footer.data( "id" ),
6514				prevPage = ui.prevPage,
6515				prevFooter = prevPage && prevPage.find( ":jqmData(role='footer')" ),
6516				prevFooterMatches = prevFooter.length && prevFooter.jqmData( "id" ) === id;
6517
6518			if ( id && prevFooterMatches ) {
6519				stickyFooter = footer;
6520				setTop( stickyFooter.removeClass( "fade in out" ).appendTo( $.mobile.pageContainer ) );
6521			}
6522		})
6523		.live( "pageshow", function( event, ui ) {
6524
6525			var $this = $( this );
6526
6527			if ( stickyFooter && stickyFooter.length ) {
6528
6529				setTimeout(function() {
6530					setTop( stickyFooter.appendTo( $this ).addClass( "fade" ) );
6531					stickyFooter = null;
6532				}, 500);
6533			}
6534
6535			$.mobile.fixedToolbars.show( true, this );
6536		});
6537
6538	// When a collapsiable is hidden or shown we need to trigger the fixed toolbar to reposition itself (#1635)
6539	$( ".ui-collapsible-contain" ).live( "collapse expand", showEventCallback );
6540
6541	// element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
6542	// coordinates inside of the rect it returns don't have the page scroll position
6543	// factored out of it like the other platforms do. To get around this,
6544	// we'll just calculate the top offset the old fashioned way until core has
6545	// a chance to figure out how to handle this situation.
6546	//
6547	// TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core.
6548
6549	function getOffsetTop( ele ) {
6550		var top = 0,
6551			op, body;
6552
6553		if ( ele ) {
6554			body = document.body;
6555			op = ele.offsetParent;
6556			top = ele.offsetTop;
6557
6558			while ( ele && ele != body ) {
6559				top += ele.scrollTop || 0;
6560
6561				if ( ele == op ) {
6562					top += op.offsetTop;
6563					op = ele.offsetParent;
6564				}
6565
6566				ele = ele.parentNode;
6567			}
6568		}
6569		return top;
6570	}
6571
6572	function setTop( el ) {
6573		var fromTop = $(window).scrollTop(),
6574			thisTop = getOffsetTop( el[ 0 ] ), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
6575			thisCSStop = el.css( "top" ) == "auto" ? 0 : parseFloat(el.css( "top" )),
6576			screenHeight = window.innerHeight,
6577			thisHeight = el.outerHeight(),
6578			useRelative = el.parents( ".ui-page:not(.ui-page-fullscreen)" ).length,
6579			relval;
6580
6581		if ( el.is( ".ui-header-fixed" ) ) {
6582
6583			relval = fromTop - thisTop + thisCSStop;
6584
6585			if ( relval < thisTop ) {
6586				relval = 0;
6587			}
6588
6589			return el.css( "top", useRelative ? relval : fromTop );
6590		} else {
6591			// relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
6592			// if ( relval > thisTop ) { relval = 0; }
6593			relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop );
6594
6595			return el.css( "top", useRelative ? relval : fromTop + screenHeight - thisHeight );
6596		}
6597	}
6598
6599	// Exposed methods
6600	return {
6601
6602		show: function( immediately, page ) {
6603
6604			$.mobile.fixedToolbars.clearShowTimer();
6605
6606			currentstate = "overlay";
6607
6608			var $ap = page ? $( page ) :
6609					( $.mobile.activePage ? $.mobile.activePage :
6610						$( ".ui-page-active" ) );
6611
6612			return $ap.children( toolbarSelector ).each(function() {
6613
6614				var el = $( this ),
6615					fromTop = $( window ).scrollTop(),
6616					// el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
6617					thisTop = getOffsetTop( el[ 0 ] ),
6618					screenHeight = window.innerHeight,
6619					thisHeight = el.outerHeight(),
6620					alreadyVisible = ( el.is( ".ui-header-fixed" ) && fromTop <= thisTop + thisHeight ) ||
6621														( el.is( ".ui-footer-fixed" ) && thisTop <= fromTop + screenHeight );
6622
6623				// Add state class
6624				el.addClass( "ui-fixed-overlay" ).removeClass( "ui-fixed-inline" );
6625
6626				if ( !alreadyVisible && !immediately ) {
6627					el.animationComplete(function() {
6628						el.removeClass( "in" );
6629					}).addClass( "in" );
6630				}
6631				setTop(el);
6632			});
6633		},
6634
6635		hide: function( immediately ) {
6636
6637			currentstate = "inline";
6638
6639			var $ap = $.mobile.activePage ? $.mobile.activePage :
6640									$( ".ui-page-active" );
6641
6642			return $ap.children( toolbarSelector ).each(function() {
6643
6644				var el = $(this),
6645					thisCSStop = el.css( "top" ),
6646					classes;
6647
6648				thisCSStop = thisCSStop == "auto" ? 0 :
6649											parseFloat(thisCSStop);
6650
6651				// Add state class
6652				el.addClass( "ui-fixed-inline" ).removeClass( "ui-fixed-overlay" );
6653
6654				if ( thisCSStop < 0 || ( el.is( ".ui-header-fixed" ) && thisCSStop !== 0 ) ) {
6655
6656					if ( immediately ) {
6657						el.css( "top", 0);
6658					} else {
6659
6660						if ( el.css( "top" ) !== "auto" && parseFloat( el.css( "top" ) ) !== 0 ) {
6661
6662							classes = "out reverse";
6663
6664							el.animationComplete(function() {
6665								el.removeClass( classes ).css( "top", 0 );
6666							}).addClass( classes );
6667						}
6668					}
6669				}
6670			});
6671		},
6672
6673		startShowTimer: function() {
6674
6675			$.mobile.fixedToolbars.clearShowTimer();
6676
6677			var args = [].slice.call( arguments );
6678
6679			delayTimer = setTimeout(function() {
6680				delayTimer = undefined;
6681				$.mobile.fixedToolbars.show.apply( null, args );
6682			}, showDelay);
6683		},
6684
6685		clearShowTimer: function() {
6686			if ( delayTimer ) {
6687				clearTimeout( delayTimer );
6688			}
6689			delayTimer = undefined;
6690		},
6691
6692		toggle: function( from ) {
6693			if ( from ) {
6694				currentstate = from;
6695			}
6696			return ( currentstate === "overlay" ) ? $.mobile.fixedToolbars.hide() :
6697								$.mobile.fixedToolbars.show();
6698		},
6699
6700		setTouchToggleEnabled: function( enabled ) {
6701			touchToggleEnabled = enabled;
6702		}
6703	};
6704})();
6705
6706//auto self-init widgets
6707$( document ).bind( "pagecreate create", function( event ) {
6708
6709	if ( $( ":jqmData(position='fixed')", event.target ).length ) {
6710
6711		$( event.target ).each(function() {
6712
6713			if ( !$.support.scrollTop || ( $.support.touchOverflow && $.mobile.touchOverflowEnabled ) ) {
6714				return this;
6715			}
6716
6717			var $this = $( this );
6718
6719			if ( $this.jqmData( "fullscreen" ) ) {
6720				$this.addClass( "ui-page-fullscreen" );
6721			}
6722
6723			// Should be slidedown
6724			$this.find( slideDownSelector ).addClass( slideDownClass );
6725
6726			// Should be slideup
6727			$this.find( slideUpSelector ).addClass( slideUpClass );
6728
6729		})
6730
6731	}
6732});
6733
6734})( jQuery );
6735/*
6736* "fixHeaderFooter" native plugin - Behavior for "fixed" headers,footers, and scrolling inner content
6737*/
6738
6739(function( $, undefined ) {
6740
6741// Enable touch overflow scrolling when it's natively supported
6742$.mobile.touchOverflowEnabled = false;
6743
6744// Enabled zoom when touch overflow is enabled. Can cause usability issues, unfortunately
6745$.mobile.touchOverflowZoomEnabled = false;
6746
6747$( document ).bind( "pagecreate", function( event ) {
6748	if( $.support.touchOverflow && $.mobile.touchOverflowEnabled ){
6749		
6750		var $target = $( event.target ),
6751			scrollStartY = 0;
6752			
6753		if( $target.is( ":jqmData(role='page')" ) ){
6754			
6755			$target.each(function() {
6756				var $page = $( this ),
6757					$fixies = $page.find( ":jqmData(role='header'), :jqmData(role='footer')" ).filter( ":jqmData(position='fixed')" ),
6758					fullScreen = $page.jqmData( "fullscreen" ),
6759					$scrollElem = $fixies.length ? $page.find( ".ui-content" ) : $page;
6760				
6761				$page.addClass( "ui-mobile-touch-overflow" );
6762				
6763				$scrollElem.bind( "scrollstop", function(){
6764					if( $scrollElem.scrollTop() > 0 ){
6765						window.scrollTo( 0, $.mobile.defaultHomeScroll );
6766					}
6767				});	
6768				
6769				if( $fixies.length ){
6770					
6771					$page.addClass( "ui-native-fixed" );
6772					
6773					if( fullScreen ){
6774
6775						$page.addClass( "ui-native-fullscreen" );
6776
6777						$fixies.addClass( "fade in" );
6778
6779						$( document ).bind( "vclick", function(){
6780							$fixies
6781								.removeClass( "ui-native-bars-hidden" )
6782								.toggleClass( "in out" )
6783								.animationComplete(function(){
6784									$(this).not( ".in" ).addClass( "ui-native-bars-hidden" );
6785								});
6786						});
6787					}
6788				}
6789			});
6790		}
6791	}
6792});
6793
6794})( jQuery );
6795/*
6796* "init" - Initialize the framework
6797*/
6798
6799(function( $, window, undefined ) {
6800	var	$html = $( "html" ),
6801			$head = $( "head" ),
6802			$window = $( window );
6803
6804 	// trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
6805	$( window.document ).trigger( "mobileinit" );
6806
6807	// support conditions
6808	// if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
6809	// otherwise, proceed with the enhancements
6810	if ( !$.mobile.gradeA() ) {
6811		return;
6812	}
6813
6814	// override ajaxEnabled on platforms that have known conflicts with hash history updates
6815	// or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
6816	if ( $.mobile.ajaxBlacklist ) {
6817		$.mobile.ajaxEnabled = false;
6818	}
6819
6820	// add mobile, initial load "rendering" classes to docEl
6821	$html.addClass( "ui-mobile ui-mobile-rendering" );
6822
6823	// loading div which appears during Ajax requests
6824	// will not appear if $.mobile.loadingMessage is false
6825	var $loader = $( "<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1></h1></div>" );
6826
6827	$.extend($.mobile, {
6828		// turn on/off page loading message.
6829		showPageLoadingMsg: function() {
6830			if ( $.mobile.loadingMessage ) {
6831				var activeBtn = $( "." + $.mobile.activeBtnClass ).first();
6832
6833				$loader
6834					.find( "h1" )
6835						.text( $.mobile.loadingMessage )
6836						.end()
6837					.appendTo( $.mobile.pageContainer )
6838					// position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
6839					.css({
6840						top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
6841						activeBtn.length && activeBtn.offset().top || 100
6842					});
6843			}
6844
6845			$html.addClass( "ui-loading" );
6846		},
6847
6848		hidePageLoadingMsg: function() {
6849			$html.removeClass( "ui-loading" );
6850		},
6851
6852		// find and enhance the pages in the dom and transition to the first page.
6853		initializePage: function() {
6854			// find present pages
6855			var $pages = $( ":jqmData(role='page')" );
6856
6857			// if no pages are found, create one with body's inner html
6858			if ( !$pages.length ) {
6859				$pages = $( "body" ).wrapInner( "<div data-" + $.mobile.ns + "role='page'></div>" ).children( 0 );
6860			}
6861
6862			// add dialogs, set data-url attrs
6863			$pages.add( ":jqmData(role='dialog')" ).each(function() {
6864				var $this = $(this);
6865
6866				// unless the data url is already set set it to the pathname
6867				if ( !$this.jqmData("url") ) {
6868					$this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) || location.pathname + location.search );
6869				}
6870			});
6871
6872			// define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
6873			$.mobile.firstPage = $pages.first();
6874
6875			// define page container
6876			$.mobile.pageContainer = $pages.first().parent().addClass( "ui-mobile-viewport" );
6877
6878			// alert listeners that the pagecontainer has been determined for binding
6879			// to events triggered on it
6880			$window.trigger( "pagecontainercreate" );
6881
6882			// cue page loading message
6883			$.mobile.showPageLoadingMsg();
6884
6885			// if hashchange listening is disabled or there's no hash deeplink, change to the first page in the DOM
6886			if ( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ) {
6887				$.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true } );
6888			}
6889			// otherwise, trigger a hashchange to load a deeplink
6890			else {
6891				$window.trigger( "hashchange", [ true ] );
6892			}
6893		}
6894	});
6895	
6896	// This function injects a meta viewport tag to prevent scaling. Off by default, on by default when touchOverflow scrolling is enabled
6897	function disableZoom() {
6898		var cont = "user-scalable=no",
6899			meta = $( "meta[name='viewport']" );
6900			
6901		if( meta.length ){
6902			meta.attr( "content", meta.attr( "content" ) + ", " + cont );
6903		}
6904		else{
6905			$( "head" ).prepend( "<meta>", { "name": "viewport", "content": cont } );
6906		}
6907	}
6908	
6909	// if touch-overflow is enabled, disable user scaling, as it creates usability issues
6910	if( $.support.touchOverflow && $.mobile.touchOverflowEnabled && !$.mobile.touchOverflowZoomEnabled ){
6911		disableZoom();
6912	}
6913
6914	// initialize events now, after mobileinit has occurred
6915	$.mobile._registerInternalEvents();
6916
6917	// check which scrollTop value should be used by scrolling to 1 immediately at domready
6918	// then check what the scroll top is. Android will report 0... others 1
6919	// note that this initial scroll won't hide the address bar. It's just for the check.
6920	$(function() {
6921		window.scrollTo( 0, 1 );
6922
6923		// if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
6924		// it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
6925		// so if it's 1, use 0 from now on
6926		$.mobile.defaultHomeScroll = ( !$.support.scrollTop || $(window).scrollTop() === 1 ) ? 0 : 1;
6927
6928		//dom-ready inits
6929		if( $.mobile.autoInitializePage ){
6930			$.mobile.initializePage();
6931		}
6932
6933		// window load event
6934		// hide iOS browser chrome on load
6935		$window.load( $.mobile.silentScroll );
6936	});
6937})( jQuery, this );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.