~suutari-olli/openlp/click-slide-to-go-live-from-blank

« back to all changes in this revision

Viewing changes to openlp/plugins/remotes/html/jquery.mobile.js

trunk

Show diffs side-by-side

added added

removed removed

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