Subversion Repositories HomeAutomation

Rev

Details | Last modification | View Log | SVN | RSS feed

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