/*! * jQuery Mobile 1.3.2 * Git HEAD hash: 528cf0e96940644ea644096bfeb913ed920ffaef <> Date: Fri Jul 19 2013 22:17:57 UTC * http://jquerymobile.com * * Copyright 2010, 2013 jQuery Foundation, Inc. and other contributors * Released under the MIT license. * http://jquery.org/license * */ (function ( root, doc, factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define( [ "jquery" ], function ( $ ) { factory( $, root, doc ); return $.mobile; }); } else { // Browser globals factory( root.jQuery, root, doc ); } }( this, document, function ( jQuery, window, document, undefined ) { (function( $ ) { $.mobile = {}; }( jQuery )); (function( $, window, undefined ) { var nsNormalizeDict = {}; // jQuery.mobile configurable options $.mobile = $.extend($.mobile, { // Version of the jQuery Mobile Framework version: "1.3.2", // Namespace used framework-wide for data-attrs. Default is no namespace ns: "", // Define the url parameter used for referencing widget-generated sub-pages. // Translates to to example.html&ui-page=subpageIdentifier // hash segment before &ui-page= is used to make Ajax request subPageUrlKey: "ui-page", // Class assigned to page currently in view, and during transitions activePageClass: "ui-page-active", // Class used for "active" button state, from CSS framework activeBtnClass: "ui-btn-active", // Class used for "focus" form element state, from CSS framework focusClass: "ui-focus", // Automatically handle clicks and form submissions through Ajax, when same-domain ajaxEnabled: true, // Automatically load and show pages based on location.hash hashListeningEnabled: true, // disable to prevent jquery from bothering with links linkBindingEnabled: true, // Set default page transition - 'none' for no transitions defaultPageTransition: "fade", // Set maximum window width for transitions to apply - 'false' for no limit maxTransitionWidth: false, // Minimum scroll distance that will be remembered when returning to a page minScrollBack: 250, // DEPRECATED: the following property is no longer in use, but defined until 2.0 to prevent conflicts touchOverflowEnabled: false, // Set default dialog transition - 'none' for no transitions defaultDialogTransition: "pop", // Error response message - appears when an Ajax page request fails pageLoadErrorMessage: "Error Loading Page", // For error messages, which theme does the box uses? pageLoadErrorMessageTheme: "e", // replace calls to window.history.back with phonegaps navigation helper // where it is provided on the window object phonegapNavigationEnabled: false, //automatically initialize the DOM when it's ready autoInitializePage: true, pushStateEnabled: true, // allows users to opt in to ignoring content by marking a parent element as // data-ignored ignoreContentEnabled: false, // turn of binding to the native orientationchange due to android orientation behavior orientationChangeEnabled: true, buttonMarkup: { hoverDelay: 200 }, // define the window and the document objects window: $( window ), document: $( document ), // TODO might be useful upstream in jquery itself ? keyCode: { ALT: 18, BACKSPACE: 8, CAPS_LOCK: 20, COMMA: 188, COMMAND: 91, COMMAND_LEFT: 91, // COMMAND COMMAND_RIGHT: 93, CONTROL: 17, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, INSERT: 45, LEFT: 37, MENU: 93, // COMMAND_RIGHT NUMPAD_ADD: 107, NUMPAD_DECIMAL: 110, NUMPAD_DIVIDE: 111, NUMPAD_ENTER: 108, NUMPAD_MULTIPLY: 106, NUMPAD_SUBTRACT: 109, PAGE_DOWN: 34, PAGE_UP: 33, PERIOD: 190, RIGHT: 39, SHIFT: 16, SPACE: 32, TAB: 9, UP: 38, WINDOWS: 91 // COMMAND }, // Place to store various widget extensions behaviors: {}, // Scroll page vertically: scroll to 0 to hide iOS address bar, or pass a Y value silentScroll: function( ypos ) { if ( $.type( ypos ) !== "number" ) { ypos = $.mobile.defaultHomeScroll; } // prevent scrollstart and scrollstop events $.event.special.scrollstart.enabled = false; setTimeout( function() { window.scrollTo( 0, ypos ); $.mobile.document.trigger( "silentscroll", { x: 0, y: ypos }); }, 20 ); setTimeout( function() { $.event.special.scrollstart.enabled = true; }, 150 ); }, // Expose our cache for testing purposes. nsNormalizeDict: nsNormalizeDict, // Take a data attribute property, prepend the namespace // and then camel case the attribute string. Add the result // to our nsNormalizeDict so we don't have to do this again. nsNormalize: function( prop ) { if ( !prop ) { return; } return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) ); }, // Find the closest parent with a theme class on it. Note that // we are not using $.fn.closest() on purpose here because this // method gets called quite a bit and we need it to be as fast // as possible. getInheritedTheme: function( el, defaultTheme ) { var e = el[ 0 ], ltr = "", re = /ui-(bar|body|overlay)-([a-z])\b/, c, m; while ( e ) { c = e.className || ""; if ( c && ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) { // We found a parent with a theme class // on it so bail from this loop. break; } e = e.parentNode; } // Return the theme letter we found, if none, return the // specified default. return ltr || defaultTheme || "a"; }, // TODO the following $ and $.fn extensions can/probably should be moved into jquery.mobile.core.helpers // // Find the closest javascript page element to gather settings data jsperf test // http://jsperf.com/single-complex-selector-vs-many-complex-selectors/edit // possibly naive, but it shows that the parsing overhead for *just* the page selector vs // the page and dialog selector is negligable. This could probably be speed up by // doing a similar parent node traversal to the one found in the inherited theme code above closestPageData: function( $target ) { return $target .closest( ':jqmData(role="page"), :jqmData(role="dialog")' ) .data( "mobile-page" ); }, enhanceable: function( $set ) { return this.haveParents( $set, "enhance" ); }, hijackable: function( $set ) { return this.haveParents( $set, "ajax" ); }, haveParents: function( $set, attr ) { if ( !$.mobile.ignoreContentEnabled ) { return $set; } var count = $set.length, $newSet = $(), e, $element, excluded; for ( var i = 0; i < count; i++ ) { $element = $set.eq( i ); excluded = false; e = $set[ i ]; while ( e ) { var c = e.getAttribute ? e.getAttribute( "data-" + $.mobile.ns + attr ) : ""; if ( c === "false" ) { excluded = true; break; } e = e.parentNode; } if ( !excluded ) { $newSet = $newSet.add( $element ); } } return $newSet; }, getScreenHeight: function() { // Native innerHeight returns more accurate value for this across platforms, // jQuery version is here as a normalized fallback for platforms like Symbian return window.innerHeight || $.mobile.window.height(); } }, $.mobile ); // Mobile version of data and removeData and hasData methods // ensures all data is set and retrieved using jQuery Mobile's data namespace $.fn.jqmData = function( prop, value ) { var result; if ( typeof prop !== "undefined" ) { if ( prop ) { prop = $.mobile.nsNormalize( prop ); } // undefined is permitted as an explicit input for the second param // in this case it returns the value and does not set it to undefined if( arguments.length < 2 || value === undefined ){ result = this.data( prop ); } else { result = this.data( prop, value ); } } return result; }; $.jqmData = function( elem, prop, value ) { var result; if ( typeof prop !== "undefined" ) { result = $.data( elem, prop ? $.mobile.nsNormalize( prop ) : prop, value ); } return result; }; $.fn.jqmRemoveData = function( prop ) { return this.removeData( $.mobile.nsNormalize( prop ) ); }; $.jqmRemoveData = function( elem, prop ) { return $.removeData( elem, $.mobile.nsNormalize( prop ) ); }; $.fn.removeWithDependents = function() { $.removeWithDependents( this ); }; $.removeWithDependents = function( elem ) { var $elem = $( elem ); ( $elem.jqmData( 'dependents' ) || $() ).remove(); $elem.remove(); }; $.fn.addDependents = function( newDependents ) { $.addDependents( $( this ), newDependents ); }; $.addDependents = function( elem, newDependents ) { var dependents = $( elem ).jqmData( 'dependents' ) || $(); $( elem ).jqmData( 'dependents', $.merge( dependents, newDependents ) ); }; // note that this helper doesn't attempt to handle the callback // or setting of an html element's text, its only purpose is // to return the html encoded version of the text in all cases. (thus the name) $.fn.getEncodedText = function() { return $( "
" ).text( $( this ).text() ).html(); }; // fluent helper function for the mobile namespaced equivalent $.fn.jqmEnhanceable = function() { return $.mobile.enhanceable( this ); }; $.fn.jqmHijackable = function() { return $.mobile.hijackable( this ); }; // Monkey-patching Sizzle to filter the :jqmData selector var oldFind = $.find, jqmDataRE = /:jqmData\(([^)]*)\)/g; $.find = function( selector, context, ret, extra ) { selector = selector.replace( jqmDataRE, "[data-" + ( $.mobile.ns || "" ) + "$1]" ); return oldFind.call( this, selector, context, ret, extra ); }; $.extend( $.find, oldFind ); $.find.matches = function( expr, set ) { return $.find( expr, null, null, set ); }; $.find.matchesSelector = function( node, expr ) { return $.find( expr, null, null, [ node ] ).length > 0; }; })( jQuery, this ); /*! * jQuery UI Widget v1.10.0pre - 2012-11-13 (ff055a0c353c3c8ce6e5bfa07ad7cb03e8885bc5) * http://jqueryui.com * * Copyright 2010, 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/jQuery.widget/ */ (function( $, undefined ) { var uuid = 0, slice = Array.prototype.slice, _cleanData = $.cleanData; $.cleanData = function( elems ) { for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { try { $( elem ).triggerHandler( "remove" ); // http://bugs.jquery.com/ticket/8235 } catch( e ) {} } _cleanData( elems ); }; $.widget = function( name, base, prototype ) { var fullName, existingConstructor, constructor, basePrototype, namespace = name.split( "." )[ 0 ]; name = name.split( "." )[ 1 ]; fullName = namespace + "-" + name; if ( !prototype ) { prototype = base; base = $.Widget; } // create selector for plugin $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { return !!$.data( elem, fullName ); }; $[ namespace ] = $[ namespace ] || {}; existingConstructor = $[ namespace ][ name ]; constructor = $[ namespace ][ name ] = function( options, element ) { // allow instantiation without "new" keyword if ( !this._createWidget ) { return new constructor( options, element ); } // allow instantiation without initializing for simple inheritance // must use "new" keyword (the code above always passes args) if ( arguments.length ) { this._createWidget( options, element ); } }; // extend with the existing constructor to carry over any static properties $.extend( constructor, existingConstructor, { version: prototype.version, // copy the object used to create the prototype in case we need to // redefine the widget later _proto: $.extend( {}, prototype ), // track widgets that inherit from this widget in case this widget is // redefined after a widget inherits from it _childConstructors: [] }); basePrototype = new base(); // we need to make the options hash a property directly on the new instance // otherwise we'll modify the options hash on the prototype that we're // inheriting from basePrototype.options = $.widget.extend( {}, basePrototype.options ); $.each( prototype, function( prop, value ) { if ( $.isFunction( value ) ) { prototype[ prop ] = (function() { var _super = function() { return base.prototype[ prop ].apply( this, arguments ); }, _superApply = function( args ) { return base.prototype[ prop ].apply( this, args ); }; return function() { var __super = this._super, __superApply = this._superApply, returnValue; this._super = _super; this._superApply = _superApply; returnValue = value.apply( this, arguments ); this._super = __super; this._superApply = __superApply; return returnValue; }; })(); } }); constructor.prototype = $.widget.extend( basePrototype, { // TODO: remove support for widgetEventPrefix // always use the name + a colon as the prefix, e.g., draggable:start // don't prefix for widgets that aren't DOM-based widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name }, prototype, { constructor: constructor, namespace: namespace, widgetName: name, widgetFullName: fullName }); // If this widget is being redefined then we need to find all widgets that // are inheriting from it and redefine all of them so that they inherit from // the new version of this widget. We're essentially trying to replace one // level in the prototype chain. if ( existingConstructor ) { $.each( existingConstructor._childConstructors, function( i, child ) { var childPrototype = child.prototype; // redefine the child widget using the same prototype that was // originally used, but inherit from the new version of the base $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); }); // remove the list of existing child constructors from the old constructor // so the old child constructors can be garbage collected delete existingConstructor._childConstructors; } else { base._childConstructors.push( constructor ); } $.widget.bridge( name, constructor ); }; $.widget.extend = function( target ) { var input = slice.call( arguments, 1 ), inputIndex = 0, inputLength = input.length, key, value; for ( ; inputIndex < inputLength; inputIndex++ ) { for ( key in input[ inputIndex ] ) { value = input[ inputIndex ][ key ]; if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { // Clone objects if ( $.isPlainObject( value ) ) { target[ key ] = $.isPlainObject( target[ key ] ) ? $.widget.extend( {}, target[ key ], value ) : // Don't extend strings, arrays, etc. with objects $.widget.extend( {}, value ); // Copy everything else by reference } else { target[ key ] = value; } } } } return target; }; $.widget.bridge = function( name, object ) { var fullName = object.prototype.widgetFullName || name; $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", args = slice.call( arguments, 1 ), returnValue = this; // allow multiple hashes to be passed on init options = !isMethodCall && args.length ? $.widget.extend.apply( null, [ options ].concat(args) ) : options; if ( isMethodCall ) { this.each(function() { var methodValue, instance = $.data( this, fullName ); if ( !instance ) { return $.error( "cannot call methods on " + name + " prior to initialization; " + "attempted to call method '" + options + "'" ); } if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { return $.error( "no such method '" + options + "' for " + name + " widget instance" ); } methodValue = instance[ options ].apply( instance, args ); if ( methodValue !== instance && methodValue !== undefined ) { returnValue = methodValue && methodValue.jquery ? returnValue.pushStack( methodValue.get() ) : methodValue; return false; } }); } else { this.each(function() { var instance = $.data( this, fullName ); if ( instance ) { instance.option( options || {} )._init(); } else { $.data( this, fullName, new object( options, this ) ); } }); } return returnValue; }; }; $.Widget = function( /* options, element */ ) {}; $.Widget._childConstructors = []; $.Widget.prototype = { widgetName: "widget", widgetEventPrefix: "", defaultElement: "
", options: { disabled: false, // callbacks create: null }, _createWidget: function( options, element ) { element = $( element || this.defaultElement || this )[ 0 ]; this.element = $( element ); this.uuid = uuid++; this.eventNamespace = "." + this.widgetName + this.uuid; this.options = $.widget.extend( {}, this.options, this._getCreateOptions(), options ); this.bindings = $(); this.hoverable = $(); this.focusable = $(); if ( element !== this ) { $.data( element, this.widgetFullName, this ); this._on( true, this.element, { remove: function( event ) { if ( event.target === element ) { this.destroy(); } } }); this.document = $( element.style ? // element within the document element.ownerDocument : // element is window or document element.document || element ); this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); } this._create(); this._trigger( "create", null, this._getCreateEventData() ); this._init(); }, _getCreateOptions: $.noop, _getCreateEventData: $.noop, _create: $.noop, _init: $.noop, destroy: function() { this._destroy(); // we can probably remove the unbind calls in 2.0 // all event bindings should go through this._on() this.element .unbind( this.eventNamespace ) // 1.9 BC for #7810 // TODO remove dual storage .removeData( this.widgetName ) .removeData( this.widgetFullName ) // support: jquery <1.6.3 // http://bugs.jquery.com/ticket/9413 .removeData( $.camelCase( this.widgetFullName ) ); this.widget() .unbind( this.eventNamespace ) .removeAttr( "aria-disabled" ) .removeClass( this.widgetFullName + "-disabled " + "ui-state-disabled" ); // clean up events and states this.bindings.unbind( this.eventNamespace ); this.hoverable.removeClass( "ui-state-hover" ); this.focusable.removeClass( "ui-state-focus" ); }, _destroy: $.noop, widget: function() { return this.element; }, option: function( key, value ) { var options = key, parts, curOption, i; if ( arguments.length === 0 ) { // don't return a reference to the internal hash return $.widget.extend( {}, this.options ); } if ( typeof key === "string" ) { // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } options = {}; parts = key.split( "." ); key = parts.shift(); if ( parts.length ) { curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); for ( i = 0; i < parts.length - 1; i++ ) { curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; curOption = curOption[ parts[ i ] ]; } key = parts.pop(); if ( value === undefined ) { return curOption[ key ] === undefined ? null : curOption[ key ]; } curOption[ key ] = value; } else { if ( value === undefined ) { return this.options[ key ] === undefined ? null : this.options[ key ]; } options[ key ] = value; } } this._setOptions( options ); return this; }, _setOptions: function( options ) { var key; for ( key in options ) { this._setOption( key, options[ key ] ); } return this; }, _setOption: function( key, value ) { this.options[ key ] = value; if ( key === "disabled" ) { this.widget() .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) .attr( "aria-disabled", value ); this.hoverable.removeClass( "ui-state-hover" ); this.focusable.removeClass( "ui-state-focus" ); } return this; }, enable: function() { return this._setOption( "disabled", false ); }, disable: function() { return this._setOption( "disabled", true ); }, _on: function( suppressDisabledCheck, element, handlers ) { var delegateElement, instance = this; // no suppressDisabledCheck flag, shuffle arguments if ( typeof suppressDisabledCheck !== "boolean" ) { handlers = element; element = suppressDisabledCheck; suppressDisabledCheck = false; } // no element argument, shuffle and use this.element if ( !handlers ) { handlers = element; element = this.element; delegateElement = this.widget(); } else { // accept selectors, DOM elements element = delegateElement = $( element ); this.bindings = this.bindings.add( element ); } $.each( handlers, function( event, handler ) { function handlerProxy() { // allow widgets to customize the disabled handling // - disabled as an array instead of boolean // - disabled class as method for disabling individual parts if ( !suppressDisabledCheck && ( instance.options.disabled === true || $( this ).hasClass( "ui-state-disabled" ) ) ) { return; } return ( typeof handler === "string" ? instance[ handler ] : handler ) .apply( instance, arguments ); } // copy the guid so direct unbinding works if ( typeof handler !== "string" ) { handlerProxy.guid = handler.guid = handler.guid || handlerProxy.guid || $.guid++; } var match = event.match( /^(\w+)\s*(.*)$/ ), eventName = match[1] + instance.eventNamespace, selector = match[2]; if ( selector ) { delegateElement.delegate( selector, eventName, handlerProxy ); } else { element.bind( eventName, handlerProxy ); } }); }, _off: function( element, eventName ) { eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; element.unbind( eventName ).undelegate( eventName ); }, _delay: function( handler, delay ) { function handlerProxy() { return ( typeof handler === "string" ? instance[ handler ] : handler ) .apply( instance, arguments ); } var instance = this; return setTimeout( handlerProxy, delay || 0 ); }, _hoverable: function( element ) { this.hoverable = this.hoverable.add( element ); this._on( element, { mouseenter: function( event ) { $( event.currentTarget ).addClass( "ui-state-hover" ); }, mouseleave: function( event ) { $( event.currentTarget ).removeClass( "ui-state-hover" ); } }); }, _focusable: function( element ) { this.focusable = this.focusable.add( element ); this._on( element, { focusin: function( event ) { $( event.currentTarget ).addClass( "ui-state-focus" ); }, focusout: function( event ) { $( event.currentTarget ).removeClass( "ui-state-focus" ); } }); }, _trigger: function( type, event, data ) { var prop, orig, callback = this.options[ type ]; data = data || {}; event = $.Event( event ); event.type = ( type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type ).toLowerCase(); // the original event may come from any element // so we need to reset the target on the new event event.target = this.element[ 0 ]; // copy original event properties over to the new event orig = event.originalEvent; if ( orig ) { for ( prop in orig ) { if ( !( prop in event ) ) { event[ prop ] = orig[ prop ]; } } } this.element.trigger( event, data ); return !( $.isFunction( callback ) && callback.apply( this.element[0], [ event ].concat( data ) ) === false || event.isDefaultPrevented() ); } }; $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { if ( typeof options === "string" ) { options = { effect: options }; } var hasOptions, effectName = !options ? method : options === true || typeof options === "number" ? defaultEffect : options.effect || defaultEffect; options = options || {}; if ( typeof options === "number" ) { options = { duration: options }; } hasOptions = !$.isEmptyObject( options ); options.complete = callback; if ( options.delay ) { element.delay( options.delay ); } if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { element[ method ]( options ); } else if ( effectName !== method && element[ effectName ] ) { element[ effectName ]( options.duration, options.easing, callback ); } else { element.queue(function( next ) { $( this )[ method ](); if ( callback ) { callback.call( element[ 0 ] ); } next(); }); } }; }); })( jQuery ); (function( $, undefined ) { $.widget( "mobile.widget", { // decorate the parent _createWidget to trigger `widgetinit` for users // who wish to do post post `widgetcreate` alterations/additions // // TODO create a pull request for jquery ui to trigger this event // in the original _createWidget _createWidget: function() { $.Widget.prototype._createWidget.apply( this, arguments ); this._trigger( 'init' ); }, _getCreateOptions: function() { var elem = this.element, options = {}; $.each( this.options, function( option ) { var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) { return "-" + c.toLowerCase(); }) ); if ( value !== undefined ) { options[ option ] = value; } }); return options; }, enhanceWithin: function( target, useKeepNative ) { this.enhance( $( this.options.initSelector, $( target )), useKeepNative ); }, enhance: function( targets, useKeepNative ) { var page, keepNative, $widgetElements = $( targets ), self = this; // if ignoreContentEnabled is set to true the framework should // only enhance the selected elements when they do NOT have a // parent with the data-namespace-ignore attribute $widgetElements = $.mobile.enhanceable( $widgetElements ); if ( useKeepNative && $widgetElements.length ) { // TODO remove dependency on the page widget for the keepNative. // Currently the keepNative value is defined on the page prototype so // the method is as well page = $.mobile.closestPageData( $widgetElements ); keepNative = ( page && page.keepNativeSelector()) || ""; $widgetElements = $widgetElements.not( keepNative ); } $widgetElements[ this.widgetName ](); }, raise: function( msg ) { throw "Widget [" + this.widgetName + "]: " + msg; } }); })( jQuery ); (function( $, window ) { // DEPRECATED // NOTE global mobile object settings $.extend( $.mobile, { // DEPRECATED Should the text be visble in the loading message? loadingMessageTextVisible: undefined, // DEPRECATED When the text is visible, what theme does the loading box use? loadingMessageTheme: undefined, // DEPRECATED default message setting loadingMessage: undefined, // DEPRECATED // Turn on/off page loading message. Theme doubles as an object argument // with the following shape: { theme: '', text: '', html: '', textVisible: '' } // NOTE that the $.mobile.loading* settings and params past the first are deprecated showPageLoadingMsg: function( theme, msgText, textonly ) { $.mobile.loading( 'show', theme, msgText, textonly ); }, // DEPRECATED hidePageLoadingMsg: function() { $.mobile.loading( 'hide' ); }, loading: function() { this.loaderWidget.loader.apply( this.loaderWidget, arguments ); } }); // TODO move loader class down into the widget settings var loaderClass = "ui-loader", $html = $( "html" ), $window = $.mobile.window; $.widget( "mobile.loader", { // NOTE if the global config settings are defined they will override these // options options: { // the theme for the loading message theme: "a", // whether the text in the loading message is shown textVisible: false, // custom html for the inner content of the loading message html: "", // the text to be displayed when the popup is shown text: "loading" }, defaultHtml: "
" + "" + "

" + "
", // For non-fixed supportin browsers. Position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top fakeFixLoader: function() { var activeBtn = $( "." + $.mobile.activeBtnClass ).first(); this.element .css({ top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 || activeBtn.length && activeBtn.offset().top || 100 }); }, // check position of loader to see if it appears to be "fixed" to center // if not, use abs positioning checkLoaderPosition: function() { var offset = this.element.offset(), scrollTop = $window.scrollTop(), screenHeight = $.mobile.getScreenHeight(); if ( offset.top < scrollTop || ( offset.top - scrollTop ) > screenHeight ) { this.element.addClass( "ui-loader-fakefix" ); this.fakeFixLoader(); $window .unbind( "scroll", this.checkLoaderPosition ) .bind( "scroll", $.proxy( this.fakeFixLoader, this ) ); } }, resetHtml: function() { this.element.html( $( this.defaultHtml ).html() ); }, // Turn on/off page loading message. Theme doubles as an object argument // with the following shape: { theme: '', text: '', html: '', textVisible: '' } // NOTE that the $.mobile.loading* settings and params past the first are deprecated // TODO sweet jesus we need to break some of this out show: function( theme, msgText, textonly ) { var textVisible, message, $header, loadSettings; this.resetHtml(); // use the prototype options so that people can set them globally at // mobile init. Consistency, it's what's for dinner if ( $.type(theme) === "object" ) { loadSettings = $.extend( {}, this.options, theme ); // prefer object property from the param then the old theme setting theme = loadSettings.theme || $.mobile.loadingMessageTheme; } else { loadSettings = this.options; // here we prefer the them value passed as a string argument, then // we prefer the global option because we can't use undefined default // prototype options, then the prototype option theme = theme || $.mobile.loadingMessageTheme || loadSettings.theme; } // set the message text, prefer the param, then the settings object // then loading message message = msgText || $.mobile.loadingMessage || loadSettings.text; // prepare the dom $html.addClass( "ui-loading" ); if ( $.mobile.loadingMessage !== false || loadSettings.html ) { // boolean values require a bit more work :P, supports object properties // and old settings if ( $.mobile.loadingMessageTextVisible !== undefined ) { textVisible = $.mobile.loadingMessageTextVisible; } else { textVisible = loadSettings.textVisible; } // add the proper css given the options (theme, text, etc) // Force text visibility if the second argument was supplied, or // if the text was explicitly set in the object args this.element.attr("class", loaderClass + " ui-corner-all ui-body-" + theme + " ui-loader-" + ( textVisible || msgText || theme.text ? "verbose" : "default" ) + ( loadSettings.textonly || textonly ? " ui-loader-textonly" : "" ) ); // TODO verify that jquery.fn.html is ok to use in both cases here // this might be overly defensive in preventing unknowing xss // if the html attribute is defined on the loading settings, use that // otherwise use the fallbacks from above if ( loadSettings.html ) { this.element.html( loadSettings.html ); } else { this.element.find( "h1" ).text( message ); } // attach the loader to the DOM this.element.appendTo( $.mobile.pageContainer ); // check that the loader is visible this.checkLoaderPosition(); // on scroll check the loader position $window.bind( "scroll", $.proxy( this.checkLoaderPosition, this ) ); } }, hide: function() { $html.removeClass( "ui-loading" ); if ( $.mobile.loadingMessage ) { this.element.removeClass( "ui-loader-fakefix" ); } $.mobile.window.unbind( "scroll", this.fakeFixLoader ); $.mobile.window.unbind( "scroll", this.checkLoaderPosition ); } }); $window.bind( 'pagecontainercreate', function() { $.mobile.loaderWidget = $.mobile.loaderWidget || $( $.mobile.loader.prototype.defaultHtml ).loader(); }); })(jQuery, this); // Script: jQuery hashchange event // // *Version: 1.3, Last updated: 7/21/2010* // // Project Home - http://benalman.com/projects/jquery-hashchange-plugin/ // GitHub - http://github.com/cowboy/jquery-hashchange/ // Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js // (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped) // // About: License // // Copyright (c) 2010 "Cowboy" Ben Alman, // Dual licensed under the MIT and GPL licenses. // http://benalman.com/about/license/ // // About: Examples // // These working examples, complete with fully commented code, illustrate a few // ways in which this plugin can be used. // // hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ // document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/ // // About: Support and Testing // // Information about what version or versions of jQuery this plugin has been // tested with, what browsers it has been tested in, and where the unit tests // reside (so you can test it yourself). // // jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2 // Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5, // Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5. // Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/ // // About: Known issues // // While this jQuery hashchange event implementation is quite stable and // robust, there are a few unfortunate browser bugs surrounding expected // hashchange event-based behaviors, independent of any JavaScript // window.onhashchange abstraction. See the following examples for more // information: // // Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/ // Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/ // WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/ // Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/ // // Also note that should a browser natively support the window.onhashchange // event, but not report that it does, the fallback polling loop will be used. // // About: Release History // // 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more // "removable" for mobile-only development. Added IE6/7 document.title // support. Attempted to make Iframe as hidden as possible by using // techniques from http://www.paciellogroup.com/blog/?p=604. Added // support for the "shortcut" format $(window).hashchange( fn ) and // $(window).hashchange() like jQuery provides for built-in events. // Renamed jQuery.hashchangeDelay to and // lowered its default value to 50. Added // and properties plus document-domain.html // file to address access denied issues when setting document.domain in // IE6/7. // 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin // from a page on another domain would cause an error in Safari 4. Also, // IE6/7 Iframe is now inserted after the body (this actually works), // which prevents the page from scrolling when the event is first bound. // Event can also now be bound before DOM ready, but it won't be usable // before then in IE6/7. // 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug // where browser version is incorrectly reported as 8.0, despite // inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag. // 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special // window.onhashchange functionality into a separate plugin for users // who want just the basic event & back button support, without all the // extra awesomeness that BBQ provides. This plugin will be included as // part of jQuery BBQ, but also be available separately. (function( $, window, undefined ) { // Reused string. var str_hashchange = 'hashchange', // Method / object references. doc = document, fake_onhashchange, special = $.event.special, // Does the browser support window.onhashchange? Note that IE8 running in // IE7 compatibility mode reports true for 'onhashchange' in window, even // though the event isn't supported, so also test document.documentMode. doc_mode = doc.documentMode, supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 ); // Get location.hash (or what you'd expect location.hash to be) sans any // leading #. Thanks for making this necessary, Firefox! function get_fragment( url ) { url = url || location.href; return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' ); }; // Method: jQuery.fn.hashchange // // Bind a handler to the window.onhashchange event or trigger all bound // window.onhashchange event handlers. This behavior is consistent with // jQuery's built-in event handlers. // // Usage: // // > jQuery(window).hashchange( [ handler ] ); // // Arguments: // // handler - (Function) Optional handler to be bound to the hashchange // event. This is a "shortcut" for the more verbose form: // jQuery(window).bind( 'hashchange', handler ). If handler is omitted, // all bound window.onhashchange event handlers will be triggered. This // is a shortcut for the more verbose // jQuery(window).trigger( 'hashchange' ). These forms are described in // the section. // // Returns: // // (jQuery) The initial jQuery collection of elements. // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and // $(elem).hashchange() for triggering, like jQuery does for built-in events. $.fn[ str_hashchange ] = function( fn ) { return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange ); }; // Property: jQuery.fn.hashchange.delay // // The numeric interval (in milliseconds) at which the // polling loop executes. Defaults to 50. // Property: jQuery.fn.hashchange.domain // // If you're setting document.domain in your JavaScript, and you want hash // history to work in IE6/7, not only must this property be set, but you must // also set document.domain BEFORE jQuery is loaded into the page. This // property is only applicable if you are supporting IE6/7 (or IE8 operating // in "IE7 compatibility" mode). // // In addition, the property must be set to the // path of the included "document-domain.html" file, which can be renamed or // modified if necessary (note that the document.domain specified must be the // same in both your main JavaScript as well as in this file). // // Usage: // // jQuery.fn.hashchange.domain = document.domain; // Property: jQuery.fn.hashchange.src // // If, for some reason, you need to specify an Iframe src file (for example, // when setting document.domain as in ), you can // do so using this property. Note that when using this property, history // won't be recorded in IE6/7 until the Iframe src file loads. This property // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7 // compatibility" mode). // // Usage: // // jQuery.fn.hashchange.src = 'path/to/file.html'; $.fn[ str_hashchange ].delay = 50; /* $.fn[ str_hashchange ].domain = null; $.fn[ str_hashchange ].src = null; */ // Event: hashchange event // // Fired when location.hash changes. In browsers that support it, the native // HTML5 window.onhashchange event is used, otherwise a polling loop is // initialized, running every milliseconds to // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7 // compatibility" mode), a hidden Iframe is created to allow the back button // and hash-based history to work. // // Usage as described in : // // > // Bind an event handler. // > jQuery(window).hashchange( function(e) { // > var hash = location.hash; // > ... // > }); // > // > // Manually trigger the event handler. // > jQuery(window).hashchange(); // // A more verbose usage that allows for event namespacing: // // > // Bind an event handler. // > jQuery(window).bind( 'hashchange', function(e) { // > var hash = location.hash; // > ... // > }); // > // > // Manually trigger the event handler. // > jQuery(window).trigger( 'hashchange' ); // // Additional Notes: // // * The polling loop and Iframe are not created until at least one handler // is actually bound to the 'hashchange' event. // * If you need the bound handler(s) to execute immediately, in cases where // a location.hash exists on page load, via bookmark or page refresh for // example, use jQuery(window).hashchange() or the more verbose // jQuery(window).trigger( 'hashchange' ). // * The event can be bound before DOM ready, but since it won't be usable // before then in IE6/7 (due to the necessary Iframe), recommended usage is // to bind it inside a DOM ready handler. // Override existing $.event.special.hashchange methods (allowing this plugin // to be defined after jQuery BBQ in BBQ's source code). special[ str_hashchange ] = $.extend( special[ str_hashchange ], { // Called only when the first 'hashchange' event is bound to window. setup: function() { // If window.onhashchange is supported natively, there's nothing to do.. if ( supports_onhashchange ) { return false; } // Otherwise, we need to create our own. And we don't want to call this // until the user binds to the event, just in case they never do, since it // will create a polling loop and possibly even a hidden Iframe. $( fake_onhashchange.start ); }, // Called only when the last 'hashchange' event is unbound from window. teardown: function() { // If window.onhashchange is supported natively, there's nothing to do.. if ( supports_onhashchange ) { return false; } // Otherwise, we need to stop ours (if possible). $( fake_onhashchange.stop ); } }); // fake_onhashchange does all the work of triggering the window.onhashchange // event for browsers that don't natively support it, including creating a // polling loop to watch for hash changes and in IE 6/7 creating a hidden // Iframe to enable back and forward. fake_onhashchange = (function() { var self = {}, timeout_id, // Remember the initial hash so it doesn't get triggered immediately. last_hash = get_fragment(), fn_retval = function( val ) { return val; }, history_set = fn_retval, history_get = fn_retval; // Start the polling loop. self.start = function() { timeout_id || poll(); }; // Stop the polling loop. self.stop = function() { timeout_id && clearTimeout( timeout_id ); timeout_id = undefined; }; // This polling loop checks every $.fn.hashchange.delay milliseconds to see // if location.hash has changed, and triggers the 'hashchange' event on // window when necessary. function poll() { var hash = get_fragment(), history_hash = history_get( last_hash ); if ( hash !== last_hash ) { history_set( last_hash = hash, history_hash ); $(window).trigger( str_hashchange ); } else if ( history_hash !== last_hash ) { location.href = location.href.replace( /#.*/, '' ) + history_hash; } timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay ); }; // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv window.attachEvent && !window.addEventListener && !supports_onhashchange && (function() { // Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8 // when running in "IE7 compatibility" mode. var iframe, iframe_src; // When the event is bound and polling starts in IE 6/7, create a hidden // Iframe for history handling. self.start = function() { if ( !iframe ) { iframe_src = $.fn[ str_hashchange ].src; iframe_src = iframe_src && iframe_src + get_fragment(); // Create hidden Iframe. Attempt to make Iframe as hidden as possible // by using techniques from http://www.paciellogroup.com/blog/?p=604. iframe = $('