Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Update some files in the benchmark corpus.

Updated libraries:
- Underscore.js 1.5.2
- jQuery Mobile 1.3.2
- AngularJS 1.0.8
- YUI 3.12.0

https://code.google.com/p/esprima/issues/detail?id=354
  • Loading branch information...
commit 2a097677dc8b32a79d7ab25fa4ab71e0f1e44895 1 parent e4be267
Ariya Hidayat authored
1,110 test/3rdparty/angular-1.0.6.js → test/3rdparty/angular-1.0.8.js
View
754 additions, 356 deletions not shown
1,552 test/3rdparty/jquery.mobile-1.3.1.js → test/3rdparty/jquery.mobile-1.3.2.js
View
@@ -1,6 +1,6 @@
-/*
-* jQuery Mobile 1.3.1
-* Git HEAD hash: 74b4bec049fd93e4fe40205e6157de16eb64eb46 <> Date: Mon Apr 8 2013 19:41:28 UTC
+/*!
+* jQuery Mobile 1.3.2
+* Git HEAD hash: 528cf0e96940644ea644096bfeb913ed920ffaef <> Date: Fri Jul 19 2013 22:17:57 UTC
* http://jquerymobile.com
*
* Copyright 2010, 2013 jQuery Foundation, Inc. and other contributors
@@ -32,7 +32,7 @@
$.mobile = $.extend($.mobile, {
// Version of the jQuery Mobile Framework
- version: "1.3.1",
+ version: "1.3.2",
// Namespace used framework-wide for data-attrs. Default is no namespace
ns: "",
@@ -4280,8 +4280,8 @@ $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defau
};
}
// Reset base to the default document base.
- // only reset if we are not prefetching
- if ( base && typeof options.prefetch === "undefined" ) {
+ // only reset if we are not prefetching
+ if ( base && ( typeof options === "undefined" || typeof options.prefetch === "undefined" ) ) {
base.reset();
}
@@ -4317,7 +4317,7 @@ $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defau
url = fileUrl = path.getFilePath( $( "<div>" + RegExp.$1 + "</div>" ).text() );
}
//dont update the base tag if we are prefetching
- if ( base && typeof options.prefetch === "undefined") {
+ if ( base && ( typeof options === "undefined" || typeof options.prefetch === "undefined" )) {
base.set( fileUrl );
}
@@ -5307,6 +5307,10 @@ $.widget( "mobile.dialog", $.mobile.widget, {
}
},
+ _handlePageBeforeHide: function() {
+ this._isCloseable = false;
+ },
+
_create: function() {
var self = this,
$el = this.element,
@@ -5341,7 +5345,8 @@ $.widget( "mobile.dialog", $.mobile.widget, {
});
this._on( $el, {
- pagebeforeshow: "_handlePageBeforeShow"
+ pagebeforeshow: "_handlePageBeforeShow",
+ pagebeforehide: "_handlePageBeforeHide"
});
$.extend( this, {
@@ -5565,11 +5570,6 @@ $.fn.buttonMarkup = function( options ) {
}
}
- if ( getAttrFixed( e, nsKey + "rel" ) === "popup" && el.attr( "href" ) ) {
- e.setAttribute( "aria-haspopup", true );
- e.setAttribute( "aria-owns", el.attr( "href" ) );
- }
-
// Check if this element is already enhanced
buttonElements = $.data( ( ( e.tagName === "INPUT" || e.tagName === "BUTTON" ) ? e.parentNode : e ), "buttonElements" );
@@ -7588,7 +7588,7 @@ $.widget( "mobile.slider", $.mobile.widget, $.extend( {
_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 ) ) {
+ if ( this.options.disabled || !( event.which === 1 || event.which === 0 || event.which === undefined ) ) {
return false;
}
if ( this._trigger( "beforestart", event ) === false ) {
@@ -8346,882 +8346,874 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
(function( $, undefined ) {
- function fitSegmentInsideSegment( winSize, segSize, offset, desired ) {
- var ret = desired;
-
- if ( winSize < segSize ) {
- // Center segment if it's bigger than the window
- ret = offset + ( winSize - segSize ) / 2;
- } else {
- // Otherwise center it at the desired coordinate while keeping it completely inside the window
- ret = Math.min( Math.max( offset, desired - segSize / 2 ), offset + winSize - segSize );
- }
-
- return ret;
- }
-
- function windowCoords() {
- var $win = $.mobile.window;
+function fitSegmentInsideSegment( winSize, segSize, offset, desired ) {
+ var ret = desired;
- return {
- x: $win.scrollLeft(),
- y: $win.scrollTop(),
- cx: ( window.innerWidth || $win.width() ),
- cy: ( window.innerHeight || $win.height() )
- };
+ if ( winSize < segSize ) {
+ // Center segment if it's bigger than the window
+ ret = offset + ( winSize - segSize ) / 2;
+ } else {
+ // Otherwise center it at the desired coordinate while keeping it completely inside the window
+ ret = Math.min( Math.max( offset, desired - segSize / 2 ), offset + winSize - segSize );
}
- $.widget( "mobile.popup", $.mobile.widget, {
- options: {
- theme: null,
- overlayTheme: null,
- shadow: true,
- corners: true,
- transition: "none",
- positionTo: "origin",
- tolerance: null,
- initSelector: ":jqmData(role='popup')",
- closeLinkSelector: "a:jqmData(rel='back')",
- closeLinkEvents: "click.popup",
- navigateEvents: "navigate.popup",
- closeEvents: "navigate.popup pagebeforechange.popup",
- dismissible: true,
-
- // NOTE Windows Phone 7 has a scroll position caching issue that
- // requires us to disable popup history management by default
- // https://github.com/jquery/jquery-mobile/issues/4784
- //
- // NOTE this option is modified in _create!
- history: !$.mobile.browser.oldIE
- },
+ return ret;
+}
- _eatEventAndClose: function( e ) {
- e.preventDefault();
- e.stopImmediatePropagation();
- if ( this.options.dismissible ) {
- this.close();
- }
- return false;
- },
+function windowCoords() {
+ var $win = $.mobile.window;
- // Make sure the screen size is increased beyond the page height if the popup's causes the document to increase in height
- _resizeScreen: function() {
- var popupHeight = this._ui.container.outerHeight( true );
+ return {
+ x: $win.scrollLeft(),
+ y: $win.scrollTop(),
+ cx: ( window.innerWidth || $win.width() ),
+ cy: ( window.innerHeight || $win.height() )
+ };
+}
- this._ui.screen.removeAttr( "style" );
- if ( popupHeight > this._ui.screen.height() ) {
- this._ui.screen.height( popupHeight );
- }
- },
+$.widget( "mobile.popup", $.mobile.widget, {
+ options: {
+ theme: null,
+ overlayTheme: null,
+ shadow: true,
+ corners: true,
+ transition: "none",
+ positionTo: "origin",
+ tolerance: null,
+ initSelector: ":jqmData(role='popup')",
+ closeLinkSelector: "a:jqmData(rel='back')",
+ closeLinkEvents: "click.popup",
+ navigateEvents: "navigate.popup",
+ closeEvents: "navigate.popup pagebeforechange.popup",
+ dismissible: true,
- _handleWindowKeyUp: function( e ) {
- if ( this._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE ) {
- return this._eatEventAndClose( e );
- }
- },
+ // NOTE Windows Phone 7 has a scroll position caching issue that
+ // requires us to disable popup history management by default
+ // https://github.com/jquery/jquery-mobile/issues/4784
+ //
+ // NOTE this option is modified in _create!
+ history: !$.mobile.browser.oldIE
+ },
- _expectResizeEvent: function() {
- var winCoords = windowCoords();
+ _eatEventAndClose: function( e ) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ if ( this.options.dismissible ) {
+ this.close();
+ }
+ return false;
+ },
- if ( this._resizeData ) {
- if ( winCoords.x === this._resizeData.winCoords.x &&
- winCoords.y === this._resizeData.winCoords.y &&
- winCoords.cx === this._resizeData.winCoords.cx &&
- winCoords.cy === this._resizeData.winCoords.cy ) {
- // timeout not refreshed
- return false;
- } else {
- // clear existing timeout - it will be refreshed below
- clearTimeout( this._resizeData.timeoutId );
- }
- }
+ // Make sure the screen size is increased beyond the page height if the popup's causes the document to increase in height
+ _resizeScreen: function() {
+ var popupHeight = this._ui.container.outerHeight( true );
- this._resizeData = {
- timeoutId: setTimeout( $.proxy( this, "_resizeTimeout" ), 200 ),
- winCoords: winCoords
- };
+ this._ui.screen.removeAttr( "style" );
+ if ( popupHeight > this._ui.screen.height() ) {
+ this._ui.screen.height( popupHeight );
+ }
+ },
- return true;
- },
+ _handleWindowKeyUp: function( e ) {
+ if ( this._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE ) {
+ return this._eatEventAndClose( e );
+ }
+ },
- _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._ui.container.removeClass( "ui-popup-hidden" );
- this.reposition( { positionTo: "window" } );
- this._ignoreResizeEvents();
- }
+ _expectResizeEvent: function() {
+ var winCoords = windowCoords();
- this._resizeScreen();
- this._resizeData = null;
- this._orientationchangeInProgress = false;
- }
+ if ( this._resizeData ) {
+ if ( winCoords.x === this._resizeData.winCoords.x &&
+ winCoords.y === this._resizeData.winCoords.y &&
+ winCoords.cx === this._resizeData.winCoords.cx &&
+ winCoords.cy === this._resizeData.winCoords.cy ) {
+ // timeout not refreshed
+ return false;
} else {
- this._resizeData = null;
- this._orientationchangeInProgress = false;
+ // clear existing timeout - it will be refreshed below
+ clearTimeout( this._resizeData.timeoutId );
}
- },
+ }
- _ignoreResizeEvents: function() {
- var self = this;
+ this._resizeData = {
+ timeoutId: setTimeout( $.proxy( this, "_resizeTimeout" ), 200 ),
+ winCoords: winCoords
+ };
- if ( this._ignoreResizeTo ) {
- clearTimeout( this._ignoreResizeTo );
- }
- this._ignoreResizeTo = setTimeout( function() { self._ignoreResizeTo = 0; }, 1000 );
- },
+ return true;
+ },
- _handleWindowResize: function( e ) {
- if ( this._isOpen && this._ignoreResizeTo === 0 ) {
- if ( ( this._expectResizeEvent() || this._orientationchangeInProgress ) &&
- !this._ui.container.hasClass( "ui-popup-hidden" ) ) {
- // effectively rapid-close the popup while leaving the screen intact
- this._ui.container
- .addClass( "ui-popup-hidden" )
- .removeAttr( "style" );
+ _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._ui.container.removeClass( "ui-popup-hidden" );
+ this.reposition( { positionTo: "window" } );
+ this._ignoreResizeEvents();
}
- }
- },
- _handleWindowOrientationchange: function( e ) {
- if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) {
- this._expectResizeEvent();
- this._orientationchangeInProgress = true;
+ this._resizeScreen();
+ this._resizeData = null;
+ this._orientationchangeInProgress = false;
}
- },
+ } else {
+ this._resizeData = null;
+ this._orientationchangeInProgress = false;
+ }
+ },
- // 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( e ) {
- var tgt = e.target, $tgt, ui = this._ui;
+ _ignoreResizeEvents: function() {
+ var self = this;
- if ( !this._isOpen ) {
- return;
- }
+ if ( this._ignoreResizeTo ) {
+ clearTimeout( this._ignoreResizeTo );
+ }
+ this._ignoreResizeTo = setTimeout( function() { self._ignoreResizeTo = 0; }, 1000 );
+ },
- if ( tgt !== ui.container[ 0 ] ) {
- $tgt = $( e.target );
- if ( 0 === $tgt.parents().filter( ui.container[ 0 ] ).length ) {
- $( document.activeElement ).one( "focus", function( e ) {
- $tgt.blur();
- });
- ui.focusElement.focus();
- e.preventDefault();
- e.stopImmediatePropagation();
- return false;
- } else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) {
- ui.focusElement = $tgt;
- }
+ _handleWindowResize: function( e ) {
+ if ( this._isOpen && this._ignoreResizeTo === 0 ) {
+ if ( ( this._expectResizeEvent() || this._orientationchangeInProgress ) &&
+ !this._ui.container.hasClass( "ui-popup-hidden" ) ) {
+ // effectively rapid-close the popup while leaving the screen intact
+ this._ui.container
+ .addClass( "ui-popup-hidden" )
+ .removeAttr( "style" );
}
+ }
+ },
- this._ignoreResizeEvents();
- },
+ _handleWindowOrientationchange: function( e ) {
+ if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) {
+ this._expectResizeEvent();
+ this._orientationchangeInProgress = true;
+ }
+ },
- _create: function() {
- var ui = {
- screen: $( "<div class='ui-screen-hidden ui-popup-screen'></div>" ),
- placeholder: $( "<div style='display: none;'><!-- placeholder --></div>" ),
- container: $( "<div class='ui-popup-container ui-popup-hidden'></div>" )
- },
- thisPage = this.element.closest( ".ui-page" ),
- myId = this.element.attr( "id" ),
- self = this;
+ // 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( e ) {
+ var tgt = e.target, $tgt, ui = this._ui;
- // We need to adjust the history option to be false if there's no AJAX nav.
- // We can't do it in the option declarations because those are run before
- // it is determined whether there shall be AJAX nav.
- this.options.history = this.options.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;
+ if ( !this._isOpen ) {
+ return;
+ }
- if ( thisPage.length === 0 ) {
- thisPage = $( "body" );
+ if ( tgt !== ui.container[ 0 ] ) {
+ $tgt = $( e.target );
+ if ( 0 === $tgt.parents().filter( ui.container[ 0 ] ).length ) {
+ $( document.activeElement ).one( "focus", function( e ) {
+ $tgt.blur();
+ });
+ ui.focusElement.focus();
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ return false;
+ } else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) {
+ ui.focusElement = $tgt;
}
+ }
- // define the container for navigation event bindings
- // TODO this would be nice at the the mobile widget level
- this.options.container = this.options.container || $.mobile.pageContainer;
+ this._ignoreResizeEvents();
+ },
- // Apply the proto
- thisPage.append( ui.screen );
- ui.container.insertAfter( ui.screen );
- // Leave a placeholder where the element used to be
- ui.placeholder.insertAfter( this.element );
- if ( myId ) {
- ui.screen.attr( "id", myId + "-screen" );
- ui.container.attr( "id", myId + "-popup" );
- ui.placeholder.html( "<!-- placeholder for " + myId + " -->" );
- }
- ui.container.append( this.element );
- ui.focusElement = ui.container;
-
- // Add class to popup element
- this.element.addClass( "ui-popup" );
-
- // Define instance variables
- $.extend( this, {
- _scrollTop: 0,
- _page: thisPage,
- _ui: ui,
- _fallbackTransition: "",
- _currentTransition: false,
- _prereqs: null,
- _isOpen: false,
- _tolerance: null,
- _resizeData: null,
- _ignoreResizeTo: 0,
- _orientationchangeInProgress: false
- });
+ _create: function() {
+ var ui = {
+ screen: $( "<div class='ui-screen-hidden ui-popup-screen'></div>" ),
+ placeholder: $( "<div style='display: none;'><!-- placeholder --></div>" ),
+ container: $( "<div class='ui-popup-container ui-popup-hidden'></div>" )
+ },
+ thisPage = this.element.closest( ".ui-page" ),
+ myId = this.element.attr( "id" ),
+ o = this.options,
+ key, value;
- $.each( this.options, function( key, value ) {
- // Cause initial options to be applied by their handler by temporarily setting the option to undefined
- // - the handler then sets it to the initial value
- self.options[ key ] = undefined;
- self._setOption( key, value, true );
- });
+ // We need to adjust the history option to be false if there's no AJAX nav.
+ // We can't do it in the option declarations because those are run before
+ // it is determined whether there shall be AJAX nav.
+ o.history = o.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;
- ui.screen.bind( "vclick", $.proxy( this, "_eatEventAndClose" ) );
+ if ( thisPage.length === 0 ) {
+ thisPage = $( "body" );
+ }
- this._on( $.mobile.window, {
- orientationchange: $.proxy( this, "_handleWindowOrientationchange" ),
- resize: $.proxy( this, "_handleWindowResize" ),
- keyup: $.proxy( this, "_handleWindowKeyUp" )
- });
- this._on( $.mobile.document, {
- focusin: $.proxy( this, "_handleDocumentFocusIn" )
- });
- },
+ // define the container for navigation event bindings
+ // TODO this would be nice at the the mobile widget level
+ o.container = o.container || $.mobile.pageContainer || thisPage;
- _applyTheme: function( dst, theme, prefix ) {
- var classes = ( dst.attr( "class" ) || "").split( " " ),
- alreadyAdded = true,
- currentTheme = null,
- matches,
- themeStr = String( theme );
-
- while ( classes.length > 0 ) {
- currentTheme = classes.pop();
- matches = ( new RegExp( "^ui-" + prefix + "-([a-z])$" ) ).exec( currentTheme );
- if ( matches && matches.length > 1 ) {
- currentTheme = matches[ 1 ];
- break;
- } else {
- currentTheme = null;
- }
- }
+ // Apply the proto
+ thisPage.append( ui.screen );
+ ui.container.insertAfter( ui.screen );
+ // Leave a placeholder where the element used to be
+ ui.placeholder.insertAfter( this.element );
+ if ( myId ) {
+ ui.screen.attr( "id", myId + "-screen" );
+ ui.container.attr( "id", myId + "-popup" );
+ ui.placeholder.html( "<!-- placeholder for " + myId + " -->" );
+ }
+ ui.container.append( this.element );
+ ui.focusElement = ui.container;
- if ( theme !== currentTheme ) {
- dst.removeClass( "ui-" + prefix + "-" + currentTheme );
- if ( ! ( theme === null || theme === "none" ) ) {
- dst.addClass( "ui-" + prefix + "-" + themeStr );
- }
- }
- },
+ // Add class to popup element
+ this.element.addClass( "ui-popup" );
- _setTheme: function( value ) {
- this._applyTheme( this.element, value, "body" );
- },
+ // Define instance variables
+ $.extend( this, {
+ _scrollTop: 0,
+ _page: thisPage,
+ _ui: ui,
+ _fallbackTransition: "",
+ _currentTransition: false,
+ _prereqs: null,
+ _isOpen: false,
+ _tolerance: null,
+ _resizeData: null,
+ _ignoreResizeTo: 0,
+ _orientationchangeInProgress: false
+ });
- _setOverlayTheme: function( value ) {
- this._applyTheme( this._ui.screen, value, "overlay" );
+ // This duplicates the code from the various option setters below for
+ // better performance. It must be kept in sync with those setters.
+ this._applyTheme( this.element, o.theme, "body" );
+ this._applyTheme( this._ui.screen, o.overlayTheme, "overlay" );
+ this._applyTransition( o.transition );
+ this.element
+ .toggleClass( "ui-overlay-shadow", o.shadow )
+ .toggleClass( "ui-corner-all", o.corners );
+ this._setTolerance( o.tolerance );
- if ( this._isOpen ) {
- this._ui.screen.addClass( "in" );
- }
- },
+ ui.screen.bind( "vclick", $.proxy( this, "_eatEventAndClose" ) );
- _setShadow: function( value ) {
- this.element.toggleClass( "ui-overlay-shadow", value );
- },
-
- _setCorners: function( value ) {
- this.element.toggleClass( "ui-corner-all", value );
- },
+ this._on( $.mobile.window, {
+ orientationchange: $.proxy( this, "_handleWindowOrientationchange" ),
+ resize: $.proxy( this, "_handleWindowResize" ),
+ keyup: $.proxy( this, "_handleWindowKeyUp" )
+ });
+ this._on( $.mobile.document, {
+ focusin: $.proxy( this, "_handleDocumentFocusIn" )
+ });
+ },
- _applyTransition: function( value ) {
- this._ui.container.removeClass( this._fallbackTransition );
- if ( value && value !== "none" ) {
- this._fallbackTransition = $.mobile._maybeDegradeTransition( value );
- if ( this._fallbackTransition === "none" ) {
- this._fallbackTransition = "";
- }
- this._ui.container.addClass( this._fallbackTransition );
+ _applyTheme: function( dst, theme, prefix ) {
+ var classes = ( dst.attr( "class" ) || "").split( " " ),
+ alreadyAdded = true,
+ currentTheme = null,
+ matches,
+ themeStr = String( theme );
+
+ while ( classes.length > 0 ) {
+ currentTheme = classes.pop();
+ matches = ( new RegExp( "^ui-" + prefix + "-([a-z])$" ) ).exec( currentTheme );
+ if ( matches && matches.length > 1 ) {
+ currentTheme = matches[ 1 ];
+ break;
+ } else {
+ currentTheme = null;
}
- },
+ }
- _setTransition: function( value ) {
- if ( !this._currentTransition ) {
- this._applyTransition( value );
+ if ( theme !== currentTheme ) {
+ dst.removeClass( "ui-" + prefix + "-" + currentTheme );
+ if ( ! ( theme === null || theme === "none" ) ) {
+ dst.addClass( "ui-" + prefix + "-" + themeStr );
}
- },
-
- _setTolerance: function( value ) {
- var tol = { t: 30, r: 15, b: 30, l: 15 };
+ }
+ },
- if ( value !== undefined ) {
- var ar = String( value ).split( "," );
+ _setTheme: function( value ) {
+ this._applyTheme( this.element, value, "body" );
+ },
- $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } );
+ _setOverlayTheme: function( value ) {
+ this._applyTheme( this._ui.screen, value, "overlay" );
- 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;
+ if ( this._isOpen ) {
+ this._ui.screen.addClass( "in" );
+ }
+ },
- // 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;
+ _setShadow: function( value ) {
+ this.element.toggleClass( "ui-overlay-shadow", value );
+ },
- // 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;
+ _setCorners: function( value ) {
+ this.element.toggleClass( "ui-corner-all", value );
+ },
- default:
- break;
- }
+ _applyTransition: function( value ) {
+ this._ui.container.removeClass( this._fallbackTransition );
+ if ( value && value !== "none" ) {
+ this._fallbackTransition = $.mobile._maybeDegradeTransition( value );
+ if ( this._fallbackTransition === "none" ) {
+ this._fallbackTransition = "";
}
+ this._ui.container.addClass( this._fallbackTransition );
+ }
+ },
- this._tolerance = tol;
- },
+ _setTransition: function( value ) {
+ if ( !this._currentTransition ) {
+ this._applyTransition( value );
+ }
+ },
- _setOption: function( key, value ) {
- var exclusions, setter = "_set" + key.charAt( 0 ).toUpperCase() + key.slice( 1 );
+ _setTolerance: function( value ) {
+ var tol = { t: 30, r: 15, b: 30, l: 15 };
- if ( this[ setter ] !== undefined ) {
- this[ setter ]( value );
- }
+ if ( value !== undefined ) {
+ var ar = String( value ).split( "," );
- // TODO REMOVE FOR 1.2.1 by moving them out to a default options object
- exclusions = [
- "initSelector",
- "closeLinkSelector",
- "closeLinkEvents",
- "navigateEvents",
- "closeEvents",
- "history",
- "container"
- ];
+ $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } );
- $.mobile.widget.prototype._setOption.apply( this, arguments );
- if ( $.inArray( key, exclusions ) === -1 ) {
- // Record the option change in the options and in the DOM data-* attributes
- this.element.attr( "data-" + ( $.mobile.ns || "" ) + ( key.replace( /([A-Z])/, "-$1" ).toLowerCase() ), value );
- }
- },
+ 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;
- // Try and center the overlay over the given coordinates
- _placementCoords: function( desired ) {
- // rectangle within which the popup must fit
- var
- winCoords = windowCoords(),
- rc = {
- x: this._tolerance.l,
- y: winCoords.y + this._tolerance.t,
- cx: winCoords.cx - this._tolerance.l - this._tolerance.r,
- cy: winCoords.cy - this._tolerance.t - this._tolerance.b
- },
- menuSize, ret;
+ // 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;
- // Clamp the width of the menu before grabbing its size
- this._ui.container.css( "max-width", rc.cx );
- menuSize = {
- cx: this._ui.container.outerWidth( true ),
- cy: this._ui.container.outerHeight( true )
- };
+ // 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;
- // 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.
- ret = {
- x: fitSegmentInsideSegment( rc.cx, menuSize.cx, rc.x, desired.x ),
- y: fitSegmentInsideSegment( rc.cy, menuSize.cy, rc.y, desired.y )
- };
+ default:
+ break;
+ }
+ }
- // Make sure the top of the menu is visible
- ret.y = Math.max( 0, ret.y );
+ this._tolerance = tol;
+ },
- // If the height of the menu is smaller than the height of the document
- // align the bottom with the bottom of the document
+ _setOption: function( key, value ) {
+ var setter = "_set" + key.charAt( 0 ).toUpperCase() + key.slice( 1 );
- // fix for $.mobile.document.height() bug in core 1.7.2.
- var docEl = document.documentElement, docBody = document.body,
- docHeight = Math.max( docEl.clientHeight, docBody.scrollHeight, docBody.offsetHeight, docEl.scrollHeight, docEl.offsetHeight );
+ if ( this[ setter ] !== undefined ) {
+ this[ setter ]( value );
+ }
- ret.y -= Math.min( ret.y, Math.max( 0, ret.y + menuSize.cy - docHeight ) );
+ this._super( key, value );
+ },
- return { left: ret.x, top: ret.y };
- },
+ // Try and center the overlay over the given coordinates
+ _placementCoords: function( desired ) {
+ // rectangle within which the popup must fit
+ var
+ winCoords = windowCoords(),
+ rc = {
+ x: this._tolerance.l,
+ y: winCoords.y + this._tolerance.t,
+ cx: winCoords.cx - this._tolerance.l - this._tolerance.r,
+ cy: winCoords.cy - this._tolerance.t - this._tolerance.b
+ },
+ menuSize, ret;
- _createPrereqs: function( screenPrereq, containerPrereq, whenDone ) {
- var self = this, prereqs;
-
- // It is important to maintain both the local variable prereqs and self._prereqs. The local variable remains in
- // the closure of the functions which call the callbacks passed in. The comparison between the local variable and
- // self._prereqs 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 prereqs to the widget-level variable self._prereqs ensures that
- // callbacks triggered by a stale .animationComplete will be ignored.
-
- prereqs = {
- screen: $.Deferred(),
- container: $.Deferred()
- };
+ // Clamp the width of the menu before grabbing its size
+ this._ui.container.css( "max-width", rc.cx );
+ menuSize = {
+ cx: this._ui.container.outerWidth( true ),
+ cy: this._ui.container.outerHeight( true )
+ };
- prereqs.screen.then( function() {
- if ( prereqs === self._prereqs ) {
- screenPrereq();
- }
- });
+ // 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.
+ ret = {
+ x: fitSegmentInsideSegment( rc.cx, menuSize.cx, rc.x, desired.x ),
+ y: fitSegmentInsideSegment( rc.cy, menuSize.cy, rc.y, desired.y )
+ };
- prereqs.container.then( function() {
- if ( prereqs === self._prereqs ) {
- containerPrereq();
- }
- });
+ // Make sure the top of the menu is visible
+ ret.y = Math.max( 0, ret.y );
- $.when( prereqs.screen, prereqs.container ).done( function() {
- if ( prereqs === self._prereqs ) {
- self._prereqs = null;
- whenDone();
- }
- });
+ // If the height of the menu is smaller than the height of the document
+ // align the bottom with the bottom of the document
- self._prereqs = prereqs;
- },
+ // fix for $.mobile.document.height() bug in core 1.7.2.
+ var docEl = document.documentElement, docBody = document.body,
+ docHeight = Math.max( docEl.clientHeight, docBody.scrollHeight, docBody.offsetHeight, docEl.scrollHeight, docEl.offsetHeight );
- _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._ui.screen
- .removeClass( args.classToRemove )
- .addClass( args.screenClassToAdd );
+ ret.y -= Math.min( ret.y, Math.max( 0, ret.y + menuSize.cy - docHeight ) );
- args.prereqs.screen.resolve();
+ return { left: ret.x, top: ret.y };
+ },
- if ( args.transition && args.transition !== "none" ) {
- if ( args.applyTransition ) {
- this._applyTransition( args.transition );
- }
- if ( this._fallbackTransition ) {
- this._ui.container
- .animationComplete( $.proxy( args.prereqs.container, "resolve" ) )
- .addClass( args.containerClassToAdd )
- .removeClass( args.classToRemove );
- return;
- }
- }
- this._ui.container.removeClass( args.classToRemove );
- args.prereqs.container.resolve();
- },
+ _createPrereqs: function( screenPrereq, containerPrereq, whenDone ) {
+ var self = this, prereqs;
+
+ // It is important to maintain both the local variable prereqs and self._prereqs. The local variable remains in
+ // the closure of the functions which call the callbacks passed in. The comparison between the local variable and
+ // self._prereqs 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 prereqs to the widget-level variable self._prereqs ensures that
+ // callbacks triggered by a stale .animationComplete will be ignored.
+
+ prereqs = {
+ screen: $.Deferred(),
+ container: $.Deferred()
+ };
- // 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( o ) {
- var dst = null, offset, winCoords = windowCoords(), x = o.x, y = o.y, pTo = o.positionTo;
-
- // Establish which element will serve as the reference
- if ( pTo && pTo !== "origin" ) {
- if ( pTo === "window" ) {
- x = winCoords.cx / 2 + winCoords.x;
- y = winCoords.cy / 2 + winCoords.y;
- } else {
- try {
- dst = $( pTo );
- } catch( e ) {
- dst = null;
- }
- if ( dst ) {
- dst.filter( ":visible" );
- if ( dst.length === 0 ) {
- dst = null;
- }
- }
- }
+ prereqs.screen.then( function() {
+ if ( prereqs === self._prereqs ) {
+ screenPrereq();
}
+ });
- // 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;
+ prereqs.container.then( function() {
+ if ( prereqs === self._prereqs ) {
+ containerPrereq();
}
+ });
- // Make sure x and y are valid numbers - center over the window
- if ( $.type( x ) !== "number" || isNaN( x ) ) {
- x = winCoords.cx / 2 + winCoords.x;
- }
- if ( $.type( y ) !== "number" || isNaN( y ) ) {
- y = winCoords.cy / 2 + winCoords.y;
+ $.when( prereqs.screen, prereqs.container ).done( function() {
+ if ( prereqs === self._prereqs ) {
+ self._prereqs = null;
+ whenDone();
}
+ });
- return { x: x, y: y };
- },
-
- _reposition: function( o ) {
- // We only care about position-related parameters for repositioning
- o = { x: o.x, y: o.y, positionTo: o.positionTo };
- this._trigger( "beforeposition", o );
- this._ui.container.offset( this._placementCoords( this._desiredCoords( o ) ) );
- },
+ self._prereqs = prereqs;
+ },
- reposition: function( o ) {
- if ( this._isOpen ) {
- this._reposition( o );
+ _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._ui.screen
+ .removeClass( args.classToRemove )
+ .addClass( args.screenClassToAdd );
+
+ args.prereqs.screen.resolve();
+
+ if ( args.transition && args.transition !== "none" ) {
+ if ( args.applyTransition ) {
+ this._applyTransition( args.transition );
+ }
+ if ( this._fallbackTransition ) {
+ this._ui.container
+ .animationComplete( $.proxy( args.prereqs.container, "resolve" ) )
+ .addClass( args.containerClassToAdd )
+ .removeClass( args.classToRemove );
+ return;
}
- },
+ }
+ this._ui.container.removeClass( args.classToRemove );
+ args.prereqs.container.resolve();
+ },
- _openPrereqsComplete: function() {
- this._ui.container.addClass( "ui-popup-active" );
- this._isOpen = true;
- this._resizeScreen();
- this._ui.container.attr( "tabindex", "0" ).focus();
- this._ignoreResizeEvents();
- this._trigger( "afteropen" );
- },
+ // 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( o ) {
+ var dst = null, offset, winCoords = windowCoords(), x = o.x, y = o.y, pTo = o.positionTo;
- _open: function( options ) {
- var o = $.extend( {}, this.options, options ),
- // TODO move blacklist to private method
- androidBlacklist = ( function() {
- var w = window,
- 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;
+ // Establish which element will serve as the reference
+ if ( pTo && pTo !== "origin" ) {
+ if ( pTo === "window" ) {
+ x = winCoords.cx / 2 + winCoords.x;
+ y = winCoords.cy / 2 + winCoords.y;
+ } else {
+ try {
+ dst = $( pTo );
+ } catch( e ) {
+ dst = null;
+ }
+ if ( dst ) {
+ dst.filter( ":visible" );
+ if ( dst.length === 0 ) {
+ dst = null;
}
- 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._createPrereqs(
- $.noop,
- $.noop,
- $.proxy( this, "_openPrereqsComplete" ) );
+ // 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;
+ }
- this._currentTransition = o.transition;
- this._applyTransition( o.transition );
+ // Make sure x and y are valid numbers - center over the window
+ if ( $.type( x ) !== "number" || isNaN( x ) ) {
+ x = winCoords.cx / 2 + winCoords.x;
+ }
+ if ( $.type( y ) !== "number" || isNaN( y ) ) {
+ y = winCoords.cy / 2 + winCoords.y;
+ }
- if ( !this.options.theme ) {
- this._setTheme( this._page.jqmData( "theme" ) || $.mobile.getInheritedTheme( this._page, "c" ) );
- }
+ return { x: x, y: y };
+ },
- this._ui.screen.removeClass( "ui-screen-hidden" );
- this._ui.container.removeClass( "ui-popup-hidden" );
+ _reposition: function( o ) {
+ // We only care about position-related parameters for repositioning
+ o = { x: o.x, y: o.y, positionTo: o.positionTo };
+ this._trigger( "beforeposition", undefined, o );
+ this._ui.container.offset( this._placementCoords( this._desiredCoords( o ) ) );
+ },
- // Give applications a chance to modify the contents of the container before it appears
+ reposition: function( o ) {
+ if ( this._isOpen ) {
this._reposition( o );
+ }
+ },
- 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.element.closest( ".ui-page" ).addClass( "ui-popup-open" );
- }
- this._animate({
- additionalCondition: true,
- transition: o.transition,
- classToRemove: "",
- screenClassToAdd: "in",
- containerClassToAdd: "in",
- applyTransition: false,
- prereqs: this._prereqs
- });
- },
-
- _closePrereqScreen: function() {
- this._ui.screen
- .removeClass( "out" )
- .addClass( "ui-screen-hidden" );
- },
+ _openPrereqsComplete: function() {
+ this._ui.container.addClass( "ui-popup-active" );
+ this._isOpen = true;
+ this._resizeScreen();
+ this._ui.container.attr( "tabindex", "0" ).focus();
+ this._ignoreResizeEvents();
+ this._trigger( "afteropen" );
+ },
- _closePrereqContainer: function() {
- this._ui.container
- .removeClass( "reverse out" )
- .addClass( "ui-popup-hidden" )
- .removeAttr( "style" );
- },
+ _open: function( options ) {
+ var o = $.extend( {}, this.options, options ),
+ // TODO move blacklist to private method
+ androidBlacklist = ( function() {
+ var w = window,
+ 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;
+ }());
- _closePrereqsDone: function() {
- var opts = this.options;
+ // Count down to triggering "popupafteropen" - we have two prerequisites:
+ // 1. The popup window animation completes (container())
+ // 2. The screen opacity animation completes (screen())
+ this._createPrereqs(
+ $.noop,
+ $.noop,
+ $.proxy( this, "_openPrereqsComplete" ) );
- this._ui.container.removeAttr( "tabindex" );
+ this._currentTransition = o.transition;
+ this._applyTransition( o.transition );
- // remove the global mutex for popups
- $.mobile.popup.active = undefined;
+ if ( !this.options.theme ) {
+ this._setTheme( this._page.jqmData( "theme" ) || $.mobile.getInheritedTheme( this._page, "c" ) );
+ }
- // alert users that the popup is closed
- this._trigger( "afterclose" );
- },
+ this._ui.screen.removeClass( "ui-screen-hidden" );
+ this._ui.container.removeClass( "ui-popup-hidden" );
- _close: function( immediate ) {
- this._ui.container.removeClass( "ui-popup-active" );
- this._page.removeClass( "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._createPrereqs(
- $.proxy( this, "_closePrereqScreen" ),
- $.proxy( this, "_closePrereqContainer" ),
- $.proxy( this, "_closePrereqsDone" ) );
-
- this._animate( {
- additionalCondition: this._ui.screen.hasClass( "in" ),
- transition: ( immediate ? "none" : ( this._currentTransition ) ),
- classToRemove: "in",
- screenClassToAdd: "out",
- containerClassToAdd: "reverse out",
- applyTransition: true,
- prereqs: this._prereqs
- });
- },
+ // Give applications a chance to modify the contents of the container before it appears
+ this._reposition( o );
- _unenhance: function() {
- // Put the element back to where the placeholder was and remove the "ui-popup" class
- this._setTheme( "none" );
- 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 )
- .removeClass( "ui-popup ui-overlay-shadow ui-corner-all" );
- this._ui.screen.remove();
- this._ui.container.remove();
- this._ui.placeholder.remove();
- },
+ 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
- _destroy: function() {
- if ( $.mobile.popup.active === this ) {
- this.element.one( "popupafterclose", $.proxy( this, "_unenhance" ) );
- this.close();
- } else {
- this._unenhance();
- }
- },
+ This fix closes the following bugs ( I use "closes" with reluctance, and stress that this issue should be revisited as soon as possible ):
- _closePopup: function( e, data ) {
- var parsedDst, toUrl, o = this.options, immediate = false;
+ https://github.com/jquery/jquery-mobile/issues/4816
+ https://github.com/jquery/jquery-mobile/issues/4844
+ https://github.com/jquery/jquery-mobile/issues/4874
+ */
- // restore location on screen
- window.scrollTo( 0, this._scrollTop );
+ // TODO sort out why this._page isn't working
+ this.element.closest( ".ui-page" ).addClass( "ui-popup-open" );
+ }
+ this._animate({
+ additionalCondition: true,
+ transition: o.transition,
+ classToRemove: "",
+ screenClassToAdd: "in",
+ containerClassToAdd: "in",
+ applyTransition: false,
+ prereqs: this._prereqs
+ });
+ },
- if ( e && e.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;
+ _closePrereqScreen: function() {
+ this._ui.screen
+ .removeClass( "out" )
+ .addClass( "ui-screen-hidden" );
+ },
- if ( this._myUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ) {
- // Going to a different page - close immediately
- immediate = true;
- } else {
- e.preventDefault();
- }
- }
+ _closePrereqContainer: function() {
+ this._ui.container
+ .removeClass( "reverse out" )
+ .addClass( "ui-popup-hidden" )
+ .removeAttr( "style" );
+ },
- // remove nav bindings
- o.container.unbind( o.closeEvents );
- // unbind click handlers added when history is disabled
- this.element.undelegate( o.closeLinkSelector, o.closeLinkEvents );
+ _closePrereqsDone: function() {
+ var container = this._ui.container;
- this._close( immediate );
- },
+ container.removeAttr( "tabindex" );
- // 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.options.container
- .one( this.options.closeEvents, $.proxy( this, "_closePopup" ) );
- },
+ // remove the global mutex for popups
+ $.mobile.popup.active = undefined;
- // 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 self = this, opts = this.options, url, hashkey, activePage, currentIsDialog, hasHash, urlHistory;
+ // Blur elements inside the container, including the container
+ $( ":focus", container[ 0 ] ).add( container[ 0 ] ).blur();
- // make sure open is idempotent
- if( $.mobile.popup.active ) {
- return;
- }
+ // alert users that the popup is closed
+ this._trigger( "afterclose" );
+ },
- // set the global popup mutex
- $.mobile.popup.active = this;
- this._scrollTop = $.mobile.window.scrollTop();
+ _close: function( immediate ) {
+ this._ui.container.removeClass( "ui-popup-active" );
+ this._page.removeClass( "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._createPrereqs(
+ $.proxy( this, "_closePrereqScreen" ),
+ $.proxy( this, "_closePrereqContainer" ),
+ $.proxy( this, "_closePrereqsDone" ) );
+
+ this._animate( {
+ additionalCondition: this._ui.screen.hasClass( "in" ),
+ transition: ( immediate ? "none" : ( this._currentTransition ) ),
+ classToRemove: "in",
+ screenClassToAdd: "out",
+ containerClassToAdd: "reverse out",
+ applyTransition: true,
+ prereqs: this._prereqs
+ });
+ },
- // if history alteration is disabled close on navigate events
- // and leave the url as is
- if( !( opts.history ) ) {
- self._open( options );
- self._bindContainerClose();
+ _unenhance: function() {
+ // Put the element back to where the placeholder was and remove the "ui-popup" class
+ this._setTheme( "none" );
+ 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 )
+ .removeClass( "ui-popup ui-overlay-shadow ui-corner-all" );
+ this._ui.screen.remove();
+ this._ui.container.remove();
+ this._ui.placeholder.remove();
+ },
- // When histoy 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
- self.element
- .delegate( opts.closeLinkSelector, opts.closeLinkEvents, function( e ) {
- self.close();
- e.preventDefault();
- });
+ _destroy: function() {
+ if ( $.mobile.popup.active === this ) {
+ this.element.one( "popupafterclose", $.proxy( this, "_unenhance" ) );
+ this.close();
+ } else {
+ this._unenhance();
+ }
+ },
- return;
- }
+ _closePopup: function( e, data ) {
+ var parsedDst, toUrl, o = this.options, immediate = false;
- // cache some values for min/readability
- urlHistory = $.mobile.urlHistory;
- hashkey = $.mobile.dialogHashKey;
- activePage = $.mobile.activePage;
- currentIsDialog = activePage.is( ".ui-dialog" );
- this._myUrl = url = urlHistory.getActive().url;
- hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog && ( urlHistory.activeIndex > 0 );
+ // restore location on screen
+ window.scrollTo( 0, this._scrollTop );
- if ( hasHash ) {
- self._open( options );
- self._bindContainerClose();
- return;
+ if ( e && e.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 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);
+ if ( this._myUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ) {
+ // Going to a different page - close immediately
+ immediate = true;
} else {
- url = $.mobile.path.parseLocation().hash + hashkey;
+ e.preventDefault();
}
+ }
- // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash
- if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
- url += hashkey;
- }
+ // remove nav bindings
+ o.container.unbind( o.closeEvents );
+ // unbind click handlers added when history is disabled
+ this.element.undelegate( o.closeLinkSelector, o.closeLinkEvents );
- // swallow the the initial navigation event, and bind for the next
- $(window).one( "beforenavigate", function( e ) {
- e.preventDefault();
- self._open( options );
- self._bindContainerClose();
- });
+ this._close( immediate );
+ },
- this.urlAltered = true;
- $.mobile.navigate( url, {role: "dialog"} );
- },
+ // 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.options.container
+ .one( this.options.closeEvents, $.proxy( this, "_closePopup" ) );
+ },
- close: function() {
- // make sure close is idempotent
- if( $.mobile.popup.active !== this ) {
- return;
- }
+ // 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 self = this, opts = this.options, url, hashkey, activePage, currentIsDialog, hasHash, urlHistory;
- this._scrollTop = $.mobile.window.scrollTop();
+ // make sure open is idempotent
+ if( $.mobile.popup.active ) {
+ return;
+ }
- if( this.options.history && this.urlAltered ) {
- $.mobile.back();
- this.urlAltered = false;
- } else {
- // simulate the nav bindings having fired
- this._closePopup();
- }
+ // set the global popup mutex
+ $.mobile.popup.active = this;
+ this._scrollTop = $.mobile.window.scrollTop();
+
+ // if history alteration is disabled close on navigate events
+ // and leave the url as is
+ if( !( opts.history ) ) {
+ self._open( options );
+ self._bindContainerClose();
+
+ // When histoy 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
+ self.element
+ .delegate( opts.closeLinkSelector, opts.closeLinkEvents, function( e ) {
+ self.close();
+ e.preventDefault();
+ });
+
+ return;
}
- });
+ // cache some values for min/readability
+ urlHistory = $.mobile.urlHistory;
+ hashkey = $.mobile.dialogHashKey;
+ activePage = $.mobile.activePage;
+ currentIsDialog = activePage.is( ".ui-dialog" );
+ this._myUrl = url = urlHistory.getActive().url;
+ hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog && ( urlHistory.activeIndex > 0 );
- // TODO this can be moved inside the widget
- $.mobile.popup.handleLink = function( $link ) {
- var closestPage = $link.closest( ":jqmData(role='page')" ),
- scope = ( ( closestPage.length === 0 ) ? $( "body" ) : closestPage ),
- // NOTE make sure to get only the hash, ie7 (wp7) return the absolute href
- // in this case ruining the element selection
- popup = $( $.mobile.path.parseUrl($link.attr( "href" )).hash, scope[0] ),
- offset;
-
- if ( 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" )
- });
+ if ( hasHash ) {
+ self._open( options );
+ self._bindContainerClose();
+ return;
}
- //remove after delay
- setTimeout( function() {
- // Check if we are in a listview
- var $parent = $link.parent().parent();
- if ($parent.hasClass("ui-li")) {
- $link = $parent.parent();
- }
- $link.removeClass( $.mobile.activeBtnClass );
- }, 300 );
- };
+ // 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;
+ }
- // TODO move inside _create
- $.mobile.document.bind( "pagebeforechange", function( e, data ) {
- if ( data.options.role === "popup" ) {
- $.mobile.popup.handleLink( data.options.link );
+ // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash
+ if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
+ url += hashkey;
+ }
+
+ // swallow the the initial navigation event, and bind for the next
+ $(window).one( "beforenavigate", function( e ) {
e.preventDefault();
+ self._open( options );
+ self._bindContainerClose();
+ });
+
+ this.urlAltered = true;
+ $.mobile.navigate( url, {role: "dialog"} );
+ },
+
+ close: function() {
+ // make sure close is idempotent
+ if( $.mobile.popup.active !== this ) {
+ return;
}
- });
- $.mobile.document.bind( "pagecreate create", function( e ) {
- $.mobile.popup.prototype.enhanceWithin( e.target, true );
- });
+ this._scrollTop = $.mobile.window.scrollTop();
+
+ if( this.options.history && this.urlAltered ) {
+ $.mobile.back();
+ this.urlAltered = false;
+ } else {
+ // simulate the nav bindings having fired
+ this._closePopup();
+ }
+ }
+});
+
+
+// TODO this can be moved inside the widget
+$.mobile.popup.handleLink = function( $link ) {
+ var closestPage = $link.closest( ":jqmData(role='page')" ),
+ scope = ( ( closestPage.length === 0 ) ? $( "body" ) : closestPage ),
+ // NOTE make sure to get only the hash, ie7 (wp7) return the absolute href
+ // in this case ruining the element selection
+ popup = $( $.mobile.path.parseUrl($link.attr( "href" )).hash, scope[0] ),
+ offset;
+
+ if ( 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() {
+ // Check if we are in a listview
+ var $parent = $link.parent().parent();
+ if ($parent.hasClass("ui-li")) {
+ $link = $parent.parent();
+ }
+ $link.removeClass( $.mobile.activeBtnClass );
+ }, 300 );
+};
+
+// TODO move inside _create
+$.mobile.document.bind( "pagebeforechange", function( e, data ) {
+ if ( data.options.role === "popup" ) {
+ $.mobile.popup.handleLink( data.options.link );
+ e.preventDefault();
+ }
+});
+
+$.mobile.document.bind( "pagecreate create", function( e ) {
+ $.mobile.popup.prototype.enhanceWithin( e.target, true );
+});
})( jQuery );
@@ -9306,7 +9298,10 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
placeholder: "",
build: function() {
- var self = this;
+ var self = this,
+ escapeId = function( id ) {
+ return id.replace( /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, "\\$1" );
+ };
// Create list from select, update state
self.refresh();
@@ -9336,9 +9331,9 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
self._decideFormat();
if ( self.menuType === "overlay" ) {
- self.button.attr( "href", "#" + self.popupID ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "popup" );
+ self.button.attr( "href", "#" + escapeId( self.popupID ) ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "popup" );
} else {
- self.button.attr( "href", "#" + self.dialogID ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "dialog" );
+ self.button.attr( "href", "#" + escapeId( self.dialogID ) ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "dialog" );
}
self.isOpen = true;
// Do not prevent default, so the navigation may have a chance to actually open the chosen format
@@ -9745,6 +9740,9 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
// Remove the popup
this.listbox.remove();
+ // Remove the dialog
+ this.menuPage.remove();
+
// Chain up
origDestroy.apply( this, arguments );
}
@@ -9874,6 +9872,25 @@ $( document ).bind( "pagecreate create", function( e ) {
$( e.target )
.find( "a" )
.jqmEnhanceable()
+ .filter( ":jqmData(rel='popup')[href][href!='']" )
+ .each( function() {
+ // Accessibility info for popups
+ var e = this,
+ href = $( this ).attr( "href" ),
+ idref = href.substring( 1 );
+
+ e.setAttribute( "aria-haspopup", true );
+ e.setAttribute( "aria-owns", idref );
+ e.setAttribute( "aria-expanded", false );
+ $( document )
+ .on( "popupafteropen", href, function() {
+ e.setAttribute( "aria-expanded", true );
+ })
+ .on( "popupafterclose", href, function() {
+ e.setAttribute( "aria-expanded", false );
+ });
+ })
+ .end()
.not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
.addClass( "ui-link" );
@@ -9938,7 +9955,7 @@ $( document ).bind( "pagecreate create", function( e ) {
$.extend( this, {
_thisPage: null
});
-
+
self._addTransitionClass();
self._bindPageEvents();
self._bindToggleHandlers();
@@ -10479,8 +10496,14 @@ $.widget( "mobile.panel", $.mobile.widget, {
self._page.on( "click.panel" , "a", function( e ) {
if ( this.href.split( "#" )[ 1 ] === self._panelID && self._panelID !== undefined ) {
e.preventDefault();
- var $link = $( this );
+ var $link = $( this ),
+ $parent;
if ( ! $link.hasClass( "ui-link" ) ) {
+ // Check if we are in a listview
+ $parent = $link.parent().parent();
+ if ( $parent.hasClass( "ui-li" ) ) {
+ $link = $parent.parent();
+ }
$link.addClass( $.mobile.activeBtnClass );
self.element.one( "panelopen panelclose", function() {
$link.removeClass( $.mobile.activeBtnClass );
@@ -11097,6 +11120,10 @@ $.mobile.document.delegate( ":jqmData(role='table')", "tablecreate refresh", fun
// define page container
$.mobile.pageContainer = $.mobile.firstPage.parent().addClass( "ui-mobile-viewport" );
+ // initialize navigation events now, after mobileinit has occurred and the page container
+ // has been created but before the rest of the library is alerted to that fact
+ $.mobile.navreadyDeferred.resolve();
+
// alert listeners that the pagecontainer has been determined for binding
// to events triggered on it
$window.trigger( "pagecontainercreate" );
@@ -11149,9 +11176,6 @@ $.mobile.document.delegate( ":jqmData(role='table')", "tablecreate refresh", fun
}
});
- // initialize events now, after mobileinit has occurred
- $.mobile.navreadyDeferred.resolve();
-
// check which scrollTop value should be used by scrolling to 1 immediately at domready
// then check what the scroll top is. Android will report 0... others 1
// note that this initial scroll won't hide the address bar. It's just for the check.
379 test/3rdparty/underscore-1.4.1.js → test/3rdparty/underscore-1.5.2.js
View
@@ -1,6 +1,6 @@
-// Underscore.js 1.4.1
+// Underscore.js 1.5.2
// http://underscorejs.org
-// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
@@ -8,7 +8,7 @@
// Baseline setup
// --------------
- // Establish the root object, `window` in the browser, or `global` on the server.
+ // Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Save the previous value of the `_` variable.
@@ -21,12 +21,12 @@
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
- var push = ArrayProto.push,
- slice = ArrayProto.slice,
- concat = ArrayProto.concat,
- unshift = ArrayProto.unshift,
- toString = ObjProto.toString,
- hasOwnProperty = ObjProto.hasOwnProperty;
+ var
+ push = ArrayProto.push,
+ slice = ArrayProto.slice,
+ concat = ArrayProto.concat,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
@@ -61,11 +61,11 @@
}
exports._ = _;
} else {
- root['_'] = _;
+ root._ = _;
}
// Current version.
- _.VERSION = '1.4.1';
+ _.VERSION = '1.5.2';
// Collection Functions
// --------------------
@@ -74,17 +74,17 @@
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
+ if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
- for (var i = 0, l = obj.length; i < l; i++) {
+ for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
- for (var key in obj) {
- if (_.has(obj, key)) {
- if (iterator.call(context, obj[key], key, obj) === breaker) return;
- }
+ var keys = _.keys(obj);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
};
@@ -93,17 +93,21 @@
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
+ if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
- results[results.length] = iterator.call(context, value, index, list);
+ results.push(iterator.call(context, value, index, list));
});
return results;
};
+ var reduceError = 'Reduce of empty array with no initial value';
+
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
+ if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
@@ -116,7 +120,7 @@
memo = iterator.call(context, memo, value, index, list);
}
});
- if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+ if (!initial) throw new TypeError(reduceError);
return memo;
};
@@ -124,9 +128,10 @@
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
+ if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
- return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
@@ -142,7 +147,7 @@
memo = iterator.call(context, memo, obj[index], index, list);
}
});
- if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+ if (!initial) throw new TypeError(reduceError);
return memo;
};
@@ -163,20 +168,19 @@
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
+ if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
- if (iterator.call(context, value, index, list)) results[results.length] = value;
+ if (iterator.call(context, value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
- var results = [];
- each(obj, function(value, index, list) {
- if (!iterator.call(context, value, index, list)) results[results.length] = value;
- });
- return results;
+ return _.filter(obj, function(value, index, list) {
+ return !iterator.call(context, value, index, list);
+ }, context);
};
// Determine whether all of the elements match a truth test.
@@ -185,6 +189,7 @@
_.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;
+ if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
@@ -198,6 +203,7 @@
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
+ if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
@@ -208,19 +214,19 @@
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
- var found = false;
+ if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
- found = any(obj, function(value) {
+ return any(obj, function(value) {
return value === target;
});
- return found;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
+ var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
- return (_.isFunction(method) ? method : value[method]).apply(value, args);
+ return (isFunc ? method : value[method]).apply(value, args);
});
};
@@ -230,10 +236,10 @@
};
// Convenience version of a common use case of `filter`: selecting only objects
- // with specific `key:value` pairs.
- _.where = function(obj, attrs) {
- if (_.isEmpty(attrs)) return [];
- return _.filter(obj, function(value) {
+ // containing specific `key:value` pairs.
+ _.where = function(obj, attrs, first) {
+ if (_.isEmpty(attrs)) return first ? void 0 : [];
+ return _[first ? 'find' : 'filter'](obj, function(value) {
for (var key in attrs) {
if (attrs[key] !== value[key]) return false;
}
@@ -241,18 +247,24 @@
});
};
+ // Convenience version of a common use case of `find`: getting the first object
+ // containing specific `key:value` pairs.
+ _.findWhere = function(obj, attrs) {
+ return _.where(obj, attrs, true);
+ };
+
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
- // See: https://bugs.webkit.org/show_bug.cgi?id=80797
+ // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;
- var result = {computed : -Infinity};
+ var result = {computed : -Infinity, value: -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
- computed >= result.computed && (result = {value : value, computed : computed});
+ computed > result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
@@ -263,7 +275,7 @@
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;
- var result = {computed : Infinity};
+ var result = {computed : Infinity, value: Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
@@ -271,7 +283,8 @@
return result.value;
};
- // Shuffle an array.
+ // Shuffle an array, using the modern version of the
+ // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) {
var rand;
var index = 0;
@@ -284,6 +297,16 @@
return shuffled;
};
+ // Sample **n** random values from an array.
+ // If **n** is not specified, returns a single random element from the array.
+ // The internal `guard` argument allows it to work with `map`.
+ _.sample = function(obj, n, guard) {
+ if (arguments.length < 2 || guard) {
+ return obj[_.random(obj.length - 1)];
+ }
+ return _.shuffle(obj).slice(0, Math.max(0, n));
+ };
+
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
@@ -294,9 +317,9 @@
var iterator = lookupIterator(value);
return _.pluck(_.map(obj, function(value, index, list) {
return {
- value : value,
- index : index,
- criteria : iterator.call(context, value, index, list)
+ value: value,
+ index: index,
+ criteria: iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
@@ -305,38 +328,41 @@
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
- return left.index < right.index ? -1 : 1;
+ return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
- var group = function(obj, value, context, behavior) {
- var result = {};
- var iterator = lookupIterator(value);
- each(obj, function(value, index) {
- var key = iterator.call(context, value, index, obj);
- behavior(result, key, value);
- });
- return result;
+ var group = function(behavior) {
+ return function(obj, value, context) {
+ var result = {};
+ var iterator = value == null ? _.identity : lookupIterator(value);
+ each(obj, function(value, index) {
+ var key = iterator.call(context, value, index, obj);
+ behavior(result, key, value);
+ });
+ return result;
+ };
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
- _.groupBy = function(obj, value, context) {
- return group(obj, value, context, function(result, key, value) {
- (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
- });
- };
+ _.groupBy = group(function(result, key, value) {
+ (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
+ });
+
+ // Indexes the object's values by a criterion, similar to `groupBy`, but for
+ // when you know that your index values will be unique.
+ _.indexBy = group(function(result, key, value) {
+ result[key] = value;
+ });
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
- _.countBy = function(obj, value, context) {
- return group(obj, value, context, function(result, key, value) {
- if (!_.has(result, key)) result[key] = 0;
- result[key]++;
- });
- };
+ _.countBy = group(function(result, key) {
+ _.has(result, key) ? result[key]++ : result[key] = 1;
+ });
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
@@ -351,15 +377,17 @@
return low;
};
- // Safely convert anything iterable into a real, live array.
+ // Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
- if (obj.length === +obj.length) return slice.call(obj);
+ if (_.isArray(obj)) return slice.call(obj);
+ if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
+ if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
@@ -370,7 +398,8 @@
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
- return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+ if (array == null) return void 0;
+ return (n == null) || guard ? array[0] : slice.call(array, 0, n);
};
// Returns everything but the last entry of the array. Especially useful on
@@ -384,10 +413,11 @@
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
- if ((n != null) && !guard) {
- return slice.call(array, Math.max(array.length - n, 0));
- } else {
+ if (array == null) return void 0;
+ if ((n == null) || guard) {
return array[array.length - 1];
+ } else {
+ return slice.call(array, Math.max(array.length - n, 0));
}
};
@@ -401,13 +431,16 @@
// Trim out all falsy values from an array.
_.compact = function(array) {
- return _.filter(array, function(value){ return !!value; });
+ return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
+ if (shallow && _.every(input, _.isArray)) {
+ return concat.apply(output, input);
+ }
each(input, function(value) {
- if (_.isArray(value)) {
+ if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
@@ -416,7 +449,7 @@
return output;
};
- // Return a completely flattened version of an array.
+ // Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
};
@@ -430,6 +463,11 @@
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
+ if (_.isFunction(isSorted)) {
+ context = iterator;
+ iterator = isSorted;
+ isSorted = false;
+ }
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
@@ -445,7 +483,7 @@
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
- return _.uniq(concat.apply(ArrayProto, arguments));
+ return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
@@ -469,11 +507,10 @@
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
- var args = slice.call(arguments);
- var length = _.max(_.pluck(args, 'length'));
+ var length = _.max(_.pluck(arguments, "length").concat(0));
var results = new Array(length);
for (var i = 0; i < length; i++) {
- results[i] = _.pluck(args, "" + i);
+ results[i] = _.pluck(arguments, '' + i);
}
return results;
};
@@ -482,8 +519,9 @@
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
+ if (list == null) return {};
var result = {};
- for (var i = 0, l = list.length; i < l; i++) {
+ for (var i = 0, length = list.length; i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
@@ -500,22 +538,24 @@
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
- var i = 0, l = array.length;
+ if (array == null) return -1;
+ var i = 0, length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
- i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
+ i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
- for (; i < l; i++) if (array[i] === item) return i;
+ for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
+ if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
@@ -535,11 +575,11 @@
}
step = arguments[2] || 1;
- var len = Math.max(Math.ceil((stop - start) / step), 0);
+ var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
- var range = new Array(len);
+ var range = new Array(length);
- while(idx < len) {
+ while(idx < length) {
range[idx++] = start;
start += step;
}
@@ -554,29 +594,38 @@
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
- // optionally). Binding with arguments is also known as `curry`.
- // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
- // We check for `func.bind` first, to fail fast when `func` is undefined.
- _.bind = function bind(func, context) {
- var bound, args;
- if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+ // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
+ // available.
+ _.bind = function(func, context) {
+ var args, bound;
+ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
+ ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
+ // Partially apply a function by creating a version that has had some of its
+ // arguments pre-filled, without changing its dynamic `this` context.
+ _.partial = function(func) {
+ var args = slice.call(arguments, 1);
+ return function() {
+ return func.apply(this, args.concat(slice.call(arguments)));
+ };
+ };
+
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
- if (funcs.length == 0) funcs = _.functions(obj);
+ if (funcs.length === 0) throw new Error("bindAll must be passed function names");
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
@@ -605,27 +654,34 @@
};
// Returns a function, that, when invoked, will only be triggered at most once
- // during a given window of time.
- _.throttle = function(func, wait) {
- var context, args, timeout, throttling, more, result;
- var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
+ // during a given window of time. Normally, the throttled function will run
+ // as much as it can, without ever going more than once per `wait` duration;
+ // but if you'd like to disable the execution on the leading edge, pass
+ // `{leading: false}`. To disable execution on the trailing edge, ditto.
+ _.throttle = function(func, wait, options) {
+ var context, args, result;