" )
},
fragment = this.document[ 0 ].createDocumentFragment();
this._ui = ui;
fragment.appendChild( ui.screen[ 0 ] );
fragment.appendChild( ui.container[ 0 ] );
if ( myId ) {
ui.screen.attr( "id", myId + "-screen" );
ui.container.attr( "id", myId + "-popup" );
ui.placeholder.attr( "id", myId + "-placeholder" );
placeholder = "placeholder for " + myId;
}
// Using a wonderful golfing tradition we leave a token where the element used to be so we
// know where to put it back upon _destroy()
ui.placeholder.html( "" ).insertBefore( this.element );
// We detach the payload, add the classes, and then insert it into the popup container
this.element.detach();
// This._ui needs to be fully established before we can all this._addClasses(), because
// this._addClasses() uses this._ui, but we don't want any of the UI elements to be
// attached, so as to avoid reflows as this._addClasses() adds classes.
this._addClasses();
// Apply the proto
this.element.appendTo( ui.container );
this._page[ 0 ].appendChild( fragment );
return ui;
},
_eatEventAndClose: function( theEvent ) {
theEvent.preventDefault();
theEvent.stopImmediatePropagation();
if ( this.options.dismissible ) {
this.close();
}
return false;
},
// Make sure the screen covers the entire document - CSS is sometimes not enough for
// accomplishing this.
_resizeScreen: function() {
var screen = this._ui.screen,
popupHeight = this._ui.container.outerHeight( true ),
screenHeight = screen.removeAttr( "style" ).height(),
// Subtracting 1 here is necessary for an obscure Android 4.0 bug where the browser
// hangs if the screen covers the entire document :/
documentHeight = this.document.height() - 1;
if ( screenHeight < documentHeight ) {
screen.height( documentHeight );
} else if ( popupHeight > screenHeight ) {
screen.height( popupHeight );
}
},
_handleWindowKeyUp: function( theEvent ) {
if ( this._isOpen && theEvent.keyCode === $.mobile.keyCode.ESCAPE ) {
return this._eatEventAndClose( theEvent );
}
},
_expectResizeEvent: function() {
var windowCoordinates = getWindowCoordinates( this.window );
if ( this._resizeData ) {
if ( windowCoordinates.x === this._resizeData.windowCoordinates.x &&
windowCoordinates.y === this._resizeData.windowCoordinates.y &&
windowCoordinates.cx === this._resizeData.windowCoordinates.cx &&
windowCoordinates.cy === this._resizeData.windowCoordinates.cy ) {
// Timeout not refreshed
return false;
} else {
// Clear existing timeout - it will be refreshed below
clearTimeout( this._resizeData.timeoutId );
}
}
this._resizeData = {
timeoutId: this._delay( "_resizeTimeout", 200 ),
windowCoordinates: windowCoordinates
};
return true;
},
_resizeTimeout: function() {
if ( this._isOpen ) {
if ( !this._expectResizeEvent() ) {
if ( this._ui.container.hasClass( "ui-popup-hidden" ) ) {
// Effectively rapid-open the popup while leaving the screen intact
this._removeClass( this._ui.container, "ui-popup-hidden ui-popup-truncate" );
this.reposition( { positionTo: "window" } );
this._ignoreResizeEvents();
}
this._resizeScreen();
this._resizeData = null;
this._orientationchangeInProgress = false;
}
} else {
this._resizeData = null;
this._orientationchangeInProgress = false;
}
},
_stopIgnoringResizeEvents: function() {
this._ignoreResizeTo = 0;
},
_ignoreResizeEvents: function() {
if ( this._ignoreResizeTo ) {
clearTimeout( this._ignoreResizeTo );
}
this._ignoreResizeTo = this._delay( "_stopIgnoringResizeEvents", 1000 );
},
_handleWindowResize: function( /* theEvent */ ) {
if ( this._isOpen && this._ignoreResizeTo === 0 ) {
if ( isOutOfSight( this._ui.container, getWindowCoordinates( this.window ) ) &&
( this._expectResizeEvent() || this._orientationchangeInProgress ) &&
!this._ui.container.hasClass( "ui-popup-hidden" ) ) {
// Effectively rapid-close the popup while leaving the screen intact
this._addClass( this._ui.container, "ui-popup-hidden ui-popup-truncate" );
this._ui.container.removeAttr( "style" );
}
}
},
_handleWindowOrientationchange: function( /* theEvent */ ) {
if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) {
this._expectResizeEvent();
this._orientationchangeInProgress = true;
}
},
// When the popup is open, attempting to focus on an element that is not a child of the popup
// will redirect focus to the popup
_handleDocumentFocusIn: function( theEvent ) {
var target,
targetElement = theEvent.target,
ui = this._ui;
if ( !this._isOpen ) {
return;
}
if ( targetElement !== ui.container[ 0 ] ) {
target = $( targetElement );
if ( !$.contains( ui.container[ 0 ], targetElement ) ) {
$( $.ui.safeActiveElement( this.document[ 0 ] ) ).one( "focus",
$.proxy( function() {
$.ui.safeBlur( targetElement );
}, this ) );
ui.focusElement.focus();
theEvent.preventDefault();
theEvent.stopImmediatePropagation();
return false;
} else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) {
ui.focusElement = target;
}
}
this._ignoreResizeEvents();
},
_applyTransition: function( value ) {
if ( value ) {
this._removeClass( this._ui.container, null, this._fallbackTransition );
if ( value !== "none" ) {
this._fallbackTransition = $.mobile._maybeDegradeTransition( value );
if ( this._fallbackTransition === "none" ) {
this._fallbackTransition = "";
}
this._addClass( this._ui.container, null, this._fallbackTransition );
}
}
return this;
},
_setOptions: function( newOptions ) {
if ( newOptions.transition !== undefined ) {
if ( !this._currentTransition ) {
this._applyTransition( newOptions.transition );
}
}
if ( newOptions.tolerance !== undefined ) {
this._setTolerance( newOptions.tolerance );
}
if ( newOptions.disabled !== undefined ) {
if ( newOptions.disabled ) {
this.close();
}
}
return this._super( newOptions );
},
_setTolerance: function( value ) {
var tol = { t: 30, r: 15, b: 30, l: 15 },
ar;
if ( value !== undefined ) {
ar = String( value ).split( "," );
$.each( ar, function( idx, val ) {
ar[ idx ] = parseInt( val, 10 );
} );
switch ( ar.length ) {
// All values are to be the same
case 1:
if ( !isNaN( ar[ 0 ] ) ) {
tol.t = tol.r = tol.b = tol.l = ar[ 0 ];
}
break;
// The first value denotes top/bottom tolerance, and the second value denotes
// left/right tolerance
case 2:
if ( !isNaN( ar[ 0 ] ) ) {
tol.t = tol.b = ar[ 0 ];
}
if ( !isNaN( ar[ 1 ] ) ) {
tol.l = tol.r = ar[ 1 ];
}
break;
// The array contains values in the order top, right, bottom, left
case 4:
if ( !isNaN( ar[ 0 ] ) ) {
tol.t = ar[ 0 ];
}
if ( !isNaN( ar[ 1 ] ) ) {
tol.r = ar[ 1 ];
}
if ( !isNaN( ar[ 2 ] ) ) {
tol.b = ar[ 2 ];
}
if ( !isNaN( ar[ 3 ] ) ) {
tol.l = ar[ 3 ];
}
break;
default:
break;
}
}
this._tolerance = tol;
return this;
},
_clampPopupWidth: function( infoOnly ) {
var menuSize,
windowCoordinates = getWindowCoordinates( this.window ),
// Rectangle within which the popup must fit
rectangle = {
x: this._tolerance.l,
y: windowCoordinates.y + this._tolerance.t,
cx: windowCoordinates.cx - this._tolerance.l - this._tolerance.r,
cy: windowCoordinates.cy - this._tolerance.t - this._tolerance.b
};
if ( !infoOnly ) {
// Clamp the width of the menu before grabbing its size
this._ui.container.css( "max-width", rectangle.cx );
}
menuSize = {
cx: this._ui.container.outerWidth( true ),
cy: this._ui.container.outerHeight( true )
};
return { rc: rectangle, menuSize: menuSize };
},
_calculateFinalLocation: function( desired, clampInfo ) {
var returnValue,
rectangle = clampInfo.rc,
menuSize = clampInfo.menuSize;
// Center the menu over the desired coordinates, while not going outside the window
// tolerances. This will center wrt. the window if the popup is too large.
returnValue = {
left: fitSegmentInsideSegment( rectangle.cx, menuSize.cx, rectangle.x, desired.x ),
top: fitSegmentInsideSegment( rectangle.cy, menuSize.cy, rectangle.y, desired.y )
};
// Make sure the top of the menu is visible
returnValue.top = Math.max( 0, returnValue.top );
// If the height of the menu is smaller than the height of the document align the bottom
// with the bottom of the document
returnValue.top -= Math.min( returnValue.top,
Math.max( 0, returnValue.top + menuSize.cy - this.document.height() ) );
return returnValue;
},
// Try and center the overlay over the given coordinates
_placementCoords: function( desired ) {
return this._calculateFinalLocation( desired, this._clampPopupWidth() );
},
_createPrerequisites: function( screenPrerequisite, containerPrerequisite, whenDone ) {
var prerequisites,
self = this;
// It is important to maintain both the local variable prerequisites and
// self._prerequisites. The local variable remains in the closure of the functions which
// call the callbacks passed in. The comparison between the local variable and
// self._prerequisites is necessary, because once a function has been passed to
// .animationComplete() it will be called next time an animation completes, even if that's
// not the animation whose end the function was supposed to catch (for example, if an abort
// happens during the opening animation, the .animationComplete handler is not called for
// that animation anymore, but the handler remains attached, so it is called the next time
// the popup is opened - making it stale. Comparing the local variable prerequisites to the
// widget-level variable self._prerequisites ensures that callbacks triggered by a stale
// .animationComplete will be ignored.
prerequisites = {
screen: $.Deferred(),
container: $.Deferred()
};
prerequisites.screen.done( function() {
if ( prerequisites === self._prerequisites ) {
screenPrerequisite();
}
} );
prerequisites.container.done( function() {
if ( prerequisites === self._prerequisites ) {
containerPrerequisite();
}
} );
$.when( prerequisites.screen, prerequisites.container ).done( function() {
if ( prerequisites === self._prerequisites ) {
self._prerequisites = null;
whenDone();
}
} );
self._prerequisites = prerequisites;
},
_animate: function( args ) {
// NOTE Before removing the default animation of the screen this had an animate callback
// that would resolve the deferred. Now the deferred is resolved immediately
// TODO Remove the dependency on the screen deferred.
this._removeClass( this._ui.screen, null, args.classToRemove )
._addClass( this._ui.screen, null, args.screenClassToAdd );
args.prerequisites.screen.resolve();
if ( args.transition && args.transition !== "none" ) {
if ( args.applyTransition ) {
this._applyTransition( args.transition );
}
if ( this._fallbackTransition ) {
this._addClass( this._ui.container, null, args.containerClassToAdd )
._removeClass( this._ui.container, null, args.classToRemove );
this._ui.container
.animationComplete( $.proxy( args.prerequisites.container, "resolve" ) );
return;
}
}
this._removeClass( this._ui.container, null, args.classToRemove );
args.prerequisites.container.resolve();
},
// The desired coordinates passed in will be returned untouched if no reference element can be
// identified via desiredPosition.positionTo. Nevertheless, this function ensures that its
// return value always contains valid x and y coordinates by specifying the center middle of
// the window if the coordinates are absent. options: {
// x: coordinate,
// y: coordinate,
// positionTo: string: "origin", "window", or jQuery selector
// }
_desiredCoords: function( openOptions ) {
var offset,
dst = null,
windowCoordinates = getWindowCoordinates( this.window ),
x = openOptions.x,
y = openOptions.y,
pTo = openOptions.positionTo;
// Establish which element will serve as the reference
if ( pTo && pTo !== "origin" ) {
if ( pTo === "window" ) {
x = windowCoordinates.cx / 2 + windowCoordinates.x;
y = windowCoordinates.cy / 2 + windowCoordinates.y;
} else {
try {
dst = $( pTo );
} catch ( err ) {
dst = null;
}
if ( dst ) {
dst.filter( ":visible" );
if ( dst.length === 0 ) {
dst = null;
}
}
}
}
// If an element was found, center over it
if ( dst ) {
offset = dst.offset();
x = offset.left + dst.outerWidth() / 2;
y = offset.top + dst.outerHeight() / 2;
}
// Make sure x and y are valid numbers - center over the window
if ( $.type( x ) !== "number" || isNaN( x ) ) {
x = windowCoordinates.cx / 2 + windowCoordinates.x;
}
if ( $.type( y ) !== "number" || isNaN( y ) ) {
y = windowCoordinates.cy / 2 + windowCoordinates.y;
}
return { x: x, y: y };
},
_reposition: function( openOptions ) {
// We only care about position-related parameters for repositioning
openOptions = {
x: openOptions.x,
y: openOptions.y,
positionTo: openOptions.positionTo
};
this._trigger( "beforeposition", undefined, openOptions );
this._ui.container.offset( this._placementCoords( this._desiredCoords( openOptions ) ) );
},
reposition: function( openOptions ) {
if ( this._isOpen ) {
this._reposition( openOptions );
}
},
_openPrerequisitesComplete: function() {
var id = this.element.attr( "id" ),
firstFocus = this._ui.container.find( ":focusable" ).first(),
focusElement = $.ui.safeActiveElement( this.document[ 0 ] );
this._addClass( this._ui.container, "ui-popup-active" );
this._isOpen = true;
this._resizeScreen();
// Check to see if currElement is not a child of the container. If it's not, blur
if ( focusElement && !$.contains( this._ui.container[ 0 ], focusElement ) ) {
$.ui.safeBlur( focusElement );
}
if ( firstFocus.length > 0 ) {
this._ui.focusElement = firstFocus;
}
this._ignoreResizeEvents();
if ( id ) {
this.document.find( "[aria-haspopup='true'][aria-owns='" +
$.mobile.path.hashToSelector( id ) + "']" ).attr( "aria-expanded", true );
}
this._ui.container.attr( "tabindex", 0 );
this._trigger( "afteropen" );
},
_open: function( options ) {
var openOptions = $.extend( {}, this.options, options ),
// TODO move blacklist to private method
androidBlacklist = ( function() {
var ua = navigator.userAgent,
// Rendering engine is Webkit, and capture major version
wkmatch = ua.match( /AppleWebKit\/([0-9\.]+)/ ),
wkversion = !!wkmatch && wkmatch[ 1 ],
androidmatch = ua.match( /Android (\d+(?:\.\d+))/ ),
andversion = !!androidmatch && androidmatch[ 1 ],
chromematch = ua.indexOf( "Chrome" ) > -1;
// Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and
// not Chrome.
if ( androidmatch !== null && andversion === "4.0" && wkversion &&
wkversion > 534.13 && !chromematch ) {
return true;
}
return false;
}() );
// Count down to triggering "popupafteropen" - we have two prerequisites:
// 1. The popup window animation completes (container())
// 2. The screen opacity animation completes (screen())
this._createPrerequisites(
$.noop,
$.noop,
$.proxy( this, "_openPrerequisitesComplete" ) );
this._currentTransition = openOptions.transition;
this._applyTransition( openOptions.transition );
this._removeClass( this._ui.screen, null, "ui-screen-hidden" );
this._removeClass( this._ui.container, "ui-popup-truncate" );
// Give applications a chance to modify the contents of the container before it appears
this._reposition( openOptions );
this._removeClass( this._ui.container, "ui-popup-hidden" );
if ( this.options.overlayTheme && androidBlacklist ) {
// TODO: The native browser on Android 4.0.X ("Ice Cream Sandwich") suffers from an
// issue where the popup overlay appears to be z-indexed above the popup itself when
// certain other styles exist on the same page -- namely, any element set to
// `position: fixed` and certain types of input. These issues are reminiscent of
// previously uncovered bugs in older versions of Android's native browser:
// https://github.com/scottjehl/Device-Bugs/issues/3
// This fix closes the following bugs ( I use "closes" with reluctance, and stress that
// this issue should be revisited as soon as possible ):
// https://github.com/jquery/jquery-mobile/issues/4816
// https://github.com/jquery/jquery-mobile/issues/4844
// https://github.com/jquery/jquery-mobile/issues/4874
// TODO sort out why this._page isn't working
this._addClass( this.element.closest( ".ui-page" ), "ui-popup-open" );
}
this._animate( {
additionalCondition: true,
transition: openOptions.transition,
classToRemove: "",
screenClassToAdd: "in",
containerClassToAdd: "in",
applyTransition: false,
prerequisites: this._prerequisites
} );
},
_closePrerequisiteScreen: function() {
this._removeClass( this._ui.screen, null, "out" )
._addClass( this._ui.screen, null, "ui-screen-hidden" );
},
_closePrerequisiteContainer: function() {
this._removeClass( this._ui.container, null, "reverse out" )
._addClass( this._ui.container, "ui-popup-hidden ui-popup-truncate" );
this._ui.container.removeAttr( "style" );
},
_closePrerequisitesDone: function() {
var container = this._ui.container,
id = this.element.attr( "id" );
// Remove the global mutex for popups
$.mobile.popup.active = undefined;
// Blur elements inside the container, including the container
$( ":focus", container[ 0 ] ).add( container[ 0 ] ).blur();
if ( id ) {
this.document.find( "[aria-haspopup='true'][aria-owns='" +
$.mobile.path.hashToSelector( id ) + "']" ).attr( "aria-expanded", false );
}
this._ui.container.removeAttr( "tabindex" );
// Alert users that the popup is closed
this._trigger( "afterclose" );
},
_close: function( immediate ) {
this._removeClass( this._ui.container, "ui-popup-active" )
._removeClass( this._page, "ui-popup-open" );
this._isOpen = false;
// Count down to triggering "popupafterclose" - we have two prerequisites:
// 1. The popup window reverse animation completes (container())
// 2. The screen opacity animation completes (screen())
this._createPrerequisites(
$.proxy( this, "_closePrerequisiteScreen" ),
$.proxy( this, "_closePrerequisiteContainer" ),
$.proxy( this, "_closePrerequisitesDone" ) );
this._animate( {
additionalCondition: this._ui.screen.hasClass( "in" ),
transition: ( immediate ? "none" : ( this._currentTransition ) ),
classToRemove: "in",
screenClassToAdd: "out",
containerClassToAdd: "reverse out",
applyTransition: true,
prerequisites: this._prerequisites
} );
},
_unenhance: function() {
if ( this.options.enhanced ) {
return;
}
this.element
// Cannot directly insertAfter() - we need to detach() first, because
// insertAfter() will do nothing if the payload div was not attached
// to the DOM at the time the widget was created, and so the payload
// will remain inside the container even after we call insertAfter().
// If that happens and we remove the container a few lines below, we
// will cause an infinite recursion - #5244
.detach()
.insertAfter( this._ui.placeholder );
this._ui.screen.remove();
this._ui.container.remove();
this._ui.placeholder.remove();
},
_destroy: function() {
if ( this.options.enhanced ) {
this.classesElementLookup = {};
}
if ( $.mobile.popup.active === this ) {
this.element.one( "popupafterclose", $.proxy( this, "_unenhance" ) );
this.close();
} else {
this._unenhance();
}
},
_closePopup: function( theEvent, data ) {
var parsedDst, toUrl,
immediate = false;
if ( $.mobile.popup.active !== this ||
( theEvent &&
( theEvent.isDefaultPrevented() ||
( theEvent.type === "navigate" && data && data.state && data.state.url &&
data.state.url === this._myUrl ) ) ) ||
!this._isOpen ) {
return;
}
// Restore location on screen
window.scrollTo( 0, this._scrollTop );
if ( theEvent && theEvent.type === "pagebeforechange" && data ) {
// Determine whether we need to rapid-close the popup, or whether we can take the time
// to run the closing transition
if ( typeof data.toPage === "string" ) {
parsedDst = data.toPage;
} else {
parsedDst = data.toPage.jqmData( "url" );
}
parsedDst = $.mobile.path.parseUrl( parsedDst );
toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;
if ( this._pageUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ||
data.options.reloadPage ) {
// Going to a different page - close immediately
immediate = true;
} else {
theEvent.preventDefault();
}
}
// Remove nav bindings
this._off( this.window, "navigate pagebeforechange" );
// Unbind click handlers added when history is disabled
this._off( this.element, "click" );
this._close( immediate );
},
// Any navigation event after a popup is opened should close the popup.
// NOTE The pagebeforechange is bound to catch navigation events that don't alter the url
// (eg, dialogs from popups)
_bindContainerClose: function() {
this._on( true, this.window, {
navigate: "_closePopup",
pagebeforechange: "_closePopup"
} );
},
widget: function() {
return this._ui.container;
},
_handleCloseLink: function( theEvent ) {
this.close();
theEvent.preventDefault();
},
// TODO No clear deliniation of what should be here and what should be in _open. Seems to be
// "visual" vs "history" for now
open: function( options ) {
var url, hashkey, activePage, currentIsDialog, hasHash, urlHistory,
events = {},
self = this,
currentOptions = this.options;
// Make sure open is idempotent
if ( $.mobile.popup.active || currentOptions.disabled ) {
return this;
}
// Set the global popup mutex
$.mobile.popup.active = this;
this._scrollTop = this.window.scrollTop();
// If history alteration is disabled close on navigate events and leave the url as is
if ( !( currentOptions.history ) ) {
self._open( options );
self._bindContainerClose();
// When history is disabled we have to grab the data-rel back link clicks so we can
// close the popup instead of relying on history to do it for us
events[ "click " + currentOptions.closeLinkSelector ] = "_handleCloseLink";
this._on( events );
return this;
}
// Cache some values for min/readability
urlHistory = $.mobile.navigate.history;
hashkey = $.mobile.dialogHashKey;
activePage = $.mobile.activePage;
currentIsDialog = ( activePage ? activePage.hasClass( "ui-page-dialog" ) : false );
this._pageUrl = url = urlHistory.getActive().url;
hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog &&
( urlHistory.activeIndex > 0 );
if ( hasHash ) {
self._open( options );
self._bindContainerClose();
return this;
}
// If the current url has no dialog hash key proceed as normal otherwise, if the page is a
// dialog simply tack on the hash key
if ( url.indexOf( hashkey ) === -1 && !currentIsDialog ) {
url = url + ( url.indexOf( "#" ) > -1 ? hashkey : "#" + hashkey );
} else {
url = $.mobile.path.parseLocation().hash + hashkey;
}
// Swallow the the initial navigation event, and bind for the next
this.window.one( "beforenavigate", function( theEvent ) {
theEvent.preventDefault();
self._open( options );
self._bindContainerClose();
} );
this.urlAltered = true;
this._myUrl = url;
$.mobile.navigate( url, { role: "dialog" } );
return this;
},
close: function() {
// Make sure close is idempotent
if ( $.mobile.popup.active !== this ) {
return this;
}
this._scrollTop = this.window.scrollTop();
if ( this.options.history && this.urlAltered ) {
$.mobile.back();
this.urlAltered = false;
} else {
// Simulate the nav bindings having fired
this._closePopup();
}
return this;
}
} );
$.widget( "mobile.popup", $.mobile.popup, $.mobile.widget.theme );
// TODO this can be moved inside the widget
$.mobile.popup.handleLink = function( $link ) {
var offset,
path = $.mobile.path,
// NOTE make sure to get only the hash from the href because ie7 (wp7)
// returns the absolute href in this case ruining the element selection
popup = $( path.hashToSelector( path.parseUrl( $link.attr( "href" ) ).hash ) ).first();
if ( popup.length > 0 && popup.data( "mobile-popup" ) ) {
offset = $link.offset();
popup.popup( "open", {
x: offset.left + $link.outerWidth() / 2,
y: offset.top + $link.outerHeight() / 2,
transition: $link.jqmData( "transition" ),
positionTo: $link.jqmData( "position-to" )
} );
}
// Remove after delay
setTimeout( function() {
$link.removeClass( "ui-button-active" );
}, 300 );
};
// TODO move inside _create
$.mobile.document.on( "pagebeforechange", function( theEvent, data ) {
if ( data.options.role === "popup" ) {
$.mobile.popup.handleLink( data.options.link );
theEvent.preventDefault();
}
} );
return $.mobile.popup;
} );
/*!
* jQuery Mobile Listview @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Listview
//>>group: Widgets
//>>description: Applies listview styling of various types (standard, numbered, split button, etc.)
//>>docs: http://api.jquerymobile.com/listview/
//>>demos: http://demos.jquerymobile.com/@VERSION/listview/
//>>css.structure: ../css/structure/jquery.mobile.listview.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/listview',[
"jquery",
"../widget",
"./addFirstLastClasses" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
function addItemToDictionary( itemClassDict, element, key, extra ) {
// Construct the dictionary key from the key class and the extra class
var dictionaryKey = [ key ].concat( extra ? [ extra ] : [] ).join( "|" );
if ( !itemClassDict[ dictionaryKey ] ) {
itemClassDict[ dictionaryKey ] = [];
}
itemClassDict[ dictionaryKey ].push( element );
}
var getAttribute = $.mobile.getAttribute,
countBubbleClassRegex = /\bui-listview-item-count-bubble\b/;
function filterBubbleSpan() {
var child, parentNode,
anchorHash = { "a": true, "A": true };
for ( child = this.firstChild ; !!child ; child = child.nextSibling ) {
// Accept list item when we've found an element with class
// ui-listview-item-count-bubble
if ( child.className && child.className.match( countBubbleClassRegex ) ) {
return true;
}
// Descend into anchor, remembering where we've been
if ( anchorHash[ child.nodeName ] ) {
parentNode = child;
child = child.firstChild;
}
// When done with anchor, resume checking children of list item
if ( !child && parentNode ) {
child = parentNode;
parentNode = null;
}
}
}
return $.widget( "mobile.listview", $.extend( {
version: "@VERSION",
options: {
classes: {
"ui-listview-inset": "ui-corner-all ui-shadow"
},
theme: "inherit",
dividerTheme: "inherit",
icon: "caret-r",
splitIcon: "caret-r",
splitTheme: "inherit",
inset: false,
enhanced: false
},
_create: function() {
this._addClass( "ui-listview" );
if ( this.options.inset ) {
this._addClass( "ui-listview-inset" );
}
this._refresh( true );
},
// We only handle the theme option through the theme extension. Theme options concerning list
// items such as splitTheme and dividerTheme have to be handled in refresh().
_themeElements: function() {
return [ {
element: this.element,
prefix: "ui-group-theme-"
} ];
},
_setOption: function( key, value ) {
if ( key === "inset" ) {
this._toggleClass( this.element, "ui-listview-inset", null, !!value );
}
return this._superApply( arguments );
},
_getChildrenByTagName: function( ele, lcName, ucName ) {
var results = [],
dict = {};
dict[ lcName ] = dict[ ucName ] = true;
ele = ele.firstChild;
while ( ele ) {
if ( dict[ ele.nodeName ] ) {
results.push( ele );
}
ele = ele.nextSibling;
}
return $( results );
},
_beforeListviewRefresh: $.noop,
_afterListviewRefresh: $.noop,
updateItems: function( items ) {
this._refresh( false, items );
},
refresh: function() {
this._refresh();
},
_processListItem: function( /* item */ ) {
return true;
},
_processListItemAnchor: function( /* a */ ) {
return true;
},
_refresh: function( create, items ) {
var buttonClass, pos, numli, item, itemClass, itemExtraClass, itemTheme, itemIcon, icon, a,
isDivider, value, last, splittheme, li, dictionaryKey, span, allItems, newSpan,
currentOptions = this.options,
list = this.element,
ol = !!$.nodeName( list[ 0 ], "ol" ),
start = list.attr( "start" ),
itemClassDict = {};
// Check if a start attribute has been set while taking a value of 0 into account
if ( ol && ( start || start === 0 ) ) {
list.css( "counter-reset", "listnumbering " + ( parseInt( start, 10 ) - 1 ) );
}
this._beforeListviewRefresh();
// We need all items even if a set was passed in - we just won't iterate over them in the
// main refresh loop.
allItems = this._getChildrenByTagName( list[ 0 ], "li", "LI" );
li = items || allItems;
for ( pos = 0, numli = li.length; pos < numli; pos++ ) {
item = li.eq( pos );
itemClass = "ui-listview-item";
itemExtraClass = undefined;
if ( create || this._processListItem( item ) ) {
a = this._getChildrenByTagName( item[ 0 ], "a", "A" );
isDivider = ( getAttribute( item[ 0 ], "role" ) === "list-divider" );
value = item.attr( "value" );
itemTheme = getAttribute( item[ 0 ], "theme" );
if ( a.length && ( ( this._processListItemAnchor( a ) && !isDivider ) ||
create ) ) {
itemIcon = getAttribute( item[ 0 ], "icon" );
icon = ( itemIcon === false ) ? false : ( itemIcon || currentOptions.icon );
buttonClass = "ui-button";
if ( itemTheme ) {
buttonClass += " ui-button-" + itemTheme;
}
if ( a.length > 1 ) {
itemClass += " ui-listview-item-has-alternate";
last = a.last();
splittheme = getAttribute( last[ 0 ], "theme" ) ||
currentOptions.splitTheme || itemTheme;
newSpan = false;
span = last.children( ".ui-listview-item-split-icon" );
if ( !span.length ) {
span = $( "
" );
newSpan = true;
}
addItemToDictionary( itemClassDict, span[ 0 ],
"ui-listview-item-split-icon", "ui-icon ui-icon-" +
( getAttribute( last[ 0 ], "icon" ) || itemIcon ||
currentOptions.splitIcon ) );
addItemToDictionary( itemClassDict, last[ 0 ],
"ui-listview-item-split-button",
"ui-button ui-button-icon-only" +
( splittheme ? " ui-button-" + splittheme : "" ) );
last.attr( "title", $.trim( last.getEncodedText() ) );
if ( newSpan ) {
last.empty().prepend( span );
}
// Reduce to the first anchor, because only the first gets the buttonClass
a = a.first();
} else if ( icon ) {
newSpan = false;
span = a.children( ".ui-listview-item-icon" );
if ( !span.length ) {
span = $( "" );
newSpan = true;
}
addItemToDictionary( itemClassDict, span[ 0 ], "ui-listview-item-icon",
"ui-icon ui-icon-" + icon + " ui-widget-icon-floatend" );
if ( newSpan ) {
a.prepend( span );
}
}
// Apply buttonClass to the (first) anchor
addItemToDictionary( itemClassDict, a[ 0 ], "ui-listview-item-button",
buttonClass );
} else if ( isDivider ) {
itemClass += " ui-listview-item-divider";
itemExtraClass = "ui-bar-" + ( itemTheme || currentOptions.dividerTheme ||
currentOptions.theme || "inherit" );
item.attr( "role", "heading" );
} else if ( a.length <= 0 ) {
itemClass += " ui-listview-item-static";
itemExtraClass = "ui-body-" + ( itemTheme ? itemTheme : "inherit" );
}
if ( ol && value ) {
item.css( "counter-reset", "listnumbering " + ( parseInt( value, 10 ) - 1 ) );
}
}
// Instead of setting item class directly on the list item
// at this point in time, push the item into a dictionary
// that tells us what class to set on it so we can do this after this
// processing loop is finished.
addItemToDictionary( itemClassDict, item[ 0 ], itemClass, itemExtraClass );
}
// Set the appropriate listview item classes on each list item.
// The main reason we didn't do this
// in the for-loop above is because we can eliminate per-item function overhead
// by calling addClass() and children() once or twice afterwards. This
// can give us a significant boost on platforms like WP7.5.
for ( dictionaryKey in itemClassDict ) {
// Split the dictionary key back into key classes and extra classes and construct the
// _addClass() parameter list
this._addClass.apply( this,
[ $( itemClassDict[ dictionaryKey ] ) ]
.concat( dictionaryKey.split( "|" ) ) );
}
this._addClass(
li.filter( filterBubbleSpan ),
"ui-listview-item-has-count" );
this._afterListviewRefresh();
// NOTE: Using the extension addFirstLastClasses is deprecated as of 1.5.0 and this and the
// extension itself will be removed in 1.6.0.
this._addFirstLastClasses( allItems, this._getVisibles( allItems, create ), create );
}
}, $.mobile.behaviors.addFirstLastClasses ) );
} );
/*!
* jQuery Mobile Navbar @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>description: navbar morebutton extension.
//>>label: NavbarMoreButton
//>>group: Widgets
//>>css.structure: ../css/structure/jquery.mobile.navbar.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/navbar.morebutton',[
"jquery",
"./navbar",
"./popup",
"./listview",
"../widget" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
return $.widget( "mobile.navbar", $.mobile.navbar, {
options: {
morebutton: false,
morebuttontext: "...",
morebuttoniconpos: "top",
morebuttonicon: null
},
_create: function() {
this._super();
if ( this.options.morebutton && this.numButtons > this.maxButton ) {
this._createNavPopup();
}
},
_id: function() {
return this.element.attr( "id" ) || ( this.widgetName + this.uuid );
},
_createNavRows: function() {
if ( this.options.morebutton ) {
return;
}
this._super();
},
_createNavPopup: function() {
var popupDiv;
var popupNav;
var moreButton;
var pos;
var buttonItem;
var id = this._id() + "-popup";
var navItems = this.navbar.find( "li" );
var buttonCount = navItems.length;
var maxButton = this.maxButton;
var iconpos = this.iconpos;
var icon = this.options.morebuttonicon;
popupDiv = $( "" );
this._addClass( popupDiv, "ui-navbar-popup" );
popupNav = $( "" );
this._addClass( popupNav, "ui-navbar-popupnav" );
popupNav.appendTo( popupDiv );
// Enhance buttons and move to new rows
for ( pos = 0; pos < buttonCount; pos++ ) {
buttonItem = navItems.eq( pos );
this._makeNavButton( buttonItem.find( "a" ), iconpos );
if ( pos + 1 === maxButton ) {
moreButton = $( "" ).append( $( "" )
.attr( "data-rel", "popup" )
.button( {
icon: icon,
iconPosition: this.options.morebuttoniconpos,
label: this.options.morebuttontext
} ) );
this._on( moreButton, {
"click": "_openMoreButton"
} );
this.navbar.find( "ul" ).first().append( moreButton );
}
if ( pos + 1 >= maxButton ) {
buttonItem.detach();
popupNav.append( buttonItem );
}
popupNav.listview();
}
popupDiv.appendTo( this.navbar );
popupDiv.popup( { positionTo: moreButton } );
this.moreButton = moreButton;
this.popupDiv = popupDiv;
},
_openMoreButton: function() {
$( "#" + this._id() + "-popup" ).popup( "open" );
},
refresh: function() {
var newitems;
var that = this;
var iconpos = this.iconpos;
if ( !this.options.morebutton ) {
this._super();
return;
}
if ( this.popupDiv ) {
newitems = this.moreButton.parent().nextAll();
newitems.find( "a" ).each( function() {
that._makeNavButton( this, iconpos );
} );
newitems.appendTo( this.popupDiv.find( "ul" ) );
}
this._createNavPopup();
},
_destroy: function() {
var navitems;
if ( !this.options.morebutton ) {
this._super();
return;
}
if ( this.popupDiv ) {
navitems = this.popupDiv.find( "li" ).detach();
this.popupDiv.remove();
this.moreButton.parent().remove();
this.navbar.find( "ul" ).append( navitems );
this.navbar.removeClass( "ui-navbar" );
this.navButtons = this.navbar.find( "a" );
this.navButtons.each( function() {
$( this ).button( "destroy" );
} );
}
}
} );
} );
/*!
* jQuery Mobile Listview Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Listview Backcompat
//>>group: Widgets
//>>description: Listview style options preserved for backwards compatibility
//>>docs: http://api.jquerymobile.com/listview/
//>>demos: http://demos.jquerymobile.com/@VERSION/listview/
//>>css.structure: ../css/structure/jquery.mobile.listview.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/listview.backcompat',[
"jquery",
"./widget.theme",
"./widget.backcompat",
"./listview" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
var listviewItemClassRegex = /\bui-listview-item-static\b|\bui-listview-item-divider\b/;
var buttonClassRegex = /\bui-button\b/;
$.widget( "mobile.listview", $.mobile.listview, {
options: {
corners: true,
shadow: true
},
classProp: "ui-listview-inset",
_processListItem: function( item ) {
return !listviewItemClassRegex.test( item[ 0 ].className );
},
_processListItemAnchor: function( a ) {
return !buttonClassRegex.test( a[ 0 ].className );
}
} );
$.widget( "mobile.listview", $.mobile.listview, $.mobile.widget.backcompat );
}
} );
/*!
* jQuery Mobile Listview Autodividers @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Listview Autodividers
//>>group: Widgets
//>>description: Generates dividers for listview items
//>>docs: http://api.jquerymobile.com/listview/#option-autodividers
//>>demos: http://demos.jquerymobile.com/@VERSION/listview/#Autodividers
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/listview.autodividers',[
"jquery",
"./listview" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var dividerClassRegex = /\bui-listview-item-divider\b/;
function defaultAutodividersSelector( elt ) {
// Look for the text in the given element
var text = $.trim( elt.text() ) || null;
if ( !text ) {
return null;
}
// Create the text for the divider (first uppercased letter)
text = text.slice( 0, 1 ).toUpperCase();
return text;
}
return $.widget( "mobile.listview", $.mobile.listview, {
options: {
autodividers: false,
autodividersSelector: defaultAutodividersSelector
},
_beforeListviewRefresh: function() {
if ( this.options.autodividers ) {
this._replaceDividers();
}
return this._superApply( arguments );
},
_replaceDividers: function() {
var existingDivider, existingDividerText, lastDividerText,
items = this._getChildrenByTagName( this.element[ 0 ], "li", "LI" );
items.each( $.proxy( function( index, item ) {
var divider, dividerText;
item = $( item );
// This tests whether the item is a divider - first we check the class name, and second
// we check the slower way, via the data attribute
if ( ( item[ 0 ].className && item[ 0 ].className.match( dividerClassRegex ) ) ||
item[ 0 ].getAttribute( "data-" + $.mobile.ns + "role" ) === "list-divider" ) {
// The last item can't be a divider
if ( index === items.length - 1 ) {
item.remove();
return false;
}
// If the previous item was a divider, remove it
if ( existingDivider ) {
existingDivider.remove();
}
// The current item becomes the previous divider
existingDivider = item;
existingDividerText = item.text();
// If we've found a divider for a heading that already has a divider, remove it to
// coalesce two adjacent groups with identical headings
if ( existingDividerText === lastDividerText ) {
existingDivider.remove();
existingDivider = null;
existingDividerText = null;
}
} else {
dividerText = this.options.autodividersSelector( item );
// If this item is preceded by a suitable divider reuse it
if ( existingDivider ) {
if ( existingDividerText === dividerText ) {
// We prevent the generation of a divider below by setting the
// lastDividerText here
lastDividerText = existingDividerText;
} else {
// The preceding item is not a suitable divider
existingDivider.remove();
}
// We only keep a reference to an existing divider for one iteration, because
// the item immediately succeeding an existing divider will inform us as to
// whether the divider we've found is suitable for the current group
existingDivider = null;
existingDividerText = null;
}
// If we haven't found a suitable divider and a new group has started, generate a
// new divider
if ( dividerText && lastDividerText !== dividerText ) {
divider = document.createElement( "li" );
divider.appendChild( document.createTextNode( dividerText ) );
divider.setAttribute( "data-" + $.mobile.ns + "role", "list-divider" );
item.before( divider );
}
lastDividerText = dividerText;
}
}, this ) );
}
} );
} );
/*!
* jQuery Mobile Listview Hide Dividers @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Listview Hide Dividers
//>>group: Widgets
//>>description: Hides dividers when all items in the section they designate become hidden
//>>docs: http://api.jquerymobile.com/listview/#option-hideDividers
//>>demos: http://demos.jquerymobile.com/@VERSION/listview/
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/listview.hidedividers',[
"jquery",
"./listview" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var rdivider = /(^|\s)ui-listview-item-divider($|\s)/,
rhidden = /(^|\s)ui-screen-hidden($|\s)/;
return $.widget( "mobile.listview", $.mobile.listview, {
options: {
hideDividers: false
},
_afterListviewRefresh: function() {
var items, idx, item,
hideDivider = true;
this._superApply( arguments );
if ( this.options.hideDividers ) {
items = this._getChildrenByTagName( this.element[ 0 ], "li", "LI" );
for ( idx = items.length - 1; idx > -1; idx-- ) {
item = items[ idx ];
if ( item.className.match( rdivider ) ) {
if ( hideDivider ) {
item.className = item.className + " ui-screen-hidden";
}
hideDivider = true;
} else {
if ( !item.className.match( rhidden ) ) {
hideDivider = false;
}
}
}
}
}
} );
} );
/*!
* jQuery Mobile No JS @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: “nojs” Classes
//>>group: Utilities
//>>description: Adds class to make elements hidden to A grade browsers
//>>docs: http://api.jquerymobile.com/global-config/#keepNative
//>>css.structure: ../css/structure/jquery.mobile.core.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'nojs',[
"jquery",
"./ns" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
$.mobile.nojs = function( target ) {
$( ":jqmData(role='nojs')", target ).addClass( "ui-nojs" );
};
return $.mobile.nojs;
} );
/*!
* jQuery UI Unique ID 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: uniqueId
//>>group: Core
//>>description: Functions to generate and remove uniqueId's
//>>docs: http://api.jqueryui.com/uniqueId/
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'jquery-ui/unique-id',[ "jquery", "./version" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} ( function( $ ) {
return $.fn.extend( {
uniqueId: ( function() {
var uuid = 0;
return function() {
return this.each( function() {
if ( !this.id ) {
this.id = "ui-id-" + ( ++uuid );
}
} );
};
} )(),
removeUniqueId: function() {
return this.each( function() {
if ( /^ui-id-\d+$/.test( this.id ) ) {
$( this ).removeAttr( "id" );
}
} );
}
} );
} ) );
/*!
* jQuery UI Accordion 1.12.1
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Accordion
//>>group: Widgets
// jscs:disable maximumLineLength
//>>description: Displays collapsible content panels for presenting information in a limited amount of space.
// jscs:enable maximumLineLength
//>>docs: http://api.jqueryui.com/accordion/
//>>demos: http://jqueryui.com/accordion/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/accordion.css
//>>css.theme: ../../themes/base/theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'jquery-ui/widgets/accordion',[
"jquery",
"../version",
"../keycode",
"../unique-id",
"../widget"
], factory );
} else {
// Browser globals
factory( jQuery );
}
}( function( $ ) {
return $.widget( "ui.accordion", {
version: "1.12.1",
options: {
active: 0,
animate: {},
classes: {
"ui-accordion-header": "ui-corner-top",
"ui-accordion-header-collapsed": "ui-corner-all",
"ui-accordion-content": "ui-corner-bottom"
},
collapsible: false,
event: "click",
header: "> li > :first-child, > :not(li):even",
heightStyle: "auto",
icons: {
activeHeader: "ui-icon-triangle-1-s",
header: "ui-icon-triangle-1-e"
},
// Callbacks
activate: null,
beforeActivate: null
},
hideProps: {
borderTopWidth: "hide",
borderBottomWidth: "hide",
paddingTop: "hide",
paddingBottom: "hide",
height: "hide"
},
showProps: {
borderTopWidth: "show",
borderBottomWidth: "show",
paddingTop: "show",
paddingBottom: "show",
height: "show"
},
_create: function() {
var options = this.options;
this.prevShow = this.prevHide = $();
this._addClass( "ui-accordion", "ui-widget ui-helper-reset" );
this.element.attr( "role", "tablist" );
// Don't allow collapsible: false and active: false / null
if ( !options.collapsible && ( options.active === false || options.active == null ) ) {
options.active = 0;
}
this._processPanels();
// handle negative values
if ( options.active < 0 ) {
options.active += this.headers.length;
}
this._refresh();
},
_getCreateEventData: function() {
return {
header: this.active,
panel: !this.active.length ? $() : this.active.next()
};
},
_createIcons: function() {
var icon, children,
icons = this.options.icons;
if ( icons ) {
icon = $( "" );
this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header );
icon.prependTo( this.headers );
children = this.active.children( ".ui-accordion-header-icon" );
this._removeClass( children, icons.header )
._addClass( children, null, icons.activeHeader )
._addClass( this.headers, "ui-accordion-icons" );
}
},
_destroyIcons: function() {
this._removeClass( this.headers, "ui-accordion-icons" );
this.headers.children( ".ui-accordion-header-icon" ).remove();
},
_destroy: function() {
var contents;
// Clean up main element
this.element.removeAttr( "role" );
// Clean up headers
this.headers
.removeAttr( "role aria-expanded aria-selected aria-controls tabIndex" )
.removeUniqueId();
this._destroyIcons();
// Clean up content panels
contents = this.headers.next()
.css( "display", "" )
.removeAttr( "role aria-hidden aria-labelledby" )
.removeUniqueId();
if ( this.options.heightStyle !== "content" ) {
contents.css( "height", "" );
}
},
_setOption: function( key, value ) {
if ( key === "active" ) {
// _activate() will handle invalid values and update this.options
this._activate( value );
return;
}
if ( key === "event" ) {
if ( this.options.event ) {
this._off( this.headers, this.options.event );
}
this._setupEvents( value );
}
this._super( key, value );
// Setting collapsible: false while collapsed; open first panel
if ( key === "collapsible" && !value && this.options.active === false ) {
this._activate( 0 );
}
if ( key === "icons" ) {
this._destroyIcons();
if ( value ) {
this._createIcons();
}
}
},
_setOptionDisabled: function( value ) {
this._super( value );
this.element.attr( "aria-disabled", value );
// Support: IE8 Only
// #5332 / #6059 - opacity doesn't cascade to positioned elements in IE
// so we need to add the disabled class to the headers and panels
this._toggleClass( null, "ui-state-disabled", !!value );
this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled",
!!value );
},
_keydown: function( event ) {
if ( event.altKey || event.ctrlKey ) {
return;
}
var keyCode = $.ui.keyCode,
length = this.headers.length,
currentIndex = this.headers.index( event.target ),
toFocus = false;
switch ( event.keyCode ) {
case keyCode.RIGHT:
case keyCode.DOWN:
toFocus = this.headers[ ( currentIndex + 1 ) % length ];
break;
case keyCode.LEFT:
case keyCode.UP:
toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
break;
case keyCode.SPACE:
case keyCode.ENTER:
this._eventHandler( event );
break;
case keyCode.HOME:
toFocus = this.headers[ 0 ];
break;
case keyCode.END:
toFocus = this.headers[ length - 1 ];
break;
}
if ( toFocus ) {
$( event.target ).attr( "tabIndex", -1 );
$( toFocus ).attr( "tabIndex", 0 );
$( toFocus ).trigger( "focus" );
event.preventDefault();
}
},
_panelKeyDown: function( event ) {
if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
$( event.currentTarget ).prev().trigger( "focus" );
}
},
refresh: function() {
var options = this.options;
this._processPanels();
// Was collapsed or no panel
if ( ( options.active === false && options.collapsible === true ) ||
!this.headers.length ) {
options.active = false;
this.active = $();
// active false only when collapsible is true
} else if ( options.active === false ) {
this._activate( 0 );
// was active, but active panel is gone
} else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
// all remaining panel are disabled
if ( this.headers.length === this.headers.find( ".ui-state-disabled" ).length ) {
options.active = false;
this.active = $();
// activate previous panel
} else {
this._activate( Math.max( 0, options.active - 1 ) );
}
// was active, active panel still exists
} else {
// make sure active index is correct
options.active = this.headers.index( this.active );
}
this._destroyIcons();
this._refresh();
},
_processPanels: function() {
var prevHeaders = this.headers,
prevPanels = this.panels;
this.headers = this.element.find( this.options.header );
this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed",
"ui-state-default" );
this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide();
this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" );
// Avoid memory leaks (#10056)
if ( prevPanels ) {
this._off( prevHeaders.not( this.headers ) );
this._off( prevPanels.not( this.panels ) );
}
},
_refresh: function() {
var maxHeight,
options = this.options,
heightStyle = options.heightStyle,
parent = this.element.parent();
this.active = this._findActive( options.active );
this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" )
._removeClass( this.active, "ui-accordion-header-collapsed" );
this._addClass( this.active.next(), "ui-accordion-content-active" );
this.active.next().show();
this.headers
.attr( "role", "tab" )
.each( function() {
var header = $( this ),
headerId = header.uniqueId().attr( "id" ),
panel = header.next(),
panelId = panel.uniqueId().attr( "id" );
header.attr( "aria-controls", panelId );
panel.attr( "aria-labelledby", headerId );
} )
.next()
.attr( "role", "tabpanel" );
this.headers
.not( this.active )
.attr( {
"aria-selected": "false",
"aria-expanded": "false",
tabIndex: -1
} )
.next()
.attr( {
"aria-hidden": "true"
} )
.hide();
// Make sure at least one header is in the tab order
if ( !this.active.length ) {
this.headers.eq( 0 ).attr( "tabIndex", 0 );
} else {
this.active.attr( {
"aria-selected": "true",
"aria-expanded": "true",
tabIndex: 0
} )
.next()
.attr( {
"aria-hidden": "false"
} );
}
this._createIcons();
this._setupEvents( options.event );
if ( heightStyle === "fill" ) {
maxHeight = parent.height();
this.element.siblings( ":visible" ).each( function() {
var elem = $( this ),
position = elem.css( "position" );
if ( position === "absolute" || position === "fixed" ) {
return;
}
maxHeight -= elem.outerHeight( true );
} );
this.headers.each( function() {
maxHeight -= $( this ).outerHeight( true );
} );
this.headers.next()
.each( function() {
$( this ).height( Math.max( 0, maxHeight -
$( this ).innerHeight() + $( this ).height() ) );
} )
.css( "overflow", "auto" );
} else if ( heightStyle === "auto" ) {
maxHeight = 0;
this.headers.next()
.each( function() {
var isVisible = $( this ).is( ":visible" );
if ( !isVisible ) {
$( this ).show();
}
maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
if ( !isVisible ) {
$( this ).hide();
}
} )
.height( maxHeight );
}
},
_activate: function( index ) {
var active = this._findActive( index )[ 0 ];
// Trying to activate the already active panel
if ( active === this.active[ 0 ] ) {
return;
}
// Trying to collapse, simulate a click on the currently active header
active = active || this.active[ 0 ];
this._eventHandler( {
target: active,
currentTarget: active,
preventDefault: $.noop
} );
},
_findActive: function( selector ) {
return typeof selector === "number" ? this.headers.eq( selector ) : $();
},
_setupEvents: function( event ) {
var events = {
keydown: "_keydown"
};
if ( event ) {
$.each( event.split( " " ), function( index, eventName ) {
events[ eventName ] = "_eventHandler";
} );
}
this._off( this.headers.add( this.headers.next() ) );
this._on( this.headers, events );
this._on( this.headers.next(), { keydown: "_panelKeyDown" } );
this._hoverable( this.headers );
this._focusable( this.headers );
},
_eventHandler: function( event ) {
var activeChildren, clickedChildren,
options = this.options,
active = this.active,
clicked = $( event.currentTarget ),
clickedIsActive = clicked[ 0 ] === active[ 0 ],
collapsing = clickedIsActive && options.collapsible,
toShow = collapsing ? $() : clicked.next(),
toHide = active.next(),
eventData = {
oldHeader: active,
oldPanel: toHide,
newHeader: collapsing ? $() : clicked,
newPanel: toShow
};
event.preventDefault();
if (
// click on active header, but not collapsible
( clickedIsActive && !options.collapsible ) ||
// allow canceling activation
( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
return;
}
options.active = collapsing ? false : this.headers.index( clicked );
// When the call to ._toggle() comes after the class changes
// it causes a very odd bug in IE 8 (see #6720)
this.active = clickedIsActive ? $() : clicked;
this._toggle( eventData );
// Switch classes
// corner classes on the previously active header stay after the animation
this._removeClass( active, "ui-accordion-header-active", "ui-state-active" );
if ( options.icons ) {
activeChildren = active.children( ".ui-accordion-header-icon" );
this._removeClass( activeChildren, null, options.icons.activeHeader )
._addClass( activeChildren, null, options.icons.header );
}
if ( !clickedIsActive ) {
this._removeClass( clicked, "ui-accordion-header-collapsed" )
._addClass( clicked, "ui-accordion-header-active", "ui-state-active" );
if ( options.icons ) {
clickedChildren = clicked.children( ".ui-accordion-header-icon" );
this._removeClass( clickedChildren, null, options.icons.header )
._addClass( clickedChildren, null, options.icons.activeHeader );
}
this._addClass( clicked.next(), "ui-accordion-content-active" );
}
},
_toggle: function( data ) {
var toShow = data.newPanel,
toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
// Handle activating a panel during the animation for another activation
this.prevShow.add( this.prevHide ).stop( true, true );
this.prevShow = toShow;
this.prevHide = toHide;
if ( this.options.animate ) {
this._animate( toShow, toHide, data );
} else {
toHide.hide();
toShow.show();
this._toggleComplete( data );
}
toHide.attr( {
"aria-hidden": "true"
} );
toHide.prev().attr( {
"aria-selected": "false",
"aria-expanded": "false"
} );
// if we're switching panels, remove the old header from the tab order
// if we're opening from collapsed state, remove the previous header from the tab order
// if we're collapsing, then keep the collapsing header in the tab order
if ( toShow.length && toHide.length ) {
toHide.prev().attr( {
"tabIndex": -1,
"aria-expanded": "false"
} );
} else if ( toShow.length ) {
this.headers.filter( function() {
return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
} )
.attr( "tabIndex", -1 );
}
toShow
.attr( "aria-hidden", "false" )
.prev()
.attr( {
"aria-selected": "true",
"aria-expanded": "true",
tabIndex: 0
} );
},
_animate: function( toShow, toHide, data ) {
var total, easing, duration,
that = this,
adjust = 0,
boxSizing = toShow.css( "box-sizing" ),
down = toShow.length &&
( !toHide.length || ( toShow.index() < toHide.index() ) ),
animate = this.options.animate || {},
options = down && animate.down || animate,
complete = function() {
that._toggleComplete( data );
};
if ( typeof options === "number" ) {
duration = options;
}
if ( typeof options === "string" ) {
easing = options;
}
// fall back from options to animation in case of partial down settings
easing = easing || options.easing || animate.easing;
duration = duration || options.duration || animate.duration;
if ( !toHide.length ) {
return toShow.animate( this.showProps, duration, easing, complete );
}
if ( !toShow.length ) {
return toHide.animate( this.hideProps, duration, easing, complete );
}
total = toShow.show().outerHeight();
toHide.animate( this.hideProps, {
duration: duration,
easing: easing,
step: function( now, fx ) {
fx.now = Math.round( now );
}
} );
toShow
.hide()
.animate( this.showProps, {
duration: duration,
easing: easing,
complete: complete,
step: function( now, fx ) {
fx.now = Math.round( now );
if ( fx.prop !== "height" ) {
if ( boxSizing === "content-box" ) {
adjust += fx.now;
}
} else if ( that.options.heightStyle !== "content" ) {
fx.now = Math.round( total - toHide.outerHeight() - adjust );
adjust = 0;
}
}
} );
},
_toggleComplete: function( data ) {
var toHide = data.oldPanel,
prev = toHide.prev();
this._removeClass( toHide, "ui-accordion-content-active" );
this._removeClass( prev, "ui-accordion-header-active" )
._addClass( prev, "ui-accordion-header-collapsed" );
// Work around for rendering bug in IE (#5421)
if ( toHide.length ) {
toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
}
this._trigger( "activate", null, data );
}
} );
} ) );
/*!
* jQuery Mobile Button Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Button
//>>group: Forms
//>>description: Backwards-compatibility for buttons.
//>>docs: http://api.jquerymobile.com/button/
//>>demos: http://demos.jquerymobile.com/@VERSION/button/
//>>css.structure: ../css/structure/jquery.mobile.core.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/button.backcompat',[
"jquery",
"../../core",
"../../widget",
"../widget.backcompat",
"./button" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
$.widget( "ui.button", $.ui.button, {
initSelector: "input[type='button'], input[type='submit'], input[type='reset'], button," +
" [data-role='button']",
options: {
iconpos: "left",
mini: false,
wrapperClass: null,
inline: null,
shadow: true,
corners: true
},
classProp: "ui-button",
_create: function() {
if ( this.options.iconPosition !== $.ui.button.prototype.options.iconPosition ) {
this._seticonPosition( this.options.iconPosition );
} else if ( this.options.iconpos !== $.ui.button.prototype.options.iconpos ) {
this._seticonpos( this.options.iconpos );
}
this._super();
},
_seticonPosition: function( value ) {
if ( value === "end" ) {
this.options.iconpos = "right";
} else if ( value !== "left" ) {
this.options.iconpos = value;
}
},
_seticonpos: function( value ) {
if ( value === "right" ) {
this._setOption( "iconPosition", "end" );
} else if ( value !== "left" ) {
this._setOption( "iconPosition", value );
}
},
_setOption: function( key, value ) {
if ( key === "iconPosition" || key === "iconpos" ) {
this[ "_set" + key ]( value );
}
this._superApply( arguments );
}
} );
return $.widget( "ui.button", $.ui.button, $.mobile.widget.backcompat );
}
} );
/*!
* jQuery Mobile Checkboxradio @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Checkboxes & Radio Buttons
//>>group: Forms
//>>description: Consistent styling for checkboxes/radio buttons.
//>>docs: http://api.jquerymobile.com/checkboxradio/
//>>demos: http://demos.jquerymobile.com/@VERSION/checkboxradio-checkbox/
//>>css.structure: ../css/structure/jquery.mobile.forms.checkboxradio.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/checkboxradio',[
"jquery",
"../../core",
"../../widget",
"jquery-ui/widgets/checkboxradio",
"../widget.theme" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
$.widget( "ui.checkboxradio", $.ui.checkboxradio, {
options: {
enhanced: false,
theme: "inherit"
},
_enhance: function() {
if ( !this.options.enhanced ) {
this._super();
} else if ( this.options.icon ) {
this.icon = this.element.parent().find( ".ui-checkboxradio-icon" );
}
},
_themeElements: function() {
return [
{
element: this.widget(),
prefix: "ui-button-"
}
];
}
} );
$.widget( "ui.checkboxradio", $.ui.checkboxradio, $.mobile.widget.theme );
return $.ui.checkboxradio;
} );
/*!
* jQuery Mobile Checkboxradio Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Checkboxes & Radio Buttons
//>>group: Forms
//>>description: Consistent styling for checkboxes/radio buttons.
//>>docs: http://api.jquerymobile.com/checkboxradio/
//>>demos: http://demos.jquerymobile.com/@VERSION/checkboxradio-checkbox/
//>>css.structure: ../css/structure/jquery.mobile.forms.checkboxradio.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/checkboxradio.backcompat',[
"jquery",
"../../core",
"../../widget",
"../widget.theme",
"../widget.backcompat",
"./checkboxradio" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
$.widget( "ui.checkboxradio", $.ui.checkboxradio, {
initSelector: "input[type='radio'],input[type='checkbox']:not(:jqmData(role='flipswitch'))",
options: {
// Unimplemented until its decided if this will move to ui widget
iconpos: "left",
mini: false,
wrapperClass: null
},
classProp: "ui-checkboxradio-label"
} );
$.widget( "ui.checkboxradio", $.ui.checkboxradio, $.mobile.widget.backcompat );
}
return $.ui.checkboxradio;
} );
/*!
* jQuery Mobile Zoom @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Zoom Handling
//>>group: Utilities
//>>description: Utility methods for enabling and disabling user scaling (pinch zoom)
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'zoom',[
"jquery",
"./core" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var meta = $( "meta[name=viewport]" ),
initialContent = meta.attr( "content" ),
disabledZoom = initialContent + ",maximum-scale=1, user-scalable=no",
enabledZoom = initialContent + ",maximum-scale=10, user-scalable=yes",
disabledInitially = /(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test( initialContent );
$.mobile.zoom = $.extend( {}, {
enabled: !disabledInitially,
locked: false,
disable: function( lock ) {
if ( !disabledInitially && !$.mobile.zoom.locked ) {
meta.attr( "content", disabledZoom );
$.mobile.zoom.enabled = false;
$.mobile.zoom.locked = lock || false;
}
},
enable: function( unlock ) {
if ( !disabledInitially && ( !$.mobile.zoom.locked || unlock === true ) ) {
meta.attr( "content", enabledZoom );
$.mobile.zoom.enabled = true;
$.mobile.zoom.locked = false;
}
},
restore: function() {
if ( !disabledInitially ) {
meta.attr( "content", initialContent );
$.mobile.zoom.enabled = true;
}
}
} );
return $.mobile.zoom;
} );
/*!
* jQuery Mobile Textinput @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Text Inputs & Textareas
//>>group: Forms
//>>description: Enhances and consistently styles text inputs.
//>>docs: http://api.jquerymobile.com/textinput/
//>>demos: http://demos.jquerymobile.com/@VERSION/textinput/
//>>css.structure: ../css/structure/jquery.mobile.forms.textinput.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/textinput',[
"jquery",
"../../core",
"../../widget",
"../../degradeInputs",
"../widget.theme",
"../../zoom" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
$.widget( "mobile.textinput", {
version: "@VERSION",
options: {
classes: {
"ui-textinput": "ui-corner-all ui-shadow-inset",
"ui-textinput-search-icon": "ui-icon ui-alt-icon ui-icon-search"
},
theme: "inherit",
// This option defaults to true on iOS devices.
preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) && navigator.userAgent.indexOf( "AppleWebKit" ) > -1,
enhanced: false
},
_create: function() {
var options = this.options,
isSearch = this.element.is( "[type='search'], :jqmData(type='search')" ),
isTextarea = this.element[ 0 ].nodeName.toLowerCase() === "textarea";
if ( this.element.prop( "disabled" ) ) {
options.disabled = true;
}
$.extend( this, {
isSearch: isSearch,
isTextarea: isTextarea
} );
this._autoCorrect();
if ( !options.enhanced ) {
this._enhance();
} else {
this._outer = ( isTextarea ? this.element : this.element.parent() );
if ( isSearch ) {
this._searchIcon = this._outer.children( ".ui-textinput-search-icon" );
}
}
this._addClass( this._outer,
"ui-textinput ui-textinput-" + ( this.isSearch ? "search" : "text" ) );
if ( this._searchIcon ) {
this._addClass( this._searchIcon, "ui-textinput-search-icon" );
}
this._on( {
"focus": "_handleFocus",
"blur": "_handleBlur"
} );
if ( options.disabled !== undefined ) {
this.element.prop( "disabled", !!options.disabled );
this._toggleClass( this._outer, null, "ui-state-disabled", !!options.disabled );
}
},
refresh: function() {
this._setOptions( {
"disabled": this.element.is( ":disabled" )
} );
},
_themeElements: function() {
return [
{
element: this._outer,
prefix: "ui-body-"
}
];
},
_enhance: function() {
var outer;
if ( !this.isTextarea ) {
outer = $( "" );
if ( this.isSearch ) {
this._searchIcon = $( "
" ).prependTo( outer );
}
} else {
outer = this.element;
}
this._outer = outer;
// Now that we're done building up the wrapper, wrap the input in it
if ( !this.isTextarea ) {
outer.insertBefore( this.element ).append( this.element );
}
},
widget: function() {
return this._outer;
},
_autoCorrect: function() {
// XXX: Temporary workaround for issue 785 (Apple bug 8910589).
// Turn off autocorrect and autocomplete on non-iOS 5 devices
// since the popup they use can't be dismissed by the user. Note
// that we test for the presence of the feature by looking for
// the autocorrect property on the input element. We currently
// have no test for iOS 5 or newer so we're temporarily using
// the touchOverflow support flag for jQM 1.0. Yes, I feel dirty.
// - jblas
if ( typeof this.element[ 0 ].autocorrect !== "undefined" &&
!$.support.touchOverflow ) {
// Set the attribute instead of the property just in case there
// is code that attempts to make modifications via HTML.
this.element[ 0 ].setAttribute( "autocorrect", "off" );
this.element[ 0 ].setAttribute( "autocomplete", "off" );
}
},
_handleBlur: function() {
this._removeClass( this._outer, null, "ui-focus" );
if ( this.options.preventFocusZoom ) {
$.mobile.zoom.enable( true );
}
},
_handleFocus: function() {
// In many situations, iOS will zoom into the input upon tap, this
// prevents that from happening
if ( this.options.preventFocusZoom ) {
$.mobile.zoom.disable( true );
}
this._addClass( this._outer, null, "ui-focus" );
},
_setOptions: function( options ) {
if ( options.disabled !== undefined ) {
this.element.prop( "disabled", !!options.disabled );
this._toggleClass( this._outer, null, "ui-state-disabled", !!options.disabled );
}
return this._superApply( arguments );
},
_destroy: function() {
if ( this.options.enhanced ) {
this.classesElementLookup = {};
return;
}
if ( this._searchIcon ) {
this._searchIcon.remove();
}
if ( !this.isTextarea ) {
this.element.unwrap();
}
}
} );
return $.widget( "mobile.textinput", $.mobile.textinput, $.mobile.widget.theme );
} );
/*!
* jQuery Mobile Form Reset @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Form Reset
//>>group: Forms
//>>description: A behavioral mixin that forces a widget to react to a form reset
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/reset',[
"jquery",
"../../core" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
$.mobile.behaviors.formReset = {
_handleFormReset: function() {
this._on( this.element.closest( "form" ), {
reset: function() {
this._delay( "_reset" );
}
} );
}
};
return $.mobile.behaviors.formReset;
} );
/*!
* jQuery Mobile Slider @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Slider
//>>group: Forms
//>>description: Slider form widget
//>>docs: http://api.jquerymobile.com/button/
//>>demos: http://demos.jquerymobile.com/@VERSION/button/
//>>css.structure: ../css/structure/jquery.mobile.forms.slider.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/slider',[
"jquery",
"../../core",
"../../widget",
"./textinput",
"../../vmouse",
"../widget.theme",
"./reset" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
$.widget( "mobile.slider", $.extend( {
version: "@VERSION",
initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",
widgetEventPrefix: "slide",
options: {
theme: "inherit",
trackTheme: "inherit",
classes: {
"ui-slider-track": "ui-shadow-inset ui-corner-all",
"ui-slider-input": "ui-shadow-inset ui-corner-all"
}
},
_create: function() {
// TODO: Each of these should have comments explain what they're for
var control = this.element,
cType = control[ 0 ].nodeName.toLowerCase(),
isRangeslider = control.parent().is( ":jqmData(role='rangeslider')" ),
controlID = control.attr( "id" ),
$label = $( "[for='" + controlID + "']" ),
labelID = $label.attr( "id" ) || controlID + "-label",
min = parseFloat( control.attr( "min" ) ),
max = parseFloat( control.attr( "max" ) ),
step = window.parseFloat( control.attr( "step" ) || 1 ),
domHandle = document.createElement( "a" ),
handle = $( domHandle ),
domSlider = document.createElement( "div" ),
slider = $( domSlider ),
wrapper;
$label.attr( "id", labelID );
domHandle.setAttribute( "href", "#" );
domSlider.setAttribute( "role", "application" );
this._addClass( slider, "ui-slider-track" );
this._addClass( handle, "ui-slider-handle" );
domSlider.appendChild( domHandle );
handle.attr( {
"role": "slider",
"aria-valuemin": min,
"aria-valuemax": max,
"aria-valuenow": this._value(),
"aria-valuetext": this._value(),
"title": this._value(),
"aria-labelledby": labelID
} );
$.extend( this, {
slider: slider,
handle: handle,
control: control,
type: cType,
step: step,
max: max,
min: min,
isRangeslider: isRangeslider,
dragging: false,
beforeStart: null,
userModified: false,
mouseMoved: false
} );
// Monitor the input for updated values
this._addClass( "ui-slider-input" );
this._on( control, {
"change": "_controlChange",
"keyup": "_controlKeyup",
"blur": "_controlBlur",
"vmouseup": "_controlVMouseUp"
} );
slider.bind( "vmousedown", $.proxy( this._sliderVMouseDown, this ) )
.bind( "vclick", false );
// We have to instantiate a new function object for the unbind to work properly
// since the method itself is defined in the prototype (causing it to unbind everything)
this._on( document, { "vmousemove": "_preventDocumentDrag" } );
this._on( slider.add( document ), { "vmouseup": "_sliderVMouseUp" } );
slider.insertAfter( control );
// Wrap in a div for styling purposes
if ( !isRangeslider ) {
wrapper = "";
control.add( slider ).wrapAll( wrapper );
}
// Bind the handle event callbacks and set the context to the widget instance
this._on( this.handle, {
"vmousedown": "_handleVMouseDown",
"keydown": "_handleKeydown",
"keyup": "_handleKeyup"
} );
this.handle.bind( "vclick", false );
this._handleFormReset();
this.refresh( undefined, undefined, true );
},
_setOptions: function( options ) {
if ( options.disabled !== undefined ) {
this._setDisabled( options.disabled );
}
this._super( options );
},
_controlChange: function( event ) {
// If the user dragged the handle, the "change" event was triggered from
// inside refresh(); don't call refresh() again
if ( this._trigger( "controlchange", event ) === false ) {
return false;
}
if ( !this.mouseMoved ) {
this.refresh( this._value(), true );
}
},
_controlKeyup: function( /* event */ ) {
// Necessary?
this.refresh( this._value(), true, true );
},
_controlBlur: function( /* event */ ) {
this.refresh( this._value(), true );
},
// It appears the clicking the up and down buttons in chrome on
// range/number inputs doesn't trigger a change until the field is
// blurred. Here we check thif the value has changed and refresh
_controlVMouseUp: function( /* event */ ) {
this._checkedRefresh();
},
// NOTE force focus on handle
_handleVMouseDown: function( /* event */ ) {
this.handle.focus();
},
_handleKeydown: function( event ) {
var index = this._value();
if ( this.options.disabled ) {
return;
}
// In all cases prevent the default and mark the handle as active
switch ( event.keyCode ) {
case $.mobile.keyCode.HOME:
case $.mobile.keyCode.END:
case $.mobile.keyCode.PAGE_UP:
case $.mobile.keyCode.PAGE_DOWN:
case $.mobile.keyCode.UP:
case $.mobile.keyCode.RIGHT:
case $.mobile.keyCode.DOWN:
case $.mobile.keyCode.LEFT:
event.preventDefault();
if ( !this._keySliding ) {
this._keySliding = true;
// TODO: We don't use this class for styling. Do we need it?
this._addClass( this.handle, null, "ui-state-active" );
}
break;
}
// Move the slider according to the keypress
switch ( event.keyCode ) {
case $.mobile.keyCode.HOME:
this.refresh( this.min );
break;
case $.mobile.keyCode.END:
this.refresh( this.max );
break;
case $.mobile.keyCode.PAGE_UP:
case $.mobile.keyCode.UP:
case $.mobile.keyCode.RIGHT:
this.refresh( index + this.step );
break;
case $.mobile.keyCode.PAGE_DOWN:
case $.mobile.keyCode.DOWN:
case $.mobile.keyCode.LEFT:
this.refresh( index - this.step );
break;
}
},
_handleKeyup: function( /* event */ ) {
if ( this._keySliding ) {
this._keySliding = false;
this._removeClass( this.handle, null, "ui-state-active" ); /* See comment above. */
}
},
_sliderVMouseDown: function( event ) {
// NOTE: we don't do this in refresh because we still want to
// support programmatic alteration of disabled inputs
if ( this.options.disabled || !( event.which === 1 ||
event.which === 0 || event.which === undefined ) ) {
return false;
}
if ( this._trigger( "beforestart", event ) === false ) {
return false;
}
this.dragging = true;
this.userModified = false;
this.mouseMoved = false;
this.refresh( event );
this._trigger( "start" );
return false;
},
_sliderVMouseUp: function() {
if ( this.dragging ) {
this.dragging = false;
this.mouseMoved = false;
this._trigger( "stop" );
return false;
}
},
_preventDocumentDrag: function( event ) {
// NOTE: we don't do this in refresh because we still want to
// support programmatic alteration of disabled inputs
if ( this._trigger( "drag", event ) === false ) {
return false;
}
if ( this.dragging && !this.options.disabled ) {
// This.mouseMoved must be updated before refresh() because it will be
// used in the control "change" event
this.mouseMoved = true;
this.refresh( event );
// Only after refresh() you can calculate this.userModified
this.userModified = this.beforeStart !== this.element[ 0 ].selectedIndex;
return false;
}
},
_checkedRefresh: function() {
if ( this.value !== this._value() ) {
this.refresh( this._value() );
}
},
_value: function() {
return parseFloat( this.element.val() );
},
_reset: function() {
this.refresh( undefined, false, true );
},
refresh: function( val, isfromControl, preventInputUpdate ) {
// NOTE: we don't return here because we want to support programmatic
// alteration of the input value, which should still update the slider
var self = this,
left, width, data, tol,
pxStep, percent,
control, min, max, step,
newval, valModStep, alignValue, percentPerStep,
handlePercent, aPercent, bPercent,
valueChanged;
this._addClass( self.slider, "ui-slider-track" );
if ( this.options.disabled || this.element.prop( "disabled" ) ) {
this.disable();
}
// Set the stored value for comparison later
this.value = this._value();
this._addClass( this.handle, null, "ui-button ui-shadow" );
control = this.element;
min = parseFloat( control.attr( "min" ) );
max = parseFloat( control.attr( "max" ) );
step = ( parseFloat( control.attr( "step" ) ) > 0 ) ?
parseFloat( control.attr( "step" ) ) : 1;
if ( typeof val === "object" ) {
data = val;
// A slight tolerance helped get to the ends of the slider
tol = 8;
left = this.slider.offset().left;
width = this.slider.width();
pxStep = width / ( ( max - min ) / step );
if ( !this.dragging ||
data.pageX < left - tol ||
data.pageX > left + width + tol ) {
return;
}
if ( pxStep > 1 ) {
percent = ( ( data.pageX - left ) / width ) * 100;
} else {
percent = Math.round( ( ( data.pageX - left ) / width ) * 100 );
}
} else {
if ( val == null ) {
val = parseFloat( control.val() || 0 ) ;
}
percent = ( parseFloat( val ) - min ) / ( max - min ) * 100;
}
if ( isNaN( percent ) ) {
return;
}
newval = ( percent / 100 ) * ( max - min ) + min;
//From jQuery UI slider, the following source will round to the nearest step
valModStep = ( newval - min ) % step;
alignValue = newval - valModStep;
if ( Math.abs( valModStep ) * 2 >= step ) {
alignValue += ( valModStep > 0 ) ? step : ( -step );
}
percentPerStep = 100 / ( ( max - min ) / step );
// Since JavaScript has problems with large floats, round
// the final value to 5 digits after the decimal point (see jQueryUI: #4124)
newval = parseFloat( alignValue.toFixed( 5 ) );
if ( typeof pxStep === "undefined" ) {
pxStep = width / ( ( max - min ) / step );
}
if ( pxStep > 1 ) {
percent = ( newval - min ) * percentPerStep * ( 1 / step );
}
if ( percent < 0 ) {
percent = 0;
}
if ( percent > 100 ) {
percent = 100;
}
if ( newval < min ) {
newval = min;
}
if ( newval > max ) {
newval = max;
}
this.handle.css( "left", percent + "%" );
this.handle[ 0 ].setAttribute( "aria-valuenow", newval );
this.handle[ 0 ].setAttribute( "aria-valuetext", newval );
this.handle[ 0 ].setAttribute( "title", newval );
if ( this.valuebg ) {
this.valuebg.css( "width", percent + "%" );
}
// Drag the label widths
if ( this._labels ) {
handlePercent = this.handle.width() / this.slider.width() * 100;
aPercent = percent && handlePercent + ( 100 - handlePercent ) * percent / 100;
bPercent = percent === 100 ? 0 : Math.min( handlePercent + 100 - aPercent, 100 );
this._labels.each( function() {
var ab = $( this ).hasClass( "ui-slider-label-a" );
$( this ).width( ( ab ? aPercent : bPercent ) + "%" );
} );
}
if ( !preventInputUpdate ) {
valueChanged = false;
// Update control"s value
valueChanged = parseFloat( control.val() ) !== newval;
control.val( newval );
if ( this._trigger( "beforechange", val ) === false ) {
return false;
}
if ( !isfromControl && valueChanged ) {
control.trigger( "change" );
}
}
},
_themeElements: function() {
return [
{
element: this.handle,
prefix: "ui-button-"
},
{
element: this.control,
prefix: "ui-body-"
},
{
element: this.slider,
prefix: "ui-body-",
option: "trackTheme"
},
{
element: this.element,
prefix: "ui-body-"
}
];
},
_setDisabled: function( value ) {
value = !!value;
this.element.prop( "disabled", value );
this._toggleClass( this.slider, null, "ui-state-disabled", value );
this.slider.attr( "aria-disabled", value );
this._toggleClass( null, "ui-state-disabled", value );
}
}, $.mobile.behaviors.formReset ) );
return $.widget( "mobile.slider", $.mobile.slider, $.mobile.widget.theme );
} );
/*!
* jQuery Mobile Slider Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Slider
//>>group: Forms
//>>description: Deprecated Slider features
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/slider.backcompat',[
"jquery",
"../widget.backcompat",
"./slider" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
$.widget( "mobile.slider", $.mobile.slider, {
options: {
corners: true,
mini: false,
highlight: false
},
classProp: "ui-slider",
_create: function() {
this._super();
if ( this.options.mini ) {
this._addClass( this.slider, "ui-mini", null );
}
if ( this.options.highlight ) {
this._setHighlight( this.options.highlight );
}
if ( this.options.corners !== undefined ) {
this._setCorners( this.options.corners );
}
},
refresh: function( val, isfromControl, preventInputUpdate ) {
this._super( val, isfromControl, preventInputUpdate );
if ( this.options.highlight && this.slider.find( ".ui-slider-bg" ).length === 0 ) {
this.valuebg = ( function( slider ) {
var bg = document.createElement( "div" );
bg.className = "ui-slider-bg " + "ui-button-active";
return $( bg ).prependTo( slider );
} )( this.slider );
}
},
_setHighlight: function( value ) {
if ( value ) {
this.options.highlight = !!value;
this.refresh();
} else if ( this.valuebg ) {
this.valuebg.remove();
this.valuebg = false;
}
},
_setCorners: function( value ) {
this._toggleClass( this.slider, null, "ui-corner-all", value );
this._toggleClass( this.element, null, "ui-corner-all", value );
}
} );
$.widget( "mobile.slider", $.mobile.slider, $.mobile.widget.backcompat );
}
return $.mobile.slider;
} );
/*!
* jQuery Mobile Slider Tooltip @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Slidertooltip
//>>group: Forms
//>>description: Slider tooltip extension
//>>docs: http://api.jquerymobile.com/slider/
//>>demos: http://demos.jquerymobile.com/@VERSION/slider-tooltip/
//>>css.structure: ../css/structure/jquery.mobile.forms.slider.tooltip.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/slider.tooltip',[
"jquery",
"./slider" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var popup;
function getPopup() {
if ( !popup ) {
popup = $( "", {
"class": "ui-slider-popup ui-shadow ui-corner-all"
} );
}
return popup.clone();
}
return $.widget( "mobile.slider", $.mobile.slider, {
options: {
popupEnabled: false,
showValue: false
},
_create: function() {
this._super();
$.extend( this, {
_currentValue: null,
_popup: null,
_popupVisible: false
} );
this._setOption( "popupEnabled", this.options.popupEnabled );
this._on( this.handle.add( this.slider ), { "vmousedown": "_showPopup" } );
this._on( this.slider.add( this.document ), { "vmouseup": "_hidePopup" } );
this._refresh();
},
// position the popup centered 5px above the handle
_positionPopup: function() {
var dstOffset = this.handle.offset();
this._popup.offset( {
left: dstOffset.left + ( this.handle.width() - this._popup.width() ) / 2,
top: dstOffset.top - this._popup.outerHeight() - 5
} );
},
_setOption: function( key, value ) {
this._super( key, value );
if ( key === "showValue" ) {
this.handle.html( value && !this.options.mini ? this._value() : "" );
} else if ( key === "popupEnabled" ) {
if ( value && !this._popup ) {
this._popup = getPopup()
.addClass( "ui-body-" + ( this.options.theme || "a" ) )
.hide()
.insertBefore( this.element );
}
}
},
// show value on the handle and in popup
refresh: function() {
this._super.apply( this, arguments );
this._refresh();
},
_refresh: function() {
var o = this.options, newValue;
if ( o.popupEnabled ) {
// remove the title attribute from the handle (which is
// responsible for the annoying tooltip); NB we have
// to do it here as the jqm slider sets it every time
// the slider's value changes :(
this.handle.removeAttr( "title" );
}
newValue = this._value();
if ( newValue === this._currentValue ) {
return;
}
this._currentValue = newValue;
if ( o.popupEnabled && this._popup ) {
this._positionPopup();
this._popup.html( newValue );
}
if ( o.showValue && !this.options.mini ) {
this.handle.html( newValue );
}
},
_showPopup: function() {
if ( this.options.popupEnabled && !this._popupVisible ) {
this._popup.show();
this._positionPopup();
this._popupVisible = true;
}
},
_hidePopup: function() {
var o = this.options;
if ( o.popupEnabled && this._popupVisible ) {
if ( o.showValue && !o.mini ) {
this.handle.html( this._value() );
}
this._popup.hide();
this._popupVisible = false;
}
}
} );
} );
/*!
* jQuery Mobile Flipswitch @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Flip Switch
//>>group: Forms
//>>description: Consistent styling for native select menus. Tapping opens a native select menu.
//>>docs: http://api.jquerymobile.com/flipswitch/
//>>demos: http://demos.jquerymobile.com/@VERSION/flipswitch/
//>>css.structure: ../css/structure/jquery.mobile.forms.flipswitch.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/flipswitch',[
"jquery",
"../../core",
"../../widget",
"../../zoom",
"./reset" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var selectorEscapeRegex = /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;
return $.widget( "mobile.flipswitch", $.extend( {
version: "@VERSION",
options: {
onText: "On",
offText: "Off",
theme: null,
enhanced: false,
classes: {
"ui-flipswitch": "ui-shadow-inset ui-corner-all",
"ui-flipswitch-on": "ui-shadow"
}
},
_create: function() {
var labels;
this._originalTabIndex = this.element.attr( "tabindex" );
this.type = this.element[ 0 ].nodeName.toLowerCase();
if ( !this.options.enhanced ) {
this._enhance();
} else {
$.extend( this, {
flipswitch: this.element.parent(),
on: this.element.find( ".ui-flipswitch-on" ).eq( 0 ),
off: this.element.find( ".ui-flipswitch-off" ).eq( 0 )
} );
}
this._handleFormReset();
this.element.attr( "tabindex", "-1" );
this._on( {
"focus": "_handleInputFocus"
} );
if ( this.element.is( ":disabled" ) ) {
this._setOptions( {
"disabled": true
} );
}
this._on( this.flipswitch, {
"click": "_toggle",
"swipeleft": "_left",
"swiperight": "_right"
} );
this._on( this.on, {
"keydown": "_keydown"
} );
this._on( {
"change": "refresh"
} );
// On iOS we need to prevent default when the label is clicked, otherwise it drops down
// the native select menu. We nevertheless pass the click onto the element like the
// native code would.
if ( this.element[ 0 ].nodeName.toLowerCase() === "select" ) {
labels = this._findLabels();
if ( labels.length ) {
this._on( labels, {
"click": function( event ) {
this.element.click();
event.preventDefault();
}
} );
}
}
},
_handleInputFocus: function() {
this.on.focus();
},
widget: function() {
return this.flipswitch;
},
_left: function() {
this.flipswitch.removeClass( "ui-flipswitch-active" );
if ( this.type === "select" ) {
this.element.get( 0 ).selectedIndex = 0;
} else {
this.element.prop( "checked", false );
}
this.element.trigger( "change" );
},
_right: function() {
this._addClass( this.flipswitch, "ui-flipswitch-active" );
if ( this.type === "select" ) {
this.element.get( 0 ).selectedIndex = 1;
} else {
this.element.prop( "checked", true );
}
this.element.trigger( "change" );
},
_enhance: function() {
var flipswitch = $( "" ),
options = this.options,
element = this.element,
tabindex = this._originalTabIndex || 0,
theme = options.theme ? options.theme : "inherit",
// The "on" button is an anchor so it's focusable
on = $( "
" ),
off = $( "
" ),
onText = ( this.type === "input" ) ?
options.onText : element.find( "option" ).eq( 1 ).text(),
offText = ( this.type === "input" ) ?
options.offText : element.find( "option" ).eq( 0 ).text();
this._addClass( on, "ui-flipswitch-on", "ui-button ui-button-inherit" );
on.text( onText );
this._addClass( off, "ui-flipswitch-off" );
off.text( offText );
this._addClass( flipswitch, "ui-flipswitch", "ui-bar-" + theme + " " +
( ( element.is( ":checked" ) ||
element
.find( "option" )
.eq( 1 )
.is( ":selected" ) ) ? "ui-flipswitch-active" : "" ) +
( element.is( ":disabled" ) ? " ui-state-disabled" : "" ) );
flipswitch.append( on, off );
this._addClass( "ui-flipswitch-input" );
element.after( flipswitch ).appendTo( flipswitch );
$.extend( this, {
flipswitch: flipswitch,
on: on,
off: off
} );
},
_reset: function() {
this.refresh();
},
refresh: function() {
var direction,
existingDirection = this.flipswitch
.hasClass( "ui-flipswitch-active" ) ? "_right" : "_left";
if ( this.type === "select" ) {
direction = ( this.element.get( 0 ).selectedIndex > 0 ) ? "_right" : "_left";
} else {
direction = this.element.prop( "checked" ) ? "_right" : "_left";
}
if ( direction !== existingDirection ) {
this[ direction ]();
}
},
// Copied with modifications from checkboxradio
_findLabels: function() {
var input = this.element[ 0 ],
labelsList = input.labels;
if ( labelsList && labelsList.length ) {
labelsList = $( labelsList );
} else {
labelsList = this.element.closest( "label" );
if ( labelsList.length === 0 ) {
// NOTE: Windows Phone could not find the label through a selector
// filter works though.
labelsList = $( this.document[ 0 ].getElementsByTagName( "label" ) )
.filter( "[for='" +
input.getAttribute( "id" ).replace( selectorEscapeRegex, "\\$1" ) +
"']" );
}
}
return labelsList;
},
_toggle: function() {
var direction = this.flipswitch.hasClass( "ui-flipswitch-active" ) ? "_left" : "_right";
this[ direction ]();
},
_keydown: function( e ) {
if ( e.which === $.mobile.keyCode.LEFT ) {
this._left();
} else if ( e.which === $.mobile.keyCode.RIGHT ) {
this._right();
} else if ( e.which === $.mobile.keyCode.SPACE ) {
this._toggle();
e.preventDefault();
}
},
_setOptions: function( options ) {
if ( options.theme !== undefined ) {
var currentTheme = this.options.theme ? this.options.theme : "inherit",
newTheme = options.theme ? options.theme : "inherit";
this._removeClass( this.flipswitch, null, "ui-bar-" + currentTheme );
this._addClass( this.flipswitch, null, "ui-bar-" + newTheme );
}
if ( options.onText !== undefined ) {
this.on.text( options.onText );
}
if ( options.offText !== undefined ) {
this.off.text( options.offText );
}
if ( options.disabled !== undefined ) {
this._toggleClass( this.flipswitch, null, "ui-state-disabled", options.disabled );
}
this._super( options );
},
_destroy: function() {
if ( this.options.enhanced ) {
return;
}
if ( this._originalTabIndex != null ) {
this.element.attr( "tabindex", this._originalTabIndex );
} else {
this.element.removeAttr( "tabindex" );
}
this.on.remove();
this.off.remove();
this.element.unwrap();
this.element.removeClass( "ui-flipswitch-input" );
this.flipswitch.remove();
}
}, $.mobile.behaviors.formReset ) );
} );
/*!
* jQuery Mobile Flipswitch Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Flipswitch
//>>group: Forms
//>>description: Deprecated rangeslider features
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/flipswitch.backcompat',[
"jquery",
"../widget.backcompat",
"./flipswitch" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
$.widget( "mobile.flipswitch", $.mobile.flipswitch, {
options: {
corners: true,
mini: false,
wrapperClass: null
},
classProp: "ui-flipswitch"
} );
$.widget( "mobile.flipswitch", $.mobile.flipswitch, $.mobile.widget.backcompat );
}
return $.mobile.flipswitch;
} );
/*!
* jQuery Mobile Range Slider @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Range Slider
//>>group: Forms
//>>description: Range Slider form widget
//>>docs: http://api.jquerymobile.com/rangeslider/
//>>demos: http://demos.jquerymobile.com/@VERSION/rangeslider/
//>>css.structure: ../css/structure/jquery.mobile.forms.rangeslider.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/rangeslider',[
"jquery",
"../../core",
"../../widget",
"./textinput",
"../../vmouse",
"./reset",
"../widget.theme",
"./slider" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
$.widget( "mobile.rangeslider", $.extend( {
version: "@VERSION",
options: {
theme: "inherit",
trackTheme: "inherit"
},
_create: function() {
var _inputFirst = this.element.find( "input" ).first(),
_inputLast = this.element.find( "input" ).last(),
_label = this.element.find( "label" ).first(),
_sliderWidgetFirst = $.data( _inputFirst.get( 0 ), "mobile-slider" ) ||
$.data( _inputFirst.slider().get( 0 ), "mobile-slider" ),
_sliderWidgetLast = $.data( _inputLast.get( 0 ), "mobile-slider" ) ||
$.data( _inputLast.slider().get( 0 ), "mobile-slider" ),
_sliderFirst = _sliderWidgetFirst.slider,
_sliderLast = _sliderWidgetLast.slider,
firstHandle = _sliderWidgetFirst.handle,
_sliders = $( "
" );
this._addClass( _sliders, "ui-rangeslider-sliders" );
_sliders.appendTo( this.element );
this._addClass( _inputFirst, "ui-rangeslider-first" );
this._addClass( _inputLast, "ui-rangeslider-last" );
this._addClass( "ui-rangeslider" );
_sliderFirst.appendTo( _sliders );
_sliderLast.appendTo( _sliders );
_label.insertBefore( this.element );
firstHandle.prependTo( _sliderLast );
$.extend( this, {
_inputFirst: _inputFirst,
_inputLast: _inputLast,
_sliderFirst: _sliderFirst,
_sliderLast: _sliderLast,
_label: _label,
_targetVal: null,
_sliderTarget: false,
_sliders: _sliders,
_proxy: false
} );
this.refresh();
this._on( this.element.find( "input.ui-slider-input" ), {
"slidebeforestart": "_slidebeforestart",
"slidestop": "_slidestop",
"slidedrag": "_slidedrag",
"slidebeforechange": "_change",
"blur": "_change",
"keyup": "_change"
} );
this._on( {
"mousedown":"_change"
} );
this._on( this.element.closest( "form" ), {
"reset":"_handleReset"
} );
this._on( firstHandle, {
"vmousedown": "_dragFirstHandle"
} );
},
_handleReset: function() {
var self = this;
// We must wait for the stack to unwind before updating
// otherwise sliders will not have updated yet
setTimeout( function() {
self._updateHighlight();
}, 0 );
},
_dragFirstHandle: function( event ) {
// If the first handle is dragged send the event to the first slider
$.data( this._inputFirst.get( 0 ), "mobile-slider" ).dragging = true;
$.data( this._inputFirst.get( 0 ), "mobile-slider" ).refresh( event );
$.data( this._inputFirst.get( 0 ), "mobile-slider" )._trigger( "start" );
return false;
},
_slidedrag: function( event ) {
var first = $( event.target ).is( this._inputFirst ),
otherSlider = ( first ) ? this._inputLast : this._inputFirst;
this._sliderTarget = false;
// If the drag was initiated on an extreme and the other handle is
// focused send the events to the closest handle
if ( ( this._proxy === "first" && first ) || ( this._proxy === "last" && !first ) ) {
$.data( otherSlider.get( 0 ), "mobile-slider" ).dragging = true;
$.data( otherSlider.get( 0 ), "mobile-slider" ).refresh( event );
return false;
}
},
_slidestop: function( event ) {
var first = $( event.target ).is( this._inputFirst );
this._proxy = false;
// This stops dragging of the handle and brings the active track to the front
// this makes clicks on the track go the the last handle used
this.element.find( "input" ).trigger( "vmouseup" );
this._sliderFirst.css( "z-index", first ? 1 : "" );
},
_slidebeforestart: function( event ) {
this._sliderTarget = false;
// If the track is the target remember this and the original value
if ( $( event.originalEvent.target ).hasClass( "ui-slider-track" ) ) {
this._sliderTarget = true;
this._targetVal = $( event.target ).val();
}
},
_setOptions: function( options ) {
if ( options.theme !== undefined ) {
this._setTheme( options.theme );
}
if ( options.trackTheme !== undefined ) {
this._setTrackTheme( options.trackTheme );
}
if ( options.disabled !== undefined ) {
this._setDisabled( options.disabled );
}
this._super( options );
this.refresh();
},
refresh: function() {
var $el = this.element,
o = this.options;
if ( this._inputFirst.is( ":disabled" ) || this._inputLast.is( ":disabled" ) ) {
this.options.disabled = true;
}
$el.find( "input" ).slider( {
theme: o.theme,
trackTheme: o.trackTheme,
disabled: o.disabled
} ).slider( "refresh" );
this._updateHighlight();
},
_change: function( event ) {
if ( event.type === "keyup" ) {
this._updateHighlight();
return false;
}
var self = this,
min = parseFloat( this._inputFirst.val(), 10 ),
max = parseFloat( this._inputLast.val(), 10 ),
first = $( event.target ).hasClass( "ui-rangeslider-first" ),
thisSlider = first ? this._inputFirst : this._inputLast,
otherSlider = first ? this._inputLast : this._inputFirst;
if ( ( this._inputFirst.val() > this._inputLast.val() && event.type === "mousedown" &&
!$( event.target ).hasClass( "ui-slider-handle" ) ) ) {
thisSlider.blur();
} else if ( event.type === "mousedown" ) {
return;
}
if ( min > max && !this._sliderTarget ) {
// This prevents min from being greater than max
thisSlider.val( first ? max : min ).slider( "refresh" );
this._trigger( "normalize" );
} else if ( min > max ) {
// This makes it so clicks on the target on either extreme go to the closest handle
thisSlider.val( this._targetVal ).slider( "refresh" );
// You must wait for the stack to unwind so
// first slider is updated before updating second
setTimeout( function() {
otherSlider.val( first ? min : max ).slider( "refresh" );
$.data( otherSlider.get( 0 ), "mobile-slider" ).handle.focus();
self._sliderFirst.css( "z-index", first ? "" : 1 );
self._trigger( "normalize" );
}, 0 );
this._proxy = ( first ) ? "first" : "last";
}
// Fixes issue where when both _sliders are at min they cannot be adjusted
if ( min === max ) {
$.data( thisSlider.get( 0 ), "mobile-slider" ).handle.css( "z-index", 1 );
$.data( otherSlider.get( 0 ), "mobile-slider" ).handle.css( "z-index", 0 );
} else {
$.data( otherSlider.get( 0 ), "mobile-slider" ).handle.css( "z-index", "" );
$.data( thisSlider.get( 0 ), "mobile-slider" ).handle.css( "z-index", "" );
}
this._updateHighlight();
if ( min > max ) {
return false;
}
},
_themeElements: function() {
return [
{
element: this.element.find( ".ui-slider-track" ),
prefix: "ui-bar-"
}
];
},
_updateHighlight: function() {
var min = parseInt( $.data( this._inputFirst.get( 0 ), "mobile-slider" )
.handle.get( 0 ).style.left, 10 ),
max = parseInt( $.data( this._inputLast.get( 0 ), "mobile-slider" )
.handle.get( 0 ).style.left, 10 ),
width = ( max - min );
this.element.find( ".ui-slider-bg" ).css( {
"margin-left": min + "%",
"width": width + "%"
} );
},
_setTheme: function( value ) {
this._inputFirst.slider( "option", "theme", value );
this._inputLast.slider( "option", "theme", value );
},
_setTrackTheme: function( value ) {
this._inputFirst.slider( "option", "trackTheme", value );
this._inputLast.slider( "option", "trackTheme", value );
},
_setDisabled: function( value ) {
this._inputFirst.prop( "disabled", value );
this._inputLast.prop( "disabled", value );
},
_destroy: function() {
this._label.prependTo( this.element );
this._inputFirst.after( this._sliderFirst );
this._inputLast.after( this._sliderLast );
this._sliders.remove();
this.element.find( "input" ).slider( "destroy" );
}
}, $.mobile.behaviors.formReset ) );
return $.widget( "mobile.rangeslider", $.mobile.rangeslider, $.mobile.widget.theme );
} );
/*!
* jQuery Mobile Rangeslider Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Rangeslider
//>>group: Forms
//>>description: Deprecated rangeslider features
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/rangeslider.backcompat',[
"jquery",
"../widget.backcompat",
"./rangeslider" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
$.widget( "mobile.rangeslider", $.mobile.rangeslider, {
options: {
corners: true,
mini: false,
highlight: true
},
classProp: "ui-rangeslider",
_create: function() {
this._super();
this.element.find( "input" ).slider( {
mini: this.options.mini,
highlight: this.options.highlight
} ).slider( "refresh" );
this._updateHighlight();
if ( this.options.mini ) {
this._addClass( "ui-mini", null );
this._addClass( this._sliderFirst, "ui-mini", null );
this._addClass( this._sliderLast, "ui-mini", null );
}
}
} );
$.widget( "mobile.rangeslider", $.mobile.rangeslider, $.mobile.widget.backcompat );
}
return $.mobile.rangeslider;
} );
/*!
* jQuery Mobile Textinput Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Text Inputs & Textareas Backcompat
//>>group: Forms
//>>description: Backcompat for textinput widgets.
//>>docs: http://api.jquerymobile.com/textinput/
//>>demos: http://demos.jquerymobile.com/@VERSION/textinput/
//>>css.structure: ../css/structure/jquery.mobile.forms.textinput.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/textinput.backcompat',[
"jquery",
"../widget.backcompat",
"./textinput" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
$.widget( "mobile.textinput", $.mobile.textinput, {
initSelector: "input[type='text']," +
"input[type='search']," +
":jqmData(type='search')," +
"input[type='number']:not(:jqmData(type='range'))," +
":jqmData(type='number')," +
"input[type='password']," +
"input[type='email']," +
"input[type='url']," +
"input[type='tel']," +
"textarea," +
"input[type='time']," +
"input[type='date']," +
"input[type='month']," +
"input[type='week']," +
"input[type='datetime']," +
"input[type='datetime-local']," +
"input[type='color']," +
"input:not([type])," +
"input[type='file']",
options: {
corners: true,
mini: false,
wrapperClass: null
},
classProp: "ui-textinput"
} );
$.widget( "mobile.textinput", $.mobile.textinput, $.mobile.widget.backcompat );
}
return $.mobile.textinput;
} );
/*!
* jQuery Mobile Clear Button @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Text Input Clear Button
//>>group: Forms
//>>description: Add the ability to have a clear button
//>>docs: http://api.jquerymobile.com/textinput/#option-clearBtn
//>>demos: http://demos.jquerymobile.com/@VERSION/textinput/
//>>css.structure: ../css/structure/jquery.mobile.forms.textinput.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/clearButton',[
"jquery",
"./textinput" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
return $.widget( "mobile.textinput", $.mobile.textinput, {
options: {
classes: {
"ui-textinput-clear-button": "ui-corner-all"
},
clearBtn: false,
clearBtnText: "Clear text"
},
_create: function() {
this._super();
if ( this.isSearch ) {
this.options.clearBtn = true;
}
// We do nothing on startup if the options is off or if this is not a wrapped input
if ( !this.options.clearBtn || this.isTextarea ) {
return;
}
if ( this.options.enhanced ) {
this._clearButton = this._outer.children( ".ui-textinput-clear-button" );
this._clearButtonIcon = this._clearButton
.children( ".ui-textinput-clear-button-icon" );
this._toggleClasses( true );
this._bindClearEvents();
} else {
this._addClearButton();
}
},
_clearButtonClick: function( event ) {
this.element.val( "" )
.focus()
.trigger( "change" );
event.preventDefault();
},
_toggleClasses: function( add ) {
this._toggleClass( this._outer, "ui-textinput-has-clear-button", null, add );
this._toggleClass( this._clearButton, "ui-textinput-clear-button",
"ui-button ui-button-icon-only ui-button-right", add );
this._toggleClass( this._clearButtonIcon, "ui-textinput-clear-button-icon",
"ui-icon-delete ui-icon", add );
this._toggleClass( "ui-textinput-hide-clear", null, add );
},
_addClearButton: function() {
this._clearButtonIcon = $( "
" );
this._clearButton = $( "" )
.attr( "title", this.options.clearBtnText )
.text( this.options.clearBtnText )
.append( this._clearButtonIcon );
this._toggleClasses( true );
this._clearButton.appendTo( this._outer );
this._bindClearEvents();
this._toggleClear();
},
_removeClearButton: function() {
this._toggleClasses( false );
this._unbindClearEvents();
this._clearButton.remove();
clearTimeout( this._toggleClearDelay );
delete this._toggleClearDelay;
},
_bindClearEvents: function() {
this._on( this._clearButton, {
"click": "_clearButtonClick"
} );
this._on( {
"keyup": "_toggleClear",
"change": "_toggleClear",
"input": "_toggleClear",
"focus": "_toggleClear",
"blur": "_toggleClear",
"cut": "_toggleClear",
"paste": "_toggleClear"
} );
},
_unbindClearEvents: function() {
this._off( this._clearButton, "click" );
this._off( this.element, "keyup change input focus blur cut paste" );
},
_setOptions: function( options ) {
this._super( options );
if ( options.clearBtn !== undefined && !this.isTextarea ) {
if ( options.clearBtn ) {
this._addClearButton();
} else {
this._removeClearButton();
}
}
if ( options.clearBtnText !== undefined && this._clearButton !== undefined ) {
this._clearButton.text( options.clearBtnText )
.attr( "title", options.clearBtnText );
}
},
_toggleClear: function() {
this._toggleClearDelay = this._delay( "_toggleClearClass", 0 );
},
_toggleClearClass: function() {
this._toggleClass( this._clearButton, "ui-textinput-clear-button-hidden",
undefined, !this.element.val() );
this._clearButton.attr( "aria-hidden", !this.element.val() );
delete this._toggleClearDelay;
},
_destroy: function() {
this._super();
if ( !this.options.enhanced && this._clearButton ) {
this._removeClearButton();
}
}
} );
} );
/*!
* jQuery Mobile Autogrow @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Textarea Autogrow
//>>group: Forms
//>>description: Textarea elements automatically grow/shrink to accommodate their contents.
//>>docs: http://api.jquerymobile.com/textinput/#option-autogrow
//>>css.structure: ../css/structure/jquery.mobile.forms.textinput.autogrow.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/autogrow',[
"jquery",
"./textinput" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
return $.widget( "mobile.textinput", $.mobile.textinput, {
options: {
autogrow: true,
keyupTimeoutBuffer: 100
},
_create: function() {
this._super();
if ( this.options.autogrow && this.isTextarea ) {
this._autogrow();
}
},
_autogrow: function() {
this._addClass( "ui-textinput-autogrow" );
this._on( {
"keyup": "_timeout",
"change": "_timeout",
"input": "_timeout",
"paste": "_timeout"
} );
// Attach to the various you-have-become-visible notifications that the
// various framework elements emit.
// TODO: Remove all but the updatelayout handler once #6426 is fixed.
this._handleShow( "create" );
this._on( true, this.document, {
"popupbeforeposition": "_handleShow",
"updatelayout": "_handleShow",
"panelopen": "_handleShow"
} );
},
// Synchronously fix the widget height if this widget's parents are such
// that they show/hide content at runtime. We still need to check whether
// the widget is actually visible in case it is contained inside multiple
// such containers. For example: panel contains collapsible contains
// autogrow textinput. The panel may emit "panelopen" indicating that its
// content has become visible, but the collapsible is still collapsed, so
// the autogrow textarea is still not visible.
_handleShow: function( event ) {
if ( event === "create" || ( $.contains( event.target, this.element[ 0 ] ) &&
this.element.is( ":visible" ) ) ) {
if ( event !== "create" && event.type !== "popupbeforeposition" ) {
this._addClass( "ui-textinput-autogrow-resize" );
this.element
.animationComplete(
$.proxy( function() {
this._removeClass( "ui-textinput-autogrow-resize" );
}, this ),
"transition" );
}
this._prepareHeightUpdate();
}
},
_unbindAutogrow: function() {
this._removeClass( "ui-textinput-autogrow" );
this._off( this.element, "keyup change input paste" );
this._off( this.document,
"pageshow popupbeforeposition updatelayout panelopen" );
},
keyupTimeout: null,
_prepareHeightUpdate: function( delay ) {
if ( this.keyupTimeout ) {
clearTimeout( this.keyupTimeout );
}
if ( delay === undefined ) {
this._updateHeight();
} else {
this.keyupTimeout = this._delay( "_updateHeight", delay );
}
},
_timeout: function() {
this._prepareHeightUpdate( this.options.keyupTimeoutBuffer );
},
_updateHeight: function() {
var paddingTop, paddingBottom, paddingHeight, scrollHeight, clientHeight,
borderTop, borderBottom, borderHeight, height,
scrollTop = this.window.scrollTop();
this.keyupTimeout = 0;
// IE8 textareas have the onpage property - others do not
if ( !( "onpage" in this.element[ 0 ] ) ) {
this.element.css( {
"height": 0,
"min-height": 0,
"max-height": 0
} );
}
scrollHeight = this.element[ 0 ].scrollHeight;
clientHeight = this.element[ 0 ].clientHeight;
borderTop = parseFloat( this.element.css( "border-top-width" ) );
borderBottom = parseFloat( this.element.css( "border-bottom-width" ) );
borderHeight = borderTop + borderBottom;
height = scrollHeight + borderHeight + 15;
// Issue 6179: Padding is not included in scrollHeight and
// clientHeight by Firefox if no scrollbar is visible. Because
// textareas use the border-box box-sizing model, padding should be
// included in the new (assigned) height. Because the height is set
// to 0, clientHeight == 0 in Firefox. Therefore, we can use this to
// check if padding must be added.
if ( clientHeight === 0 ) {
paddingTop = parseFloat( this.element.css( "padding-top" ) );
paddingBottom = parseFloat( this.element.css( "padding-bottom" ) );
paddingHeight = paddingTop + paddingBottom;
height += paddingHeight;
}
this.element.css( {
"height": height,
"min-height": "",
"max-height": ""
} );
this.window.scrollTop( scrollTop );
},
refresh: function() {
if ( this.options.autogrow && this.isTextarea ) {
this._updateHeight();
}
},
_setOptions: function( options ) {
this._super( options );
if ( options.autogrow !== undefined && this.isTextarea ) {
if ( options.autogrow ) {
this._autogrow();
} else {
this._unbindAutogrow();
}
}
},
_destroy: function() {
this._unbindAutogrow();
this._super();
}
} );
} );
/*!
* jQuery Mobile Select Menu @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Selects
//>>group: Forms
//>>description: Consistent styling for native select menus. Tapping opens a native select menu.
//>>docs: http://api.jquerymobile.com/selectmenu/
//>>demos: http://demos.jquerymobile.com/@VERSION/selectmenu/
//>>css.structure: ../css/structure/jquery.mobile.forms.select.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/select',[
"jquery",
"jquery-ui/labels",
"../../core",
"../../widget",
"../../zoom",
"../../navigation/path",
"../widget.theme",
"jquery-ui/form-reset-mixin" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var selectmenu = $.widget( "mobile.selectmenu", [ {
version: "@VERSION",
options: {
classes: {
"ui-selectmenu-button": "ui-corner-all ui-shadow"
},
theme: "inherit",
icon: "caret-d",
iconpos: "right",
nativeMenu: true,
// This option defaults to true on iOS devices.
preventFocusZoom: /iPhone|iPad|iPod/.test( navigator.platform ) &&
navigator.userAgent.indexOf( "AppleWebKit" ) > -1
},
_button: function() {
return $( "" );
},
_themeElements: function() {
return [
{
element: this.button,
prefix: "ui-button-"
}
];
},
_setDisabled: function( value ) {
this.element.prop( "disabled", value );
this.button.attr( "aria-disabled", value );
return this._setOption( "disabled", value );
},
_focusButton: function() {
var that = this;
setTimeout( function() {
that.button.focus();
}, 40 );
},
_selectOptions: function() {
return this.element.find( "option" );
},
// Setup items that are generally necessary for select menu extension
_preExtension: function() {
var classes = "";
this.element = this.element;
this.selectWrapper = $( "" );
this._addClass( this.selectWrapper, "ui-selectmenu", classes );
this.selectWrapper.insertBefore( this.element );
this.element.detach();
this.selectId = this.element.attr( "id" ) || ( "select-" + this.uuid );
this.buttonId = this.selectId + "-button";
this.isMultiple = this.element[ 0 ].multiple;
this.element.appendTo( this.selectWrapper );
this.label = this.element.labels().first();
},
_destroy: function() {
if ( this.selectWrapper.length > 0 ) {
this.element.insertAfter( this.selectWrapper );
this.selectWrapper.remove();
}
this._unbindFormResetHandler();
},
_create: function() {
var options = this.options,
iconpos = options.icon ?
( options.iconpos || this.element.attr( "data-" + this._ns() + "iconpos" ) ) :
false;
this._preExtension();
this.button = this._button();
this.button.attr( "id", this.buttonId );
this._addClass( this.button, "ui-selectmenu-button", "ui-button" );
this.button.insertBefore( this.element );
if ( this.options.icon ) {
this.icon = $( "
" );
this._addClass( this.icon, "ui-selectmenu-button-icon",
"ui-icon-" + options.icon + " ui-icon ui-widget-icon-float" +
( iconpos === "right" ? "end" : "beginning" ) );
this.button.prepend( this.icon );
}
this.setButtonText();
// Opera does not properly support opacity on select elements
// In Mini, it hides the element, but not its text
// On the desktop,it seems to do the opposite
// for these reasons, using the nativeMenu option results in a full native select in Opera
if ( options.nativeMenu && window.opera && window.opera.version ) {
this._addClass( this.button, "ui-selectmenu-nativeonly" );
}
// Add counter for multi selects
if ( this.isMultiple ) {
this.buttonCount = $( "" ).hide();
this._addClass( this.buttonCount, "ui-selectmenu-count-bubble",
"ui-listview-item-count-bubble ui-body-inherit" );
this._addClass( this.button, null, "ui-listview-item-has-count" );
this.buttonCount.appendTo( this.button );
}
// Disable if specified
if ( options.disabled || this.element.prop( "disabled" ) ) {
this.disable();
}
// Events on native select
this._on( this.element, {
change: "refresh"
} );
this._bindFormResetHandler();
this._on( this.button, {
keydown: "_handleKeydown"
} );
this.build();
},
build: function() {
var that = this;
this.element
.appendTo( that.button )
.bind( "vmousedown", function() {
// Add active class to button
that.button.addClass( "ui-button-active" );
} )
.bind( "focus", function() {
that.button.addClass( "ui-focus" );
} )
.bind( "blur", function() {
that.button.removeClass( "ui-focus" );
} )
.bind( "focus vmouseover", function() {
that.button.trigger( "vmouseover" );
} )
.bind( "vmousemove", function() {
// Remove active class on scroll/touchmove
that.button.removeClass( "ui-button-active" );
} )
.bind( "change blur vmouseout", function() {
that.button.trigger( "vmouseout" )
.removeClass( "ui-button-active" );
} );
// In many situations, iOS will zoom into the select upon tap, this prevents that from
// happening
that.button.bind( "vmousedown", function() {
if ( that.options.preventFocusZoom ) {
$.mobile.zoom.disable( true );
}
} );
that.label.bind( "click focus", function() {
if ( that.options.preventFocusZoom ) {
$.mobile.zoom.disable( true );
}
} );
that.element.bind( "focus", function() {
if ( that.options.preventFocusZoom ) {
$.mobile.zoom.disable( true );
}
} );
that.button.bind( "mouseup", function() {
if ( that.options.preventFocusZoom ) {
setTimeout( function() {
$.mobile.zoom.enable( true );
}, 0 );
}
} );
that.element.bind( "blur", function() {
if ( that.options.preventFocusZoom ) {
$.mobile.zoom.enable( true );
}
} );
},
selected: function() {
return this._selectOptions().filter( ":selected" );
},
selectedIndices: function() {
var that = this;
return this.selected().map( function() {
return that._selectOptions().index( this );
} ).get();
},
setButtonText: function() {
var that = this,
selected = this.selected(),
text = this.placeholder,
span = $( "" );
this.button.children( "span" )
.not( ".ui-selectmenu-count-bubble,.ui-selectmenu-button-icon" )
.remove().end().end()
.append( ( function() {
if ( selected.length ) {
text = selected.map( function() {
return $( this ).text();
} ).get().join( ", " );
}
if ( text ) {
span.text( text );
} else {
// Set the contents to which we write as to be XHTML compliant.
// See gh-6699
span.html( " " );
}
// Hide from assistive technologies, as otherwise this will create redundant text
// announcement - see gh-8256
span.attr( "aria-hidden", "true" );
// TODO possibly aggregate multiple select option classes
that._addClass( span, "ui-selectmenu-button-text",
[ that.element.attr( "class" ), selected.attr( "class" ) ].join( " " ) );
that._removeClass( span, null, "ui-screen-hidden" );
return span;
} )() );
},
setButtonCount: function() {
var selected = this.selected();
// Multiple count inside button
if ( this.isMultiple ) {
this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
}
},
_handleKeydown: function( /* event */ ) {
this._delay( "_refreshButton" );
},
_refreshButton: function() {
this.setButtonText();
this.setButtonCount();
},
refresh: function() {
this._refreshButton();
},
// Functions open and close preserved in native selects to simplify users code when looping
// over selects
open: $.noop,
close: $.noop,
disable: function() {
this._setDisabled( true );
this.button.addClass( "ui-state-disabled" );
},
enable: function() {
this._setDisabled( false );
this.button.removeClass( "ui-state-disabled" );
}
}, $.ui.formResetMixin ] );
return $.widget( "mobile.selectmenu", selectmenu, $.mobile.widget.theme );
} );
/*!
* jQuery Mobile Selectmenu Backcompat @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Popups
//>>group: Widgets
//>>description: Deprecated selectmenu features
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/select.backcompat',[
"jquery",
"../widget.backcompat",
"./select" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
if ( $.mobileBackcompat !== false ) {
$.widget( "mobile.selectmenu", $.mobile.selectmenu, {
options: {
inline: false,
corners: true,
shadow: true,
mini: false
},
initSelector: "select:not( :jqmData(role='slider')):not( :jqmData(role='flipswitch') )",
classProp: "ui-selectmenu-button"
} );
$.widget( "mobile.selectmenu", $.mobile.selectmenu, $.mobile.widget.backcompat );
}
return $.mobile.selectmenu;
} );
/*!
* jQuery Mobile Toolbar @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Toolbars
//>>group: Widgets
//>>description: Headers and footers
//>>docs: http://api.jquerymobile.com/toolbar/
//>>demos: http://demos.jquerymobile.com/@VERSION/toolbar/
//>>css.structure: ../css/structure/jquery.mobile.core.css
//>>css.structure: ../css/structure/jquery.mobile.toolbar.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/toolbar',[
"jquery",
"../widget",
"../core",
"../navigation",
"./widget.theme",
"../zoom" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
$.widget( "mobile.toolbar", {
version: "@VERSION",
options: {
theme: "inherit",
addBackBtn: false,
backBtnTheme: null,
backBtnText: "Back",
type: "toolbar",
ariaRole: null
},
_create: function() {
var leftbutton, rightbutton,
role = this.options.type,
page = this.element.closest( ".ui-page" ),
toolbarAriaRole = this.options.ariaRole === null ?
role === "header" ? "banner" :
( role === "footer" ? "contentinfo" : "toolbar" ) :
this.options.ariaRole;
if ( page.length === 0 ) {
page = false;
this._on( this.document, {
"pageshow": "refresh"
} );
}
$.extend( this, {
role: role,
page: page,
leftbutton: leftbutton,
rightbutton: rightbutton
} );
this.element.attr( "role", toolbarAriaRole );
this._addClass( "ui-toolbar" + ( role !== "toolbar" ? "-" + role : "" ) );
this.refresh();
this._setOptions( this.options );
},
_setOptions: function( o ) {
if ( o.addBackBtn ) {
this._updateBackButton();
}
if ( o.backBtnText !== undefined ) {
this.element
.find( ".ui-toolbar-back-button .ui-button-text" ).text( o.backBtnText );
}
this._super( o );
},
refresh: function() {
if ( !this.page ) {
this._setRelative();
if ( this.role === "footer" ) {
this.element.appendTo( "body" );
} else if ( this.role === "header" ) {
this._updateBackButton();
}
}
this._addHeadingClasses();
},
//We only want this to run on non fixed toolbars so make it easy to override
_setRelative: function() {
$( "[data-" + $.mobile.ns + "role='page']" ).css( { "position": "relative" } );
},
_updateBackButton: function() {
var backButton,
options = this.options,
theme = options.backBtnTheme || options.theme;
// Retrieve the back button or create a new, empty one
backButton = this._backButton = ( this._backButton || {} );
// We add a back button only if the option to do so is on
if ( this.options.addBackBtn &&
// This must also be a header toolbar
this.role === "header" &&
// There must be multiple pages in the DOM
$( ".ui-page" ).length > 1 &&
( this.page ?
// If the toolbar is internal the page's URL must differ from the hash
( this.page[ 0 ].getAttribute( "data-" + $.mobile.ns + "url" ) !==
$.mobile.path.stripHash( location.hash ) ) :
// Otherwise, if the toolbar is external there must be at least one
// history item to which one can go back
( $.mobile.navigate && $.mobile.navigate.history &&
$.mobile.navigate.history.activeIndex > 0 ) ) &&
// The toolbar does not have a left button
!this.leftbutton ) {
// Skip back button creation if one is already present
if ( !backButton.attached ) {
this.backButton = backButton.element = ( backButton.element ||
$( "" + options.backBtnText +
"" ) )
.prependTo( this.element );
backButton.attached = true;
}
// If we are not adding a back button, then remove the one present, if any
} else if ( backButton.element ) {
backButton.element.detach();
backButton.attached = false;
}
},
_addHeadingClasses: function() {
this.headerElements = this.element.children( "h1, h2, h3, h4, h5, h6" );
this._addClass( this.headerElements, "ui-toolbar-title" );
this.headerElements
// Regardless of h element number in src, it becomes h1 for the enhanced page
.attr( {
"role": "heading",
"aria-level": "1"
} );
},
_destroy: function() {
var currentTheme;
this.headerElements.removeAttr( "role aria-level" );
if ( this.role === "header" ) {
if ( this.backButton ) {
this.backButton.remove();
}
}
currentTheme = this.options.theme ? this.options.theme : "inherit";
this.element.removeAttr( "role" );
},
_themeElements: function() {
var elements = [
{
element: this.element,
prefix: "ui-bar-"
}
];
if ( this.options.addBackBtn && this.backButton !== undefined ) {
elements.push( {
element: this.backButton,
prefix: "ui-button-",
option: "backBtnTheme"
} );
}
return elements;
}
} );
return $.widget( "mobile.toolbar", $.mobile.toolbar, $.mobile.widget.theme );
} );
/*!
* jQuery Mobile Custom Select @VERSION
* http://jquerymobile.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Selects: Custom menus
//>>group: Forms
//>>description: Select menu extension for menu styling, placeholder options, and multi-select.
//>>docs: http://api.jquerymobile.com/selectmenu/
//>>demos: http://demos.jquerymobile.com/@VERSION/selectmenu-custom/
//>>css.structure: ../css/structure/jquery.mobile.forms.select.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( 'widgets/forms/select.custom',[
"jquery",
"../../core",
"../../navigation",
"./select",
"../toolbar",
"../listview",
"../page.dialog.backcompat",
"../popup" ], factory );
} else {
// Browser globals
factory( jQuery );
}
} )( function( $ ) {
var unfocusableItemSelector = ".ui-disabled,.ui-state-disabled,.ui-listview-item-divider," +
".ui-screen-hidden";
return $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
options: {
classes: {
"ui-selectmenu-custom-header-close-button": "ui-corner-all"
},
overlayTheme: null,
dividerTheme: null,
hidePlaceholderMenuItems: true,
closeText: "Close"
},
_ns: function() {
return "ui-";
},
_create: function() {
var o = this.options;
this._origTabIndex = ( this.element.attr( "tabindex" ) === undefined ) ? false :
this.element.attr( "tabindex" );
// Custom selects cannot exist inside popups, so revert the "nativeMenu" option to true if
// a parent is a popup
o.nativeMenu = o.nativeMenu ||
( this.element.closest( "[data-" + this._ns() +
"role='popup'],:mobile-popup" ).length > 0 );
return this._super();
},
_handleSelectFocus: function() {
this.element.blur();
this.button.focus();
},
_handleKeydown: function( event ) {
this._super( event );
this._handleButtonVclickKeydown( event );
},
_handleButtonVclickKeydown: function( event ) {
if ( this.options.disabled || this.isOpen || this.options.nativeMenu ) {
return;
}
if ( event.type === "vclick" ||
event.keyCode &&
( event.keyCode === $.ui.keyCode.ENTER ||
event.keyCode === $.ui.keyCode.SPACE ) ) {
this._decideFormat();
if ( this.menuType === "overlay" ) {
this.button
.attr( "href", "#" + this.popupId )
.attr( "data-" + this._ns() + "rel", "popup" );
} else {
this.button
.attr( "href", "#" + this.dialogId )
.attr( "data-" + this._ns() + "rel", "dialog" );
}
this.isOpen = true;
// Do not prevent default, so the navigation may have a chance to actually open the
// chosen format
}
},
_handleListFocus: function( e ) {
var params = ( e.type === "focusin" ) ?
{ tabindex: "0", event: "vmouseover" } :
{ tabindex: "-1", event: "vmouseout" };
$( e.target )
.attr( "tabindex", params.tabindex )
.trigger( params.event );
},
_goToAdjacentItem: function( item, target, direction ) {
var adjacent = item[ direction + "All" ]()
.not( unfocusableItemSelector + ",[data-" + this._ns() + "role='placeholder']" )
.first();
// If there's a previous option, focus it
if ( adjacent.length ) {
target
.blur()
.attr( "tabindex", "-1" );
adjacent.find( "a" ).first().focus();
}
},
_handleListKeydown: function( event ) {
var target = $( event.target ),
li = target.closest( "li" );
// Switch logic based on which key was pressed
switch ( event.keyCode ) {
// Up or left arrow keys
case 38:
this._goToAdjacentItem( li, target, "prev" );
return false;
// Down or right arrow keys
case 40:
this._goToAdjacentItem( li, target, "next" );
return false;
// If enter or space is pressed, trigger click
case 13:
case 32:
target.trigger( "click" );
return false;
}
},
// Focus the button before the page containing the widget replaces the dialog page
_handleBeforeTransition: function( event, data ) {
var focusButton;
if ( data && data.prevPage && data.prevPage[ 0 ] === this.menuPage[ 0 ] ) {
focusButton = $.proxy( function() {
this._delay( function() {
this._focusButton();
} );
}, this );
if ( data.options && data.options.transition && data.options.transition !== "none" ) {
data.prevPage.animationComplete( focusButton );
} else {
focusButton();
}
}
},
_handleHeaderCloseClick: function() {
if ( this.menuType === "overlay" ) {
this.close();
return false;
}
},
_handleListItemClick: function( event ) {
var anchors,
listItem = $( event.target ).closest( "li" ),
// Index of option tag to be selected
oldIndex = this.element[ 0 ].selectedIndex,
newIndex = $.mobile.getAttribute( listItem, "option-index" ),
option = this._selectOptions().eq( newIndex )[ 0 ];
// Toggle selected status on the tag for multi selects
option.selected = this.isMultiple ? !option.selected : true;
// Toggle checkbox class for multiple selects
if ( this.isMultiple ) {
anchors = listItem.find( "a" );
this._toggleClass( anchors, null, "ui-checkbox-on", option.selected );
this._toggleClass( anchors, null, "ui-checkbox-off", !option.selected );
}
// If it's not a multiple select, trigger change after it has finished closing
if ( !this.isMultiple && oldIndex !== newIndex ) {
this._triggerChange = true;
}
// Trigger change if it's a multiple select
// Hide custom select for single selects only - otherwise focus clicked item
// We need to grab the clicked item the hard way, because the list may have been rebuilt
if ( this.isMultiple ) {
this.element.trigger( "change" );
this.list.find( "li:not(.ui-listview-item-divider)" ).eq( newIndex )
.find( "a" ).first().focus();
} else {
this.close();
}
event.preventDefault();
},
build: function() {
if ( this.options.nativeMenu ) {
return this._super();
}
var selectId, popupId, dialogId, label, thisPage, isMultiple, menuId,
themeAttr, overlayTheme, overlayThemeAttr, dividerThemeAttr,
menuPage, menuPageHeader, listbox, list, header, headerTitle, menuPageContent,
menuPageClose, headerClose, headerCloseIcon,
o = this.options;
selectId = this.selectId;
popupId = selectId + "-listbox";
dialogId = selectId + "-dialog";
label = this.label;
thisPage = this.element.closest( ".ui-page" );
isMultiple = this.element[ 0 ].multiple;
menuId = selectId + "-menu";
themeAttr = o.theme ? ( " data-" + this._ns() + "theme='" + o.theme + "'" ) : "";
overlayTheme = o.overlayTheme || o.theme || null;
overlayThemeAttr = overlayTheme ? ( " data-" + this._ns() +
"overlay-theme='" + overlayTheme + "'" ) : "";
dividerThemeAttr = ( o.dividerTheme && this.element.children( "optgroup" ).length > 0 ) ?
( " data-" + this._ns() + "divider-theme='" + o.dividerTheme + "'" ) : "";
menuPage = $( "" )
.attr( "id", dialogId );
menuPageContent = menuPage.children();
// Adding the data-type attribute allows the dialog widget to place the close button before
// the toolbar is instantiated
menuPageHeader = $( "" )
.prependTo( menuPage );
listbox = $( "" )
.attr( "id", popupId )
.insertAfter( this.element )
.popup();
list = $( "" )
.attr( "id", menuId )
.appendTo( listbox );
header = $( "" )
.prependTo( listbox );
headerTitle = $( "
" ).appendTo( header );
menuPage.page();
// Instantiate the toolbars after everything else so that when they are created they find
// the page in which they are contained.
menuPageHeader.add( header ).toolbar( { type: "header" } );
this._addClass( menuPage, "ui-selectmenu-custom" );
this._addClass( menuPageContent, null, "ui-content" );
this._addClass( listbox, null, "ui-selectmenu-custom" );
this._addClass( list, null, "ui-selectmenu-custom-list" );
if ( this.isMultiple ) {
headerClose = $( "
", {
"role": "button",
"href": "#"
} );
headerCloseIcon = $( "" );
this._addClass( headerCloseIcon, "ui-selectmenu-custom-header-close-button-icon",
"ui-icon ui-icon-delete" );
headerClose.append( headerCloseIcon );
this._addClass( headerClose, "ui-selectmenu-custom-header-close-button",
"ui-button ui-toolbar-header-button-left ui-button-icon-only" );
headerClose.appendTo( header );
}
$.extend( this, {
selectId: selectId,
menuId: menuId,
popupId: popupId,
dialogId: dialogId,
thisPage: thisPage,
menuPage: menuPage,
menuPageHeader: menuPageHeader,
label: label,
isMultiple: isMultiple,
theme: o.theme,
listbox: listbox,
list: list,
header: header,
headerTitle: headerTitle,
headerClose: headerClose,
menuPageContent: menuPageContent,
menuPageClose: menuPageClose,
placeholder: ""
} );
// Create list from select, update state
this.refresh();
this.element.attr( "tabindex", "-1" );
this._on( this.element, { focus: "_handleSelectFocus" } );
// Button events
this._on( this.button, {
vclick: "_handleButtonVclickKeydown"
} );
// Events for list items
this.list.attr( "role", "listbox" );
this._on( this.list, {
"focusin": "_handleListFocus",
"focusout": "_handleListFocus",
"keydown": "_handleListKeydown",
"click li:not(.ui-disabled,.ui-state-disabled,.ui-listview-item-divider)":
"_handleListItemClick"
} );
// Events on the popup
this._on( this.listbox, { popupafterclose: "_popupClosed" } );
// Close button on small overlays
if ( this.isMultiple ) {
this._on( this.headerClose, { click: "_handleHeaderCloseClick" } );
}
this._on( this.document, { pagecontainerbeforetransition: "_handleBeforeTransition" } );
return this;
},
_popupClosed: function() {
this.close();
this._delayedTrigger();
},
_delayedTrigger: function() {
if ( this._triggerChange ) {
this.element.trigger( "change" );
}
this._triggerChange = false;
},
_isRebuildRequired: function() {
var list = this.list.find( "li" ),
options = this._selectOptions().not( ".ui-screen-hidden" );
// TODO exceedingly naive method to determine difference ignores value changes etc in favor
// of a forcedRebuild from the user in the refresh method
return options.text() !== list.text();
},
selected: function() {
return this._selectOptions()
.filter( ":selected:not( [data-" + this._ns() + "placeholder='true'] )" );
},
refresh: function( force ) {
var indices, items;
if ( this.options.nativeMenu ) {
return this._super( force );
}
if ( force || this._isRebuildRequired() ) {
this._buildList();
}
indices = this.selectedIndices();
this.setButtonText();
this.setButtonCount();
items = this.list.find( "li:not(.ui-listview-item-divider)" );
this._removeClass( items.find( "a" ), null, "ui-button-active" );
items.attr( "aria-selected", false );
items.each( $.proxy( function( i, element ) {
var anchors,
item = $( element );
if ( $.inArray( i, indices ) > -1 ) {
// Aria selected attr
item.attr( "aria-selected", true );
// Multiple selects: add the "on" checkbox state to the icon
if ( this.isMultiple ) {
anchors = item.find( "a" );
this._removeClass( anchors, null, "ui-checkbox-off" );
this._addClass( anchors, null, "ui-checkbox-on" );
} else {
if ( item.hasClass( "ui-screen-hidden" ) ) {
this._addClass( item.next().find( "a" ), null, "ui-button-active" );
} else {
this._addClass( item.find( "a" ), null, "ui-button-active" );
}
}
} else if ( this.isMultiple ) {
anchors = item.find( "a" );
this._removeClass( anchors, null, "ui-checkbox-on" );
this._addClass( anchors, null, "ui-checkbox-off" );
}
}, this ) );
},
close: function() {
if ( this.options.disabled || !this.isOpen ) {
return;
}
if ( this.menuType === "page" ) {
if ( this.menuPage.hasClass( "ui-page-active" ) ) {
$.mobile.back();
}
} else {
this.listbox.popup( "close" );
}
this._focusButton();
// Allow the dialog to be closed again
this.isOpen = false;
},
open: function() {
this.button.click();
},
_focusMenuItem: function() {
var selector = this.list.find( "a.ui-button-active" );
if ( selector.length === 0 ) {
selector = this.list.find( "li:not(" + unfocusableItemSelector +
",[data-" + this._ns() + "role='placeholder'] ) a.ui-button" );
}
selector.first().focus();
},
_setTheme: function( key, value ) {
this.listbox.popup( "option", key, value );
// We cannot pass inherit to the dialog because pages are supposed to set the theme for
// the pagecontainer in which they reside. If they set it to inherit the pagecontainer
// will not inherit from anything above it.
if ( value !== "inherit" ) {
this.menuPage.page( "option", key, value );
}
if ( key === "theme" ) {
this.header.toolbar( "option", key, value );
this.menuPageHeader.toolbar( "option", key, value );
}
},
_setOption: function( key, value ) {
if ( !this.options.nativeMenu && ( key === "theme" || key === "overlayTheme" ) ) {
this._setTheme( key, value );
}
if ( key === "hidePlaceholderMenuItems" ) {
this._superApply( arguments );
this.refresh( true );
return;
}
if ( key === "closeText" ) {
this.headerClose.text( value );
}
return this._superApply( arguments );
},
_decideFormat: function() {
var pageWidget,
theWindow = this.window,
selfListParent = this.list.parent(),
menuHeight = selfListParent.outerHeight(),
scrollTop = theWindow.scrollTop(),
buttonOffset = this.button.offset().top,
screenHeight = theWindow.height();
if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
this.menuPage.appendTo( this.element.closest( ".ui-pagecontainer" ) );
this.menuPageClose = this.menuPage.find( ".ui-toolbar-header a" );
// Prevent the parent page from being removed from the DOM, otherwise the results of
// selecting a list item in the dialog fall into a black hole
pageWidget = this.thisPage.page( "instance" );
pageWidget._off( pageWidget.document, "pagecontainerhide" );
// For WebOS/Opera Mini (set lastscroll using button offset)
if ( scrollTop === 0 && buttonOffset > screenHeight ) {
this.thisPage.one( "pagehide", function() {
$( this ).data( $.camelCase( this._ns() + "lastScroll" ), buttonOffset );
} );
}
this._on( this.document, {
pagecontainershow: "_handlePageContainerShow",
pagecontainerhide: "_handlePageContainerHide"
} );
this.menuType = "page";
this.menuPageContent.append( this.list );
this.menuPage
.find( "div .ui-toolbar-title" )
.text( this.label.getEncodedText() || this.placeholder );
} else {
this.menuType = "overlay";
this.listbox.one( { popupafteropen: $.proxy( this, "_focusMenuItem" ) } );
}
this._setTheme( "theme", this.options.theme );
this._setTheme( "overlayTheme", this.options.overlayTheme );
},
_handlePageContainerShow: function( event, data ) {
if ( data.toPage[ 0 ] === this.menuPage[ 0 ] ) {
this._off( this.document, "pagecontainershow" );
this._focusMenuItem();
}
},
_handlePageContainerHide: function( event, data ) {
if ( data.prevPage[ 0 ] === this.menuPage[ 0 ] ) {
this._off( this.document, "pagecontainershow" );
// After the dialog's done, we may want to trigger change if the value has actually
// changed
this._delayedTrigger();
// TODO centralize page removal binding / handling in the page plugin.
// Suggestion from @jblas to do refcounting.
//
// TODO extremely confusing dependency on the open method where the pagehide.remove
// bindings are stripped to prevent the parent page from disappearing. The way we're
// keeping pages in the DOM right now sucks
//
// Rebind the page remove that was unbound in the open function to allow for the parent
// page removal from actions other than the use of a dialog sized custom select
//
// Doing this here provides for the back button on the custom select dialog
this.thisPage.page( "bindRemove" );
this.menuPage.detach();
this.list.appendTo( this.listbox );
this.close();
}
},
_buildList: function() {
var o = this.options,
placeholder = this.placeholder,
needPlaceholder = true,
dataIcon = "false",
optionsList, numOptions, select,
dataPrefix = "data-" + this._ns(),
dataIndexAttr = dataPrefix + "option-index",
dataIconAttr = dataPrefix + "icon",
dataRoleAttr = dataPrefix + "role",
dataPlaceholderAttr = dataPrefix + "placeholder",
fragment = document.createDocumentFragment(),
isPlaceholderItem = false,
optGroup,
i,
option, optionElement, parent, text, anchor, classes,
optLabel, divider, item;
this.list.empty().filter( ".ui-listview" ).listview( "destroy" );
optionsList = this._selectOptions();
numOptions = optionsList.length;
select = this.element[ 0 ];
for ( i = 0; i < numOptions; i++, isPlaceholderItem = false ) {
option = optionsList[ i ];
optionElement = $( option );
// Do not create options based on ui-screen-hidden select options
if ( optionElement.hasClass( "ui-screen-hidden" ) ) {
continue;
}
parent = option.parentNode;
classes = [];
// Although using .text() here raises the risk that, when we later paste this into the
// list item we end up pasting possibly malicious things like