add jquery mobile from git
[shapado:shapado.git] / public / javascripts / jquery.mobile-1.0a3pre.js
1 /*!
2  * jQuery Mobile v1.0a3pre
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.data( 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 = $( "<style type='text/css'>" +
327                                 "@media " + query + "{#jquery-mediatest{position:absolute;}}" +
328                                 "</style>" );
329                         $html.prepend( fakeBody ).prepend( styleBlock );
330                         cache[ query ] = testDiv.css( "position" ) === "absolute";
331                         fakeBody.add( styleBlock ).remove();
332                 }
333                 return cache[ query ];
334         };
335 })();
336
337 /*
338         private function for adding/removing breakpoint classes to HTML element for faux media-query support
339         It does not require media query support, instead using JS to detect screen width > cross-browser support
340         This function is called on orientationchange, resize, and mobileinit, and is bound via the 'htmlclass' event namespace
341 */
342 function detectResolutionBreakpoints(){
343         var currWidth = $window.width(),
344                 minPrefix = "min-width-",
345                 maxPrefix = "max-width-",
346                 minBreakpoints = [],
347                 maxBreakpoints = [],
348                 unit = "px",
349                 breakpointClasses;
350
351         $html.removeClass( minPrefix + resolutionBreakpoints.join(unit + " " + minPrefix) + unit + " " +
352                 maxPrefix + resolutionBreakpoints.join( unit + " " + maxPrefix) + unit );
353
354         $.each(resolutionBreakpoints,function( i, breakPoint ){
355                 if( currWidth >= breakPoint ){
356                         minBreakpoints.push( minPrefix + breakPoint + unit );
357                 }
358                 if( currWidth <= breakPoint ){
359                         maxBreakpoints.push( maxPrefix + breakPoint + unit );
360                 }
361         });
362
363         if( minBreakpoints.length ){ breakpointClasses = minBreakpoints.join(" "); }
364         if( maxBreakpoints.length ){ breakpointClasses += " " +  maxBreakpoints.join(" "); }
365
366         $html.addClass( breakpointClasses );
367 };
368
369 /* $.mobile.addResolutionBreakpoints method:
370         pass either a number or an array of numbers and they'll be added to the min/max breakpoint classes
371         Examples:
372                 $.mobile.addResolutionBreakpoints( 500 );
373                 $.mobile.addResolutionBreakpoints( [500, 1200] );
374 */
375 $.mobile.addResolutionBreakpoints = function( newbps ){
376         if( $.type( newbps ) === "array" ){
377                 resolutionBreakpoints = resolutionBreakpoints.concat( newbps );
378         }
379         else {
380                 resolutionBreakpoints.push( newbps );
381         }
382         resolutionBreakpoints.sort(function(a,b){ return a-b; });
383         detectResolutionBreakpoints();
384 };
385
386 /*      on mobileinit, add classes to HTML element
387         and set handlers to update those on orientationchange and resize*/
388 $(document).bind("mobileinit.htmlclass", function(){
389         /* bind to orientationchange and resize
390         to add classes to HTML element for min/max breakpoints and orientation */
391         $window.bind("orientationchange.htmlclass resize.htmlclass", function(event){
392                 //add orientation class to HTML element on flip/resize.
393                 if(event.orientation){
394                         $html.removeClass( "portrait landscape" ).addClass( event.orientation );
395                 }
396                 //add classes to HTML element for min/max breakpoints
397                 detectResolutionBreakpoints();
398         });
399
400         //trigger event manually
401         $window.trigger( "orientationchange.htmlclass" );
402 });
403
404 })(jQuery);/*
405 * jQuery Mobile Framework : support tests
406 * Copyright (c) jQuery Project
407 * Dual licensed under the MIT or GPL Version 2 licenses.
408 * http://jquery.org/license
409 */
410 (function($, undefined ) {
411
412
413
414 var fakeBody = $( "<body>" ).prependTo( "html" ),
415         fbCSS = fakeBody[0].style,
416         vendors = ['webkit','moz','o'],
417         webos = window.palmGetResource || window.PalmServiceBridge, //only used to rule out scrollTop 
418         bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
419
420 //thx Modernizr
421 function propExists( prop ){
422         var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1),
423                 props   = (prop + ' ' + vendors.join(uc_prop + ' ') + uc_prop).split(' ');
424         for(var v in props){
425                 if( fbCSS[ props[v] ] !== undefined ){
426                         return true;
427                 }
428         }
429 };
430
431 //test for dynamic-updating base tag support (allows us to avoid href,src attr rewriting)
432 function baseTagTest(){
433         var fauxBase = location.protocol + '//' + location.host + location.pathname + "ui-dir/",
434                 base = $("<base>", {"href": fauxBase}).appendTo("head"),
435                 link = $( "<a href='testurl'></a>" ).prependTo( fakeBody ),
436                 rebase = link[0].href;
437         base.remove();
438         return rebase.indexOf(fauxBase) === 0;
439 };
440
441 $.extend( $.support, {
442         orientation: "orientation" in window,
443         touch: "ontouchend" in document,
444         cssTransitions: "WebKitTransitionEvent" in window,
445         pushState: !!history.pushState,
446         mediaquery: $.mobile.media('only all'),
447         cssPseudoElement: !!propExists('content'),
448         boxShadow: !!propExists('boxShadow') && !bb,
449         scrollTop: ("pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[0]) && !webos,
450         dynamicBaseTag: baseTagTest()
451 });
452
453 fakeBody.remove();
454
455 //for ruling out shadows via css
456 if( !$.support.boxShadow ){ $('html').addClass('ui-mobile-nosupport-boxshadow'); }
457
458 })( jQuery );/*
459 * jQuery Mobile Framework : events
460 * Copyright (c) jQuery Project
461 * Dual licensed under the MIT or GPL Version 2 licenses.
462 * http://jquery.org/license
463 */
464 (function($, undefined ) {
465
466 // add new event shortcuts
467 $.each( "touchstart touchmove touchend orientationchange tap taphold swipe swipeleft swiperight scrollstart scrollstop".split( " " ), function( i, name ) {
468         $.fn[ name ] = function( fn ) {
469                 return fn ? this.bind( name, fn ) : this.trigger( name );
470         };
471         $.attrFn[ name ] = true;
472 });
473
474 var supportTouch = $.support.touch,
475         scrollEvent = "touchmove scroll",
476         touchStartEvent = supportTouch ? "touchstart" : "mousedown",
477         touchStopEvent = supportTouch ? "touchend" : "mouseup",
478         touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
479
480 // also handles scrollstop
481 $.event.special.scrollstart = {
482         enabled: true,
483         
484         setup: function() {
485                 var thisObject = this,
486                         $this = $( thisObject ),
487                         scrolling,
488                         timer;
489                 
490                 function trigger( event, state ) {
491                         scrolling = state;
492                         var originalType = event.type;
493                         event.type = scrolling ? "scrollstart" : "scrollstop";
494                         $.event.handle.call( thisObject, event );
495                         event.type = originalType;
496                 }
497                 
498                 // iPhone triggers scroll after a small delay; use touchmove instead
499                 $this.bind( scrollEvent, function( event ) {
500                         if ( !$.event.special.scrollstart.enabled ) {
501                                 return;
502                         }
503                         
504                         if ( !scrolling ) {
505                                 trigger( event, true );
506                         }
507                         
508                         clearTimeout( timer );
509                         timer = setTimeout(function() {
510                                 trigger( event, false );
511                         }, 50 );
512                 });
513         }
514 };
515
516 // also handles taphold
517 $.event.special.tap = {
518         setup: function() {
519                 var thisObject = this,
520                         $this = $( thisObject );
521                 
522                 $this
523                         .bind( touchStartEvent, function( event ) {
524                                 if ( event.which && event.which !== 1 ) {
525                                         return;
526                                 }
527                                 
528                                 var moved = false,
529                                         touching = true,
530                                         origPos = [ event.pageX, event.pageY ],
531                                         originalType,
532                                         timer;
533                                 
534                                 function moveHandler() {
535                                         if ((Math.abs(origPos[0] - event.pageX) > 10) ||
536                                             (Math.abs(origPos[1] - event.pageY) > 10)) {
537                                             moved = true;
538                                         }
539                                 }
540                                 
541                                 timer = setTimeout(function() {
542                                         if ( touching && !moved ) {
543                                                 originalType = event.type;
544                                                 event.type = "taphold";
545                                                 $.event.handle.call( thisObject, event );
546                                                 event.type = originalType;
547                                         }
548                                 }, 750 );
549                                 
550                                 $this
551                                         .one( touchMoveEvent, moveHandler)
552                                         .one( touchStopEvent, function( event ) {
553                                                 $this.unbind( touchMoveEvent, moveHandler );
554                                                 clearTimeout( timer );
555                                                 touching = false;
556                                                 
557                                                 if ( !moved ) {
558                                                         originalType = event.type;
559                                                         event.type = "tap";
560                                                         $.event.handle.call( thisObject, event );
561                                                         event.type = originalType;
562                                                 }
563                                         });
564                         });
565         }
566 };
567
568 // also handles swipeleft, swiperight
569 $.event.special.swipe = {
570         setup: function() {
571                 var thisObject = this,
572                         $this = $( thisObject );
573                 
574                 $this
575                         .bind( touchStartEvent, function( event ) {
576                                 var data = event.originalEvent.touches ?
577                                                 event.originalEvent.touches[ 0 ] :
578                                                 event,
579                                         start = {
580                                                 time: (new Date).getTime(),
581                                                 coords: [ data.pageX, data.pageY ],
582                                                 origin: $( event.target )
583                                         },
584                                         stop;
585                                 
586                                 function moveHandler( event ) {
587                                         if ( !start ) {
588                                                 return;
589                                         }
590                                         
591                                         var data = event.originalEvent.touches ?
592                                                         event.originalEvent.touches[ 0 ] :
593                                                         event;
594                                         stop = {
595                                                         time: (new Date).getTime(),
596                                                         coords: [ data.pageX, data.pageY ]
597                                         };
598                                         
599                                         // prevent scrolling
600                                         if ( Math.abs( start.coords[0] - stop.coords[0] ) > 10 ) {
601                                                 event.preventDefault();
602                                         }
603                                 }
604                                 
605                                 $this
606                                         .bind( touchMoveEvent, moveHandler )
607                                         .one( touchStopEvent, function( event ) {
608                                                 $this.unbind( touchMoveEvent, moveHandler );
609                                                 if ( start && stop ) {
610                                                         if ( stop.time - start.time < 1000 && 
611                                                                         Math.abs( start.coords[0] - stop.coords[0]) > 30 &&
612                                                                         Math.abs( start.coords[1] - stop.coords[1]) < 75 ) {
613                                                                 start.origin
614                                                                 .trigger( "swipe" )
615                                                                 .trigger( start.coords[0] > stop.coords[0] ? "swipeleft" : "swiperight" );
616                                                         }
617                                                 }
618                                                 start = stop = undefined;
619                                         });
620                         });
621         }
622 };
623
624 (function($){
625         // "Cowboy" Ben Alman
626         
627         var win = $(window),
628                 special_event,
629                 get_orientation,
630                 last_orientation;
631         
632         $.event.special.orientationchange = special_event = {
633                 setup: function(){
634                         // If the event is supported natively, return false so that jQuery
635                         // will bind to the event using DOM methods.
636                         if ( $.support.orientation ) { return false; }
637                         
638                         // Get the current orientation to avoid initial double-triggering.
639                         last_orientation = get_orientation();
640                         
641                         // Because the orientationchange event doesn't exist, simulate the
642                         // event by testing window dimensions on resize.
643                         win.bind( "resize", handler );
644                 },
645                 teardown: function(){
646                         // If the event is not supported natively, return false so that
647                         // jQuery will unbind the event using DOM methods.
648                         if ( $.support.orientation ) { return false; }
649                         
650                         // Because the orientationchange event doesn't exist, unbind the
651                         // resize event handler.
652                         win.unbind( "resize", handler );
653                 },
654                 add: function( handleObj ) {
655                         // Save a reference to the bound event handler.
656                         var old_handler = handleObj.handler;
657                         
658                         handleObj.handler = function( event ) {
659                                 // Modify event object, adding the .orientation property.
660                                 event.orientation = get_orientation();
661                                 
662                                 // Call the originally-bound event handler and return its result.
663                                 return old_handler.apply( this, arguments );
664                         };
665                 }
666         };
667         
668         // If the event is not supported natively, this handler will be bound to
669         // the window resize event to simulate the orientationchange event.
670         function handler() {
671                 // Get the current orientation.
672                 var orientation = get_orientation();
673                 
674                 if ( orientation !== last_orientation ) {
675                         // The orientation has changed, so trigger the orientationchange event.
676                         last_orientation = orientation;
677                         win.trigger( "orientationchange" );
678                 }
679         };
680         
681         // Get the current page orientation. This method is exposed publicly, should it
682         // be needed, as jQuery.event.special.orientationchange.orientation()
683         special_event.orientation = get_orientation = function() {
684                 var elem = document.documentElement;
685                 return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape";
686         };
687         
688 })(jQuery);
689
690 $.each({
691         scrollstop: "scrollstart",
692         taphold: "tap",
693         swipeleft: "swipe",
694         swiperight: "swipe"
695 }, function( event, sourceEvent ) {
696         $.event.special[ event ] = {
697                 setup: function() {
698                         $( this ).bind( sourceEvent, $.noop );
699                 }
700         };
701 });
702
703 })( jQuery );
704 /*!
705  * jQuery hashchange event - v1.3 - 7/21/2010
706  * http://benalman.com/projects/jquery-hashchange-plugin/
707  * 
708  * Copyright (c) 2010 "Cowboy" Ben Alman
709  * Dual licensed under the MIT and GPL licenses.
710  * http://benalman.com/about/license/
711  */
712
713 // Script: jQuery hashchange event
714 //
715 // *Version: 1.3, Last updated: 7/21/2010*
716 // 
717 // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
718 // GitHub       - http://github.com/cowboy/jquery-hashchange/
719 // Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
720 // (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
721 // 
722 // About: License
723 // 
724 // Copyright (c) 2010 "Cowboy" Ben Alman,
725 // Dual licensed under the MIT and GPL licenses.
726 // http://benalman.com/about/license/
727 // 
728 // About: Examples
729 // 
730 // These working examples, complete with fully commented code, illustrate a few
731 // ways in which this plugin can be used.
732 // 
733 // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
734 // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
735 // 
736 // About: Support and Testing
737 // 
738 // Information about what version or versions of jQuery this plugin has been
739 // tested with, what browsers it has been tested in, and where the unit tests
740 // reside (so you can test it yourself).
741 // 
742 // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
743 // Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
744 //                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
745 // Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
746 // 
747 // About: Known issues
748 // 
749 // While this jQuery hashchange event implementation is quite stable and
750 // robust, there are a few unfortunate browser bugs surrounding expected
751 // hashchange event-based behaviors, independent of any JavaScript
752 // window.onhashchange abstraction. See the following examples for more
753 // information:
754 // 
755 // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
756 // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
757 // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
758 // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
759 // 
760 // Also note that should a browser natively support the window.onhashchange 
761 // event, but not report that it does, the fallback polling loop will be used.
762 // 
763 // About: Release History
764 // 
765 // 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
766 //         "removable" for mobile-only development. Added IE6/7 document.title
767 //         support. Attempted to make Iframe as hidden as possible by using
768 //         techniques from http://www.paciellogroup.com/blog/?p=604. Added 
769 //         support for the "shortcut" format $(window).hashchange( fn ) and
770 //         $(window).hashchange() like jQuery provides for built-in events.
771 //         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
772 //         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
773 //         and <jQuery.fn.hashchange.src> properties plus document-domain.html
774 //         file to address access denied issues when setting document.domain in
775 //         IE6/7.
776 // 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
777 //         from a page on another domain would cause an error in Safari 4. Also,
778 //         IE6/7 Iframe is now inserted after the body (this actually works),
779 //         which prevents the page from scrolling when the event is first bound.
780 //         Event can also now be bound before DOM ready, but it won't be usable
781 //         before then in IE6/7.
782 // 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
783 //         where browser version is incorrectly reported as 8.0, despite
784 //         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
785 // 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
786 //         window.onhashchange functionality into a separate plugin for users
787 //         who want just the basic event & back button support, without all the
788 //         extra awesomeness that BBQ provides. This plugin will be included as
789 //         part of jQuery BBQ, but also be available separately.
790
791 (function($,window,undefined){
792   '$:nomunge'; // Used by YUI compressor.
793   
794   // Reused string.
795   var str_hashchange = 'hashchange',
796     
797     // Method / object references.
798     doc = document,
799     fake_onhashchange,
800     special = $.event.special,
801     
802     // Does the browser support window.onhashchange? Note that IE8 running in
803     // IE7 compatibility mode reports true for 'onhashchange' in window, even
804     // though the event isn't supported, so also test document.documentMode.
805     doc_mode = doc.documentMode,
806     supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
807   
808   // Get location.hash (or what you'd expect location.hash to be) sans any
809   // leading #. Thanks for making this necessary, Firefox!
810   function get_fragment( url ) {
811     url = url || location.href;
812     return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
813   };
814   
815   // Method: jQuery.fn.hashchange
816   // 
817   // Bind a handler to the window.onhashchange event or trigger all bound
818   // window.onhashchange event handlers. This behavior is consistent with
819   // jQuery's built-in event handlers.
820   // 
821   // Usage:
822   // 
823   // > jQuery(window).hashchange( [ handler ] );
824   // 
825   // Arguments:
826   // 
827   //  handler - (Function) Optional handler to be bound to the hashchange
828   //    event. This is a "shortcut" for the more verbose form:
829   //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
830   //    all bound window.onhashchange event handlers will be triggered. This
831   //    is a shortcut for the more verbose
832   //    jQuery(window).trigger( 'hashchange' ). These forms are described in
833   //    the <hashchange event> section.
834   // 
835   // Returns:
836   // 
837   //  (jQuery) The initial jQuery collection of elements.
838   
839   // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
840   // $(elem).hashchange() for triggering, like jQuery does for built-in events.
841   $.fn[ str_hashchange ] = function( fn ) {
842     return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
843   };
844   
845   // Property: jQuery.fn.hashchange.delay
846   // 
847   // The numeric interval (in milliseconds) at which the <hashchange event>
848   // polling loop executes. Defaults to 50.
849   
850   // Property: jQuery.fn.hashchange.domain
851   // 
852   // If you're setting document.domain in your JavaScript, and you want hash
853   // history to work in IE6/7, not only must this property be set, but you must
854   // also set document.domain BEFORE jQuery is loaded into the page. This
855   // property is only applicable if you are supporting IE6/7 (or IE8 operating
856   // in "IE7 compatibility" mode).
857   // 
858   // In addition, the <jQuery.fn.hashchange.src> property must be set to the
859   // path of the included "document-domain.html" file, which can be renamed or
860   // modified if necessary (note that the document.domain specified must be the
861   // same in both your main JavaScript as well as in this file).
862   // 
863   // Usage:
864   // 
865   // jQuery.fn.hashchange.domain = document.domain;
866   
867   // Property: jQuery.fn.hashchange.src
868   // 
869   // If, for some reason, you need to specify an Iframe src file (for example,
870   // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
871   // do so using this property. Note that when using this property, history
872   // won't be recorded in IE6/7 until the Iframe src file loads. This property
873   // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
874   // compatibility" mode).
875   // 
876   // Usage:
877   // 
878   // jQuery.fn.hashchange.src = 'path/to/file.html';
879   
880   $.fn[ str_hashchange ].delay = 50;
881   /*
882   $.fn[ str_hashchange ].domain = null;
883   $.fn[ str_hashchange ].src = null;
884   */
885   
886   // Event: hashchange event
887   // 
888   // Fired when location.hash changes. In browsers that support it, the native
889   // HTML5 window.onhashchange event is used, otherwise a polling loop is
890   // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
891   // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
892   // compatibility" mode), a hidden Iframe is created to allow the back button
893   // and hash-based history to work.
894   // 
895   // Usage as described in <jQuery.fn.hashchange>:
896   // 
897   // > // Bind an event handler.
898   // > jQuery(window).hashchange( function(e) {
899   // >   var hash = location.hash;
900   // >   ...
901   // > });
902   // > 
903   // > // Manually trigger the event handler.
904   // > jQuery(window).hashchange();
905   // 
906   // A more verbose usage that allows for event namespacing:
907   // 
908   // > // Bind an event handler.
909   // > jQuery(window).bind( 'hashchange', function(e) {
910   // >   var hash = location.hash;
911   // >   ...
912   // > });
913   // > 
914   // > // Manually trigger the event handler.
915   // > jQuery(window).trigger( 'hashchange' );
916   // 
917   // Additional Notes:
918   // 
919   // * The polling loop and Iframe are not created until at least one handler
920   //   is actually bound to the 'hashchange' event.
921   // * If you need the bound handler(s) to execute immediately, in cases where
922   //   a location.hash exists on page load, via bookmark or page refresh for
923   //   example, use jQuery(window).hashchange() or the more verbose 
924   //   jQuery(window).trigger( 'hashchange' ).
925   // * The event can be bound before DOM ready, but since it won't be usable
926   //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
927   //   to bind it inside a DOM ready handler.
928   
929   // Override existing $.event.special.hashchange methods (allowing this plugin
930   // to be defined after jQuery BBQ in BBQ's source code).
931   special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
932     
933     // Called only when the first 'hashchange' event is bound to window.
934     setup: function() {
935       // If window.onhashchange is supported natively, there's nothing to do..
936       if ( supports_onhashchange ) { return false; }
937       
938       // Otherwise, we need to create our own. And we don't want to call this
939       // until the user binds to the event, just in case they never do, since it
940       // will create a polling loop and possibly even a hidden Iframe.
941       $( fake_onhashchange.start );
942     },
943     
944     // Called only when the last 'hashchange' event is unbound from window.
945     teardown: function() {
946       // If window.onhashchange is supported natively, there's nothing to do..
947       if ( supports_onhashchange ) { return false; }
948       
949       // Otherwise, we need to stop ours (if possible).
950       $( fake_onhashchange.stop );
951     }
952     
953   });
954   
955   // fake_onhashchange does all the work of triggering the window.onhashchange
956   // event for browsers that don't natively support it, including creating a
957   // polling loop to watch for hash changes and in IE 6/7 creating a hidden
958   // Iframe to enable back and forward.
959   fake_onhashchange = (function(){
960     var self = {},
961       timeout_id,
962       
963       // Remember the initial hash so it doesn't get triggered immediately.
964       last_hash = get_fragment(),
965       
966       fn_retval = function(val){ return val; },
967       history_set = fn_retval,
968       history_get = fn_retval;
969     
970     // Start the polling loop.
971     self.start = function() {
972       timeout_id || poll();
973     };
974     
975     // Stop the polling loop.
976     self.stop = function() {
977       timeout_id && clearTimeout( timeout_id );
978       timeout_id = undefined;
979     };
980     
981     // This polling loop checks every $.fn.hashchange.delay milliseconds to see
982     // if location.hash has changed, and triggers the 'hashchange' event on
983     // window when necessary.
984     function poll() {
985       var hash = get_fragment(),
986         history_hash = history_get( last_hash );
987       
988       if ( hash !== last_hash ) {
989         history_set( last_hash = hash, history_hash );
990         
991         $(window).trigger( str_hashchange );
992         
993       } else if ( history_hash !== last_hash ) {
994         location.href = location.href.replace( /#.*/, '' ) + history_hash;
995       }
996       
997       timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
998     };
999     
1000     // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1001     // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
1002     // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
1003     $.browser.msie && !supports_onhashchange && (function(){
1004       // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
1005       // when running in "IE7 compatibility" mode.
1006       
1007       var iframe,
1008         iframe_src;
1009       
1010       // When the event is bound and polling starts in IE 6/7, create a hidden
1011       // Iframe for history handling.
1012       self.start = function(){
1013         if ( !iframe ) {
1014           iframe_src = $.fn[ str_hashchange ].src;
1015           iframe_src = iframe_src && iframe_src + get_fragment();
1016           
1017           // Create hidden Iframe. Attempt to make Iframe as hidden as possible
1018           // by using techniques from http://www.paciellogroup.com/blog/?p=604.
1019           iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
1020             
1021             // When Iframe has completely loaded, initialize the history and
1022             // start polling.
1023             .one( 'load', function(){
1024               iframe_src || history_set( get_fragment() );
1025               poll();
1026             })
1027             
1028             // Load Iframe src if specified, otherwise nothing.
1029             .attr( 'src', iframe_src || 'javascript:0' )
1030             
1031             // Append Iframe after the end of the body to prevent unnecessary
1032             // initial page scrolling (yes, this works).
1033             .insertAfter( 'body' )[0].contentWindow;
1034           
1035           // Whenever `document.title` changes, update the Iframe's title to
1036           // prettify the back/next history menu entries. Since IE sometimes
1037           // errors with "Unspecified error" the very first time this is set
1038           // (yes, very useful) wrap this with a try/catch block.
1039           doc.onpropertychange = function(){
1040             try {
1041               if ( event.propertyName === 'title' ) {
1042                 iframe.document.title = doc.title;
1043               }
1044             } catch(e) {}
1045           };
1046           
1047         }
1048       };
1049       
1050       // Override the "stop" method since an IE6/7 Iframe was created. Even
1051       // if there are no longer any bound event handlers, the polling loop
1052       // is still necessary for back/next to work at all!
1053       self.stop = fn_retval;
1054       
1055       // Get history by looking at the hidden Iframe's location.hash.
1056       history_get = function() {
1057         return get_fragment( iframe.location.href );
1058       };
1059       
1060       // Set a new history item by opening and then closing the Iframe
1061       // document, *then* setting its location.hash. If document.domain has
1062       // been set, update that as well.
1063       history_set = function( hash, history_hash ) {
1064         var iframe_doc = iframe.document,
1065           domain = $.fn[ str_hashchange ].domain;
1066         
1067         if ( hash !== history_hash ) {
1068           // Update Iframe with any initial `document.title` that might be set.
1069           iframe_doc.title = doc.title;
1070           
1071           // Opening the Iframe's document after it has been closed is what
1072           // actually adds a history entry.
1073           iframe_doc.open();
1074           
1075           // Set document.domain for the Iframe document as well, if necessary.
1076           domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
1077           
1078           iframe_doc.close();
1079           
1080           // Update the Iframe's hash, for great justice.
1081           iframe.location.hash = hash;
1082         }
1083       };
1084       
1085     })();
1086     // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1087     // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
1088     // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1089     
1090     return self;
1091   })();
1092   
1093 })(jQuery,this);
1094 /*!
1095  * jQuery Mobile v@VERSION
1096  * http://jquerymobile.com/
1097  *
1098  * Copyright 2010, jQuery Project
1099  * Dual licensed under the MIT or GPL Version 2 licenses.
1100  * http://jquery.org/license
1101  */
1102
1103 (function( $, window, undefined ) {
1104         
1105         //jQuery.mobile configurable options
1106         $.extend( $.mobile, {
1107                 
1108                 //define the url parameter used for referencing widget-generated sub-pages. 
1109                 //Translates to to example.html&ui-page=subpageIdentifier
1110                 //hash segment before &ui-page= is used to make Ajax request
1111                 subPageUrlKey: 'ui-page',
1112                 
1113                 //anchor links with a data-rel, or pages with a data-role, that match these selectors will be untrackable in history 
1114                 //(no change in URL, not bookmarkable)
1115                 nonHistorySelectors: 'dialog',
1116                 
1117                 //class assigned to page currently in view, and during transitions
1118                 activePageClass: 'ui-page-active',
1119                 
1120                 //class used for "active" button state, from CSS framework
1121                 activeBtnClass: 'ui-btn-active',
1122                 
1123                 //automatically handle link clicks through Ajax, when possible
1124                 ajaxLinksEnabled: true,
1125                 
1126                 //automatically handle form submissions through Ajax, when possible
1127                 ajaxFormsEnabled: true,
1128                 
1129                 //set default transition - 'none' for no transitions
1130                 defaultTransition: 'slide',
1131                 
1132                 //show loading message during Ajax requests
1133                 //if false, message will not appear, but loading classes will still be toggled on html el
1134                 loadingMessage: "loading",
1135                 
1136                 //configure meta viewport tag's content attr:
1137                 metaViewportContent: "width=device-width, minimum-scale=1, maximum-scale=1",
1138                 
1139                 //support conditions that must be met in order to proceed
1140                 gradeA: function(){
1141                         return $.support.mediaquery;
1142                 }
1143         });
1144         
1145         
1146 //trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
1147         $( window.document ).trigger('mobileinit');
1148
1149
1150 //support conditions    
1151         //if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
1152         //otherwise, proceed with the enhancements
1153         if ( !$.mobile.gradeA() ) {
1154                 return;
1155         }       
1156
1157
1158 //define vars for interal use
1159         var $window = $(window),
1160                 $html = $('html'),
1161                 $head = $('head'),
1162                 
1163                 //loading div which appears during Ajax requests
1164                 //will not appear if $.mobile.loadingMessage is false
1165                 $loader = $.mobile.loadingMessage ? 
1166                         $('<div class="ui-loader ui-body-a ui-corner-all">'+
1167                                                 '<span class="ui-icon ui-icon-loading spin"></span>'+
1168                                                 '<h1>'+ $.mobile.loadingMessage +'</h1>'+
1169                                         '</div>')
1170                         : undefined;
1171
1172
1173 //add mobile, initial load "rendering" classes to docEl
1174         $html.addClass('ui-mobile ui-mobile-rendering');                        
1175                 
1176                         
1177 //define & prepend meta viewport tag, if content is defined     
1178         $.mobile.metaViewportContent ? $("<meta>", { name: "viewport", content: $.mobile.metaViewportContent}).prependTo( $head ) : undefined;                  
1179
1180         
1181 //expose some core utilities
1182         $.extend($.mobile, {
1183         
1184                 // turn on/off page loading message.
1185                 pageLoading: function ( done ) {
1186                         if ( done ) {
1187                                 $html.removeClass( "ui-loading" );
1188                         } else {
1189                                 if( $.mobile.loadingMessage ){
1190                                         $loader.appendTo($.mobile.pageContainer).css({top: $(window).scrollTop() + 75});
1191                                 }       
1192                                 $html.addClass( "ui-loading" );
1193                         }
1194                 },
1195                 
1196                 //scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value
1197                 silentScroll: function( ypos ) {
1198                         // prevent scrollstart and scrollstop events
1199                         $.event.special.scrollstart.enabled = false;
1200                         setTimeout(function() {
1201                                 window.scrollTo( 0, ypos || 0 );
1202                         },20);  
1203                         setTimeout(function() {
1204                                 $.event.special.scrollstart.enabled = true;
1205                         }, 150 );
1206                 }
1207         });     
1208         
1209         
1210 //dom-ready inits
1211         $(function(){
1212                 
1213                 //find present pages            
1214                 var $pages = $("[data-role='page']");
1215                 
1216                 //set up active page
1217                 $.mobile.startPage = $.mobile.activePage = $pages.first();
1218                 
1219                 //set page container
1220                 $.mobile.pageContainer = $.mobile.startPage.parent().addClass('ui-mobile-viewport');
1221                 
1222                 //cue page loading message
1223                 $.mobile.pageLoading();
1224                 
1225                 //initialize all pages present
1226                 $pages.page();
1227                 
1228                 //trigger a new hashchange, hash or not
1229                 $window.trigger( "hashchange", [ true ] );
1230                 
1231                 //remove rendering class
1232                 $html.removeClass('ui-mobile-rendering');
1233         });
1234         
1235         
1236 //window load event     
1237         //hide iOS browser chrome on load
1238         $window.load( $.mobile.silentScroll );  
1239         
1240 })( jQuery, this );
1241 /*
1242 * jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt, 
1243 * Copyright (c) jQuery Project
1244 * Dual licensed under the MIT or GPL Version 2 licenses.
1245 * http://jquery.org/license
1246 */ 
1247 (function($, undefined ) {
1248
1249         //define vars for interal use
1250         var $window = $(window),
1251                 $html = $('html'),
1252                 $head = $('head'),
1253
1254                 //url path helpers for use in relative url management
1255                 path = {
1256                         
1257                         //get path from current hash, or from a file path
1258                         get: function( newPath ){
1259                                 if( newPath == undefined ){
1260                                         newPath = location.hash;
1261                                 }
1262                                 newPath = newPath.replace(/#/,'').split('/');
1263                                 newPath.pop();
1264                                 return newPath.join('/') + (newPath.length ? '/' : '');
1265                         },
1266                         
1267                         //return the substring of a filepath before the sub-page key, for making a server request 
1268                         getFilePath: function( path ){
1269                                 var splitkey = '&' + $.mobile.subPageUrlKey;
1270                                 return path.indexOf( splitkey ) > -1 ? path.split( splitkey )[0] : path;
1271                         },
1272                         
1273                         set: function( path, disableListening){
1274                                 if(disableListening) { hashListener = false; }
1275                                 location.hash = path;
1276                         },
1277                         
1278                         //location pathname from intial directory request
1279                         origin: null,
1280                         
1281                         setOrigin: function(){
1282                                 path.origin = path.get( location.protocol + '//' + location.host + location.pathname );
1283                         }
1284                 },
1285                                 
1286                 //base element management, defined depending on dynamic base tag support
1287                 base = $.support.dynamicBaseTag ? {
1288                 
1289                         //define base element, for use in routing asset urls that are referenced in Ajax-requested markup
1290                         element: $("<base>", { href: path.origin }).prependTo( $head ),
1291                         
1292                         //set the generated BASE element's href attribute to a new page's base path
1293                         set: function( href ){
1294                                 base.element.attr('href', path.origin + path.get( href ));
1295                         },
1296                         
1297                         //set the generated BASE element's href attribute to a new page's base path
1298                         reset: function(){
1299                                 base.element.attr('href', path.origin );
1300                         }
1301                         
1302                 } : undefined,  
1303
1304                 
1305                 //will be defined when a link is clicked and given an active class
1306                 $activeClickedLink = null,
1307                 
1308                 //array of pages that are visited during a single page load
1309                 //length will grow as pages are visited, and shrink as "back" link/button is clicked
1310                 //each item has a url (string matches ID), and transition (saved for reuse when "back" link/button is clicked)
1311                 urlStack = [ {
1312                         url: location.hash.replace( /^#/, "" ),
1313                         transition: undefined
1314                 } ],
1315                 
1316                 //define first selector to receive focus when a page is shown
1317                 focusable = "[tabindex],a,button:visible,select:visible,input",
1318                 
1319                 //contains role for next page, if defined on clicked link via data-rel
1320                 nextPageRole = null,
1321                 
1322                 //enable/disable hashchange event listener
1323                 //toggled internally when location.hash is updated to match the url of a successful page load
1324                 hashListener = true;    
1325                 
1326                 //set location pathname from intial directory request
1327                 path.setOrigin();
1328         
1329
1330 /* 
1331         internal utility functions
1332 --------------------------------------*/        
1333
1334
1335         //direct focus to the page title, or otherwise first focusable element
1336         function reFocus( page ){
1337                 var pageTitle = page.find( ".ui-title:eq(0)" );
1338                 if( pageTitle.length ){
1339                         pageTitle.focus();
1340                 }
1341                 else{
1342                         page.find( focusable ).eq(0).focus();
1343                 }
1344         };
1345         
1346         //remove active classes after page transition or error
1347         function removeActiveLinkClass( forceRemoval ){
1348                 if( !!$activeClickedLink && (!$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval )){
1349                         $activeClickedLink.removeClass( $.mobile.activeBtnClass );
1350                 }
1351                 $activeClickedLink = null;
1352         };
1353
1354         
1355         //animation complete callback
1356         $.fn.animationComplete = function( callback ){
1357                 if($.support.cssTransitions){
1358                         return $(this).one('webkitAnimationEnd', callback);
1359                 }
1360                 else{
1361                         callback();
1362                 }
1363         };      
1364
1365
1366
1367 /* exposed $.mobile methods      */
1368
1369         //update location.hash, with or without triggering hashchange event
1370         $.mobile.updateHash = path.set;
1371         
1372         //url stack, useful when plugins need to be aware of previous pages viewed
1373         $.mobile.urlStack = urlStack;           
1374
1375         // changepage function 
1376         $.mobile.changePage = function( to, transition, back, changeHash){
1377
1378                 //from is always the currently viewed page
1379                 var toIsArray = $.type(to) === "array",
1380                         from = toIsArray ? to[0] : $.mobile.activePage,
1381                         to = toIsArray ? to[1] : to,
1382                         url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null,
1383                         data = undefined,
1384                         type = 'get',
1385                         isFormRequest = false,
1386                         duplicateCachedPage = null,
1387                         back = (back !== undefined) ? back : ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ),
1388                         transition = (transition !== undefined) ? transition : $.mobile.defaultTransition;
1389                 
1390                 if( $.type(to) === "object" && to.url ){
1391                         url = to.url,
1392                         data = to.data,
1393                         type = to.type,
1394                         isFormRequest = true;
1395                         //make get requests bookmarkable
1396                         if( data && type == 'get' ){
1397                                 url += "?" + data;
1398                                 data = undefined;
1399                         }
1400                 }
1401                         
1402                 //reset base to pathname for new request
1403                 if(base){ base.reset(); }
1404                         
1405                 // if the new href is the same as the previous one
1406                 if ( back ) {
1407                         var pop = urlStack.pop();
1408                         if( pop ){
1409                                 transition = pop.transition;
1410                         }
1411                 } else {
1412                         urlStack.push({ url: url, transition: transition });
1413                 }
1414                 
1415                 //function for transitioning between two existing pages
1416                 function transitionPages() {
1417                                 
1418                         //kill the keyboard
1419                         $( window.document.activeElement ).blur();
1420                         
1421                         //get current scroll distance
1422                         var currScroll = $window.scrollTop();
1423                         
1424                         //set as data for returning to that spot
1425                         from.data('lastScroll', currScroll);
1426                         
1427                         //trigger before show/hide events
1428                         from.data("page")._trigger("beforehide", {nextPage: to});
1429                         to.data("page")._trigger("beforeshow", {prevPage: from});
1430                         
1431                         function loadComplete(){
1432                                 $.mobile.pageLoading( true );
1433                                 
1434                                 reFocus( to );
1435                                 
1436                                 if( changeHash !== false && url ){
1437                                         path.set(url, (back !== true));
1438                                 }
1439                                 removeActiveLinkClass();
1440                                 
1441                                 //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
1442                                 if( duplicateCachedPage != null ){
1443                                         duplicateCachedPage.remove();
1444                                 }
1445                                 
1446                                 //jump to top or prev scroll, if set
1447                                 $.mobile.silentScroll( to.data( 'lastScroll' ) );
1448                                 
1449                                 //trigger show/hide events, allow preventing focus change through return false          
1450                                 if( from.data("page")._trigger("hide", null, {nextPage: to}) !== false && to.data("page")._trigger("show", null, {prevPage: from}) !== false ){
1451                                         $.mobile.activePage = to;
1452                                 }
1453                         };
1454                         
1455                         if(transition && (transition !== 'none')){      
1456                                 $.mobile.pageContainer.addClass('ui-mobile-viewport-transitioning');
1457                                 // animate in / out
1458                                 from.addClass( transition + " out " + ( back ? "reverse" : "" ) );
1459                                 to.addClass( $.mobile.activePageClass + " " + transition +
1460                                         " in " + ( back ? "reverse" : "" ) );
1461                                 
1462                                 // callback - remove classes, etc
1463                                 to.animationComplete(function() {
1464                                         from.add( to ).removeClass("out in reverse " + transition );
1465                                         from.removeClass( $.mobile.activePageClass );
1466                                         loadComplete();
1467                                         $.mobile.pageContainer.removeClass('ui-mobile-viewport-transitioning');
1468                                 });
1469                         }
1470                         else{
1471                                 from.removeClass( $.mobile.activePageClass );
1472                                 to.addClass( $.mobile.activePageClass );
1473                                 loadComplete();
1474                         }
1475                 };
1476                 
1477                 //shared page enhancements
1478                 function enhancePage(){
1479                         
1480                         //set next page role, if defined
1481                         if ( nextPageRole ) {
1482                                 to.attr( "data-role", nextPageRole );
1483                                 nextPageRole = undefined;
1484                         }
1485                         
1486                         //run page plugin                       
1487                         to.page();
1488                 };
1489
1490                 //if url is a string
1491                 if( url ){
1492                         to = $( "[id='" + url + "']" ),
1493                         fileUrl = path.getFilePath(url);
1494                 }
1495                 else{ //find base url of element, if avail
1496                         var toID = to.attr('id'),
1497                                 toIDfileurl = path.getFilePath(toID);
1498                                 
1499                         if(toID != toIDfileurl){
1500                                 fileUrl = toIDfileurl;
1501                         }       
1502                 }
1503                 
1504                 // find the "to" page, either locally existing in the dom or by creating it through ajax
1505                 if ( to.length && !isFormRequest ) {
1506                         if( fileUrl && base ){
1507                                 base.set( fileUrl );
1508                         }       
1509                         enhancePage();
1510                         transitionPages();
1511                 } else { 
1512                 
1513                         //if to exists in DOM, save a reference to it in duplicateCachedPage for removal after page change
1514                         if( to.length ){
1515                                 duplicateCachedPage = to;
1516                         }
1517                         
1518                         $.mobile.pageLoading();
1519
1520                         $.ajax({
1521                                 url: fileUrl,
1522                                 type: type,
1523                                 data: data,
1524                                 success: function( html ) {
1525                                 
1526                                         if(base){ base.set(fileUrl); }
1527                                         
1528                                         var all = $("<div></div>");
1529                                         //workaround to allow scripts to execute when included in page divs
1530                                         all.get(0).innerHTML = html;
1531                                         to = all.find('[data-role="page"]');
1532                                         
1533                                         //rewrite src and href attrs to use a base url
1534                                         if( !$.support.dynamicBaseTag ){
1535                                                 var newPath = path.get( fileUrl );
1536                                                 to.find('[src],link[href]').each(function(){
1537                                                         var thisAttr = $(this).is('[href]') ? 'href' : 'src',
1538                                                                 thisUrl = $(this).attr(thisAttr);
1539                                                         
1540                                                         //if full path exists and is same, chop it - helps IE out
1541                                                         thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' );
1542                                                                 
1543                                                         if( !/^(\w+:|#|\/)/.test(thisUrl) ){
1544                                                                 $(this).attr(thisAttr, newPath + thisUrl);
1545                                                         }
1546                                                 });
1547                                         }
1548                                         
1549                                         //preserve ID on a retrieved page
1550                                         if ( to.attr('id') ) {
1551                                                 //wrap page and transfer data-attrs if it has an ID
1552                                                 var copyAttrs = ['data-role', 'data-theme', 'data-fullscreen'], //TODO: more page-level attrs?
1553                                                         wrapper = to.wrap( "<div>" ).parent();
1554                                                         
1555                                                 $.each(copyAttrs,function(i){
1556                                                         if( to.attr( copyAttrs[ i ] ) ){
1557                                                                 wrapper.attr( copyAttrs[ i ], to.attr( copyAttrs[ i ] ) );
1558                                                                 to.removeAttr( copyAttrs[ i ] );
1559                                                         }
1560                                                 });     
1561                                                 to = wrapper;
1562                                         }
1563
1564                                         to
1565                                                 .attr( "id", fileUrl )
1566                                                 .appendTo( $.mobile.pageContainer );
1567                                                 
1568                                         enhancePage();
1569                                         transitionPages();
1570                                 },
1571                                 error: function() {
1572                                         $.mobile.pageLoading( true );
1573                                         removeActiveLinkClass(true);
1574                                         $("<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>Error Loading Page</h1></div>")
1575                                                 .css({ "display": "block", "opacity": 0.96, "top": $(window).scrollTop() + 100 })
1576                                                 .appendTo( $.mobile.pageContainer )
1577                                                 .delay( 800 )
1578                                                 .fadeOut( 400, function(){
1579                                                         $(this).remove();
1580                                                 });
1581                                 }
1582                         });
1583                 }
1584
1585         };
1586
1587
1588
1589         
1590 /* Event Bindings - hashchange, submit, and click */    
1591         
1592         //bind to form submit events, handle with Ajax
1593         $('form').live('submit', function(event){
1594                 if( !$.mobile.ajaxFormsEnabled ){ return; }
1595                 
1596                 var type = $(this).attr("method"),
1597                         url = $(this).attr( "action" ).replace( location.protocol + "//" + location.host, "");  
1598                 
1599                 //external submits use regular HTTP
1600                 if( /^(:?\w+:)/.test( url ) ){
1601                         return;
1602                 }       
1603                 
1604                 //if it's a relative href, prefix href with base url
1605                 if( url.indexOf('/') && url.indexOf('#') !== 0 ){
1606                         url = path.get() + url;
1607                 }
1608                         
1609                 $.mobile.changePage({
1610                                 url: url,
1611                                 type: type,
1612                                 data: $(this).serialize()
1613                         },
1614                         undefined,
1615                         undefined,
1616                         true
1617                 );
1618                 event.preventDefault();
1619         });     
1620         
1621         
1622         //click routing - direct to HTTP or Ajax, accordingly
1623         $( "a" ).live( "click", function(event) {
1624                 if( !$.mobile.ajaxLinksEnabled ){ return; }
1625                 var $this = $(this),
1626                         //get href, remove same-domain protocol and host
1627                         href = $this.attr( "href" ).replace( location.protocol + "//" + location.host, ""),
1628                         //if target attr is specified, it's external, and we mimic _blank... for now
1629                         target = $this.is( "[target]" ),
1630                         //if it still starts with a protocol, it's external, or could be :mailto, etc
1631                         external = target || /^(:?\w+:)/.test( href ) || $this.is( "[rel=external]" ),
1632                         target = $this.is( "[target]" );
1633
1634                 if( href === '#' ){
1635                         //for links created purely for interaction - ignore
1636                         return false;
1637                 }
1638                 
1639                 $activeClickedLink = $this.closest( ".ui-btn" ).addClass( $.mobile.activeBtnClass );
1640                 
1641                 if( external || !$.mobile.ajaxLinksEnabled ){
1642                         //remove active link class if external
1643                         removeActiveLinkClass(true);
1644                         
1645                         //deliberately redirect, in case click was triggered
1646                         if( target ){
1647                                 window.open(href);
1648                         }
1649                         else{
1650                                 location.href = href;
1651                         }
1652                 }
1653                 else {  
1654                         //use ajax
1655                         var transition = $this.data( "transition" ),
1656                                 back = $this.data( "back" ),
1657                                 changeHashOnSuccess = !$this.is( "[data-rel="+ $.mobile.nonHistorySelectors +"]" );
1658                                 
1659                         nextPageRole = $this.attr( "data-rel" );        
1660         
1661                         //if it's a relative href, prefix href with base url
1662                         if( href.indexOf('/') && href.indexOf('#') !== 0 ){
1663                                 href = path.get() + href;
1664                         }
1665                         
1666                         href.replace(/^#/,'');
1667                         
1668                         $.mobile.changePage(href, transition, back, changeHashOnSuccess);                       
1669                 }
1670                 event.preventDefault();
1671         });
1672                 
1673                 
1674         
1675         //hashchange event handler      
1676         $window.bind( "hashchange", function(e, triggered) {
1677                 if( !hashListener ){ 
1678                         hashListener = true;
1679                         return; 
1680                 } 
1681                 
1682                 if( $(".ui-page-active").is("[data-role=" + $.mobile.nonHistorySelectors + "]") ){
1683                         return;
1684                 }
1685                 
1686                 var to = location.hash,
1687                         transition = triggered ? false : undefined;
1688                         
1689                 //if to is defined, use it
1690                 if ( to ){
1691                         $.mobile.changePage( to, transition);
1692                 }
1693                 //there's no hash, the active page is not the start page, and it's not manually triggered hashchange
1694                 //we probably backed out to the first page visited
1695                 else if( $.mobile.activePage.length && $.mobile.startPage[0] !== $.mobile.activePage[0] && !triggered ) {
1696                         $.mobile.changePage( $.mobile.startPage, transition, true );
1697                 }
1698                 //probably the first page - show it
1699                 else{
1700                         $.mobile.startPage.trigger("pagebeforeshow", {prevPage: $('')});
1701                         $.mobile.startPage.addClass( $.mobile.activePageClass );
1702                         $.mobile.pageLoading( true );
1703                         
1704                         if( $.mobile.startPage.trigger("pageshow", {prevPage: $('')}) !== false ){
1705                                 reFocus($.mobile.startPage);
1706                         }
1707                 }
1708         });
1709 })( jQuery );/*
1710 * jQuery Mobile Framework : "page" plugin
1711 * Copyright (c) jQuery Project
1712 * Dual licensed under the MIT or GPL Version 2 licenses.
1713 * http://jquery.org/license
1714 */
1715 (function($, undefined ) {
1716
1717 $.widget( "mobile.page", $.mobile.widget, {
1718         options: {
1719                 backBtnText: "Back",
1720                 addBackBtn: true,
1721                 degradeInputs: {
1722                         color: false,
1723                         date: false,
1724                         datetime: false,
1725                         "datetime-local": false,
1726                         email: false,
1727                         month: false,
1728                         number: false,
1729                         range: "number",
1730                         search: true,
1731                         tel: false,
1732                         time: false,
1733                         url: false,
1734                         week: false
1735                 },
1736                 keepNative: null
1737         },
1738         
1739         _create: function() {
1740                 var $elem = this.element,
1741                         o = this.options;
1742                         
1743                 this.keepNative = "[data-role='none'], [data-role='nojs']" + (o.keepNative ? ", " + o.keepNative : "");         
1744
1745                 if ( this._trigger( "beforeCreate" ) === false ) {
1746                         return;
1747                 }
1748                 
1749                 //some of the form elements currently rely on the presence of ui-page and ui-content
1750                 // classes so we'll handle page and content roles outside of the main role processing
1751                 // loop below.
1752                 $elem.find( "[data-role='page'], [data-role='content']" ).andSelf().each(function() {
1753                         $(this).addClass( "ui-" + $(this).data( "role" ) );
1754                 });
1755                 
1756                 $elem.find( "[data-role='nojs']" ).addClass( "ui-nojs" );
1757
1758                 this._enchanceControls();
1759                 
1760                 // pre-find data els
1761                 var $dataEls = $elem.find( "[data-role]" ).andSelf().each(function() {
1762                         var $this = $( this ),
1763                                 role = $this.data( "role" ),
1764                                 theme = $this.data( "theme" );
1765                         
1766                         //apply theming and markup modifications to page,header,content,footer
1767                         if ( role === "header" || role === "footer" ) {
1768                                 $this.addClass( "ui-bar-" + (theme || $this.parent('[data-role=page]').data( "theme" ) || "a") );
1769                                 
1770                                 // add ARIA role
1771                                 $this.attr( "role", role === "header" ? "banner" : "contentinfo" );
1772                                 
1773                                 //right,left buttons
1774                                 var $headeranchors = $this.children( "a" ),
1775                                         leftbtn = $headeranchors.hasClass( "ui-btn-left" ),
1776                                         rightbtn = $headeranchors.hasClass( "ui-btn-right" );
1777                                 
1778                                 if ( !leftbtn ) {
1779                                         leftbtn = $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
1780                                 }
1781
1782                                 if ( !rightbtn ) {
1783                                         rightbtn = $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
1784                                 }
1785                                 
1786                                 // auto-add back btn on pages beyond first view
1787                                 if ( o.addBackBtn && role === "header" &&
1788                                                 ($.mobile.urlStack.length > 1 || $(".ui-page").length > 1) &&
1789                                                 !leftbtn && !$this.data( "noBackBtn" ) ) {
1790
1791                                         $( "<a href='#' class='ui-btn-left' data-icon='arrow-l'>"+ o.backBtnText +"</a>" )
1792                                                 .click(function() {
1793                                                         history.back();
1794                                                         return false;
1795                                                 })
1796                                                 .prependTo( $this );
1797                                 }
1798                                 
1799                                 //page title    
1800                                 $this.children( "h1, h2, h3, h4, h5, h6" )
1801                                         .addClass( "ui-title" )
1802                                         //regardless of h element number in src, it becomes h1 for the enhanced page
1803                                         .attr({ "tabindex": "0", "role": "heading", "aria-level": "1" });
1804
1805                         } else if ( role === "content" ) {
1806                                 if ( theme ) {
1807                                         $this.addClass( "ui-body-" + theme );
1808                                 }
1809
1810                                 // add ARIA role
1811                                 $this.attr( "role", "main" );
1812
1813                         } else if ( role === "page" ) {
1814                                 $this.addClass( "ui-body-" + (theme || "c") );
1815                         }
1816                         
1817                         switch(role) {
1818                                 case "header":
1819                                 case "footer":
1820                                 case "page":
1821                                 case "content":
1822                                         $this.addClass( "ui-" + role );
1823                                         break;
1824                                 case "collapsible":
1825                                 case "fieldcontain":
1826                                 case "navbar":
1827                                 case "listview":
1828                                 case "dialog":
1829                                         $this[ role ]();
1830                                         break;
1831                         }
1832                 });
1833                 
1834                 //links in bars, or those with data-role become buttons
1835                 $elem.find( "[data-role='button'], .ui-bar a, .ui-header a, .ui-footer a" )
1836                         .not( ".ui-btn" )
1837                         .not(this.keepNative)
1838                         .buttonMarkup();
1839
1840                 $elem   
1841                         .find("[data-role='controlgroup']")
1842                         .controlgroup();
1843                 
1844                 //links within content areas
1845                 $elem.find( "a:not(.ui-btn):not(.ui-link-inherit)" )
1846                         .not(this.keepNative)
1847                         .addClass( "ui-link" ); 
1848                 
1849                 //fix toolbars
1850                 $elem.fixHeaderFooter();
1851         },
1852         
1853         _enchanceControls: function() {
1854                 var o = this.options;
1855                         
1856                 // degrade inputs to avoid poorly implemented native functionality
1857                 this.element.find( "input" ).not(this.keepNative).each(function() {
1858                         var type = this.getAttribute( "type" ),
1859                                 optType = o.degradeInputs[ type ] || "text";
1860                         
1861                         if ( o.degradeInputs[ type ] ) {
1862                                 $( this ).replaceWith(
1863                                         $( "<div>" ).html( $(this).clone() ).html()
1864                                                 .replace( /type="([a-zA-Z]+)"/, "type="+ optType +" data-type='$1'" ) );
1865                         }
1866                 });
1867                 
1868                 // enchance form controls
1869                 this.element
1870                         .find( "[type='radio'], [type='checkbox']" )
1871                         .not(this.keepNative)
1872                         .checkboxradio();
1873
1874                 this.element
1875                         .find( "button, [type='button'], [type='submit'], [type='reset'], [type='image']" )
1876                         .not(this.keepNative)
1877                         .button();
1878
1879                 this.element
1880                         .find( "input, textarea" )
1881                         .not( "[type='radio'], [type='checkbox'], button, [type='button'], [type='submit'], [type='reset'], [type='image']" )
1882                         .not(this.keepNative)
1883                         .textinput();
1884
1885                 this.element
1886                         .find( "input, select" )
1887                         .not(this.keepNative)
1888                         .filter( "[data-role='slider'], [data-type='range']" )
1889                         .slider();
1890
1891                 this.element
1892                         .find( "select:not([data-role='slider'])" )
1893                         .not(this.keepNative)
1894                         .selectmenu();
1895         }
1896 });
1897
1898 })( jQuery );
1899 /*
1900  * jQuery UI Position @VERSION
1901  *
1902  * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
1903  * Dual licensed under the MIT or GPL Version 2 licenses.
1904  * http://jquery.org/license
1905  *
1906  * http://docs.jquery.com/UI/Position
1907  */
1908 (function( $, undefined ) {
1909
1910 $.ui = $.ui || {};
1911
1912 var horizontalPositions = /left|center|right/,
1913         verticalPositions = /top|center|bottom/,
1914         center = "center",
1915         _position = $.fn.position,
1916         _offset = $.fn.offset;
1917
1918 $.fn.position = function( options ) {
1919         if ( !options || !options.of ) {
1920                 return _position.apply( this, arguments );
1921         }
1922
1923         // make a copy, we don't want to modify arguments
1924         options = $.extend( {}, options );
1925
1926         var target = $( options.of ),
1927                 targetElem = target[0],
1928                 collision = ( options.collision || "flip" ).split( " " ),
1929                 offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
1930                 targetWidth,
1931                 targetHeight,
1932                 basePosition;
1933
1934         if ( targetElem.nodeType === 9 ) {
1935                 targetWidth = target.width();
1936                 targetHeight = target.height();
1937                 basePosition = { top: 0, left: 0 };
1938         // TODO: use $.isWindow() in 1.9
1939         } else if ( targetElem.setTimeout ) {
1940                 targetWidth = target.width();
1941                 targetHeight = target.height();
1942                 basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
1943         } else if ( targetElem.preventDefault ) {
1944                 // force left top to allow flipping
1945                 options.at = "left top";
1946                 targetWidth = targetHeight = 0;
1947                 basePosition = { top: options.of.pageY, left: options.of.pageX };
1948         } else {
1949                 targetWidth = target.outerWidth();
1950                 targetHeight = target.outerHeight();
1951                 basePosition = target.offset();
1952         }
1953
1954         // force my and at to have valid horizontal and veritcal positions
1955         // if a value is missing or invalid, it will be converted to center 
1956         $.each( [ "my", "at" ], function() {
1957                 var pos = ( options[this] || "" ).split( " " );
1958                 if ( pos.length === 1) {
1959                         pos = horizontalPositions.test( pos[0] ) ?
1960                                 pos.concat( [center] ) :
1961                                 verticalPositions.test( pos[0] ) ?
1962                                         [ center ].concat( pos ) :
1963                                         [ center, center ];
1964                 }
1965                 pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
1966                 pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
1967                 options[ this ] = pos;
1968         });
1969
1970         // normalize collision option
1971         if ( collision.length === 1 ) {
1972                 collision[ 1 ] = collision[ 0 ];
1973         }
1974
1975         // normalize offset option
1976         offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
1977         if ( offset.length === 1 ) {
1978                 offset[ 1 ] = offset[ 0 ];
1979         }
1980         offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
1981
1982         if ( options.at[0] === "right" ) {
1983                 basePosition.left += targetWidth;
1984         } else if (options.at[0] === center ) {
1985                 basePosition.left += targetWidth / 2;
1986         }
1987
1988         if ( options.at[1] === "bottom" ) {
1989                 basePosition.top += targetHeight;
1990         } else if ( options.at[1] === center ) {
1991                 basePosition.top += targetHeight / 2;
1992         }
1993
1994         basePosition.left += offset[ 0 ];
1995         basePosition.top += offset[ 1 ];
1996
1997         return this.each(function() {
1998                 var elem = $( this ),
1999                         elemWidth = elem.outerWidth(),
2000                         elemHeight = elem.outerHeight(),
2001                         marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
2002                         marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
2003                         collisionWidth = elemWidth + marginLeft +
2004                                 parseInt( $.curCSS( this, "marginRight", true ) ) || 0,
2005                         collisionHeight = elemHeight + marginTop +
2006                                 parseInt( $.curCSS( this, "marginBottom", true ) ) || 0,
2007                         position = $.extend( {}, basePosition ),
2008                         collisionPosition;
2009
2010                 if ( options.my[0] === "right" ) {
2011                         position.left -= elemWidth;
2012                 } else if ( options.my[0] === center ) {
2013                         position.left -= elemWidth / 2;
2014                 }
2015
2016                 if ( options.my[1] === "bottom" ) {
2017                         position.top -= elemHeight;
2018                 } else if ( options.my[1] === center ) {
2019                         position.top -= elemHeight / 2;
2020                 }
2021
2022                 // prevent fractions (see #5280)
2023                 position.left = parseInt( position.left );
2024                 position.top = parseInt( position.top );
2025
2026                 collisionPosition = {
2027                         left: position.left - marginLeft,
2028                         top: position.top - marginTop
2029                 };
2030
2031                 $.each( [ "left", "top" ], function( i, dir ) {
2032                         if ( $.ui.position[ collision[i] ] ) {
2033                                 $.ui.position[ collision[i] ][ dir ]( position, {
2034                                         targetWidth: targetWidth,
2035                                         targetHeight: targetHeight,
2036                                         elemWidth: elemWidth,
2037                                         elemHeight: elemHeight,
2038                                         collisionPosition: collisionPosition,
2039                                         collisionWidth: collisionWidth,
2040                                         collisionHeight: collisionHeight,
2041                                         offset: offset,
2042                                         my: options.my,
2043                                         at: options.at
2044                                 });
2045                         }
2046                 });
2047
2048                 if ( $.fn.bgiframe ) {
2049                         elem.bgiframe();
2050                 }
2051                 elem.offset( $.extend( position, { using: options.using } ) );
2052         });
2053 };
2054
2055 $.ui.position = {
2056         fit: {
2057                 left: function( position, data ) {
2058                         var win = $( window ),
2059                                 over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
2060                         position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
2061                 },
2062                 top: function( position, data ) {
2063                         var win = $( window ),
2064                                 over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
2065                         position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
2066                 }
2067         },
2068
2069         flip: {
2070                 left: function( position, data ) {
2071                         if ( data.at[0] === center ) {
2072                                 return;
2073                         }
2074                         var win = $( window ),
2075                                 over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
2076                                 myOffset = data.my[ 0 ] === "left" ?
2077                                         -data.elemWidth :
2078                                         data.my[ 0 ] === "right" ?
2079                                                 data.elemWidth :
2080                                                 0,
2081                                 atOffset = data.at[ 0 ] === "left" ?
2082                                         data.targetWidth :
2083                                         -data.targetWidth,
2084                                 offset = -2 * data.offset[ 0 ];
2085                         position.left += data.collisionPosition.left < 0 ?
2086                                 myOffset + atOffset + offset :
2087                                 over > 0 ?
2088                                         myOffset + atOffset + offset :
2089                                         0;
2090                 },
2091                 top: function( position, data ) {
2092                         if ( data.at[1] === center ) {
2093                                 return;
2094                         }
2095                         var win = $( window ),
2096                                 over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
2097                                 myOffset = data.my[ 1 ] === "top" ?
2098                                         -data.elemHeight :
2099                                         data.my[ 1 ] === "bottom" ?
2100                                                 data.elemHeight :
2101                                                 0,
2102                                 atOffset = data.at[ 1 ] === "top" ?
2103                                         data.targetHeight :
2104                                         -data.targetHeight,
2105                                 offset = -2 * data.offset[ 1 ];
2106                         position.top += data.collisionPosition.top < 0 ?
2107                                 myOffset + atOffset + offset :
2108                                 over > 0 ?
2109                                         myOffset + atOffset + offset :
2110                                         0;
2111                 }
2112         }
2113 };
2114
2115 // offset setter from jQuery 1.4
2116 if ( !$.offset.setOffset ) {
2117         $.offset.setOffset = function( elem, options ) {
2118                 // set position first, in-case top/left are set even on static elem
2119                 if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
2120                         elem.style.position = "relative";
2121                 }
2122                 var curElem   = $( elem ),
2123                         curOffset = curElem.offset(),
2124                         curTop    = parseInt( $.curCSS( elem, "top",  true ), 10 ) || 0,
2125                         curLeft   = parseInt( $.curCSS( elem, "left", true ), 10)  || 0,
2126                         props     = {
2127                                 top:  (options.top  - curOffset.top)  + curTop,
2128                                 left: (options.left - curOffset.left) + curLeft
2129                         };
2130                 
2131                 if ( 'using' in options ) {
2132                         options.using.call( elem, props );
2133                 } else {
2134                         curElem.css( props );
2135                 }
2136         };
2137
2138         $.fn.offset = function( options ) {
2139                 var elem = this[ 0 ];
2140                 if ( !elem || !elem.ownerDocument ) { return null; }
2141                 if ( options ) { 
2142                         return this.each(function() {
2143                                 $.offset.setOffset( this, options );
2144                         });
2145                 }
2146                 return _offset.call( this );
2147         };
2148 }
2149
2150 }( jQuery ));/*
2151 * jQuery Mobile Framework : "fixHeaderFooter" plugin - on-demand positioning for headers,footers
2152 * Copyright (c) jQuery Project
2153 * Dual licensed under the MIT or GPL Version 2 licenses.
2154 * http://jquery.org/license
2155 */
2156 (function($, undefined ) {
2157 $.fn.fixHeaderFooter = function(options){
2158         if( !$.support.scrollTop ){ return $(this); }
2159         return $(this).each(function(){
2160                 if( $(this).data('fullscreen') ){ $(this).addClass('ui-page-fullscreen'); }
2161                 $(this).find('.ui-header[data-position="fixed"]').addClass('ui-header-fixed ui-fixed-inline fade'); //should be slidedown
2162                 $(this).find('.ui-footer[data-position="fixed"]').addClass('ui-footer-fixed ui-fixed-inline fade'); //should be slideup         
2163         });
2164 };                              
2165
2166 //single controller for all showing,hiding,toggling             
2167 $.fixedToolbars = (function(){
2168         if( !$.support.scrollTop ){ return; }
2169         var currentstate = 'inline',
2170                 delayTimer,
2171                 ignoreTargets = 'a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed',
2172                 toolbarSelector = '.ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last',
2173                 stickyFooter, //for storing quick references to duplicate footers
2174                 supportTouch = $.support.touch,
2175                 touchStartEvent = supportTouch ? "touchstart" : "mousedown",
2176                 touchStopEvent = supportTouch ? "touchend" : "mouseup",
2177                 stateBefore = null,
2178                 scrollTriggered = false;
2179                 
2180         $(function() {
2181                 $(document)
2182                         .bind(touchStartEvent,function(event){
2183                                 if( $(event.target).closest(ignoreTargets).length ){ return; }
2184                                 stateBefore = currentstate;
2185                                 $.fixedToolbars.hide(true);
2186                         })
2187                         .bind('scrollstart',function(event){
2188                                 if( $(event.target).closest(ignoreTargets).length ){ return; } //because it could be a touchmove...
2189                                 scrollTriggered = true;
2190                                 if(stateBefore == null){ stateBefore = currentstate; }
2191                                 $.fixedToolbars.hide(true);
2192                         })
2193                         .bind(touchStopEvent,function(event){
2194                                 if( $(event.target).closest(ignoreTargets).length ){ return; }
2195                                 if( !scrollTriggered ){
2196                                         $.fixedToolbars.toggle(stateBefore);
2197                                         stateBefore = null;
2198                                 }
2199                         })
2200                         .bind('scrollstop',function(event){
2201                                 if( $(event.target).closest(ignoreTargets).length ){ return; }
2202                                 scrollTriggered = false;
2203                                 $.fixedToolbars.toggle( stateBefore == 'overlay' ? 'inline' : 'overlay' );
2204                                 stateBefore = null;
2205                         });
2206                 
2207                 //function to return another footer already in the dom with the same data-id
2208                 function findStickyFooter(el){
2209                         var thisFooter = el.find('[data-role="footer"]');
2210                         return $( '.ui-footer[data-id="'+ thisFooter.data('id') +'"]:not(.ui-footer-duplicate)' ).not(thisFooter);
2211                 }
2212                 
2213                 //before page is shown, check for duplicate footer
2214                 $('.ui-page').live('pagebeforeshow', function(event, ui){
2215                         stickyFooter = findStickyFooter( $(event.target) );
2216                         if( stickyFooter.length ){
2217                                 //if the existing footer is the first of its kind, create a placeholder before stealing it 
2218                                 if( stickyFooter.parents('.ui-page:eq(0)').find('.ui-footer[data-id="'+ stickyFooter.data('id') +'"]').length == 1 ){
2219                                         stickyFooter.before( stickyFooter.clone().addClass('ui-footer-duplicate') );
2220                                 }
2221                                 $(event.target).find('[data-role="footer"]').addClass('ui-footer-duplicate');
2222                                 stickyFooter.appendTo($.pageContainer).css('top',0);
2223                                 setTop(stickyFooter);
2224                         }
2225                 });
2226
2227                 //after page is shown, append footer to new page
2228                 $('.ui-page').live('pageshow', function(event, ui){
2229                         if( stickyFooter && stickyFooter.length ){
2230                                 stickyFooter.appendTo(event.target).css('top',0);
2231                         }
2232                         $.fixedToolbars.show(true, this);
2233                 });
2234                 
2235         });
2236         
2237         // element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
2238         // coordinates inside of the rect it returns don't have the page scroll position
2239         // factored out of it like the other platforms do. To get around this,
2240         // we'll just calculate the top offset the old fashioned way until core has
2241         // a chance to figure out how to handle this situation.
2242         //
2243         // TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core.
2244
2245         function getOffsetTop(ele)
2246         {
2247                 var top = 0;
2248                 if (ele)
2249                 {
2250                         var op = ele.offsetParent, body = document.body;
2251                         top = ele.offsetTop;
2252                         while (ele && ele != body)
2253                         {
2254                                 top += ele.scrollTop || 0;
2255                                 if (ele == op)
2256                                 {
2257                                         top += op.offsetTop;
2258                                         op = ele.offsetParent;
2259                                 }
2260                                 ele = ele.parentNode;
2261                         }
2262                 }
2263                 return top;
2264         }
2265
2266         function setTop(el){
2267                 var fromTop = $(window).scrollTop(),
2268                         thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
2269                         thisCSStop = el.css('top') == 'auto' ? 0 : parseFloat(el.css('top')),
2270                         screenHeight = window.innerHeight,
2271                         thisHeight = el.outerHeight(),
2272                         useRelative = el.parents('.ui-page:not(.ui-page-fullscreen)').length,
2273                         relval;
2274                 if( el.is('.ui-header-fixed') ){
2275                         relval = fromTop - thisTop + thisCSStop;
2276                         if( relval < thisTop){ relval = 0; }
2277                         return el.css('top', ( useRelative ) ? relval : fromTop);
2278                 }
2279                 else{
2280                         //relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
2281                         //if( relval > thisTop ){ relval = 0; }
2282                         relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop);
2283                         return el.css('top', ( useRelative ) ? relval : fromTop + screenHeight - thisHeight );
2284                 }
2285         }
2286
2287         //exposed methods
2288         return {
2289                 show: function(immediately, page){
2290                         currentstate = 'overlay';
2291                         var $ap = page ? $(page) : ($.mobile.activePage ? $.mobile.activePage : $(".ui-page-active"));
2292                         return $ap.children( toolbarSelector ).each(function(){
2293                                 var el = $(this),
2294                                         fromTop = $(window).scrollTop(),
2295                                         thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
2296                                         screenHeight = window.innerHeight,
2297                                         thisHeight = el.outerHeight(),
2298                                         alreadyVisible = (el.is('.ui-header-fixed') && fromTop <= thisTop + thisHeight) || (el.is('.ui-footer-fixed') && thisTop <= fromTop + screenHeight);    
2299                                 
2300                                 //add state class
2301                                 el.addClass('ui-fixed-overlay').removeClass('ui-fixed-inline'); 
2302                                         
2303                                 if( !alreadyVisible && !immediately ){
2304                                         el.addClass('in').animationComplete(function(){
2305                                                 el.removeClass('in');
2306                                         });
2307                                 }
2308                                 setTop(el);
2309                         });     
2310                 },
2311                 hide: function(immediately){
2312                         currentstate = 'inline';
2313                         var $ap = $.mobile.activePage ? $.mobile.activePage : $(".ui-page-active");
2314                         return $ap.children( toolbarSelector ).each(function(){
2315                                 var el = $(this);
2316
2317                                 var thisCSStop = el.css('top'); thisCSStop = thisCSStop == 'auto' ? 0 : parseFloat(thisCSStop);
2318                                 
2319                                 //add state class
2320                                 el.addClass('ui-fixed-inline').removeClass('ui-fixed-overlay');
2321                                 
2322                                 if (thisCSStop < 0 || (el.is('.ui-header-fixed') && thisCSStop != 0))