1 /*! 2 * jQuery Mobile v1.0a2 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 (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 275 * Note: Code is in draft form and is subject to change 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 : support tests 298 * Copyright (c) jQuery Project 299 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 300 * Note: Code is in draft form and is subject to change 301 */ 302 (function($, undefined ) { 303 304 // test whether a CSS media type or query applies 305 $.media = (function() { 306 // TODO: use window.matchMedia once at least one UA implements it 307 var cache = {}, 308 $html = $( "html" ), 309 testDiv = $( "<div id='jquery-mediatest'>" ), 310 fakeBody = $( "<body>" ).append( testDiv ); 311 312 return function( query ) { 313 if ( !( query in cache ) ) { 314 var styleBlock = $( "<style type='text/css'>" + 315 "@media " + query + "{#jquery-mediatest{position:absolute;}}" + 316 "</style>" ); 317 $html.prepend( fakeBody ).prepend( styleBlock ); 318 cache[ query ] = testDiv.css( "position" ) === "absolute"; 319 fakeBody.add( styleBlock ).remove(); 320 } 321 return cache[ query ]; 322 }; 323 })(); 324 325 var fakeBody = $( "<body>" ).prependTo( "html" ), 326 fbCSS = fakeBody[0].style, 327 vendors = ['webkit','moz','o'], 328 webos = window.palmGetResource || window.PalmServiceBridge, //only used to rule out scrollTop 329 bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB 330 331 //thx Modernizr 332 function propExists( prop ){ 333 var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1), 334 props = (prop + ' ' + vendors.join(uc_prop + ' ') + uc_prop).split(' '); 335 for(var v in props){ 336 if( fbCSS[ v ] !== undefined ){ 337 return true; 338 } 339 } 340 }; 341 342 //test for dynamic-updating base tag support (allows us to avoid href,src attr rewriting) 343 function baseTagTest(){ 344 var fauxBase = location.protocol + '//' + location.host + location.pathname + "ui-dir/", 345 base = $("<base>", {"href": fauxBase}).appendTo("head"), 346 link = $( "<a href='testurl'></a>" ).prependTo( fakeBody ), 347 rebase = link[0].href; 348 base.remove(); 349 return rebase.indexOf(fauxBase) === 0; 350 }; 351 352 $.extend( $.support, { 353 orientation: "orientation" in window, 354 touch: "ontouchend" in document, 355 cssTransitions: "WebKitTransitionEvent" in window, 356 pushState: !!history.pushState, 357 mediaquery: $.media('only all'), 358 cssPseudoElement: !!propExists('content'), 359 boxShadow: !!propExists('boxShadow') && !bb, 360 scrollTop: ("pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[0]) && !webos, 361 dynamicBaseTag: baseTagTest() 362 }); 363 364 fakeBody.remove(); 365 366 //for ruling out shadows via css 367 if( !$.support.boxShadow ){ $('html').addClass('ui-mobile-nosupport-boxshadow'); } 368 369 })( jQuery );(function($, undefined ) { 370 371 // add new event shortcuts 372 $.each( "touchstart touchmove touchend orientationchange tap taphold swipe swipeleft swiperight scrollstart scrollstop".split( " " ), function( i, name ) { 373 $.fn[ name ] = function( fn ) { 374 return fn ? this.bind( name, fn ) : this.trigger( name ); 375 }; 376 $.attrFn[ name ] = true; 377 }); 378 379 var supportTouch = $.support.touch, 380 scrollEvent = "touchmove scroll", 381 touchStartEvent = supportTouch ? "touchstart" : "mousedown", 382 touchStopEvent = supportTouch ? "touchend" : "mouseup", 383 touchMoveEvent = supportTouch ? "touchmove" : "mousemove"; 384 385 // also handles scrollstop 386 $.event.special.scrollstart = { 387 enabled: true, 388 389 setup: function() { 390 var thisObject = this, 391 $this = $( thisObject ), 392 scrolling, 393 timer; 394 395 function trigger( event, state ) { 396 scrolling = state; 397 var originalType = event.type; 398 event.type = scrolling ? "scrollstart" : "scrollstop"; 399 $.event.handle.call( thisObject, event ); 400 event.type = originalType; 401 } 402 403 // iPhone triggers scroll after a small delay; use touchmove instead 404 $this.bind( scrollEvent, function( event ) { 405 if ( !$.event.special.scrollstart.enabled ) { 406 return; 407 } 408 409 if ( !scrolling ) { 410 trigger( event, true ); 411 } 412 413 clearTimeout( timer ); 414 timer = setTimeout(function() { 415 trigger( event, false ); 416 }, 50 ); 417 }); 418 } 419 }; 420 421 // also handles taphold 422 $.event.special.tap = { 423 setup: function() { 424 var thisObject = this, 425 $this = $( thisObject ); 426 427 $this 428 .bind( touchStartEvent, function( event ) { 429 if ( event.which && event.which !== 1 ) { 430 return; 431 } 432 433 var moved = false, 434 touching = true, 435 origPos = [ event.pageX, event.pageY ], 436 originalType, 437 timer; 438 439 function moveHandler() { 440 if ((Math.abs(origPos[0] - event.pageX) > 10) || 441 (Math.abs(origPos[1] - event.pageY) > 10)) { 442 moved = true; 443 } 444 } 445 446 timer = setTimeout(function() { 447 if ( touching && !moved ) { 448 originalType = event.type; 449 event.type = "taphold"; 450 $.event.handle.call( thisObject, event ); 451 event.type = originalType; 452 } 453 }, 750 ); 454 455 $this 456 .one( touchMoveEvent, moveHandler) 457 .one( touchStopEvent, function( event ) { 458 $this.unbind( touchMoveEvent, moveHandler ); 459 clearTimeout( timer ); 460 touching = false; 461 462 if ( !moved ) { 463 originalType = event.type; 464 event.type = "tap"; 465 $.event.handle.call( thisObject, event ); 466 event.type = originalType; 467 } 468 }); 469 }); 470 } 471 }; 472 473 // also handles swipeleft, swiperight 474 $.event.special.swipe = { 475 setup: function() { 476 var thisObject = this, 477 $this = $( thisObject ); 478 479 $this 480 .bind( touchStartEvent, function( event ) { 481 var data = event.originalEvent.touches ? 482 event.originalEvent.touches[ 0 ] : 483 event, 484 start = { 485 time: (new Date).getTime(), 486 coords: [ data.pageX, data.pageY ], 487 origin: $( event.target ) 488 }, 489 stop; 490 491 function moveHandler( event ) { 492 if ( !start ) { 493 return; 494 } 495 496 var data = event.originalEvent.touches ? 497 event.originalEvent.touches[ 0 ] : 498 event; 499 stop = { 500 time: (new Date).getTime(), 501 coords: [ data.pageX, data.pageY ] 502 }; 503 504 // prevent scrolling 505 if ( Math.abs( start.coords[0] - stop.coords[0] ) > 10 ) { 506 event.preventDefault(); 507 } 508 } 509 510 $this 511 .bind( touchMoveEvent, moveHandler ) 512 .one( touchStopEvent, function( event ) { 513 $this.unbind( touchMoveEvent, moveHandler ); 514 if ( start && stop ) { 515 if ( stop.time - start.time < 1000 && 516 Math.abs( start.coords[0] - stop.coords[0]) > 30 && 517 Math.abs( start.coords[1] - stop.coords[1]) < 75 ) { 518 start.origin 519 .trigger( "swipe" ) 520 .trigger( start.coords[0] > stop.coords[0] ? "swipeleft" : "swiperight" ); 521 } 522 } 523 start = stop = undefined; 524 }); 525 }); 526 } 527 }; 528 529 (function($){ 530 // "Cowboy" Ben Alman 531 532 var win = $(window), 533 special_event, 534 get_orientation, 535 last_orientation; 536 537 $.event.special.orientationchange = special_event = { 538 setup: function(){ 539 // If the event is supported natively, return false so that jQuery 540 // will bind to the event using DOM methods. 541 if ( $.support.orientation ) { return false; } 542 543 // Get the current orientation to avoid initial double-triggering. 544 last_orientation = get_orientation(); 545 546 // Because the orientationchange event doesn't exist, simulate the 547 // event by testing window dimensions on resize. 548 win.bind( "resize", handler ); 549 }, 550 teardown: function(){ 551 // If the event is not supported natively, return false so that 552 // jQuery will unbind the event using DOM methods. 553 if ( $.support.orientation ) { return false; } 554 555 // Because the orientationchange event doesn't exist, unbind the 556 // resize event handler. 557 win.unbind( "resize", handler ); 558 }, 559 add: function( handleObj ) { 560 // Save a reference to the bound event handler. 561 var old_handler = handleObj.handler; 562 563 handleObj.handler = function( event ) { 564 // Modify event object, adding the .orientation property. 565 event.orientation = get_orientation(); 566 567 // Call the originally-bound event handler and return its result. 568 return old_handler.apply( this, arguments ); 569 }; 570 } 571 }; 572 573 // If the event is not supported natively, this handler will be bound to 574 // the window resize event to simulate the orientationchange event. 575 function handler() { 576 // Get the current orientation. 577 var orientation = get_orientation(); 578 579 if ( orientation !== last_orientation ) { 580 // The orientation has changed, so trigger the orientationchange event. 581 last_orientation = orientation; 582 win.trigger( "orientationchange" ); 583 } 584 }; 585 586 // Get the current page orientation. This method is exposed publicly, should it 587 // be needed, as jQuery.event.special.orientationchange.orientation() 588 special_event.orientation = get_orientation = function() { 589 var elem = document.documentElement; 590 return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape"; 591 }; 592 593 })(jQuery); 594 595 $.each({ 596 scrollstop: "scrollstart", 597 taphold: "tap", 598 swipeleft: "swipe", 599 swiperight: "swipe" 600 }, function( event, sourceEvent ) { 601 $.event.special[ event ] = { 602 setup: function() { 603 $( this ).bind( sourceEvent, $.noop ); 604 } 605 }; 606 }); 607 608 })( jQuery ); 609 /*! 610 * jQuery hashchange event - v1.3 - 7/21/2010 611 * http://benalman.com/projects/jquery-hashchange-plugin/ 612 * 613 * Copyright (c) 2010 "Cowboy" Ben Alman 614 * Dual licensed under the MIT and GPL licenses. 615 * http://benalman.com/about/license/ 616 */ 617 618 // Script: jQuery hashchange event 619 // 620 // *Version: 1.3, Last updated: 7/21/2010* 621 // 622 // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/ 623 // GitHub - http://github.com/cowboy/jquery-hashchange/ 624 // Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js 625 // (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped) 626 // 627 // About: License 628 // 629 // Copyright (c) 2010 "Cowboy" Ben Alman, 630 // Dual licensed under the MIT and GPL licenses. 631 // http://benalman.com/about/license/ 632 // 633 // About: Examples 634 // 635 // These working examples, complete with fully commented code, illustrate a few 636 // ways in which this plugin can be used. 637 // 638 // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ 639 // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/ 640 // 641 // About: Support and Testing 642 // 643 // Information about what version or versions of jQuery this plugin has been 644 // tested with, what browsers it has been tested in, and where the unit tests 645 // reside (so you can test it yourself). 646 // 647 // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2 648 // Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5, 649 // Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5. 650 // Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/ 651 // 652 // About: Known issues 653 // 654 // While this jQuery hashchange event implementation is quite stable and 655 // robust, there are a few unfortunate browser bugs surrounding expected 656 // hashchange event-based behaviors, independent of any JavaScript 657 // window.onhashchange abstraction. See the following examples for more 658 // information: 659 // 660 // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/ 661 // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/ 662 // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/ 663 // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/ 664 // 665 // Also note that should a browser natively support the window.onhashchange 666 // event, but not report that it does, the fallback polling loop will be used. 667 // 668 // About: Release History 669 // 670 // 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more 671 // "removable" for mobile-only development. Added IE6/7 document.title 672 // support. Attempted to make Iframe as hidden as possible by using 673 // techniques from http://www.paciellogroup.com/blog/?p=604. Added 674 // support for the "shortcut" format $(window).hashchange( fn ) and 675 // $(window).hashchange() like jQuery provides for built-in events. 676 // Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and 677 // lowered its default value to 50. Added <jQuery.fn.hashchange.domain> 678 // and <jQuery.fn.hashchange.src> properties plus document-domain.html 679 // file to address access denied issues when setting document.domain in 680 // IE6/7. 681 // 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin 682 // from a page on another domain would cause an error in Safari 4. Also, 683 // IE6/7 Iframe is now inserted after the body (this actually works), 684 // which prevents the page from scrolling when the event is first bound. 685 // Event can also now be bound before DOM ready, but it won't be usable 686 // before then in IE6/7. 687 // 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug 688 // where browser version is incorrectly reported as 8.0, despite 689 // inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag. 690 // 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special 691 // window.onhashchange functionality into a separate plugin for users 692 // who want just the basic event & back button support, without all the 693 // extra awesomeness that BBQ provides. This plugin will be included as 694 // part of jQuery BBQ, but also be available separately. 695 696 (function($,window,undefined){ 697 '$:nomunge'; // Used by YUI compressor. 698 699 // Reused string. 700 var str_hashchange = 'hashchange', 701 702 // Method / object references. 703 doc = document, 704 fake_onhashchange, 705 special = $.event.special, 706 707 // Does the browser support window.onhashchange? Note that IE8 running in 708 // IE7 compatibility mode reports true for 'onhashchange' in window, even 709 // though the event isn't supported, so also test document.documentMode. 710 doc_mode = doc.documentMode, 711 supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 ); 712 713 // Get location.hash (or what you'd expect location.hash to be) sans any 714 // leading #. Thanks for making this necessary, Firefox! 715 function get_fragment( url ) { 716 url = url || location.href; 717 return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); 718 }; 719 720 // Method: jQuery.fn.hashchange 721 // 722 // Bind a handler to the window.onhashchange event or trigger all bound 723 // window.onhashchange event handlers. This behavior is consistent with 724 // jQuery's built-in event handlers. 725 // 726 // Usage: 727 // 728 // > jQuery(window).hashchange( [ handler ] ); 729 // 730 // Arguments: 731 // 732 // handler - (Function) Optional handler to be bound to the hashchange 733 // event. This is a "shortcut" for the more verbose form: 734 // jQuery(window).bind( 'hashchange', handler ). If handler is omitted, 735 // all bound window.onhashchange event handlers will be triggered. This 736 // is a shortcut for the more verbose 737 // jQuery(window).trigger( 'hashchange' ). These forms are described in 738 // the <hashchange event> section. 739 // 740 // Returns: 741 // 742 // (jQuery) The initial jQuery collection of elements. 743 744 // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and 745 // $(elem).hashchange() for triggering, like jQuery does for built-in events. 746 $.fn[ str_hashchange ] = function( fn ) { 747 return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange ); 748 }; 749 750 // Property: jQuery.fn.hashchange.delay 751 // 752 // The numeric interval (in milliseconds) at which the <hashchange event> 753 // polling loop executes. Defaults to 50. 754 755 // Property: jQuery.fn.hashchange.domain 756 // 757 // If you're setting document.domain in your JavaScript, and you want hash 758 // history to work in IE6/7, not only must this property be set, but you must 759 // also set document.domain BEFORE jQuery is loaded into the page. This 760 // property is only applicable if you are supporting IE6/7 (or IE8 operating 761 // in "IE7 compatibility" mode). 762 // 763 // In addition, the <jQuery.fn.hashchange.src> property must be set to the 764 // path of the included "document-domain.html" file, which can be renamed or 765 // modified if necessary (note that the document.domain specified must be the 766 // same in both your main JavaScript as well as in this file). 767 // 768 // Usage: 769 // 770 // jQuery.fn.hashchange.domain = document.domain; 771 772 // Property: jQuery.fn.hashchange.src 773 // 774 // If, for some reason, you need to specify an Iframe src file (for example, 775 // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can 776 // do so using this property. Note that when using this property, history 777 // won't be recorded in IE6/7 until the Iframe src file loads. This property 778 // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7 779 // compatibility" mode). 780 // 781 // Usage: 782 // 783 // jQuery.fn.hashchange.src = 'path/to/file.html'; 784 785 $.fn[ str_hashchange ].delay = 50; 786 /* 787 $.fn[ str_hashchange ].domain = null; 788 $.fn[ str_hashchange ].src = null; 789 */ 790 791 // Event: hashchange event 792 // 793 // Fired when location.hash changes. In browsers that support it, the native 794 // HTML5 window.onhashchange event is used, otherwise a polling loop is 795 // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to 796 // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7 797 // compatibility" mode), a hidden Iframe is created to allow the back button 798 // and hash-based history to work. 799 // 800 // Usage as described in <jQuery.fn.hashchange>: 801 // 802 // > // Bind an event handler. 803 // > jQuery(window).hashchange( function(e) { 804 // > var hash = location.hash; 805 // > ... 806 // > }); 807 // > 808 // > // Manually trigger the event handler. 809 // > jQuery(window).hashchange(); 810 // 811 // A more verbose usage that allows for event namespacing: 812 // 813 // > // Bind an event handler. 814 // > jQuery(window).bind( 'hashchange', function(e) { 815 // > var hash = location.hash; 816 // > ... 817 // > }); 818 // > 819 // > // Manually trigger the event handler. 820 // > jQuery(window).trigger( 'hashchange' ); 821 // 822 // Additional Notes: 823 // 824 // * The polling loop and Iframe are not created until at least one handler 825 // is actually bound to the 'hashchange' event. 826 // * If you need the bound handler(s) to execute immediately, in cases where 827 // a location.hash exists on page load, via bookmark or page refresh for 828 // example, use jQuery(window).hashchange() or the more verbose 829 // jQuery(window).trigger( 'hashchange' ). 830 // * The event can be bound before DOM ready, but since it won't be usable 831 // before then in IE6/7 (due to the necessary Iframe), recommended usage is 832 // to bind it inside a DOM ready handler. 833 834 // Override existing $.event.special.hashchange methods (allowing this plugin 835 // to be defined after jQuery BBQ in BBQ's source code). 836 special[ str_hashchange ] = $.extend( special[ str_hashchange ], { 837 838 // Called only when the first 'hashchange' event is bound to window. 839 setup: function() { 840 // If window.onhashchange is supported natively, there's nothing to do.. 841 if ( supports_onhashchange ) { return false; } 842 843 // Otherwise, we need to create our own. And we don't want to call this 844 // until the user binds to the event, just in case they never do, since it 845 // will create a polling loop and possibly even a hidden Iframe. 846 $( fake_onhashchange.start ); 847 }, 848 849 // Called only when the last 'hashchange' event is unbound from window. 850 teardown: function() { 851 // If window.onhashchange is supported natively, there's nothing to do.. 852 if ( supports_onhashchange ) { return false; } 853 854 // Otherwise, we need to stop ours (if possible). 855 $( fake_onhashchange.stop ); 856 } 857 858 }); 859 860 // fake_onhashchange does all the work of triggering the window.onhashchange 861 // event for browsers that don't natively support it, including creating a 862 // polling loop to watch for hash changes and in IE 6/7 creating a hidden 863 // Iframe to enable back and forward. 864 fake_onhashchange = (function(){ 865 var self = {}, 866 timeout_id, 867 868 // Remember the initial hash so it doesn't get triggered immediately. 869 last_hash = get_fragment(), 870 871 fn_retval = function(val){ return val; }, 872 history_set = fn_retval, 873 history_get = fn_retval; 874 875 // Start the polling loop. 876 self.start = function() { 877 timeout_id || poll(); 878 }; 879 880 // Stop the polling loop. 881 self.stop = function() { 882 timeout_id && clearTimeout( timeout_id ); 883 timeout_id = undefined; 884 }; 885 886 // This polling loop checks every $.fn.hashchange.delay milliseconds to see 887 // if location.hash has changed, and triggers the 'hashchange' event on 888 // window when necessary. 889 function poll() { 890 var hash = get_fragment(), 891 history_hash = history_get( last_hash ); 892 893 if ( hash !== last_hash ) { 894 history_set( last_hash = hash, history_hash ); 895 896 $(window).trigger( str_hashchange ); 897 898 } else if ( history_hash !== last_hash ) { 899 location.href = location.href.replace( /#.*/, '' ) + history_hash; 900 } 901 902 timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay ); 903 }; 904 905 // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 906 // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv 907 // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 908 $.browser.msie && !supports_onhashchange && (function(){ 909 // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8 910 // when running in "IE7 compatibility" mode. 911 912 var iframe, 913 iframe_src; 914 915 // When the event is bound and polling starts in IE 6/7, create a hidden 916 // Iframe for history handling. 917 self.start = function(){ 918 if ( !iframe ) { 919 iframe_src = $.fn[ str_hashchange ].src; 920 iframe_src = iframe_src && iframe_src + get_fragment(); 921 922 // Create hidden Iframe. Attempt to make Iframe as hidden as possible 923 // by using techniques from http://www.paciellogroup.com/blog/?p=604. 924 iframe = $('<iframe tabindex="-1" title="empty"/>').hide() 925 926 // When Iframe has completely loaded, initialize the history and 927 // start polling. 928 .one( 'load', function(){ 929 iframe_src || history_set( get_fragment() ); 930 poll(); 931 }) 932 933 // Load Iframe src if specified, otherwise nothing. 934 .attr( 'src', iframe_src || 'javascript:0' ) 935 936 // Append Iframe after the end of the body to prevent unnecessary 937 // initial page scrolling (yes, this works). 938 .insertAfter( 'body' )[0].contentWindow; 939 940 // Whenever `document.title` changes, update the Iframe's title to 941 // prettify the back/next history menu entries. Since IE sometimes 942 // errors with "Unspecified error" the very first time this is set 943 // (yes, very useful) wrap this with a try/catch block. 944 doc.onpropertychange = function(){ 945 try { 946 if ( event.propertyName === 'title' ) { 947 iframe.document.title = doc.title; 948 } 949 } catch(e) {} 950 }; 951 952 } 953 }; 954 955 // Override the "stop" method since an IE6/7 Iframe was created. Even 956 // if there are no longer any bound event handlers, the polling loop 957 // is still necessary for back/next to work at all! 958 self.stop = fn_retval; 959 960 // Get history by looking at the hidden Iframe's location.hash. 961 history_get = function() { 962 return get_fragment( iframe.location.href ); 963 }; 964 965 // Set a new history item by opening and then closing the Iframe 966 // document, *then* setting its location.hash. If document.domain has 967 // been set, update that as well. 968 history_set = function( hash, history_hash ) { 969 var iframe_doc = iframe.document, 970 domain = $.fn[ str_hashchange ].domain; 971 972 if ( hash !== history_hash ) { 973 // Update Iframe with any initial `document.title` that might be set. 974 iframe_doc.title = doc.title; 975 976 // Opening the Iframe's document after it has been closed is what 977 // actually adds a history entry. 978 iframe_doc.open(); 979 980 // Set document.domain for the Iframe document as well, if necessary. 981 domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' ); 982 983 iframe_doc.close(); 984 985 // Update the Iframe's hash, for great justice. 986 iframe.location.hash = hash; 987 } 988 }; 989 990 })(); 991 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 992 // ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^ 993 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 994 995 return self; 996 })(); 997 998 })(jQuery,this); 999 /* 1000 * jQuery Mobile Framework : "page" plugin 1001 * Copyright (c) jQuery Project 1002 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 1003 * Note: Code is in draft form and is subject to change 1004 */ 1005 (function($, undefined ) { 1006 1007 $.widget( "mobile.page", $.mobile.widget, { 1008 options: { 1009 backBtnText: "Back", 1010 addBackBtn: true, 1011 degradeInputs: { 1012 color: false, 1013 date: false, 1014 datetime: false, 1015 "datetime-local": false, 1016 email: false, 1017 month: false, 1018 number: false, 1019 range: true, 1020 search: true, 1021 tel: false, 1022 time: false, 1023 url: false, 1024 week: false 1025 } 1026 }, 1027 1028 _create: function() { 1029 var $elem = this.element, 1030 o = this.options; 1031 1032 if ( this._trigger( "beforeCreate" ) === false ) { 1033 return; 1034 } 1035 1036 //some of the form elements currently rely on the presence of ui-page and ui-content 1037 // classes so we'll handle page and content roles outside of the main role processing 1038 // loop below. 1039 $elem.find( "[data-role='page'], [data-role='content']" ).andSelf().each(function() { 1040 $(this).addClass( "ui-" + $(this).data( "role" ) ); 1041 }); 1042 1043 $elem.find( "[data-role='nojs']" ).addClass( "ui-nojs" ); 1044 1045 this._enchanceControls(); 1046 1047 // pre-find data els 1048 var $dataEls = $elem.find( "[data-role]" ).andSelf().each(function() { 1049 var $this = $( this ), 1050 role = $this.data( "role" ), 1051 theme = $this.data( "theme" ); 1052 1053 //apply theming and markup modifications to page,header,content,footer 1054 if ( role === "header" || role === "footer" ) { 1055 $this.addClass( "ui-bar-" + (theme || $this.parent('[data-role=page]').data( "theme" ) || "a") ); 1056 1057 // add ARIA role 1058 $this.attr( "role", role === "header" ? "banner" : "contentinfo" ); 1059 1060 //right,left buttons 1061 var $headeranchors = $this.children( "a" ), 1062 leftbtn = $headeranchors.hasClass( "ui-btn-left" ), 1063 rightbtn = $headeranchors.hasClass( "ui-btn-right" ); 1064 1065 if ( !leftbtn ) { 1066 leftbtn = $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length; 1067 } 1068 1069 if ( !rightbtn ) { 1070 rightbtn = $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length; 1071 } 1072 1073 // auto-add back btn on pages beyond first view 1074 if ( o.addBackBtn && role === "header" && 1075 ($.mobile.urlStack.length > 1 || $(".ui-page").length > 1) && 1076 !leftbtn && !$this.data( "noBackBtn" ) ) { 1077 1078 $( "<a href='#' class='ui-btn-left' data-icon='arrow-l'>"+ o.backBtnText +"</a>" ) 1079 .click(function() { 1080 history.back(); 1081 return false; 1082 }) 1083 .prependTo( $this ); 1084 } 1085 1086 //page title 1087 $this.children( "h1, h2, h3, h4, h5, h6" ) 1088 .addClass( "ui-title" ) 1089 //regardless of h element number in src, it becomes h1 for the enhanced page 1090 .attr({ "tabindex": "0", "role": "heading", "aria-level": "1" }); 1091 1092 } else if ( role === "content" ) { 1093 if ( theme ) { 1094 $this.addClass( "ui-body-" + theme ); 1095 } 1096 1097 // add ARIA role 1098 $this.attr( "role", "main" ); 1099 1100 } else if ( role === "page" ) { 1101 $this.addClass( "ui-body-" + (theme || "c") ); 1102 } 1103 1104 switch(role) { 1105 case "header": 1106 case "footer": 1107 case "page": 1108 case "content": 1109 $this.addClass( "ui-" + role ); 1110 break; 1111 case "collapsible": 1112 case "fieldcontain": 1113 case "navbar": 1114 case "listview": 1115 case "dialog": 1116 $this[ role ](); 1117 break; 1118 } 1119 }); 1120 1121 //links in bars, or those with data-role become buttons 1122 $elem.find( "[data-role='button'], .ui-bar a, .ui-header a, .ui-footer a" ) 1123 .not( ".ui-btn" ) 1124 .buttonMarkup(); 1125 1126 $elem 1127 .find("[data-role='controlgroup']") 1128 .controlgroup(); 1129 1130 //links within content areas 1131 $elem.find( "a:not(.ui-btn):not(.ui-link-inherit)" ) 1132 .addClass( "ui-link" ); 1133 1134 //fix toolbars 1135 $elem.fixHeaderFooter(); 1136 }, 1137 1138 _enchanceControls: function() { 1139 var o = this.options; 1140 // degrade inputs to avoid poorly implemented native functionality 1141 this.element.find( "input" ).each(function() { 1142 var type = this.getAttribute( "type" ); 1143 if ( o.degradeInputs[ type ] ) { 1144 $( this ).replaceWith( 1145 $( "<div>" ).html( $(this).clone() ).html() 1146 .replace( /type="([a-zA-Z]+)"/, "data-type='$1'" ) ); 1147 } 1148 }); 1149 1150 // enchance form controls 1151 this.element 1152 .find( "[type='radio'], [type='checkbox']" ) 1153 .checkboxradio(); 1154 1155 this.element 1156 .find( "button, [type='button'], [type='submit'], [type='reset'], [type='image']" ) 1157 .not( ".ui-nojs" ) 1158 .button(); 1159 1160 this.element 1161 .find( "input, textarea" ) 1162 .not( "[type='radio'], [type='checkbox'], button, [type='button'], [type='submit'], [type='reset'], [type='image']" ) 1163 .textinput(); 1164 1165 this.element 1166 .find( "input, select" ) 1167 .filter( "[data-role='slider'], [data-type='range']" ) 1168 .slider(); 1169 1170 this.element 1171 .find( "select:not([data-role='slider'])" ) 1172 .selectmenu(); 1173 } 1174 }); 1175 1176 })( jQuery ); 1177 /* 1178 * jQuery Mobile Framework : "fixHeaderFooter" plugin - on-demand positioning for headers,footers 1179 * Copyright (c) jQuery Project 1180 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 1181 * Note: Code is in draft form and is subject to change 1182 */ 1183 (function($, undefined ) { 1184 $.fn.fixHeaderFooter = function(options){ 1185 if( !$.support.scrollTop ){ return $(this); } 1186 return $(this).each(function(){ 1187 if( $(this).data('fullscreen') ){ $(this).addClass('ui-page-fullscreen'); } 1188 $(this).find('.ui-header[data-position="fixed"]').addClass('ui-header-fixed ui-fixed-inline fade'); //should be slidedown 1189 $(this).find('.ui-footer[data-position="fixed"]').addClass('ui-footer-fixed ui-fixed-inline fade'); //should be slideup 1190 }); 1191 }; 1192 1193 //single controller for all showing,hiding,toggling 1194 $.fixedToolbars = (function(){ 1195 if( !$.support.scrollTop ){ return; } 1196 var currentstate = 'inline', 1197 delayTimer, 1198 ignoreTargets = 'a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed', 1199 toolbarSelector = '.ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last', 1200 stickyFooter, //for storing quick references to duplicate footers 1201 supportTouch = $.support.touch, 1202 touchStartEvent = supportTouch ? "touchstart" : "mousedown", 1203 touchStopEvent = supportTouch ? "touchend" : "mouseup", 1204 stateBefore = null, 1205 scrollTriggered = false; 1206 1207 $(function() { 1208 $(document) 1209 .bind(touchStartEvent,function(event){ 1210 if( $(event.target).closest(ignoreTargets).length ){ return; } 1211 stateBefore = currentstate; 1212 $.fixedToolbars.hide(true); 1213 }) 1214 .bind('scrollstart',function(event){ 1215 if( $(event.target).closest(ignoreTargets).length ){ return; } //because it could be a touchmove... 1216 scrollTriggered = true; 1217 if(stateBefore == null){ stateBefore = currentstate; } 1218 $.fixedToolbars.hide(true); 1219 }) 1220 .bind(touchStopEvent,function(event){ 1221 if( $(event.target).closest(ignoreTargets).length ){ return; } 1222 if( !scrollTriggered ){ 1223 $.fixedToolbars.toggle(stateBefore); 1224 stateBefore = null; 1225 } 1226 }) 1227 .bind('scrollstop',function(event){ 1228 if( $(event.target).closest(ignoreTargets).length ){ return; } 1229 scrollTriggered = false; 1230 $.fixedToolbars.toggle( stateBefore == 'overlay' ? 'inline' : 'overlay' ); 1231 stateBefore = null; 1232 }); 1233 1234 //function to return another footer already in the dom with the same data-id 1235 function findStickyFooter(el){ 1236 var thisFooter = el.find('[data-role="footer"]'); 1237 return $( '.ui-footer[data-id="'+ thisFooter.data('id') +'"]:not(.ui-footer-duplicate)' ).not(thisFooter); 1238 } 1239 1240 //before page is shown, check for duplicate footer 1241 $('.ui-page').live('pagebeforeshow', function(event, ui){ 1242 stickyFooter = findStickyFooter( $(event.target) ); 1243 if( stickyFooter.length ){ 1244 //if the existing footer is the first of its kind, create a placeholder before stealing it 1245 if( stickyFooter.parents('.ui-page:eq(0)').find('.ui-footer[data-id="'+ stickyFooter.data('id') +'"]').length == 1 ){ 1246 stickyFooter.before( stickyFooter.clone().addClass('ui-footer-duplicate') ); 1247 } 1248 $(event.target).find('[data-role="footer"]').addClass('ui-footer-duplicate'); 1249 stickyFooter.appendTo($.pageContainer).css('top',0); 1250 setTop(stickyFooter); 1251 } 1252 }); 1253 1254 //after page is shown, append footer to new page 1255 $('.ui-page').live('pageshow', function(event, ui){ 1256 if( stickyFooter && stickyFooter.length ){ 1257 stickyFooter.appendTo(event.target).css('top',0); 1258 } 1259 $.fixedToolbars.show(true, this); 1260 }); 1261 1262 }); 1263 1264 // element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The 1265 // coordinates inside of the rect it returns don't have the page scroll position 1266 // factored out of it like the other platforms do. To get around this, 1267 // we'll just calculate the top offset the old fashioned way until core has 1268 // a chance to figure out how to handle this situation. 1269 // 1270 // TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core. 1271 1272 function getOffsetTop(ele) 1273 { 1274 var top = 0; 1275 if (ele) 1276 { 1277 var op = ele.offsetParent, body = document.body; 1278 top = ele.offsetTop; 1279 while (ele && ele != body) 1280 { 1281 top += ele.scrollTop || 0; 1282 if (ele == op) 1283 { 1284 top += op.offsetTop; 1285 op = ele.offsetParent; 1286 } 1287 ele = ele.parentNode; 1288 } 1289 } 1290 return top; 1291 } 1292 1293 function setTop(el){ 1294 var fromTop = $(window).scrollTop(), 1295 thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead. 1296 thisCSStop = el.css('top') == 'auto' ? 0 : parseFloat(el.css('top')), 1297 screenHeight = window.innerHeight, 1298 thisHeight = el.outerHeight(), 1299 useRelative = el.parents('.ui-page:not(.ui-page-fullscreen)').length, 1300 relval; 1301 if( el.is('.ui-header-fixed') ){ 1302 relval = fromTop - thisTop + thisCSStop; 1303 if( relval < thisTop){ relval = 0; } 1304 return el.css('top', ( useRelative ) ? relval : fromTop); 1305 } 1306 else{ 1307 //relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight); 1308 //if( relval > thisTop ){ relval = 0; } 1309 relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop); 1310 return el.css('top', ( useRelative ) ? relval : fromTop + screenHeight - thisHeight ); 1311 } 1312 } 1313 1314 //exposed methods 1315 return { 1316 show: function(immediately, page){ 1317 currentstate = 'overlay'; 1318 var $ap = page ? $(page) : ($.mobile.activePage ? $.mobile.activePage : $(".ui-page-active")); 1319 return $ap.children( toolbarSelector ).each(function(){ 1320 var el = $(this), 1321 fromTop = $(window).scrollTop(), 1322 thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead. 1323 screenHeight = window.innerHeight, 1324 thisHeight = el.outerHeight(), 1325 alreadyVisible = (el.is('.ui-header-fixed') && fromTop <= thisTop + thisHeight) || (el.is('.ui-footer-fixed') && thisTop <= fromTop + screenHeight); 1326 1327 //add state class 1328 el.addClass('ui-fixed-overlay').removeClass('ui-fixed-inline'); 1329 1330 if( !alreadyVisible && !immediately ){ 1331 el.addClass('in').animationComplete(function(){ 1332 el.removeClass('in'); 1333 }); 1334 } 1335 setTop(el); 1336 }); 1337 }, 1338 hide: function(immediately){ 1339 currentstate = 'inline'; 1340 var $ap = $.mobile.activePage ? $.mobile.activePage : $(".ui-page-active"); 1341 return $ap.children( toolbarSelector ).each(function(){ 1342 var el = $(this); 1343 1344 var thisCSStop = el.css('top'); thisCSStop = thisCSStop == 'auto' ? 0 : parseFloat(thisCSStop); 1345 1346 //add state class 1347 el.addClass('ui-fixed-inline').removeClass('ui-fixed-overlay'); 1348 1349 if (thisCSStop < 0 || (el.is('.ui-header-fixed') && thisCSStop != 0)) 1350 { 1351 if(immediately){ 1352 el.css('top',0); 1353 } 1354 else{ 1355 if( el.css('top') !== 'auto' && parseFloat(el.css('top')) !== 0 ){ 1356 var classes = 'out reverse'; 1357 el.addClass(classes).animationComplete(function(){ 1358 el.removeClass(classes); 1359 el.css('top',0); 1360 }); 1361 } 1362 } 1363 } 1364 }); 1365 }, 1366 hideAfterDelay: function(){ 1367 delayTimer = setTimeout(function(){ 1368 $.fixedToolbars.hide(); 1369 }, 3000); 1370 }, 1371 toggle: function(from){ 1372 if(from){ currentstate = from; } 1373 return (currentstate == 'overlay') ? $.fixedToolbars.hide() : $.fixedToolbars.show(); 1374 } 1375 }; 1376 })(); 1377 1378 })(jQuery);/* 1379 * jQuery Mobile Framework : "checkboxradio" plugin 1380 * Copyright (c) jQuery Project 1381 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 1382 * Note: Code is in draft form and is subject to change 1383 */ 1384 (function($, undefined ) { 1385 $.widget( "mobile.checkboxradio", $.mobile.widget, { 1386 options: { 1387 theme: null 1388 }, 1389 _create: function(){ 1390 var input = this.element, 1391 label = $("label[for='" + input.attr( "id" ) + "']"), 1392 inputtype = input.attr( "type" ), 1393 checkedicon = "ui-icon-" + inputtype + "-on", 1394 uncheckedicon = "ui-icon-" + inputtype + "-off"; 1395 1396 if ( inputtype != "checkbox" && inputtype != "radio" ) { return; } 1397 1398 label 1399 .buttonMarkup({ 1400 theme: this.options.theme, 1401 icon: this.element.parents( "[data-type='horizontal']" ).length ? undefined : uncheckedicon, 1402 shadow: false 1403 }); 1404 1405 // wrap the input + label in a div 1406 input 1407 .add( label ) 1408 .wrapAll( "<div class='ui-" + inputtype +"'></div>" ); 1409 1410 label.bind({ 1411 mouseover: function() { 1412 if( $(this).parent().is('.ui-disabled') ){ return false; } 1413 }, 1414 1415 mousedown: function() { 1416 if( $(this).parent().is('.ui-disabled') ){ return false; } 1417 label.data( "state", input.attr( "checked" ) ); 1418 }, 1419 1420 click: function() { 1421 setTimeout(function() { 1422 if ( input.attr( "checked" ) === label.data( "state" ) ) { 1423 input.trigger( "click" ); 1424 } 1425 }, 1); 1426 } 1427 }); 1428 1429 input 1430 .bind({ 1431 1432 click: function() { 1433 $( "input[name='" + input.attr( "name" ) + "'][type='" + inputtype + "']" ).checkboxradio( "refresh" ); 1434 }, 1435 1436 focus: function() { 1437 label.addClass( "ui-focus" ); 1438 }, 1439 1440 blur: function() { 1441 label.removeClass( "ui-focus" ); 1442 } 1443 }); 1444 1445 this.refresh(); 1446 1447 }, 1448 1449 refresh: function( ){ 1450 var input = this.element, 1451 label = $("label[for='" + input.attr( "id" ) + "']"), 1452 inputtype = input.attr( "type" ), 1453 icon = label.find( ".ui-icon" ), 1454 checkedicon = "ui-icon-" + inputtype + "-on", 1455 uncheckedicon = "ui-icon-" + inputtype + "-off"; 1456 1457 if ( input[0].checked ) { 1458 label.addClass( "ui-btn-active" ); 1459 icon.addClass( checkedicon ); 1460 icon.removeClass( uncheckedicon ); 1461 1462 } else { 1463 label.removeClass( "ui-btn-active" ); 1464 icon.removeClass( checkedicon ); 1465 icon.addClass( uncheckedicon ); 1466 } 1467 1468 if( input.is( ":disabled" ) ){ 1469 this.disable(); 1470 } 1471 else { 1472 this.enable(); 1473 } 1474 }, 1475 1476 disable: function(){ 1477 this.element.attr("disabled",true).parent().addClass("ui-disabled"); 1478 }, 1479 1480 enable: function(){ 1481 this.element.attr("disabled",false).parent().removeClass("ui-disabled"); 1482 } 1483 }); 1484 })( jQuery ); 1485 /* 1486 * jQuery Mobile Framework : "textinput" plugin for text inputs, textareas 1487 * Copyright (c) jQuery Project 1488 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 1489 * Note: Code is in draft form and is subject to change 1490 */ 1491 (function($, undefined ) { 1492 $.widget( "mobile.textinput", $.mobile.widget, { 1493 options: { 1494 theme: null 1495 }, 1496 _create: function(){ 1497 var input = this.element, 1498 o = this.options, 1499 theme = o.theme, 1500 themeclass; 1501 1502 if ( !theme ) { 1503 var themedParent = this.element.closest("[class*='ui-bar-'],[class*='ui-body-']"); 1504 theme = themedParent.length ? 1505 /ui-(bar|body)-([a-z])/.exec( themedParent.attr("class") )[2] : 1506 "c"; 1507 } 1508 1509 themeclass = " ui-body-" + theme; 1510 1511 $('label[for='+input.attr('id')+']').addClass('ui-input-text'); 1512 1513 input.addClass('ui-input-text ui-body-'+ o.theme); 1514 1515 var focusedEl = input; 1516 1517 //"search" input widget 1518 if( input.is('[type="search"],[data-type="search"]') ){ 1519 focusedEl = input.wrap('<div class="ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-search'+ themeclass +'"></div>').parent(); 1520 var clearbtn = $('<a href="#" class="ui-input-clear" title="clear text">clear text</a>') 1521 .click(function(){ 1522 input.val('').focus(); 1523 input.trigger('change'); 1524 clearbtn.addClass('ui-input-clear-hidden'); 1525 return false; 1526 }) 1527 .appendTo(focusedEl) 1528 .buttonMarkup({icon: 'delete', iconpos: 'notext', corners:true, shadow:true}); 1529 1530 function toggleClear(){ 1531 if(input.val() == ''){ 1532 clearbtn.addClass('ui-input-clear-hidden'); 1533 } 1534 else{ 1535 clearbtn.removeClass('ui-input-clear-hidden'); 1536 } 1537 } 1538 1539 toggleClear(); 1540 input.keyup(toggleClear); 1541 } 1542 else{ 1543 input.addClass('ui-corner-all ui-shadow-inset' + themeclass); 1544 } 1545 1546 input 1547 .focus(function(){ 1548 focusedEl.addClass('ui-focus'); 1549 }) 1550 .blur(function(){ 1551 focusedEl.removeClass('ui-focus'); 1552 }); 1553 1554 //autogrow 1555 if ( input.is('textarea') ) { 1556 var extraLineHeight = 15, 1557 keyupTimeoutBuffer = 100, 1558 keyup = function() { 1559 var scrollHeight = input[0].scrollHeight, 1560 clientHeight = input[0].clientHeight; 1561 if ( clientHeight < scrollHeight ) { 1562 input.css({ height: (scrollHeight + extraLineHeight) }); 1563 } 1564 }, 1565 keyupTimeout; 1566 input.keyup(function() { 1567 clearTimeout( keyupTimeout ); 1568 keyupTimeout = setTimeout( keyup, keyupTimeoutBuffer ); 1569 }); 1570 } 1571 }, 1572 1573 disable: function(){ 1574 ( this.element.attr("disabled",true).is('[type="search"],[data-type="search"]') ? this.element.parent() : this.element ).addClass("ui-disabled"); 1575 }, 1576 1577 enable: function(){ 1578 ( this.element.attr("disabled", false).is('[type="search"],[data-type="search"]') ? this.element.parent() : this.element ).removeClass("ui-disabled"); 1579 } 1580 }); 1581 })( jQuery ); 1582 /* 1583 * jQuery Mobile Framework : "selectmenu" plugin 1584 * Copyright (c) jQuery Project 1585 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 1586 * Note: Code is in draft form and is subject to change 1587 */ 1588 (function($, undefined ) { 1589 $.widget( "mobile.selectmenu", $.mobile.widget, { 1590 options: { 1591 theme: null, 1592 disabled: false, 1593 icon: 'arrow-d', 1594 iconpos: 'right', 1595 inline: null, 1596 corners: true, 1597 shadow: true, 1598 iconshadow: true, 1599 menuPageTheme: 'b', 1600 overlayTheme: 'a' 1601 }, 1602 _create: function(){ 1603 1604 var self = this, 1605 1606 o = this.options, 1607 1608 select = this.element 1609 .attr( "tabindex", "-1" ) 1610 .wrap( "<div class='ui-select'>" ), 1611 1612 selectID = select.attr( "id" ), 1613 1614 label = $( "label[for="+ selectID +"]" ).addClass( "ui-select" ), 1615 1616 buttonId = selectID + "-button", 1617 1618 menuId = selectID + "-menu", 1619 1620 thisPage = select.closest( ".ui-page" ), 1621 1622 button = $( "<a>", { 1623 "href": "#", 1624 "role": "button", 1625 "id": buttonId, 1626 "aria-haspopup": "true", 1627 "aria-owns": menuId 1628 }) 1629 .text( $( select[0].options.item(select[0].selectedIndex) ).text() ) 1630 .insertBefore( select ) 1631 .buttonMarkup({ 1632 theme: o.theme, 1633 icon: o.icon, 1634 iconpos: o.iconpos, 1635 inline: o.inline, 1636 corners: o.corners, 1637 shadow: o.shadow, 1638 iconshadow: o.iconshadow 1639 }), 1640 1641 theme = /ui-btn-up-([a-z])/.exec( button.attr("class") )[1], 1642 1643 menuPage = $( "<div data-role='dialog' data-theme='"+ o.menuPageTheme +"'>" + 1644 "<div data-role='header'>" + 1645 "<div class='ui-title'>" + label.text() + "</div>"+ 1646 "</div>"+ 1647 "<div data-role='content'></div>"+ 1648 "</div>" ) 1649 .appendTo( $.pageContainer ) 1650 .page(), 1651 1652 menuPageContent = menuPage.find( ".ui-content" ), 1653 1654 screen = $( "<div>", {"class": "ui-listbox-screen ui-overlay ui-screen-hidden fade"}) 1655 .appendTo( thisPage ), 1656 1657 listbox = $( "<div>", { "class": "ui-listbox ui-listbox-hidden ui-overlay-shadow ui-corner-all pop ui-body-" + o.overlayTheme } ) 1658 .insertAfter(screen), 1659 1660 list = $( "<ul>", { 1661 "class": "ui-listbox-list", 1662 "id": menuId, 1663 "role": "listbox", 1664 "aria-labelledby": buttonId, 1665 "data-theme": theme 1666 }) 1667 .appendTo( listbox ), 1668 1669 menuType; 1670 1671 //expose to other methods 1672 $.extend(self, { 1673 select: select, 1674 selectID: selectID, 1675 label: label, 1676 buttonId:buttonId, 1677 menuId:menuId, 1678 thisPage:thisPage, 1679 button:button, 1680 menuPage:menuPage, 1681 menuPageContent:menuPageContent, 1682 screen:screen, 1683 listbox:listbox, 1684 list:list, 1685 menuType:menuType 1686 }); 1687 1688 1689 //create list from select, update state 1690 self.refresh(); 1691 1692 //disable if specified 1693 if( this.options.disabled ){ this.disable(); } 1694 1695 //events on native select 1696 select 1697 .change(function(){ 1698 self.refresh(); 1699 }) 1700 .focus(function(){ 1701 $(this).blur(); 1702 button.focus(); 1703 }); 1704 1705 //button events 1706 button.click(function(event){ 1707 self.open(); 1708 return false; 1709 }); 1710 1711 //events for list items 1712 list.delegate("li",'click', function(){ 1713 //update select 1714 var newIndex = list.find( "li" ).index( this ), 1715 prevIndex = select[0].selectedIndex; 1716 1717 select[0].selectedIndex = newIndex; 1718 1719 //trigger change event 1720 if(newIndex !== prevIndex){ 1721 select.trigger( "change" ); 1722 } 1723 1724 self.refresh(); 1725 1726 //hide custom select 1727 self.close(); 1728 return false; 1729 }); 1730 1731 //events on "screen" overlay 1732 screen.click(function(){ 1733 self.close(); 1734 return false; 1735 }); 1736 }, 1737 1738 _buildList: function(){ 1739 var self = this; 1740 1741 self.list.empty().filter('.ui-listview').listview('destroy'); 1742 1743 //populate menu with options from select element 1744 self.select.find( "option" ).each(function( i ){ 1745 var anchor = $("<a>", { 1746 "role": "option", 1747 "href": "#" 1748 }) 1749 .text( $(this).text() ); 1750 1751 $( "<li>", {"data-icon": "checkbox-on"}) 1752 .append( anchor ) 1753 .appendTo( self.list ); 1754 }); 1755 1756 //now populated, create listview 1757 self.list.listview(); 1758 }, 1759 1760 refresh: function( forceRebuild ){ 1761 var self = this, 1762 select = this.element, 1763 selected = select[0].selectedIndex; 1764 1765 if( forceRebuild || select[0].options.length > self.list.find('li').length ){ 1766 self._buildList(); 1767 } 1768 1769 self.button.find( ".ui-btn-text" ).text( $(select[0].options.item(selected)).text() ); 1770 self.list 1771 .find('li').removeClass( $.mobile.activeBtnClass ).attr('aria-selected', false) 1772 .eq(selected).addClass( $.mobile.activeBtnClass ).find('a').attr('aria-selected', true); 1773 }, 1774 1775 open: function(){ 1776 if( this.options.disabled ){ return; } 1777 1778 var self = this, 1779 menuHeight = self.list.outerHeight(), 1780 scrollTop = $(window).scrollTop(), 1781 btnOffset = self.button.offset().top, 1782 screenHeight = window.innerHeight; 1783 1784 function focusMenuItem(){ 1785 self.list.find( ".ui-btn-active" ).focus(); 1786 } 1787 1788 if( menuHeight > screenHeight - 80 || !$.support.scrollTop ){ 1789 1790 //for webos (set lastscroll using button offset) 1791 if( scrollTop == 0 && btnOffset > screenHeight ){ 1792 self.thisPage.one('pagehide',function(){ 1793 $(this).data('lastScroll', btnOffset); 1794 }); 1795 } 1796 1797 self.menuPage.one('pageshow',focusMenuItem); 1798 1799 self.menuType = "page"; 1800 self.menuPageContent.append( self.list ); 1801 $.mobile.changePage(self.menuPage, 'pop', false, false); 1802 } 1803 else { 1804 self.menuType = "overlay"; 1805 1806 self.screen 1807 .height( $(document).height() ) 1808 .removeClass('ui-screen-hidden'); 1809 1810 self.listbox 1811 .append( self.list ) 1812 .removeClass( "ui-listbox-hidden" ) 1813 .css({ 1814 top: scrollTop + (screenHeight/2), 1815 "margin-top": -menuHeight/2, 1816 left: window.innerWidth/2, 1817 "margin-left": -1* self.listbox.outerWidth() / 2 1818 }) 1819 .addClass("in"); 1820 1821 focusMenuItem(); 1822 } 1823 }, 1824 1825 close: function(){ 1826 if( this.options.disabled ){ return; } 1827 var self = this; 1828 1829 function focusButton(){ 1830 setTimeout(function(){ 1831 self.button.focus(); 1832 }, 40); 1833 1834 self.listbox.removeAttr('style').append( self.list ); 1835 } 1836 1837 if(self.menuType == "page"){ 1838 $.mobile.changePage([self.menuPage,self.thisPage], 'pop', true, false); 1839 self.menuPage.one("pagehide",function(){ 1840 focusButton(); 1841 //return false; 1842 }); 1843 } 1844 else{ 1845 self.screen.addClass( "ui-screen-hidden" ); 1846 self.listbox.addClass( "ui-listbox-hidden" ).removeAttr( "style" ).removeClass("in"); 1847 focusButton(); 1848 } 1849 1850 }, 1851 1852 disable: function(){ 1853 this.element.attr("disabled",true); 1854 this.button.addClass('ui-disabled').attr("aria-disabled", true); 1855 return this._setOption( "disabled", true ); 1856 }, 1857 1858 enable: function(){ 1859 this.element.attr("disabled",false); 1860 this.button.removeClass('ui-disabled').attr("aria-disabled", false); 1861 return this._setOption( "disabled", false ); 1862 } 1863 }); 1864 })( jQuery ); 1865 1866 /* 1867 * jQuery Mobile Framework : plugin for making button-like links 1868 * Copyright (c) jQuery Project 1869 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 1870 * Note: Code is in draft form and is subject to change 1871 */ 1872 (function($, undefined ) { 1873 1874 $.fn.buttonMarkup = function( options ){ 1875 return this.each( function() { 1876 var el = $( this ), 1877 o = $.extend( {}, $.fn.buttonMarkup.defaults, el.data(), options), 1878 1879 // Classes Defined 1880 buttonClass, 1881 innerClass = "ui-btn-inner", 1882 iconClass; 1883 1884 if ( attachEvents ) { 1885 attachEvents(); 1886 } 1887 1888 // if not, try to find closest theme container 1889 if ( !o.theme ) { 1890 var themedParent = el.closest("[class*='ui-bar-'],[class*='ui-body-']"); 1891 o.theme = themedParent.length ? 1892 /ui-(bar|body)-([a-z])/.exec( themedParent.attr("class") )[2] : 1893 "c"; 1894 } 1895 1896 buttonClass = "ui-btn ui-btn-up-" + o.theme; 1897 1898 if ( o.inline ) { 1899 buttonClass += " ui-btn-inline"; 1900 } 1901 1902 if ( o.icon ) { 1903 o.icon = "ui-icon-" + o.icon; 1904 o.iconpos = o.iconpos || "left"; 1905 1906 iconClass = "ui-icon " + o.icon; 1907 1908 if ( o.shadow ) { 1909 iconClass += " ui-icon-shadow" 1910 } 1911 } 1912 1913 if ( o.iconpos ) { 1914 buttonClass += " ui-btn-icon-" + o.iconpos; 1915 1916 if ( o.iconpos == "notext" && !el.attr("title") ) { 1917 el.attr( "title", el.text() ); 1918 } 1919 } 1920 1921 if ( o.corners ) { 1922 buttonClass += " ui-btn-corner-all"; 1923 innerClass += " ui-btn-corner-all"; 1924 } 1925 1926 if ( o.shadow ) { 1927 buttonClass += " ui-shadow"; 1928 } 1929 1930 el 1931 .attr( "data-theme", o.theme ) 1932 .addClass( buttonClass ); 1933 1934 var wrap = ("<D class='" + innerClass + "'><D class='ui-btn-text'></D>" + 1935 ( o.icon ? "<span class='" + iconClass + "'></span>" : "" ) + 1936 "</D>").replace(/D/g, o.wrapperEls); 1937 1938 el.wrapInner( wrap ); 1939 }); 1940 }; 1941 1942 $.fn.buttonMarkup.defaults = { 1943 corners: true, 1944 shadow: true, 1945 iconshadow: true, 1946 wrapperEls: "span" 1947 }; 1948 1949 var attachEvents = function() { 1950 $(".ui-btn").live({ 1951 mousedown: function() { 1952 var theme = $(this).attr( "data-theme" ); 1953 $(this).removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme ); 1954 }, 1955 mouseup: function() { 1956 var theme = $(this).attr( "data-theme" ); 1957 $(this).removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme ); 1958 }, 1959 "mouseover focus": function() { 1960 var theme = $(this).attr( "data-theme" ); 1961 $(this).removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-hover-" + theme ); 1962 }, 1963 "mouseout blur": function() { 1964 var theme = $(this).attr( "data-theme" ); 1965 $(this).removeClass( "ui-btn-hover-" + theme ).addClass( "ui-btn-up-" + theme ); 1966 } 1967 }); 1968 1969 attachEvents = null; 1970 }; 1971 1972 })(jQuery); 1973 /* 1974 * jQuery Mobile Framework : "button" plugin - links that proxy to native input/buttons 1975 * Copyright (c) jQuery Project 1976 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 1977 * Note: Code is in draft form and is subject to change 1978 */ 1979 (function($, undefined ) { 1980 $.widget( "mobile.button", $.mobile.widget, { 1981 options: { 1982 theme: null, 1983 icon: null, 1984 iconpos: null, 1985 inline: null, 1986 corners: true, 1987 shadow: true, 1988 iconshadow: true 1989 }, 1990 _create: function(){ 1991 var $el = this.element, 1992 o = this.options, 1993 type = $el.attr('type'); 1994 $el 1995 .addClass('ui-btn-hidden') 1996 .attr('tabindex','-1'); 1997 1998 //add ARIA role 1999 $( "<a>", { 2000 "href": "#", 2001 "role": "button", 2002 "aria-label": $el.attr( "type" ) 2003 } ) 2004 .text( $el.text() || $el.val() ) 2005 .insertBefore( $el ) 2006 .click(function(){ 2007 if( type == "submit" ){ 2008 $(this).closest('form').submit(); 2009 } 2010 else{ 2011 $el.click(); 2012 } 2013 2014 return false; 2015 }) 2016 .buttonMarkup({ 2017 theme: o.theme, 2018 icon: o.icon, 2019 iconpos: o.iconpos, 2020 inline: o.inline, 2021 corners: o.corners, 2022 shadow: o.shadow, 2023 iconshadow: o.iconshadow 2024 }); 2025 } 2026 }); 2027 })( jQuery );/* 2028 * jQuery Mobile Framework : "slider" plugin 2029 * Copyright (c) jQuery Project 2030 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2031 * Note: Code is in draft form and is subject to change 2032 */ 2033 (function($, undefined ) { 2034 $.widget( "mobile.slider", $.mobile.widget, { 2035 options: { 2036 theme: null, 2037 trackTheme: null 2038 }, 2039 _create: function(){ 2040 var control = this.element, 2041 2042 parentTheme = control.parents('[class*=ui-bar-],[class*=ui-body-]').eq(0), 2043 2044 parentTheme = parentTheme.length ? parentTheme.attr('class').match(/ui-(bar|body)-([a-z])/)[2] : 'c', 2045 2046 theme = this.options.theme ? this.options.theme : parentTheme, 2047 2048 trackTheme = this.options.trackTheme ? this.options.trackTheme : parentTheme, 2049 2050 cType = control[0].nodeName.toLowerCase(), 2051 selectClass = (cType == 'select') ? 'ui-slider-switch' : '', 2052 controlID = control.attr('id'), 2053 labelID = controlID + '-label', 2054 label = $('[for='+ controlID +']').attr('id',labelID), 2055 val = (cType == 'input') ? parseFloat(control.val()) : control[0].selectedIndex, 2056 min = (cType == 'input') ? parseFloat(control.attr('min')) : 0, 2057 max = (cType == 'input') ? parseFloat(control.attr('max')) : control.find('option').length-1, 2058 percent = ((parseFloat(val) - min) / (max - min)) * 100, 2059 snappedPercent = percent, 2060 slider = $('<div class="ui-slider '+ selectClass +' ui-btn-down-'+ trackTheme+' ui-btn-corner-all" role="application"></div>'), 2061 handle = $('<a href="#" class="ui-slider-handle"></a>') 2062 .appendTo(slider) 2063 .buttonMarkup({corners: true, theme: theme, shadow: true}) 2064 .attr({ 2065 'role': 'slider', 2066 'aria-valuemin': min, 2067 'aria-valuemax': max, 2068 'aria-valuenow': val, 2069 'aria-valuetext': val, 2070 'title': val, 2071 'aria-labelledby': labelID 2072 }), 2073 dragging = false; 2074 2075 2076 if(cType == 'select'){ 2077 slider.wrapInner('<div class="ui-slider-inneroffset"></div>'); 2078 var options = control.find('option'); 2079 2080 control.find('option').each(function(i){ 2081 var side = (i==0) ?'b':'a', 2082 corners = (i==0) ? 'right' :'left', 2083 theme = (i==0) ? ' ui-btn-down-' + trackTheme :' ui-btn-active'; 2084 $('<div class="ui-slider-labelbg ui-slider-labelbg-'+ side + theme +' ui-btn-corner-'+ corners+'"></div>').prependTo(slider); 2085 $('<span class="ui-slider-label ui-slider-label-'+ side + theme +' ui-btn-corner-'+ corners+'" role="img">'+$(this).text()+'</span>').prependTo(handle); 2086 }); 2087 2088 } 2089 2090 function updateControl(val){ 2091 if(cType == 'input'){ 2092 control.val(val); 2093 } 2094 else { 2095 control[0].selectedIndex = val; 2096 } 2097 control.trigger("change"); 2098 } 2099 2100 function slideUpdate(event, val){ 2101 if (val){ 2102 percent = (parseFloat(val) - min) / (max - min) * 100; 2103 } else { 2104 var data = event.originalEvent.touches ? event.originalEvent.touches[ 0 ] : event, 2105 // a slight tolerance helped get to the ends of the slider 2106 tol = 8; 2107 if( !dragging 2108 || data.pageX < slider.offset().left - tol 2109 || data.pageX > slider.offset().left + slider.width() + tol ){ 2110 return; 2111 } 2112 percent = Math.round(((data.pageX - slider.offset().left) / slider.width() ) * 100); 2113 } 2114 2115 if( percent < 0 ) { percent = 0; } 2116 if( percent > 100 ) { percent = 100; } 2117 var newval = Math.round( (percent/100) * (max-min) ) + min; 2118 2119 if( newval < min ) { newval = min; } 2120 if( newval > max ) { newval = max; } 2121 //flip the stack of the bg colors 2122 if(percent > 60 && cType == 'select'){ 2123 2124 } 2125 snappedPercent = Math.round( newval / (max-min) * 100 ); 2126 handle.css('left', percent + '%'); 2127 handle.attr({ 2128 'aria-valuenow': (cType == 'input') ? newval : control.find('option').eq(newval).attr('value'), 2129 'aria-valuetext': (cType == 'input') ? newval : control.find('option').eq(newval).text(), 2130 'title': newval 2131 }); 2132 updateSwitchClass(newval); 2133 updateControl(newval); 2134 } 2135 2136 function updateSwitchClass(val){ 2137 if(cType == 'input'){return;} 2138 if(val == 0){ slider.addClass('ui-slider-switch-a').removeClass('ui-slider-switch-b'); } 2139 else { slider.addClass('ui-slider-switch-b').removeClass('ui-slider-switch-a'); } 2140 } 2141 2142 updateSwitchClass(val); 2143 2144 function updateSnap(){ 2145 if(cType == 'select'){ 2146 handle 2147 .addClass('ui-slider-handle-snapping') 2148 .css('left', snappedPercent + '%') 2149 .animationComplete(function(){ 2150 handle.removeClass('ui-slider-handle-snapping'); 2151 }); 2152 } 2153 } 2154 2155 label.addClass('ui-slider'); 2156 2157 control 2158 .addClass((cType == 'input') ? 'ui-slider-input' : 'ui-slider-switch') 2159 .keyup(function(e){ 2160 slideUpdate(e, $(this).val() ); 2161 }); 2162 2163 $(document).bind($.support.touch ? "touchmove" : "mousemove", function(event){ 2164 if(dragging){ 2165 slideUpdate(event); 2166 return false; 2167 } 2168 }); 2169 2170 slider 2171 .bind($.support.touch ? "touchstart" : "mousedown", function(event){ 2172 dragging = true; 2173 if((cType == 'select')){ 2174 val = control[0].selectedIndex; 2175 } 2176 slideUpdate(event); 2177 return false; 2178 }); 2179 2180 slider 2181 .add(document) 2182 .bind($.support.touch ? "touchend" : "mouseup", function(event){ 2183 if(dragging){ 2184 dragging = false; 2185 if(cType == 'select'){ 2186 if(val == control[0].selectedIndex){ 2187 val = val == 0 ? 1 : 0; 2188 //tap occurred, but value didn't change. flip it! 2189 slideUpdate(event,val); 2190 } 2191 updateSnap(); 2192 } 2193 return false; 2194 } 2195 }); 2196 2197 slider.insertAfter(control); 2198 2199 handle 2200 .css('left', percent + '%') 2201 .bind('click', function(e){ return false; }); 2202 } 2203 }); 2204 })( jQuery ); 2205 2206 /* 2207 * jQuery Mobile Framework : "collapsible" plugin 2208 * Copyright (c) jQuery Project 2209 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2210 * Note: Code is in draft form and is subject to change 2211 */ 2212 (function($, undefined ) { 2213 $.widget( "mobile.collapsible", $.mobile.widget, { 2214 options: { 2215 expandCueText: ' click to expand contents', 2216 collapseCueText: ' click to collapse contents', 2217 collapsed: false, 2218 heading: '>:header,>legend', 2219 theme: null, 2220 iconTheme: 'd' 2221 }, 2222 _create: function(){ 2223 2224 var $el = this.element, 2225 o = this.options, 2226 collapsibleContain = $el.addClass('ui-collapsible-contain'), 2227 collapsibleHeading = $el.find(o.heading).eq(0), 2228 collapsibleContent = collapsibleContain.wrapInner('<div class="ui-collapsible-content"></div>').find('.ui-collapsible-content'), 2229 collapsibleParent = $el.closest('[data-role="collapsible-set"]').addClass('ui-collapsible-set'); 2230 2231 //replace collapsibleHeading if it's a legend 2232 if(collapsibleHeading.is('legend')){ 2233 collapsibleHeading = $('<div role="heading">'+ collapsibleHeading.html() +'</div>').insertBefore(collapsibleHeading); 2234 collapsibleHeading.next().remove(); 2235 } 2236 2237 //drop heading in before content 2238 collapsibleHeading.insertBefore(collapsibleContent); 2239 2240 //modify markup & attributes 2241 collapsibleHeading.addClass('ui-collapsible-heading') 2242 .append('<span class="ui-collapsible-heading-status"></span>') 2243 .wrapInner('<a href="#" class="ui-collapsible-heading-toggle"></a>') 2244 .find('a:eq(0)') 2245 .buttonMarkup({ 2246 shadow: !!!collapsibleParent.length, 2247 corners:false, 2248 iconPos: 'left', 2249 icon: 'plus', 2250 theme: o.theme 2251 }) 2252 .find('.ui-icon') 2253 .removeAttr('class') 2254 .buttonMarkup({ 2255 shadow: true, 2256 corners:true, 2257 iconPos: 'notext', 2258 icon: 'plus', 2259 theme: o.iconTheme 2260 }); 2261 2262 if( !collapsibleParent.length ){ 2263 collapsibleHeading 2264 .find('a:eq(0)') 2265 .addClass('ui-corner-all') 2266 .find('.ui-btn-inner') 2267 .addClass('ui-corner-all'); 2268 } 2269 else { 2270 if( collapsibleContain.data('collapsible-last') ){ 2271 collapsibleHeading 2272 .find('a:eq(0), .ui-btn-inner') 2273 .addClass('ui-corner-bottom'); 2274 } 2275 } 2276 2277 2278 //events 2279 collapsibleContain 2280 .bind('collapse', function(event){ 2281 if( !event.isDefaultPrevented() ){ 2282 event.preventDefault(); 2283 collapsibleHeading 2284 .addClass('ui-collapsible-heading-collapsed') 2285 .find('.ui-collapsible-heading-status').text(o.expandCueText); 2286 2287 collapsibleHeading.find('.ui-icon').removeClass('ui-icon-minus').addClass('ui-icon-plus'); 2288 collapsibleContent.addClass('ui-collapsible-content-collapsed').attr('aria-hidden',true); 2289 2290 if( collapsibleContain.data('collapsible-last') ){ 2291 collapsibleHeading 2292 .find('a:eq(0), .ui-btn-inner') 2293 .addClass('ui-corner-bottom'); 2294 } 2295 } 2296 2297 }) 2298 .bind('expand', function(event){ 2299 if( !event.isDefaultPrevented() ){ 2300 event.preventDefault(); 2301 collapsibleHeading 2302 .removeClass('ui-collapsible-heading-collapsed') 2303 .find('.ui-collapsible-heading-status').text(o.collapseCueText); 2304 2305 collapsibleHeading.find('.ui-icon').removeClass('ui-icon-plus').addClass('ui-icon-minus'); 2306 collapsibleContent.removeClass('ui-collapsible-content-collapsed').attr('aria-hidden',false); 2307 2308 if( collapsibleContain.data('collapsible-last') ){ 2309 collapsibleHeading 2310 .find('a:eq(0), .ui-btn-inner') 2311 .removeClass('ui-corner-bottom'); 2312 } 2313 2314 } 2315 }) 2316 .trigger(o.collapsed ? 'collapse' : 'expand'); 2317 2318 2319 //close others in a set 2320 if( collapsibleParent.length && !collapsibleParent.data("collapsiblebound") ){ 2321 collapsibleParent 2322 .data("collapsiblebound", true) 2323 .bind("expand", function( event ){ 2324 $(this).find( ".ui-collapsible-contain" ) 2325 .not( $(event.target).closest( ".ui-collapsible-contain" ) ) 2326 .not( "> .ui-collapsible-contain .ui-collapsible-contain" ) 2327 .trigger( "collapse" ); 2328 }) 2329 var set = collapsibleParent.find('[data-role=collapsible]') 2330 2331 set.first() 2332 .find('a:eq(0)') 2333 .addClass('ui-corner-top') 2334 .find('.ui-btn-inner') 2335 .addClass('ui-corner-top'); 2336 2337 set.last().data('collapsible-last', true) 2338 } 2339 2340 collapsibleHeading.click(function(){ 2341 if( collapsibleHeading.is('.ui-collapsible-heading-collapsed') ){ 2342 collapsibleContain.trigger('expand'); 2343 } 2344 else { 2345 collapsibleContain.trigger('collapse'); 2346 } 2347 return false; 2348 }); 2349 2350 } 2351 }); 2352 })( jQuery );/* 2353 * jQuery Mobile Framework: "controlgroup" plugin - corner-rounding for groups of buttons, checks, radios, etc 2354 * Copyright (c) jQuery Project 2355 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2356 * Note: Code is in draft form and is subject to change 2357 */ 2358 (function($, undefined ) { 2359 $.fn.controlgroup = function(options){ 2360 2361 return $(this).each(function(){ 2362 var o = $.extend({ 2363 direction: $( this ).data( "type" ) || "vertical", 2364 shadow: false 2365 },options); 2366 var groupheading = $(this).find('>legend'), 2367 flCorners = o.direction == 'horizontal' ? ['ui-corner-left', 'ui-corner-right'] : ['ui-corner-top', 'ui-corner-bottom'], 2368 type = $(this).find('input:eq(0)').attr('type'); 2369 2370 //replace legend with more stylable replacement div 2371 if( groupheading.length ){ 2372 $(this).wrapInner('<div class="ui-controlgroup-controls"></div>'); 2373 $('<div role="heading" class="ui-controlgroup-label">'+ groupheading.html() +'</div>').insertBefore( $(this).children(0) ); 2374 groupheading.remove(); 2375 } 2376 2377 $(this).addClass('ui-corner-all ui-controlgroup ui-controlgroup-'+o.direction); 2378 2379 function flipClasses(els){ 2380 els 2381 .removeClass('ui-btn-corner-all ui-shadow') 2382 .eq(0).addClass(flCorners[0]) 2383 .end() 2384 .filter(':last').addClass(flCorners[1]).addClass('ui-controlgroup-last'); 2385 } 2386 flipClasses($(this).find('.ui-btn')); 2387 flipClasses($(this).find('.ui-btn-inner')); 2388 if(o.shadow){ 2389 $(this).addClass('ui-shadow'); 2390 } 2391 }); 2392 }; 2393 })(jQuery);/* 2394 * jQuery Mobile Framework : "fieldcontain" plugin - simple class additions to make form row separators 2395 * Copyright (c) jQuery Project 2396 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2397 * Note: Code is in draft form and is subject to change 2398 */ 2399 (function($, undefined ) { 2400 $.fn.fieldcontain = function(options){ 2401 var o = $.extend({ 2402 theme: 'c' 2403 },options); 2404 return $(this).addClass('ui-field-contain ui-body ui-br'); 2405 }; 2406 })(jQuery);/* 2407 * jQuery Mobile Framework : "listview" plugin 2408 * Copyright (c) jQuery Project 2409 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2410 * Note: Code is in draft form and is subject to change 2411 */ 2412 (function($, undefined ) { 2413 2414 $.widget( "mobile.listview", $.mobile.widget, { 2415 options: { 2416 theme: "c", 2417 countTheme: "c", 2418 headerTheme: "b", 2419 dividerTheme: "b", 2420 splitIcon: "arrow-r", 2421 splitTheme: "b", 2422 inset: false 2423 }, 2424 2425 _create: function() { 2426 var $list = this.element, 2427 o = this.options; 2428 2429 // create listview markup 2430 $list 2431 .addClass( "ui-listview" ) 2432 .attr( "role", "listbox" ) 2433 2434 if ( o.inset ) { 2435 $list.addClass( "ui-listview-inset ui-corner-all ui-shadow" ); 2436 } 2437 2438 $list.delegate( ".ui-li", "focusin", function() { 2439 $( this ).attr( "tabindex", "0" ); 2440 }); 2441 2442 this._itemApply( $list, $list ); 2443 2444 this.refresh( true ); 2445 2446 //keyboard events for menu items 2447 $list.keydown(function( e ) { 2448 var target = $( e.target ), 2449 li = target.closest( "li" ); 2450 2451 // switch logic based on which key was pressed 2452 switch ( e.keyCode ) { 2453 // up or left arrow keys 2454 case 38: 2455 var prev = li.prev(); 2456 2457 // if there's a previous option, focus it 2458 if ( prev.length ) { 2459 target 2460 .blur() 2461 .attr( "tabindex", "-1" ); 2462 2463 prev.find( "a" ).first().focus(); 2464 } 2465 2466 return false; 2467 break; 2468 2469 // down or right arrow keys 2470 case 40: 2471 var next = li.next(); 2472 2473 // if there's a next option, focus it 2474 if ( next.length ) { 2475 target 2476 .blur() 2477 .attr( "tabindex", "-1" ); 2478 2479 next.find( "a" ).first().focus(); 2480 } 2481 2482 return false; 2483 break; 2484 2485 case 39: 2486 var a = li.find( "a.ui-li-link-alt" ); 2487 2488 if ( a.length ) { 2489 target.blur(); 2490 a.first().focus(); 2491 } 2492 2493 return false; 2494 break; 2495 2496 case 37: 2497 var a = li.find( "a.ui-link-inherit" ); 2498 2499 if ( a.length ) { 2500 target.blur(); 2501 a.first().focus(); 2502 } 2503 2504 return false; 2505 break; 2506 2507 // if enter or space is pressed, trigger click 2508 case 13: 2509 case 32: 2510 target.trigger( "click" ); 2511 2512 return false; 2513 break; 2514 } 2515 }); 2516 2517 // tapping the whole LI triggers click on the first link 2518 $list.delegate( "li", "click", function(event) { 2519 if ( !$( event.target ).closest( "a" ).length ) { 2520 $( this ).find( "a" ).first().trigger( "click" ); 2521 return false; 2522 } 2523 }); 2524 }, 2525 2526 _itemApply: function( $list, item ) { 2527 // TODO class has to be defined in markup 2528 item.find( ".ui-li-count" ) 2529 .addClass( "ui-btn-up-" + ($list.data( "counttheme" ) || this.options.countTheme) + " ui-btn-corner-all" ); 2530 2531 item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ); 2532 2533 item.find( "p, dl" ).addClass( "ui-li-desc" ); 2534 2535 item.find( "img" ).addClass( "ui-li-thumb" ).each(function() { 2536 $( this ).closest( "li" ) 2537 .addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" ); 2538 }); 2539 2540 var aside = item.find( ".ui-li-aside" ); 2541 2542 if ( aside.length ) { 2543 aside.each(function(i, el) { 2544 $(el).prependTo( $(el).parent() ); //shift aside to front for css float 2545 }); 2546 } 2547 2548 if ( $.support.cssPseudoElement || !$.nodeName( item[0], "ol" ) ) { 2549 return; 2550 } 2551 }, 2552 2553 refresh: function( create ) { 2554 this._createSubPages(); 2555 2556 var o = this.options, 2557 $list = this.element, 2558 self = this, 2559 dividertheme = $list.data( "dividertheme" ) || o.dividerTheme, 2560 li = $list.children( "li" ), 2561 counter = $.support.cssPseudoElement || !$.nodeName( $list[0], "ol" ) ? 0 : 1; 2562 2563 if ( counter ) { 2564 $list.find( ".ui-li-dec" ).remove(); 2565 } 2566 2567 li.attr({ "role": "option", "tabindex": "-1" }); 2568 2569 li.first().attr( "tabindex", "0" ); 2570 2571 li.each(function( pos ) { 2572 var item = $( this ), 2573 itemClass = "ui-li"; 2574 2575 // If we're creating the element, we update it regardless 2576 if ( !create && item.hasClass( "ui-li" ) ) { 2577 return; 2578 } 2579 2580 var a = item.find( "a" ); 2581 2582 if ( a.length ) { 2583 item 2584 .buttonMarkup({ 2585 wrapperEls: "div", 2586 shadow: false, 2587 corners: false, 2588 iconpos: "right", 2589 icon: a.length > 1 ? false : item.data("icon") || "arrow-r", 2590 theme: o.theme 2591 }); 2592 2593 a.first().addClass( "ui-link-inherit" ); 2594 2595 if ( a.length > 1 ) { 2596 itemClass += " ui-li-has-alt"; 2597 2598 var last = a.last(), 2599 splittheme = $list.data( "splittheme" ) || last.data( "theme" ) || o.splitTheme; 2600 2601 last 2602 .attr( "title", last.text() ) 2603 .addClass( "ui-li-link-alt" ) 2604 .empty() 2605 .buttonMarkup({ 2606 shadow: false, 2607 corners: false, 2608 theme: o.theme, 2609 icon: false, 2610 iconpos: false 2611 }) 2612 .find( ".ui-btn-inner" ) 2613 .append( $( "<span>" ).buttonMarkup({ 2614 shadow: true, 2615 corners: true, 2616 theme: splittheme, 2617 iconpos: "notext", 2618 icon: $list.data( "spliticon" ) || last.data( "icon" ) || o.splitIcon 2619 } ) ); 2620 } 2621 2622 } else if ( item.data( "role" ) === "list-divider" ) { 2623 itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme; 2624 item.attr( "role", "heading" ); 2625 2626 //reset counter when a divider heading is encountered 2627 if ( counter ) { 2628 counter = 1; 2629 } 2630 2631 } else { 2632 itemClass += " ui-li-static ui-btn-up-" + o.theme; 2633 } 2634 2635 if ( pos === 0 ) { 2636 if ( o.inset ) { 2637 itemClass += " ui-corner-top"; 2638 2639 item 2640 .add( item.find( ".ui-btn-inner" ) ) 2641 .find( ".ui-li-link-alt" ) 2642 .addClass( "ui-corner-tr" ) 2643 .end() 2644 .find( ".ui-li-thumb" ) 2645 .addClass( "ui-corner-tl" ); 2646 } 2647 2648 } else if ( pos === li.length - 1 ) { 2649 2650 if ( o.inset ) { 2651 itemClass += " ui-corner-bottom"; 2652 2653 item 2654 .add( item.find( ".ui-btn-inner" ) ) 2655 .find( ".ui-li-link-alt" ) 2656 .addClass( "ui-corner-br" ) 2657 .end() 2658 .find( ".ui-li-thumb" ) 2659 .addClass( "ui-corner-bl" ); 2660 } 2661 } 2662 2663 if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) { 2664 item 2665 .find( ".ui-link-inherit" ).first() 2666 .addClass( "ui-li-jsnumbering" ) 2667 .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" ); 2668 } 2669 2670 item.addClass( itemClass ); 2671 2672 if ( !create ) { 2673 self._itemApply( $list, item ); 2674 } 2675 }); 2676 }, 2677 2678 _createSubPages: function() { 2679 var parentList = this.element, 2680 parentPage = parentList.closest( ".ui-page" ), 2681 parentId = parentPage.attr( "id" ), 2682 o = this.options, 2683 persistentFooterID = parentPage.find( "[data-role='footer']" ).data( "id" ); 2684 2685 $( parentList.find( "ul, ol" ).toArray().reverse() ).each(function( i ) { 2686 var list = $( this ), 2687 parent = list.parent(), 2688 title = parent.contents()[ 0 ].nodeValue.split("\n")[0], 2689 id = parentId + "&" + $.mobile.subPageUrlKey + "=" + $.mobile.idStringEscape(title + " " + i), 2690 theme = list.data( "theme" ) || o.theme, 2691 countTheme = list.data( "counttheme" ) || parentList.data( "counttheme" ) || o.countTheme, 2692 newPage = list.wrap( "<div data-role='page'><div data-role='content'></div></div>" ) 2693 .parent() 2694 .before( "<div data-role='header' data-theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" ) 2695 .after( persistentFooterID ? $( "<div>", { "data-role": "footer", "data-id": persistentFooterID, "class": "ui-footer-duplicate" } ) : "" ) 2696 .parent() 2697 .attr({ 2698 id: id, 2699 "data-theme": theme, 2700 "data-count-theme": countTheme 2701 }) 2702 .appendTo( $.pageContainer ); 2703 2704 2705 2706 newPage.page(); 2707 2708 parent.html( "<a href='#" + id + "'>" + title + "</a>" ); 2709 }).listview(); 2710 } 2711 }); 2712 2713 })( jQuery ); 2714 /* 2715 * jQuery Mobile Framework : "listview" filter extension 2716 * Copyright (c) jQuery Project 2717 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2718 * Note: Code is in draft form and is subject to change 2719 */ 2720 (function($, undefined ) { 2721 2722 $.mobile.listview.prototype.options.filter = false; 2723 2724 $( "[data-role='listview']" ).live( "listviewcreate", function() { 2725 var list = $( this ), 2726 listview = list.data( "listview" ); 2727 if ( !listview.options.filter ) { 2728 return; 2729 } 2730 2731 var wrapper = $( "<form>", { "class": "ui-listview-filter ui-bar-c", "role": "search" } ), 2732 2733 search = $( "<input>", { 2734 placeholder: "Filter results...", 2735 "data-type": "search" 2736 }) 2737 .bind( "keyup change", function() { 2738 var val = this.value.toLowerCase();; 2739 list.children().show(); 2740 if ( val ) { 2741 list.children().filter(function() { 2742 return $( this ).text().toLowerCase().indexOf( val ) === -1; 2743 }).hide(); 2744 } 2745 2746 //listview._numberItems(); 2747 }) 2748 .appendTo( wrapper ) 2749 .textinput(); 2750 2751 wrapper.insertBefore( list ); 2752 }); 2753 2754 })( jQuery ); 2755 /* 2756 * jQuery Mobile Framework : "dialog" plugin. 2757 * Copyright (c) jQuery Project 2758 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2759 * Note: Code is in draft form and is subject to change 2760 */ 2761 (function($, undefined ) { 2762 $.widget( "mobile.dialog", $.mobile.widget, { 2763 options: {}, 2764 _create: function(){ 2765 var self = this, 2766 $el = self.element, 2767 $prevPage = $.mobile.activePage, 2768 $closeBtn = $('<a href="#" data-icon="delete" data-iconpos="notext">Close</a>'); 2769 2770 $el.delegate("a, submit", "click submit", function(e){ 2771 if( e.type == "click" && ( $(e.target).closest('[data-back]') || $(e.target).closest($closeBtn) ) ){ 2772 self.close(); 2773 return false; 2774 } 2775 //otherwise, assume we're headed somewhere new. set activepage to dialog so the transition will work 2776 $.mobile.activePage = this.element; 2777 }); 2778 2779 this.element 2780 .bind("pageshow",function(){ 2781 return false; 2782 }) 2783 //add ARIA role 2784 .attr("role","dialog") 2785 .addClass('ui-page ui-dialog ui-body-a') 2786 .find('[data-role=header]') 2787 .addClass('ui-corner-top ui-overlay-shadow') 2788 .prepend( $closeBtn ) 2789 .end() 2790 .find('.ui-content:not([class*="ui-body-"])') 2791 .addClass('ui-body-c') 2792 .end() 2793 .find('.ui-content,[data-role=footer]') 2794 .last() 2795 .addClass('ui-corner-bottom ui-overlay-shadow'); 2796 2797 $(window).bind('hashchange',function(){ 2798 if( $el.is('.ui-page-active') ){ 2799 self.close(); 2800 $el.bind('pagehide',function(){ 2801 $.mobile.updateHash( $prevPage.attr('id'), true); 2802 }); 2803 } 2804 }); 2805 2806 }, 2807 2808 close: function(){ 2809 $.mobile.changePage([this.element, $.mobile.activePage], undefined, true, true ); 2810 } 2811 }); 2812 })( jQuery );/* 2813 * jQuery Mobile Framework : "navbar" plugin 2814 * Copyright (c) jQuery Project 2815 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2816 * Note: Code is in draft form and is subject to change 2817 */ 2818 (function($, undefined ) { 2819 $.widget( "mobile.navbar", $.mobile.widget, { 2820 options: { 2821 iconpos: 'top', 2822 grid: null 2823 }, 2824 _create: function(){ 2825 var $navbar = this.element, 2826 $navbtns = $navbar.find("a"), 2827 iconpos = $navbtns.filter('[data-icon]').length ? this.options.iconpos : undefined; 2828 2829 $navbar 2830 .addClass('ui-navbar') 2831 .attr("role","navigation") 2832 .find("ul") 2833 .grid({grid: this.options.grid }); 2834 2835 if( !iconpos ){ 2836 $navbar.addClass("ui-navbar-noicons"); 2837 } 2838 2839 $navbtns 2840 .buttonMarkup({ 2841 corners: false, 2842 shadow: false, 2843 iconpos: iconpos 2844 }); 2845 2846 $navbar.delegate("a", "click",function(event){ 2847 $navbtns.removeClass("ui-btn-active"); 2848 }); 2849 } 2850 }); 2851 })( jQuery );/* 2852 * jQuery Mobile Framework : plugin for creating CSS grids 2853 * Copyright (c) jQuery Project 2854 * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. 2855 * Note: Code is in draft form and is subject to change 2856 */ 2857 (function($, undefined ) { 2858 $.fn.grid = function(options){ 2859 return $(this).each(function(){ 2860 var o = $.extend({ 2861 grid: null 2862 },options); 2863 2864 2865 var $kids = $(this).children(), 2866 gridCols = {a: 2, b:3, c:4, d:5}, 2867 grid = o.grid, 2868 iterator; 2869 2870 if( !grid ){ 2871 if( $kids.length <= 5 ){ 2872 for(var letter in gridCols){ 2873 if(gridCols[letter] == $kids.length){ grid = letter; } 2874 } 2875 } 2876 else{ 2877 grid = 'a'; 2878 } 2879 } 2880 iterator = gridCols[grid]; 2881 2882 $(this).addClass('ui-grid-' + grid); 2883 2884 $kids.filter(':nth-child(' + iterator + 'n+1)').addClass('ui-block-a'); 2885 $kids.filter(':nth-child(' + iterator + 'n+2)').addClass('ui-block-b'); 2886 2887 if(iterator > 2){ 2888 $kids.filter(':nth-child(3n+3)').addClass('ui-block-c'); 2889 } 2890 if(iterator> 3){ 2891 $kids.filter(':nth-child(4n+4)').addClass('ui-block-d'); 2892 } 2893 if(iterator > 4){ 2894 $kids.filter(':nth-child(5n+5)').addClass('ui-block-e'); 2895 } 2896 2897 }); 2898 }; 2899 })(jQuery); 2900 2901 /*! 2902 * jQuery Mobile v@VERSION 2903 * http://jquerymobile.com/ 2904 * 2905 * Copyright 2010, jQuery Project 2906 * Dual licensed under the MIT or GPL Version 2 licenses. 2907 * http://jquery.org/license 2908 */ 2909 2910 (function( $, window, undefined ) { 2911 2912 //jQuery.mobile configurable options 2913 $.extend( $.mobile, { 2914 2915 //define the url parameter used for referencing widget-generated sub-pages. 2916 //Translates to to example.html&ui-page=subpageIdentifier 2917 //hash segment before &ui-page= is used to make Ajax request 2918 subPageUrlKey: 'ui-page', 2919 2920 //anchor links with a data-rel, or pages with a data-role, that match these selectors will be untrackable in history 2921 //(no change in URL, not bookmarkable) 2922 nonHistorySelectors: 'dialog', 2923 2924 //class assigned to page currently in view, and during transitions 2925 activePageClass: 'ui-page-active', 2926 2927 //class used for "active" button state, from CSS framework 2928 activeBtnClass: 'ui-btn-active', 2929 2930 //automatically handle link clicks through Ajax, when possible 2931 ajaxLinksEnabled: true, 2932 2933 //automatically handle form submissions through Ajax, when possible 2934 ajaxFormsEnabled: true, 2935 2936 //available CSS transitions 2937 transitions: ['slide', 'slideup', 'slidedown', 'pop', 'flip', 'fade'], 2938 2939 //set default transition - 'none' for no transitions 2940 defaultTransition: 'slide', 2941 2942 //show loading message during Ajax requests 2943 //if false, message will not appear, but loading classes will still be toggled on html el 2944 loadingMessage: "loading", 2945 2946 //configure meta viewport tag's content attr: 2947 metaViewportContent: "width=device-width, minimum-scale=1, maximum-scale=1", 2948 2949 //support conditions that must be met in order to proceed 2950 gradeA: function(){ 2951 return $.support.mediaquery; 2952 } 2953 }); 2954 2955 //trigger mobileinit event - useful hook for configuring $.mobile settings before they're used 2956 $( window.document ).trigger('mobileinit'); 2957 2958 //if device support condition(s) aren't met, leave things as they are -> a basic, usable experience, 2959 //otherwise, proceed with the enhancements 2960 if ( !$.mobile.gradeA() ) { 2961 return; 2962 } 2963 2964 //define vars for interal use 2965 var $window = $(window), 2966 $html = $('html'), 2967 $head = $('head'), 2968 2969 //to be populated at DOM ready 2970 $body, 2971 2972 //loading div which appears during Ajax requests 2973 //will not appear if $.mobile.loadingMessage is false 2974 $loader = $.mobile.loadingMessage ? 2975 $('<div class="ui-loader ui-body-a ui-corner-all">'+ 2976 '<span class="ui-icon ui-icon-loading spin"></span>'+ 2977 '<h1>'+ $.mobile.loadingMessage +'</h1>'+ 2978 '</div>') 2979 : undefined, 2980 2981 //define meta viewport tag, if content is defined 2982 $metaViewport = $.mobile.metaViewportContent ? $("<meta>", { name: "viewport", content: $.mobile.metaViewportContent}).prependTo( $head ) : undefined, 2983 2984 //define baseUrl for use in relative url management 2985 baseUrl = getPathDir( location.protocol + '//' + location.host + location.pathname ), 2986 2987 //define base element, for use in routing asset urls that are referenced in Ajax-requested markup 2988 $base = $.support.dynamicBaseTag ? $("<base>", { href: baseUrl }).prependTo( $head ) : undefined, 2989 2990 //will be defined as first page element in DOM 2991 $startPage, 2992 2993 //will be defined as $startPage.parent(), which is usually the body element 2994 //will receive ui-mobile-viewport class 2995 $pageContainer, 2996 2997 //will be defined when a link is clicked and given an active class 2998 $activeClickedLink = null, 2999 3000 //array of pages that are visited during a single page load 3001 //length will grow as pages are visited, and shrink as "back" link/button is clicked 3002 //each item has a url (string matches ID), and transition (saved for reuse when "back" link/button is clicked) 3003 urlStack = [ { 3004 url: location.hash.replace( /^#/, "" ), 3005 transition: undefined 3006 } ], 3007 3008 //define first selector to receive focus when a page is shown 3009 focusable = "[tabindex],a,button:visible,select:visible,input", 3010 3011 //contains role for next page, if defined on clicked link via data-rel 3012 nextPageRole = null, 3013 3014 //enable/disable hashchange event listener 3015 //toggled internally when location.hash is updated to match the url of a successful page load 3016 hashListener = true, 3017 3018 //media-query-like width breakpoints, which are translated to classes on the html element 3019 resolutionBreakpoints = [320,480,768,1024]; 3020 3021 //add mobile, initial load "rendering" classes to docEl 3022 $html.addClass('ui-mobile ui-mobile-rendering'); 3023 3024 // TODO: don't expose (temporary during code reorg) 3025 $.mobile.urlStack = urlStack; 3026 3027 //consistent string escaping for urls and IDs 3028 function idStringEscape(str){ 3029 return str.replace(/[^a-zA-Z0-9]/g, '-'); 3030 } 3031 3032 $.mobile.idStringEscape = idStringEscape; 3033 3034 // hide address bar 3035 function silentScroll( ypos ) { 3036 // prevent scrollstart and scrollstop events 3037 $.event.special.scrollstart.enabled = false; 3038 setTimeout(function() { 3039 window.scrollTo( 0, ypos || 0 ); 3040 },20); 3041 setTimeout(function() { 3042 $.event.special.scrollstart.enabled = true; 3043 }, 150 ); 3044 } 3045 3046 function getPathDir( path ){ 3047 var newPath = path.replace(/#/,'').split('/'); 3048 newPath.pop(); 3049 return newPath.join('/') + (newPath.length ? '/' : ''); 3050 } 3051 3052 function getBaseURL( nonHashPath ){ 3053 return getPathDir( nonHashPath || location.hash ); 3054 } 3055 3056 var setBaseURL = !$.support.dynamicBaseTag ? $.noop : function( nonHashPath ){ 3057 //set base url for new page assets 3058 $base.attr('href', baseUrl + getBaseURL( nonHashPath )); 3059 } 3060 3061 var resetBaseURL = !$.support.dynamicBaseTag ? $.noop : function(){ 3062 $base.attr('href', baseUrl); 3063 } 3064 3065 //set base href to pathname 3066 resetBaseURL(); 3067 3068 //for form submission 3069 $('form').live('submit', function(event){ 3070 if( !$.mobile.ajaxFormsEnabled ){ return; } 3071 3072 var type = $(this).attr("method"), 3073 url = $(this).attr( "action" ).replace( location.protocol + "//" + location.host, ""); 3074 3075 //external submits use regular HTTP 3076 if( /^(:?\w+:)/.test( url ) ){ 3077 return; 3078 } 3079 3080 //if it's a relative href, prefix href with base url 3081 if( url.indexOf('/') && url.indexOf('#') !== 0 ){ 3082 url = getBaseURL() + url; 3083 } 3084 3085 changePage({ 3086 url: url, 3087 type: type, 3088 data: $(this).serialize() 3089 }, 3090 undefined, 3091 undefined, 3092 true 3093 ); 3094 event.preventDefault(); 3095 }); 3096 3097 //click routing - direct to HTTP or Ajax, accordingly 3098 $( "a" ).live( "click", function(event) { 3099 var $this = $(this), 3100 //get href, remove same-domain protocol and host 3101 href = $this.attr( "href" ).replace( location.protocol + "//" + location.host, ""), 3102 //if target attr is specified, it's external, and we mimic _blank... for now 3103 target = $this.is( "[target]" ), 3104 //if it still starts with a protocol, it's external, or could be :mailto, etc 3105 external = target || /^(:?\w+:)/.test( href ) || $this.is( "[rel=external]" ), 3106 target = $this.is( "[target]" ); 3107 3108 if( href === '#' ){ 3109 //for links created purely for interaction - ignore 3110 return false; 3111 } 3112 3113 $activeClickedLink = $this.closest( ".ui-btn" ).addClass( $.mobile.activeBtnClass ); 3114 3115 if( external || !$.mobile.ajaxLinksEnabled ){ 3116 //remove active link class if external 3117 removeActiveLinkClass(true); 3118 3119 //deliberately redirect, in case click was triggered 3120 if( target ){ 3121 window.open(href); 3122 } 3123 else{ 3124 location.href = href; 3125 } 3126 } 3127 else { 3128 //use ajax 3129 var transition = $this.data( "transition" ), 3130 back = $this.data( "back" ), 3131 changeHashOnSuccess = !$this.is( "[data-rel="+ $.mobile.nonHistorySelectors +"]" ); 3132 3133 nextPageRole = $this.attr( "data-rel" ); 3134 3135 //if it's a relative href, prefix href with base url 3136 if( href.indexOf('/') && href.indexOf('#') !== 0 ){ 3137 href = getBaseURL() + href; 3138 } 3139 3140 href.replace(/^#/,''); 3141 3142 changePage(href, transition, back, changeHashOnSuccess); 3143 } 3144 event.preventDefault(); 3145 }); 3146 3147 // turn on/off page loading message. 3148 function pageLoading( done ) { 3149 if ( done ) { 3150 $html.removeClass( "ui-loading" ); 3151 } else { 3152 3153 if( $.mobile.loadingMessage ){ 3154 $loader.appendTo($pageContainer).css({top: $(window).scrollTop() + 75}); 3155 } 3156 $html.addClass( "ui-loading" ); 3157 } 3158 }; 3159 3160 //for directing focus to the page title, or otherwise first focusable element 3161 function reFocus(page){ 3162 var pageTitle = page.find( ".ui-title:eq(0)" ); 3163 if( pageTitle.length ){ 3164 pageTitle.focus(); 3165 } 3166 else{ 3167 page.find( focusable ).eq(0).focus(); 3168 } 3169 } 3170 3171 //function for setting role of next page 3172 function setPageRole( newPage ) { 3173 if ( nextPageRole ) { 3174 newPage.attr( "data-role", nextPageRole ); 3175 nextPageRole = undefined; 3176 } 3177 } 3178 3179 //update hash, with or without triggering hashchange event 3180 $.mobile.updateHash = function(url, disableListening){ 3181 if(disableListening) { hashListener = false; } 3182 location.hash = url; 3183 } 3184 3185 //wrap page and transfer data-attrs if it has an ID 3186 function wrapNewPage( newPage ){ 3187 var copyAttrs = ['data-role', 'data-theme', 'data-fullscreen'], //TODO: more page-level attrs? 3188 wrapper = newPage.wrap( "<div>" ).parent(); 3189 3190 $.each(copyAttrs,function(i){ 3191 if( newPage.attr( copyAttrs[ i ] ) ){ 3192 wrapper.attr( copyAttrs[ i ], newPage.attr( copyAttrs[ i ] ) ); 3193 newPage.removeAttr( copyAttrs[ i ] ); 3194 } 3195 }); 3196 return wrapper; 3197 } 3198 3199 //remove active classes after page transition or error 3200 function removeActiveLinkClass(forceRemoval){ 3201 if( !!$activeClickedLink && (!$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval )){ 3202 $activeClickedLink.removeClass( $.mobile.activeBtnClass ); 3203 } 3204 $activeClickedLink = null; 3205 } 3206 3207 3208 //for getting or creating a new page 3209 function changePage( to, transition, back, changeHash){ 3210 3211 //from is always the currently viewed page 3212 var toIsArray = $.type(to) === "array", 3213 from = toIsArray ? to[0] : $.mobile.activePage, 3214 to = toIsArray ? to[1] : to, 3215 url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null, 3216 data = undefined, 3217 type = 'get', 3218 isFormRequest = false, 3219 duplicateCachedPage = null, 3220 back = (back !== undefined) ? back : ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ), 3221 transition = (transition !== undefined) ? transition : $.mobile.defaultTransition; 3222 3223 if( $.type(to) === "object" && to.url ){ 3224 url = to.url, 3225 data = to.data, 3226 type = to.type, 3227 isFormRequest = true; 3228 //make get requests bookmarkable 3229 if( data && type == 'get' ){ 3230 url += "?" + data; 3231 data = undefined; 3232 } 3233 } 3234 3235 //reset base to pathname for new request 3236 resetBaseURL(); 3237 3238 // if the new href is the same as the previous one 3239 if ( back ) { 3240 transition = urlStack.pop().transition; 3241 } else { 3242 urlStack.push({ url: url, transition: transition }); 3243 } 3244 3245 //function for transitioning between two existing pages 3246 function transitionPages() { 3247 3248 //kill the keyboard 3249 $( window.document.activeElement ).blur(); 3250 3251 //get current scroll distance 3252 var currScroll = $window.scrollTop(); 3253 3254 //set as data for returning to that spot 3255 from.data('lastScroll', currScroll); 3256 3257 //trigger before show/hide events 3258 from.data("page")._trigger("beforehide", {nextPage: to}); 3259 to.data("page")._trigger("beforeshow", {prevPage: from}); 3260 3261 function loadComplete(){ 3262 pageLoading( true ); 3263 //trigger show/hide events, allow preventing focus change through return false 3264 if( from.data("page")._trigger("hide", null, {nextPage: to}) !== false && to.data("page")._trigger("show", null, {prevPage: from}) !== false ){ 3265 $.mobile.activePage = to; 3266 } 3267 reFocus( to ); 3268 if( changeHash && url ){ 3269 $.mobile.updateHash(url, true); 3270 } 3271 removeActiveLinkClass(); 3272 3273 //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden 3274 if( duplicateCachedPage != null ){ 3275 duplicateCachedPage.remove(); 3276 } 3277 3278 //jump to top or prev scroll, if set 3279 silentScroll( to.data( 'lastScroll' ) ); 3280 } 3281 3282 if(transition && (transition !== 'none')){ 3283 $pageContainer.addClass('ui-mobile-viewport-transitioning'); 3284 // animate in / out 3285 from.addClass( transition + " out " + ( back ? "reverse" : "" ) ); 3286 to.addClass( $.mobile.activePageClass + " " + transition + 3287 " in " + ( back ? "reverse" : "" ) ); 3288 3289 // callback - remove classes, etc 3290 to.animationComplete(function() { 3291 from.add( to ).removeClass(" out in reverse " + $.mobile.transitions.join(' ') ); 3292 from.removeClass( $.mobile.activePageClass ); 3293 loadComplete(); 3294 $pageContainer.removeClass('ui-mobile-viewport-transitioning'); 3295 }); 3296 } 3297 else{ 3298 from.removeClass( $.mobile.activePageClass ); 3299 to.addClass( $.mobile.activePageClass ); 3300 loadComplete(); 3301 } 3302 }; 3303 3304 //shared page enhancements 3305 function enhancePage(){ 3306 setPageRole( to ); 3307 to.page(); 3308 } 3309 3310 //get the actual file in a jq-mobile nested url 3311 function getFileURL( url ){ 3312 return url.match( '&' + $.mobile.subPageUrlKey ) ? url.split( '&' + $.mobile.subPageUrlKey )[0] : url; 3313 } 3314 3315 //if url is a string 3316 if( url ){ 3317 to = $( "[id='" + url + "']" ), 3318 fileUrl = getFileURL(url); 3319 } 3320 else{ //find base url of element, if avail 3321 var toID = to.attr('id'), 3322 toIDfileurl = getFileURL(toID); 3323 3324 if(toID != toIDfileurl){ 3325 fileUrl = toIDfileurl; 3326 } 3327 } 3328 3329 // find the "to" page, either locally existing in the dom or by creating it through ajax 3330 if ( to.length && !isFormRequest ) { 3331 if( fileUrl ){ 3332 setBaseURL(fileUrl); 3333 } 3334 enhancePage(); 3335 transitionPages(); 3336 } else { 3337 3338 //if to exists in DOM, save a reference to it in duplicateCachedPage for removal after page change 3339 if( to.length ){ 3340 duplicateCachedPage = to; 3341 } 3342 3343 pageLoading(); 3344 3345 $.ajax({ 3346 url: fileUrl, 3347 type: type, 3348 data: data, 3349 success: function( html ) { 3350 setBaseURL(fileUrl); 3351 var all = $("<div></div>"); 3352 //workaround to allow scripts to execute when included in page divs 3353 all.get(0).innerHTML = html; 3354 to = all.find('[data-role="page"]'); 3355 3356 //rewrite src and href attrs to use a base url 3357 if( !$.support.dynamicBaseTag ){ 3358 var baseUrl = getBaseURL(fileUrl); 3359 to.find('[src],link[href]').each(function(){ 3360 var thisAttr = $(this).is('[href]') ? 'href' : 'src', 3361 thisUrl = $(this).attr(thisAttr); 3362 3363 //if full path exists and is same, chop it - helps IE out 3364 thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' ); 3365 3366 if( !/^(\w+:|#|\/)/.test(thisUrl) ){ 3367 $(this).attr(thisAttr, baseUrl + thisUrl); 3368 } 3369 }); 3370 } 3371 3372 //preserve ID on a retrieved page 3373 if ( to.attr('id') ) { 3374 to = wrapNewPage( to ); 3375 } 3376 3377 to 3378 .attr( "id", fileUrl ) 3379 .appendTo( $pageContainer ); 3380 3381 enhancePage(); 3382 transitionPages(); 3383 }, 3384 error: function() { 3385 pageLoading( true ); 3386 removeActiveLinkClass(true); 3387 $("<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>Error Loading Page</h1></div>") 3388 .css({ "display": "block", "opacity": 0.96, "top": $(window).scrollTop() + 100 }) 3389 .appendTo( $pageContainer ) 3390 .delay( 800 ) 3391 .fadeOut( 400, function(){ 3392 $(this).remove(); 3393 }); 3394 } 3395 }); 3396 } 3397 3398 }; 3399 3400 3401 $(function() { 3402 3403 $body = $( "body" ); 3404 pageLoading(); 3405 3406 // needs to be bound at domready (for IE6) 3407 // find or load content, make it active 3408 $window.bind( "hashchange", function(e, triggered) { 3409 if( !hashListener ){ 3410 hashListener = true; 3411 return; 3412 } 3413 3414 if( $(".ui-page-active").is("[data-role=" + $.mobile.nonHistorySelectors + "]") ){ 3415 return; 3416 } 3417 3418 var to = location.hash, 3419 transition = triggered ? false : undefined; 3420 3421 // either we've backed up to the root page url 3422 // or it's the first page load with no hash present 3423 //there's a hash and it wasn't manually triggered 3424 // > probably a new page, "back" will be figured out by changePage 3425 if ( to ){ 3426 changePage( to, transition); 3427 } 3428 //there's no hash, the active page is not the start page, and it's not manually triggered hashchange 3429 // > probably backed out to the first page visited 3430 else if( $.mobile.activePage.length && $startPage[0] !== $.mobile.activePage[0] && !triggered ) { 3431 changePage( $startPage, transition, true ); 3432 } 3433 else{ 3434 $startPage.trigger("pagebeforeshow", {prevPage: $('')}); 3435 $startPage.addClass( $.mobile.activePageClass ); 3436 pageLoading( true ); 3437 3438 if( $startPage.trigger("pageshow", {prevPage: $('')}) !== false ){ 3439 reFocus($startPage); 3440 } 3441 } 3442 3443 }); 3444 }); 3445 3446 //add orientation class on flip/resize. 3447 $window.bind( "orientationchange.htmlclass", function( event ) { 3448 $html.removeClass( "portrait landscape" ).addClass( event.orientation ); 3449 }); 3450 3451 //add breakpoint classes for faux media-q support 3452 function detectResolutionBreakpoints(){ 3453 var currWidth = $window.width(), 3454 minPrefix = "min-width-", 3455 maxPrefix = "max-width-", 3456 minBreakpoints = [], 3457 maxBreakpoints = [], 3458 unit = "px", 3459 breakpointClasses; 3460 3461 $html.removeClass( minPrefix + resolutionBreakpoints.join(unit + " " + minPrefix) + unit + " " + 3462 maxPrefix + resolutionBreakpoints.join( unit + " " + maxPrefix) + unit ); 3463 3464 $.each(resolutionBreakpoints,function( i ){ 3465 if( currWidth >= resolutionBreakpoints[ i ] ){ 3466 minBreakpoints.push( minPrefix + resolutionBreakpoints[ i ] + unit ); 3467 } 3468 if( currWidth <= resolutionBreakpoints[ i ] ){ 3469 maxBreakpoints.push( maxPrefix + resolutionBreakpoints[ i ] + unit ); 3470 } 3471 }); 3472 3473 if( minBreakpoints.length ){ breakpointClasses = minBreakpoints.join(" "); } 3474 if( maxBreakpoints.length ){ breakpointClasses += " " + maxBreakpoints.join(" "); } 3475 3476 $html.addClass( breakpointClasses ); 3477 }; 3478 3479 //add breakpoints now and on oc/resize events 3480 $window.bind( "orientationchange resize", detectResolutionBreakpoints); 3481 detectResolutionBreakpoints(); 3482 3483 //common breakpoints, overrideable, changeable 3484 $.mobile.addResolutionBreakpoints = function( newbps ){ 3485 if( $.type( newbps ) === "array" ){ 3486 resolutionBreakpoints = resolutionBreakpoints.concat( newbps ); 3487 } 3488 else { 3489 resolutionBreakpoints.push( newbps ); 3490 } 3491 detectResolutionBreakpoints(); 3492 } 3493 3494 //animation complete callback 3495 //TODO - update support test and create special event for transitions 3496 //check out transitionEnd (opera per Paul's request) 3497 $.fn.animationComplete = function(callback){ 3498 if($.support.cssTransitions){ 3499 return $(this).one('webkitAnimationEnd', callback); 3500 } 3501 else{ 3502 callback(); 3503 } 3504 }; 3505 3506 //TODO - add to jQuery.mobile, not $ 3507 $.extend($.mobile, { 3508 pageLoading: pageLoading, 3509 changePage: changePage, 3510 silentScroll: silentScroll 3511 }); 3512 3513 //dom-ready 3514 $(function(){ 3515 var $pages = $("[data-role='page']"); 3516 //set up active page 3517 $startPage = $.mobile.activePage = $pages.first(); 3518 3519 //set page container 3520 $pageContainer = $startPage.parent().addClass('ui-mobile-viewport'); 3521 3522 $.extend({ 3523 pageContainer: $pageContainer 3524 }); 3525 3526 //initialize all pages present 3527 $pages.page(); 3528 3529 //trigger a new hashchange, hash or not 3530 $window.trigger( "hashchange", [ true ] ); 3531 3532 //update orientation 3533 $window.trigger( "orientationchange.htmlclass" ); 3534 3535 //remove rendering class 3536 $html.removeClass('ui-mobile-rendering'); 3537 }); 3538 3539 $window.load(silentScroll); 3540 3541 })( jQuery, this ); 3542