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