function-vs-object

JavaScript performance comparison

Test case created by Gabriel Schulhof

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    window.theFn = function( a, b, c ) {
      var x = a, y = b, z = c;
    };
    window.theInc = 0;
    window.theObjStr = "window.theFn( \"a\", {}, {\n" +
    "           options: {\n" +
    "                   theme: null,\n" +
    "                   overlayTheme: null,\n" +
    "                   shadow: true,\n" +
    "                   corners: true,\n" +
    "                   transition: \"none\",\n" +
    "                   positionTo: \"origin\",\n" +
    "                   tolerance: null,\n" +
    "                   closeLinkSelector: \"a:jqmData(rel='back')\",\n" +
    "                   closeLinkEvents: \"click.popup\",\n" +
    "                   navigateEvents: \"navigate.popup\",\n" +
    "                   closeEvents: \"navigate.popup pagebeforechange.popup\",\n" +
    "                   dismissible: true\n" +
    "           },\n" +
    "\n" +
    "           _eatEventAndClose: function( e ) {\n" +
    "                   e.preventDefault();\n" +
    "                   e.stopImmediatePropagation();\n" +
    "                   if ( this.options.dismissible ) {\n" +
    "                           this.close();\n" +
    "                   }\n" +
    "                   return false;\n" +
    "           },\n" +
    "\n" +
    "           // Make sure the screen size is increased beyond the page height if the popup's causes the document to increase in height\n" +
    "           _resizeScreen: function() {\n" +
    "                   var popupHeight = this._ui.container.outerHeight( true );\n" +
    "\n" +
    "                   this._ui.screen.removeAttr( \"style\" );\n" +
    "                   if ( popupHeight > this._ui.screen.height() ) {\n" +
    "                           this._ui.screen.height( popupHeight );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _handleWindowKeyUp: function( e ) {\n" +
    "                   if ( this._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE ) {\n" +
    "                           return this._eatEventAndClose( e );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _expectResizeEvent: function() {\n" +
    "                   var winCoords = windowCoords();\n" +
    "\n" +
    "                   if ( this._resizeData ) {\n" +
    "                           if ( winCoords.x === this._resizeData.winCoords.x &&\n" +
    "                                   winCoords.y === this._resizeData.winCoords.y &&\n" +
    "                                   winCoords.cx === this._resizeData.winCoords.cx &&\n" +
    "                                   winCoords.cy === this._resizeData.winCoords.cy ) {\n" +
    "                                   // timeout not refreshed\n" +
    "                                   return false;\n" +
    "                           } else {\n" +
    "                                   // clear existing timeout - it will be refreshed below\n" +
    "                                   clearTimeout( this._resizeData.timeoutId );\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   this._resizeData = {\n" +
    "                           timeoutId: setTimeout( $.proxy( this, \"_resizeTimeout\" ), 200 ),\n" +
    "                           winCoords: winCoords\n" +
    "                   };\n" +
    "\n" +
    "                   return true;\n" +
    "           },\n" +
    "\n" +
    "           _resizeTimeout: function() {\n" +
    "                   if ( this._isOpen ) {\n" +
    "                           if ( !this._expectResizeEvent() ) {\n" +
    "                                   if ( this._ui.container.hasClass( \"ui-popup-hidden\" ) ) {\n" +
    "                                           // effectively rapid-open the popup while leaving the screen intact\n" +
    "                                           this._ui.container.removeClass( \"ui-popup-hidden\" );\n" +
    "                                           this.reposition( { positionTo: \"window\" } );\n" +
    "                                           this._ignoreResizeEvents();\n" +
    "                                   }\n" +
    "\n" +
    "                                   this._resizeScreen();\n" +
    "                                   this._resizeData = null;\n" +
    "                                   this._orientationchangeInProgress = false;\n" +
    "                           }\n" +
    "                   } else {\n" +
    "                           this._resizeData = null;\n" +
    "                           this._orientationchangeInProgress = false;\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _ignoreResizeEvents: function() {\n" +
    "                   var self = this;\n" +
    "\n" +
    "                   if ( this._ignoreResizeTo ) {\n" +
    "                           clearTimeout( this._ignoreResizeTo );\n" +
    "                   }\n" +
    "                   this._ignoreResizeTo = setTimeout( function() { self._ignoreResizeTo = 0; }, 1000 );\n" +
    "           },\n" +
    "\n" +
    "           _handleWindowResize: function( e ) {\n" +
    "                   if ( this._isOpen && this._ignoreResizeTo === 0 ) {\n" +
    "                           if ( ( this._expectResizeEvent() || this._orientationchangeInProgress ) &&\n" +
    "                                   !this._ui.container.hasClass( \"ui-popup-hidden\" ) ) {\n" +
    "                                   // effectively rapid-close the popup while leaving the screen intact\n" +
    "                                   this._ui.container\n" +
    "                                           .addClass( \"ui-popup-hidden\" )\n" +
    "                                           .removeAttr( \"style\" );\n" +
    "                           }\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _handleWindowOrientationchange: function( e ) {\n" +
    "                   if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) {\n" +
    "                           this._expectResizeEvent();\n" +
    "                           this._orientationchangeInProgress = true;\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           // When the popup is open, attempting to focus on an element that is not a\n" +
    "           // child of the popup will redirect focus to the popup\n" +
    "           _handleDocumentFocusIn: function( e ) {\n" +
    "                   var tgt = e.target, $tgt, ui = this._ui;\n" +
    "\n" +
    "                   if ( !this._isOpen ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   if ( tgt !== ui.container[ 0 ] ) {\n" +
    "                           $tgt = $( e.target );\n" +
    "                           if ( 0 === $tgt.parents().filter( ui.container[ 0 ] ).length ) {\n" +
    "                                   $( document.activeElement ).one( \"focus\", function( e ) {\n" +
    "                                           $tgt.blur();\n" +
    "                                   });\n" +
    "                                   ui.focusElement.focus();\n" +
    "                                   e.preventDefault();\n" +
    "                                   e.stopImmediatePropagation();\n" +
    "                                   return false;\n" +
    "                           } else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) {\n" +
    "                                   ui.focusElement = $tgt;\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   this._ignoreResizeEvents();\n" +
    "           },\n" +
    "\n" +
    "           _create: function() {\n" +
    "                   var ui = {\n" +
    "                                   screen: $( \"<div class='ui-screen-hidden ui-popup-screen'></div>\" ),\n" +
    "                                   placeholder: $( \"<div style='display: none;'><!-- placeholder --></div>\" ),\n" +
    "                                   container: $( \"<div class='ui-popup-container ui-popup-hidden'></div>\" )\n" +
    "                           },\n" +
    "                           thisPage = this.element.closest( \".ui-page\" ),\n" +
    "                           myId = this.element.attr( \"id\" ),\n" +
    "                           o = this.options,\n" +
    "                           key, value;\n" +
    "\n" +
    "                   // We need to adjust the history option to be false if there's no AJAX nav.\n" +
    "                   // We can't do it in the option declarations because those are run before\n" +
    "                   // it is determined whether there shall be AJAX nav.\n" +
    "                   o.history = o.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;\n" +
    "\n" +
    "                   if ( thisPage.length === 0 ) {\n" +
    "                           thisPage = $( \"body\" );\n" +
    "                   }\n" +
    "\n" +
    "                   // define the container for navigation event bindings\n" +
    "                   // TODO this would be nice at the the mobile widget level\n" +
    "                   o.container = o.container || $.mobile.pageContainer || thisPage;\n" +
    "\n" +
    "                   // Apply the proto\n" +
    "                   thisPage.append( ui.screen );\n" +
    "                   ui.container.insertAfter( ui.screen );\n" +
    "                   // Leave a placeholder where the element used to be\n" +
    "                   ui.placeholder.insertAfter( this.element );\n" +
    "                   if ( myId ) {\n" +
    "                           ui.screen.attr( \"id\", myId + \"-screen\" );\n" +
    "                           ui.container.attr( \"id\", myId + \"-popup\" );\n" +
    "                           ui.placeholder.html( \"<!-- placeholder for \" + myId + \" -->\" );\n" +
    "                   }\n" +
    "                   ui.container.append( this.element );\n" +
    "                   ui.focusElement = ui.container;\n" +
    "\n" +
    "                   // Add class to popup element\n" +
    "                   this.element.addClass( \"ui-popup\" );\n" +
    "\n" +
    "                   // Define instance variables\n" +
    "                   $.extend( this, {\n" +
    "                           _scrollTop: 0,\n" +
    "                           _page: thisPage,\n" +
    "                           _ui: ui,\n" +
    "                           _fallbackTransition: \"\",\n" +
    "                           _currentTransition: false,\n" +
    "                           _prereqs: null,\n" +
    "                           _isOpen: false,\n" +
    "                           _tolerance: null,\n" +
    "                           _resizeData: null,\n" +
    "                           _ignoreResizeTo: 0,\n" +
    "                           _orientationchangeInProgress: false\n" +
    "                   });\n" +
    "\n" +
    "                   // This duplicates the code from the various option setters below for\n" +
    "                   // better performance. It must be kept in sync with those setters.\n" +
    "                   this._applyTheme( this.element, o.theme, \"body\" );\n" +
    "                   this._applyTheme( this._ui.screen, o.overlayTheme, \"overlay\" );\n" +
    "                   this._applyTransition( o.transition );\n" +
    "                   this.element\n" +
    "                           .toggleClass( \"ui-overlay-shadow\", o.shadow )\n" +
    "                           .toggleClass( \"ui-corner-all\", o.corners );\n" +
    "                   this._setTolerance( o.tolerance );\n" +
    "\n" +
    "                   ui.screen.bind( \"vclick\", $.proxy( this, \"_eatEventAndClose\" ) );\n" +
    "\n" +
    "                   this._on( $.mobile.window, {\n" +
    "                           orientationchange: $.proxy( this, \"_handleWindowOrientationchange\" ),\n" +
    "                           resize: $.proxy( this, \"_handleWindowResize\" ),\n" +
    "                           keyup: $.proxy( this, \"_handleWindowKeyUp\" )\n" +
    "                   });\n" +
    "                   this._on( $.mobile.document, {\n" +
    "                           focusin: $.proxy( this, \"_handleDocumentFocusIn\" )\n" +
    "                   });\n" +
    "           },\n" +
    "\n" +
    "           _applyTheme: function( dst, theme, prefix ) {\n" +
    "                   var classes = ( dst.attr( \"class\" ) || \"\").split( \" \" ),\n" +
    "                           alreadyAdded = true,\n" +
    "                           currentTheme = null,\n" +
    "                           matches,\n" +
    "                           themeStr = String( theme );\n" +
    "\n" +
    "                   while ( classes.length > 0 ) {\n" +
    "                           currentTheme = classes.pop();\n" +
    "                           matches = ( new RegExp( \"^ui-\" + prefix + \"-([a-z])$\" ) ).exec( currentTheme );\n" +
    "                           if ( matches && matches.length > 1 ) {\n" +
    "                                   currentTheme = matches[ 1 ];\n" +
    "                                   break;\n" +
    "                           } else {\n" +
    "                                   currentTheme = null;\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   if ( theme !== currentTheme ) {\n" +
    "                           dst.removeClass( \"ui-\" + prefix + \"-\" + currentTheme );\n" +
    "                           if ( ! ( theme === null || theme === \"none\" ) ) {\n" +
    "                                   dst.addClass( \"ui-\" + prefix + \"-\" + themeStr );\n" +
    "                           }\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setTheme: function( value ) {\n" +
    "                   this._applyTheme( this.element, value, \"body\" );\n" +
    "           },\n" +
    "\n" +
    "           _setOverlayTheme: function( value ) {\n" +
    "                   this._applyTheme( this._ui.screen, value, \"overlay\" );\n" +
    "\n" +
    "                   if ( this._isOpen ) {\n" +
    "                           this._ui.screen.addClass( \"in\" );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setShadow: function( value ) {\n" +
    "                   this.element.toggleClass( \"ui-overlay-shadow\", value );\n" +
    "           },\n" +
    "\n" +
    "           _setCorners: function( value ) {\n" +
    "                   this.element.toggleClass( \"ui-corner-all\", value );\n" +
    "           },\n" +
    "\n" +
    "           _applyTransition: function( value ) {\n" +
    "                   this._ui.container.removeClass( this._fallbackTransition );\n" +
    "                   if ( value && value !== \"none\" ) {\n" +
    "                           this._fallbackTransition = $.mobile._maybeDegradeTransition( value );\n" +
    "                           if ( this._fallbackTransition === \"none\" ) {\n" +
    "                                   this._fallbackTransition = \"\";\n" +
    "                           }\n" +
    "                           this._ui.container.addClass( this._fallbackTransition );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setTransition: function( value ) {\n" +
    "                   if ( !this._currentTransition ) {\n" +
    "                           this._applyTransition( value );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setTolerance: function( value ) {\n" +
    "                   var tol = { t: 30, r: 15, b: 30, l: 15 };\n" +
    "\n" +
    "                   if ( value !== undefined ) {\n" +
    "                           var ar = String( value ).split( \",\" );\n" +
    "\n" +
    "                           $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } );\n" +
    "\n" +
    "                           switch( ar.length ) {\n" +
    "                                   // All values are to be the same\n" +
    "                                   case 1:\n" +
    "                                           if ( !isNaN( ar[ 0 ] ) ) {\n" +
    "                                                   tol.t = tol.r = tol.b = tol.l = ar[ 0 ];\n" +
    "                                           }\n" +
    "                                           break;\n" +
    "\n" +
    "                                   // The first value denotes top/bottom tolerance, and the second value denotes left/right tolerance\n" +
    "                                   case 2:\n" +
    "                                           if ( !isNaN( ar[ 0 ] ) ) {\n" +
    "                                                   tol.t = tol.b = ar[ 0 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 1 ] ) ) {\n" +
    "                                                   tol.l = tol.r = ar[ 1 ];\n" +
    "                                           }\n" +
    "                                           break;\n" +
    "\n" +
    "                                   // The array contains values in the order top, right, bottom, left\n" +
    "                                   case 4:\n" +
    "                                           if ( !isNaN( ar[ 0 ] ) ) {\n" +
    "                                                   tol.t = ar[ 0 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 1 ] ) ) {\n" +
    "                                                   tol.r = ar[ 1 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 2 ] ) ) {\n" +
    "                                                   tol.b = ar[ 2 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 3 ] ) ) {\n" +
    "                                                   tol.l = ar[ 3 ];\n" +
    "                                           }\n" +
    "                                           break;\n" +
    "\n" +
    "                                   default:\n" +
    "                                           break;\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   this._tolerance = tol;\n" +
    "           },\n" +
    "\n" +
    "           _setOption: function( key, value ) {\n" +
    "                   var setter = \"_set\" + key.charAt( 0 ).toUpperCase() + key.slice( 1 );\n" +
    "\n" +
    "                   if ( this[ setter ] !== undefined ) {\n" +
    "                           this[ setter ]( value );\n" +
    "                   }\n" +
    "\n" +
    "                   this._super( key, value );\n" +
    "           },\n" +
    "\n" +
    "           _clampPopupWidth: function( infoOnly ) {\n" +
    "                   var menuSize,\n" +
    "                           winCoords = windowCoords(),\n" +
    "                           // rectangle within which the popup must fit\n" +
    "                           rc = {\n" +
    "                                   x: this._tolerance.l,\n" +
    "                                   y: winCoords.y + this._tolerance.t,\n" +
    "                                   cx: winCoords.cx - this._tolerance.l - this._tolerance.r,\n" +
    "                                   cy: winCoords.cy - this._tolerance.t - this._tolerance.b\n" +
    "                           },\n" +
    "                           ret;\n" +
    "\n" +
    "                   if ( !infoOnly ) {\n" +
    "                           // Clamp the width of the menu before grabbing its size\n" +
    "                           this._ui.container.css( \"max-width\", rc.cx );\n" +
    "                   }\n" +
    "\n" +
    "                   menuSize = {\n" +
    "                           cx: this._ui.container.outerWidth( true ),\n" +
    "                           cy: this._ui.container.outerHeight( true )\n" +
    "                   };\n" +
    "\n" +
    "                   return { rc: rc, menuSize: menuSize };\n" +
    "           },\n" +
    "\n" +
    "           _calculateFinalLocation: function( desired, clampInfo ) {\n" +
    "                   var ret,\n" +
    "                           rc = clampInfo.rc,\n" +
    "                           menuSize = clampInfo.menuSize;\n" +
    "\n" +
    "                   // Center the menu over the desired coordinates, while not going outside\n" +
    "                   // the window tolerances. This will center wrt. the window if the popup is too large.\n" +
    "                   ret = {\n" +
    "                           x: fitSegmentInsideSegment( rc.cx, menuSize.cx, rc.x, desired.x ),\n" +
    "                           y: fitSegmentInsideSegment( rc.cy, menuSize.cy, rc.y, desired.y )\n" +
    "                   };\n" +
    "\n" +
    "                   // Make sure the top of the menu is visible\n" +
    "                   ret.y = Math.max( 0, ret.y );\n" +
    "\n" +
    "                   // If the height of the menu is smaller than the height of the document\n" +
    "                   // align the bottom with the bottom of the document\n" +
    "\n" +
    "                   // fix for $.mobile.document.height() bug in core 1.7.2.\n" +
    "                   var docEl = document.documentElement, docBody = document.body,\n" +
    "                           docHeight = Math.max( docEl.clientHeight, docBody.scrollHeight, docBody.offsetHeight, docEl.scrollHeight, docEl.offsetHeight );\n" +
    "\n" +
    "                   ret.y -= Math.min( ret.y, Math.max( 0, ret.y + menuSize.cy - docHeight ) );\n" +
    "\n" +
    "                   return { left: ret.x, top: ret.y };\n" +
    "           },\n" +
    "\n" +
    "           // Try and center the overlay over the given coordinates\n" +
    "           _placementCoords: function( desired ) {\n" +
    "                   return this._calculateFinalLocation( desired, this._clampPopupWidth() );\n" +
    "           },\n" +
    "\n" +
    "           _createPrereqs: function( screenPrereq, containerPrereq, whenDone ) {\n" +
    "                   var self = this, prereqs;\n" +
    "\n" +
    "                   // It is important to maintain both the local variable prereqs and self._prereqs. The local variable remains in\n" +
    "                   // the closure of the functions which call the callbacks passed in. The comparison between the local variable and\n" +
    "                   // self._prereqs is necessary, because once a function has been passed to .animationComplete() it will be called\n" +
    "                   // next time an animation completes, even if that's not the animation whose end the function was supposed to catch\n" +
    "                   // (for example, if an abort happens during the opening animation, the .animationComplete handler is not called for\n" +
    "                   // that animation anymore, but the handler remains attached, so it is called the next time the popup is opened\n" +
    "                   // - making it stale. Comparing the local variable prereqs to the widget-level variable self._prereqs ensures that\n" +
    "                   // callbacks triggered by a stale .animationComplete will be ignored.\n" +
    "\n" +
    "                   prereqs = {\n" +
    "                           screen: $.Deferred(),\n" +
    "                           container: $.Deferred()\n" +
    "                   };\n" +
    "\n" +
    "                   prereqs.screen.then( function() {\n" +
    "                           if ( prereqs === self._prereqs ) {\n" +
    "                                   screenPrereq();\n" +
    "                           }\n" +
    "                   });\n" +
    "\n" +
    "                   prereqs.container.then( function() {\n" +
    "                           if ( prereqs === self._prereqs ) {\n" +
    "                                   containerPrereq();\n" +
    "                           }\n" +
    "                   });\n" +
    "\n" +
    "                   $.when( prereqs.screen, prereqs.container ).done( function() {\n" +
    "                           if ( prereqs === self._prereqs ) {\n" +
    "                                   self._prereqs = null;\n" +
    "                                   whenDone();\n" +
    "                           }\n" +
    "                   });\n" +
    "\n" +
    "                   self._prereqs = prereqs;\n" +
    "           },\n" +
    "\n" +
    "           _animate: function( args ) {\n" +
    "                   // NOTE before removing the default animation of the screen\n" +
    "                   //      this had an animate callback that would resolve the deferred\n" +
    "                   //      now the deferred is resolved immediately\n" +
    "                   // TODO remove the dependency on the screen deferred\n" +
    "                   this._ui.screen\n" +
    "                           .removeClass( args.classToRemove )\n" +
    "                           .addClass( args.screenClassToAdd );\n" +
    "\n" +
    "                   args.prereqs.screen.resolve();\n" +
    "\n" +
    "                   if ( args.transition && args.transition !== \"none\" ) {\n" +
    "                           if ( args.applyTransition ) {\n" +
    "                                   this._applyTransition( args.transition );\n" +
    "                           }\n" +
    "                           if ( this._fallbackTransition ) {\n" +
    "                                   this._ui.container\n" +
    "                                           .animationComplete( $.proxy( args.prereqs.container, \"resolve\" ) )\n" +
    "                                           .addClass( args.containerClassToAdd )\n" +
    "                                           .removeClass( args.classToRemove );\n" +
    "                                   return;\n" +
    "                           }\n" +
    "                   }\n" +
    "                   this._ui.container.removeClass( args.classToRemove );\n" +
    "                   args.prereqs.container.resolve();\n" +
    "           },\n" +
    "\n" +
    "           // The desired coordinates passed in will be returned untouched if no reference element can be identified via\n" +
    "           // desiredPosition.positionTo. Nevertheless, this function ensures that its return value always contains valid\n" +
    "           // x and y coordinates by specifying the center middle of the window if the coordinates are absent.\n" +
    "           // options: { x: coordinate, y: coordinate, positionTo: string: \"origin\", \"window\", or jQuery selector\n" +
    "           _desiredCoords: function( o ) {\n" +
    "                   var dst = null, offset, winCoords = windowCoords(), x = o.x, y = o.y, pTo = o.positionTo;\n" +
    "\n" +
    "                   // Establish which element will serve as the reference\n" +
    "                   if ( pTo && pTo !== \"origin\" ) {\n" +
    "                           if ( pTo === \"window\" ) {\n" +
    "                                   x = winCoords.cx / 2 + winCoords.x;\n" +
    "                                   y = winCoords.cy / 2 + winCoords.y;\n" +
    "                           } else {\n" +
    "                                   try {\n" +
    "                                           dst = $( pTo );\n" +
    "                                   } catch( e ) {\n" +
    "                                           dst = null;\n" +
    "                                   }\n" +
    "                                   if ( dst ) {\n" +
    "                                           dst.filter( \":visible\" );\n" +
    "                                           if ( dst.length === 0 ) {\n" +
    "                                                   dst = null;\n" +
    "                                           }\n" +
    "                                   }\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   // If an element was found, center over it\n" +
    "                   if ( dst ) {\n" +
    "                           offset = dst.offset();\n" +
    "                           x = offset.left + dst.outerWidth() / 2;\n" +
    "                           y = offset.top + dst.outerHeight() / 2;\n" +
    "                   }\n" +
    "\n" +
    "                   // Make sure x and y are valid numbers - center over the window\n" +
    "                   if ( $.type( x ) !== \"number\" || isNaN( x ) ) {\n" +
    "                           x = winCoords.cx / 2 + winCoords.x;\n" +
    "                   }\n" +
    "                   if ( $.type( y ) !== \"number\" || isNaN( y ) ) {\n" +
    "                           y = winCoords.cy / 2 + winCoords.y;\n" +
    "                   }\n" +
    "\n" +
    "                   return { x: x, y: y };\n" +
    "           },\n" +
    "\n" +
    "           _reposition: function( o ) {\n" +
    "                   // We only care about position-related parameters for repositioning\n" +
    "                   o = { x: o.x, y: o.y, positionTo: o.positionTo };\n" +
    "                   this._trigger( \"beforeposition\", o );\n" +
    "                   this._ui.container.offset( this._placementCoords( this._desiredCoords( o ) ) );\n" +
    "           },\n" +
    "\n" +
    "           reposition: function( o ) {\n" +
    "                   if ( this._isOpen ) {\n" +
    "                           this._reposition( o );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _openPrereqsComplete: function() {\n" +
    "                   this._ui.container.addClass( \"ui-popup-active\" );\n" +
    "                   this._isOpen = true;\n" +
    "                   this._resizeScreen();\n" +
    "                   this._ui.container.attr( \"tabindex\", \"0\" ).focus();\n" +
    "                   this._ignoreResizeEvents();\n" +
    "                   this._trigger( \"afteropen\" );\n" +
    "           },\n" +
    "\n" +
    "           _open: function( options ) {\n" +
    "                   var o = $.extend( {}, this.options, options ),\n" +
    "                           // TODO move blacklist to private method\n" +
    "                           androidBlacklist = ( function() {\n" +
    "                                   var w = window,\n" +
    "                                           ua = navigator.userAgent,\n" +
    "                                           // Rendering engine is Webkit, and capture major version\n" +
    "                                           wkversion = !!wkmatch && wkmatch[ 1 ],\n" +
    "                                           androidmatch = ua.match( /Android (d+(?:.d+))/ ),\n" +
    "                                           andversion = !!androidmatch && androidmatch[ 1 ],\n" +
    "                                           chromematch = ua.indexOf( \"Chrome\" ) > -1;\n" +
    "\n" +
    "                                   // Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and not Chrome.\n" +
    "                                   if( androidmatch !== null && andversion === \"4.0\" && wkversion && wkversion > 534.13 && !chromematch ) {\n" +
    "                                           return true;\n" +
    "                                   }\n" +
    "                                   return false;\n" +
    "                           }());\n" +
    "\n" +
    "                   // Count down to triggering \"popupafteropen\" - we have two prerequisites:\n" +
    "                   // 1. The popup window animation completes (container())\n" +
    "                   // 2. The screen opacity animation completes (screen())\n" +
    "                   this._createPrereqs(\n" +
    "                           $.noop,\n" +
    "                           $.noop,\n" +
    "                           $.proxy( this, \"_openPrereqsComplete\" ) );\n" +
    "\n" +
    "                   this._currentTransition = o.transition;\n" +
    "                   this._applyTransition( o.transition );\n" +
    "\n" +
    "                   if ( !this.options.theme ) {\n" +
    "                           this._setTheme( this._page.jqmData( \"theme\" ) || $.mobile.getInheritedTheme( this._page, \"c\" ) );\n" +
    "                   }\n" +
    "\n" +
    "                   this._ui.screen.removeClass( \"ui-screen-hidden\" );\n" +
    "                   this._ui.container.removeClass( \"ui-popup-hidden\" );\n" +
    "\n" +
    "                   // Give applications a chance to modify the contents of the container before it appears\n" +
    "                   this._reposition( o );\n" +
    "\n" +
    "                   if ( this.options.overlayTheme && androidBlacklist ) {\n" +
    "                           /* TODO:\n" +
    "                           The native browser on Android 4.0.X (\"Ice Cream Sandwich\") suffers from an issue where the popup overlay appears to be z-indexed\n" +
    "                           above the popup itself when certain other styles exist on the same page -- namely, any element set to `position: fixed` and certain\n" +
    "                           types of input. These issues are reminiscent of previously uncovered bugs in older versions of Android's native browser:\n" +
    "                           https://github.com/scottjehl/Device-Bugs/issues/3\n" +
    "\n" +
    "                           This fix closes the following bugs ( I use \"closes\" with reluctance, and stress that this issue should be revisited as soon as possible ):\n" +
    "\n" +
    "                           https://github.com/jquery/jquery-mobile/issues/4816\n" +
    "                           https://github.com/jquery/jquery-mobile/issues/4844\n" +
    "                           https://github.com/jquery/jquery-mobile/issues/4874\n" +
    "                           */\n" +
    "\n" +
    "                           // TODO sort out why this._page isn't working\n" +
    "                           this.element.closest( \".ui-page\" ).addClass( \"ui-popup-open\" );\n" +
    "                   }\n" +
    "                   this._animate({\n" +
    "                           additionalCondition: true,\n" +
    "                           transition: o.transition,\n" +
    "                           classToRemove: \"\",\n" +
    "                           screenClassToAdd: \"in\",\n" +
    "                           containerClassToAdd: \"in\",\n" +
    "                           applyTransition: false,\n" +
    "                           prereqs: this._prereqs\n" +
    "                   });\n" +
    "           },\n" +
    "\n" +
    "           _closePrereqScreen: function() {\n" +
    "                   this._ui.screen\n" +
    "                           .removeClass( \"out\" )\n" +
    "                           .addClass( \"ui-screen-hidden\" );\n" +
    "           },\n" +
    "\n" +
    "           _closePrereqContainer: function() {\n" +
    "                   this._ui.container\n" +
    "                           .removeClass( \"reverse out\" )\n" +
    "                           .addClass( \"ui-popup-hidden\" )\n" +
    "                           .removeAttr( \"style\" );\n" +
    "           },\n" +
    "\n" +
    "           _closePrereqsDone: function() {\n" +
    "                   var opts = this.options;\n" +
    "\n" +
    "                   this._ui.container.removeAttr( \"tabindex\" );\n" +
    "\n" +
    "                   // remove the global mutex for popups\n" +
    "                   $.mobile.popup.active = undefined;\n" +
    "\n" +
    "                   // alert users that the popup is closed\n" +
    "                   this._trigger( \"afterclose\" );\n" +
    "           },\n" +
    "\n" +
    "           _close: function( immediate ) {\n" +
    "                   this._ui.container.removeClass( \"ui-popup-active\" );\n" +
    "                   this._page.removeClass( \"ui-popup-open\" );\n" +
    "\n" +
    "                   this._isOpen = false;\n" +
    "\n" +
    "                   // Count down to triggering \"popupafterclose\" - we have two prerequisites:\n" +
    "                   // 1. The popup window reverse animation completes (container())\n" +
    "                   // 2. The screen opacity animation completes (screen())\n" +
    "                   this._createPrereqs(\n" +
    "                           $.proxy( this, \"_closePrereqScreen\" ),\n" +
    "                           $.proxy( this, \"_closePrereqContainer\" ),\n" +
    "                           $.proxy( this, \"_closePrereqsDone\" ) );\n" +
    "\n" +
    "                   this._animate( {\n" +
    "                           additionalCondition: this._ui.screen.hasClass( \"in\" ),\n" +
    "                           transition: ( immediate ? \"none\" : ( this._currentTransition ) ),\n" +
    "                           classToRemove: \"in\",\n" +
    "                           screenClassToAdd: \"out\",\n" +
    "                           containerClassToAdd: \"reverse out\",\n" +
    "                           applyTransition: true,\n" +
    "                           prereqs: this._prereqs\n" +
    "                   });\n" +
    "           },\n" +
    "\n" +
    "           _unenhance: function() {\n" +
    "                   // Put the element back to where the placeholder was and remove the \"ui-popup\" class\n" +
    "                   this._setTheme( \"none\" );\n" +
    "                   this.element\n" +
    "                           // Cannot directly insertAfter() - we need to detach() first, because\n" +
    "                           // insertAfter() will do nothing if the payload div was not attached\n" +
    "                           // to the DOM at the time the widget was created, and so the payload\n" +
    "                           // will remain inside the container even after we call insertAfter().\n" +
    "                           // If that happens and we remove the container a few lines below, we\n" +
    "                           // will cause an infinite recursion - #5244\n" +
    "                           .detach()\n" +
    "                           .insertAfter( this._ui.placeholder )\n" +
    "                           .removeClass( \"ui-popup ui-overlay-shadow ui-corner-all\" );\n" +
    "                   this._ui.screen.remove();\n" +
    "                   this._ui.container.remove();\n" +
    "                   this._ui.placeholder.remove();\n" +
    "           },\n" +
    "\n" +
    "           _destroy: function() {\n" +
    "                   if ( $.mobile.popup.active === this ) {\n" +
    "                           this.element.one( \"popupafterclose\", $.proxy( this, \"_unenhance\" ) );\n" +
    "                           this.close();\n" +
    "                   } else {\n" +
    "                           this._unenhance();\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _closePopup: function( e, data ) {\n" +
    "                   var parsedDst, toUrl, o = this.options, immediate = false;\n" +
    "\n" +
    "                   if ( e && e.isDefaultPrevented() ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // restore location on screen\n" +
    "                   window.scrollTo( 0, this._scrollTop );\n" +
    "\n" +
    "                   if ( e && e.type === \"pagebeforechange\" && data ) {\n" +
    "                           // Determine whether we need to rapid-close the popup, or whether we can\n" +
    "                           // take the time to run the closing transition\n" +
    "                           if ( typeof data.toPage === \"string\" ) {\n" +
    "                                   parsedDst = data.toPage;\n" +
    "                           } else {\n" +
    "                                   parsedDst = data.toPage.jqmData( \"url\" );\n" +
    "                           }\n" +
    "                           parsedDst = $.mobile.path.parseUrl( parsedDst );\n" +
    "                           toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;\n" +
    "\n" +
    "                           if ( this._myUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ) {\n" +
    "                                   // Going to a different page - close immediately\n" +
    "                                   immediate = true;\n" +
    "                           } else {\n" +
    "                                   e.preventDefault();\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   // remove nav bindings\n" +
    "                   $.mobile.window.off( o.closeEvents );\n" +
    "                   // unbind click handlers added when history is disabled\n" +
    "                   this.element.undelegate( o.closeLinkSelector, o.closeLinkEvents );\n" +
    "\n" +
    "                   this._close( immediate );\n" +
    "           },\n" +
    "\n" +
    "           // any navigation event after a popup is opened should close the popup\n" +
    "           // NOTE the pagebeforechange is bound to catch navigation events that don't\n" +
    "           //      alter the url (eg, dialogs from popups)\n" +
    "           _bindContainerClose: function() {\n" +
    "                   $.mobile.window\n" +
    "                           .on( this.options.closeEvents, $.proxy( this, \"_closePopup\" ) );\n" +
    "           },\n" +
    "\n" +
    "           // TODO no clear deliniation of what should be here and\n" +
    "           // what should be in _open. Seems to be \"visual\" vs \"history\" for now\n" +
    "           open: function( options ) {\n" +
    "                   var self = this, opts = this.options, url, hashkey, activePage, currentIsDialog, hasHash, urlHistory;\n" +
    "\n" +
    "                   // make sure open is idempotent\n" +
    "                   if( $.mobile.popup.active ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // set the global popup mutex\n" +
    "                   $.mobile.popup.active = this;\n" +
    "                   this._scrollTop = $.mobile.window.scrollTop();\n" +
    "\n" +
    "                   // if history alteration is disabled close on navigate events\n" +
    "                   // and leave the url as is\n" +
    "                   if( !( opts.history ) ) {\n" +
    "                           self._open( options );\n" +
    "                           self._bindContainerClose();\n" +
    "\n" +
    "                           // When histoy is disabled we have to grab the data-rel\n" +
    "                           // back link clicks so we can close the popup instead of\n" +
    "                           // relying on history to do it for us\n" +
    "                           self.element\n" +
    "                                   .delegate( opts.closeLinkSelector, opts.closeLinkEvents, function( e ) {\n" +
    "                                           self.close();\n" +
    "                                           e.preventDefault();\n" +
    "                                   });\n" +
    "\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // cache some values for min/readability\n" +
    "                   urlHistory = $.mobile.urlHistory;\n" +
    "                   hashkey = $.mobile.dialogHashKey;\n" +
    "                   activePage = $.mobile.activePage;\n" +
    "                   currentIsDialog = ( activePage ? activePage.hasClass( \"ui-dialog\" ) : false );\n" +
    "                   this._myUrl = url = urlHistory.getActive().url;\n" +
    "                   hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog && ( urlHistory.activeIndex > 0 );\n" +
    "\n" +
    "                   if ( hasHash ) {\n" +
    "                           self._open( options );\n" +
    "                           self._bindContainerClose();\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // if the current url has no dialog hash key proceed as normal\n" +
    "                   // otherwise, if the page is a dialog simply tack on the hash key\n" +
    "                   if ( url.indexOf( hashkey ) === -1 && !currentIsDialog ){\n" +
    "                           url = url + (url.indexOf( \"#\" ) > -1 ? hashkey : \"#\" + hashkey);\n" +
    "                   } else {\n" +
    "                           url = $.mobile.path.parseLocation().hash + hashkey;\n" +
    "                   }\n" +
    "\n" +
    "                   // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash\n" +
    "                   if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {\n" +
    "                           url += hashkey;\n" +
    "                   }\n" +
    "\n" +
    "                   // swallow the the initial navigation event, and bind for the next\n" +
    "                   $.mobile.window.one( \"beforenavigate\", function( e ) {\n" +
    "                           e.preventDefault();\n" +
    "                           self._open( options );\n" +
    "                           self._bindContainerClose();\n" +
    "                   });\n" +
    "\n" +
    "                   this.urlAltered = true;\n" +
    "                   $.mobile.navigate( url, {role: \"dialog\"} );\n" +
    "           },\n" +
    "\n" +
    "           close: function() {\n" +
    "                   // make sure close is idempotent\n" +
    "                   if( $.mobile.popup.active !== this ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   this._scrollTop = $.mobile.window.scrollTop();\n" +
    "\n" +
    "                   if( this.options.history && this.urlAltered ) {\n" +
    "                           $.mobile.back();\n" +
    "                           this.urlAltered = false;\n" +
    "                   } else {\n" +
    "                           // simulate the nav bindings having fired\n" +
    "                           this._closePopup();\n" +
    "                   }\n" +
    "           }\n" +
    "\n" +
    "  });\n" +
    " window.someRandomVar = ";
    window.theFnStr = "window.theFn( \"a\", {}, function() { return {\n" +
    "           options: {\n" +
    "                   theme: null,\n" +
    "                   overlayTheme: null,\n" +
    "                   shadow: true,\n" +
    "                   corners: true,\n" +
    "                   transition: \"none\",\n" +
    "                   positionTo: \"origin\",\n" +
    "                   tolerance: null,\n" +
    "                   closeLinkSelector: \"a:jqmData(rel='back')\",\n" +
    "                   closeLinkEvents: \"click.popup\",\n" +
    "                   navigateEvents: \"navigate.popup\",\n" +
    "                   closeEvents: \"navigate.popup pagebeforechange.popup\",\n" +
    "                   dismissible: true\n" +
    "           },\n" +
    "\n" +
    "           _eatEventAndClose: function( e ) {\n" +
    "                   e.preventDefault();\n" +
    "                   e.stopImmediatePropagation();\n" +
    "                   if ( this.options.dismissible ) {\n" +
    "                           this.close();\n" +
    "                   }\n" +
    "                   return false;\n" +
    "           },\n" +
    "\n" +
    "           // Make sure the screen size is increased beyond the page height if the popup's causes the document to increase in height\n" +
    "           _resizeScreen: function() {\n" +
    "                   var popupHeight = this._ui.container.outerHeight( true );\n" +
    "\n" +
    "                   this._ui.screen.removeAttr( \"style\" );\n" +
    "                   if ( popupHeight > this._ui.screen.height() ) {\n" +
    "                           this._ui.screen.height( popupHeight );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _handleWindowKeyUp: function( e ) {\n" +
    "                   if ( this._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE ) {\n" +
    "                           return this._eatEventAndClose( e );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _expectResizeEvent: function() {\n" +
    "                   var winCoords = windowCoords();\n" +
    "\n" +
    "                   if ( this._resizeData ) {\n" +
    "                           if ( winCoords.x === this._resizeData.winCoords.x &&\n" +
    "                                   winCoords.y === this._resizeData.winCoords.y &&\n" +
    "                                   winCoords.cx === this._resizeData.winCoords.cx &&\n" +
    "                                   winCoords.cy === this._resizeData.winCoords.cy ) {\n" +
    "                                   // timeout not refreshed\n" +
    "                                   return false;\n" +
    "                           } else {\n" +
    "                                   // clear existing timeout - it will be refreshed below\n" +
    "                                   clearTimeout( this._resizeData.timeoutId );\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   this._resizeData = {\n" +
    "                           timeoutId: setTimeout( $.proxy( this, \"_resizeTimeout\" ), 200 ),\n" +
    "                           winCoords: winCoords\n" +
    "                   };\n" +
    "\n" +
    "                   return true;\n" +
    "           },\n" +
    "\n" +
    "           _resizeTimeout: function() {\n" +
    "                   if ( this._isOpen ) {\n" +
    "                           if ( !this._expectResizeEvent() ) {\n" +
    "                                   if ( this._ui.container.hasClass( \"ui-popup-hidden\" ) ) {\n" +
    "                                           // effectively rapid-open the popup while leaving the screen intact\n" +
    "                                           this._ui.container.removeClass( \"ui-popup-hidden\" );\n" +
    "                                           this.reposition( { positionTo: \"window\" } );\n" +
    "                                           this._ignoreResizeEvents();\n" +
    "                                   }\n" +
    "\n" +
    "                                   this._resizeScreen();\n" +
    "                                   this._resizeData = null;\n" +
    "                                   this._orientationchangeInProgress = false;\n" +
    "                           }\n" +
    "                   } else {\n" +
    "                           this._resizeData = null;\n" +
    "                           this._orientationchangeInProgress = false;\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _ignoreResizeEvents: function() {\n" +
    "                   var self = this;\n" +
    "\n" +
    "                   if ( this._ignoreResizeTo ) {\n" +
    "                           clearTimeout( this._ignoreResizeTo );\n" +
    "                   }\n" +
    "                   this._ignoreResizeTo = setTimeout( function() { self._ignoreResizeTo = 0; }, 1000 );\n" +
    "           },\n" +
    "\n" +
    "           _handleWindowResize: function( e ) {\n" +
    "                   if ( this._isOpen && this._ignoreResizeTo === 0 ) {\n" +
    "                           if ( ( this._expectResizeEvent() || this._orientationchangeInProgress ) &&\n" +
    "                                   !this._ui.container.hasClass( \"ui-popup-hidden\" ) ) {\n" +
    "                                   // effectively rapid-close the popup while leaving the screen intact\n" +
    "                                   this._ui.container\n" +
    "                                           .addClass( \"ui-popup-hidden\" )\n" +
    "                                           .removeAttr( \"style\" );\n" +
    "                           }\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _handleWindowOrientationchange: function( e ) {\n" +
    "                   if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) {\n" +
    "                           this._expectResizeEvent();\n" +
    "                           this._orientationchangeInProgress = true;\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           // When the popup is open, attempting to focus on an element that is not a\n" +
    "           // child of the popup will redirect focus to the popup\n" +
    "           _handleDocumentFocusIn: function( e ) {\n" +
    "                   var tgt = e.target, $tgt, ui = this._ui;\n" +
    "\n" +
    "                   if ( !this._isOpen ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   if ( tgt !== ui.container[ 0 ] ) {\n" +
    "                           $tgt = $( e.target );\n" +
    "                           if ( 0 === $tgt.parents().filter( ui.container[ 0 ] ).length ) {\n" +
    "                                   $( document.activeElement ).one( \"focus\", function( e ) {\n" +
    "                                           $tgt.blur();\n" +
    "                                   });\n" +
    "                                   ui.focusElement.focus();\n" +
    "                                   e.preventDefault();\n" +
    "                                   e.stopImmediatePropagation();\n" +
    "                                   return false;\n" +
    "                           } else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) {\n" +
    "                                   ui.focusElement = $tgt;\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   this._ignoreResizeEvents();\n" +
    "           },\n" +
    "\n" +
    "           _create: function() {\n" +
    "                   var ui = {\n" +
    "                                   screen: $( \"<div class='ui-screen-hidden ui-popup-screen'></div>\" ),\n" +
    "                                   placeholder: $( \"<div style='display: none;'><!-- placeholder --></div>\" ),\n" +
    "                                   container: $( \"<div class='ui-popup-container ui-popup-hidden'></div>\" )\n" +
    "                           },\n" +
    "                           thisPage = this.element.closest( \".ui-page\" ),\n" +
    "                           myId = this.element.attr( \"id\" ),\n" +
    "                           o = this.options,\n" +
    "                           key, value;\n" +
    "\n" +
    "                   // We need to adjust the history option to be false if there's no AJAX nav.\n" +
    "                   // We can't do it in the option declarations because those are run before\n" +
    "                   // it is determined whether there shall be AJAX nav.\n" +
    "                   o.history = o.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;\n" +
    "\n" +
    "                   if ( thisPage.length === 0 ) {\n" +
    "                           thisPage = $( \"body\" );\n" +
    "                   }\n" +
    "\n" +
    "                   // define the container for navigation event bindings\n" +
    "                   // TODO this would be nice at the the mobile widget level\n" +
    "                   o.container = o.container || $.mobile.pageContainer || thisPage;\n" +
    "\n" +
    "                   // Apply the proto\n" +
    "                   thisPage.append( ui.screen );\n" +
    "                   ui.container.insertAfter( ui.screen );\n" +
    "                   // Leave a placeholder where the element used to be\n" +
    "                   ui.placeholder.insertAfter( this.element );\n" +
    "                   if ( myId ) {\n" +
    "                           ui.screen.attr( \"id\", myId + \"-screen\" );\n" +
    "                           ui.container.attr( \"id\", myId + \"-popup\" );\n" +
    "                           ui.placeholder.html( \"<!-- placeholder for \" + myId + \" -->\" );\n" +
    "                   }\n" +
    "                   ui.container.append( this.element );\n" +
    "                   ui.focusElement = ui.container;\n" +
    "\n" +
    "                   // Add class to popup element\n" +
    "                   this.element.addClass( \"ui-popup\" );\n" +
    "\n" +
    "                   // Define instance variables\n" +
    "                   $.extend( this, {\n" +
    "                           _scrollTop: 0,\n" +
    "                           _page: thisPage,\n" +
    "                           _ui: ui,\n" +
    "                           _fallbackTransition: \"\",\n" +
    "                           _currentTransition: false,\n" +
    "                           _prereqs: null,\n" +
    "                           _isOpen: false,\n" +
    "                           _tolerance: null,\n" +
    "                           _resizeData: null,\n" +
    "                           _ignoreResizeTo: 0,\n" +
    "                           _orientationchangeInProgress: false\n" +
    "                   });\n" +
    "\n" +
    "                   // This duplicates the code from the various option setters below for\n" +
    "                   // better performance. It must be kept in sync with those setters.\n" +
    "                   this._applyTheme( this.element, o.theme, \"body\" );\n" +
    "                   this._applyTheme( this._ui.screen, o.overlayTheme, \"overlay\" );\n" +
    "                   this._applyTransition( o.transition );\n" +
    "                   this.element\n" +
    "                           .toggleClass( \"ui-overlay-shadow\", o.shadow )\n" +
    "                           .toggleClass( \"ui-corner-all\", o.corners );\n" +
    "                   this._setTolerance( o.tolerance );\n" +
    "\n" +
    "                   ui.screen.bind( \"vclick\", $.proxy( this, \"_eatEventAndClose\" ) );\n" +
    "\n" +
    "                   this._on( $.mobile.window, {\n" +
    "                           orientationchange: $.proxy( this, \"_handleWindowOrientationchange\" ),\n" +
    "                           resize: $.proxy( this, \"_handleWindowResize\" ),\n" +
    "                           keyup: $.proxy( this, \"_handleWindowKeyUp\" )\n" +
    "                   });\n" +
    "                   this._on( $.mobile.document, {\n" +
    "                           focusin: $.proxy( this, \"_handleDocumentFocusIn\" )\n" +
    "                   });\n" +
    "           },\n" +
    "\n" +
    "           _applyTheme: function( dst, theme, prefix ) {\n" +
    "                   var classes = ( dst.attr( \"class\" ) || \"\").split( \" \" ),\n" +
    "                           alreadyAdded = true,\n" +
    "                           currentTheme = null,\n" +
    "                           matches,\n" +
    "                           themeStr = String( theme );\n" +
    "\n" +
    "                   while ( classes.length > 0 ) {\n" +
    "                           currentTheme = classes.pop();\n" +
    "                           matches = ( new RegExp( \"^ui-\" + prefix + \"-([a-z])$\" ) ).exec( currentTheme );\n" +
    "                           if ( matches && matches.length > 1 ) {\n" +
    "                                   currentTheme = matches[ 1 ];\n" +
    "                                   break;\n" +
    "                           } else {\n" +
    "                                   currentTheme = null;\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   if ( theme !== currentTheme ) {\n" +
    "                           dst.removeClass( \"ui-\" + prefix + \"-\" + currentTheme );\n" +
    "                           if ( ! ( theme === null || theme === \"none\" ) ) {\n" +
    "                                   dst.addClass( \"ui-\" + prefix + \"-\" + themeStr );\n" +
    "                           }\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setTheme: function( value ) {\n" +
    "                   this._applyTheme( this.element, value, \"body\" );\n" +
    "           },\n" +
    "\n" +
    "           _setOverlayTheme: function( value ) {\n" +
    "                   this._applyTheme( this._ui.screen, value, \"overlay\" );\n" +
    "\n" +
    "                   if ( this._isOpen ) {\n" +
    "                           this._ui.screen.addClass( \"in\" );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setShadow: function( value ) {\n" +
    "                   this.element.toggleClass( \"ui-overlay-shadow\", value );\n" +
    "           },\n" +
    "\n" +
    "           _setCorners: function( value ) {\n" +
    "                   this.element.toggleClass( \"ui-corner-all\", value );\n" +
    "           },\n" +
    "\n" +
    "           _applyTransition: function( value ) {\n" +
    "                   this._ui.container.removeClass( this._fallbackTransition );\n" +
    "                   if ( value && value !== \"none\" ) {\n" +
    "                           this._fallbackTransition = $.mobile._maybeDegradeTransition( value );\n" +
    "                           if ( this._fallbackTransition === \"none\" ) {\n" +
    "                                   this._fallbackTransition = \"\";\n" +
    "                           }\n" +
    "                           this._ui.container.addClass( this._fallbackTransition );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setTransition: function( value ) {\n" +
    "                   if ( !this._currentTransition ) {\n" +
    "                           this._applyTransition( value );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _setTolerance: function( value ) {\n" +
    "                   var tol = { t: 30, r: 15, b: 30, l: 15 };\n" +
    "\n" +
    "                   if ( value !== undefined ) {\n" +
    "                           var ar = String( value ).split( \",\" );\n" +
    "\n" +
    "                           $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } );\n" +
    "\n" +
    "                           switch( ar.length ) {\n" +
    "                                   // All values are to be the same\n" +
    "                                   case 1:\n" +
    "                                           if ( !isNaN( ar[ 0 ] ) ) {\n" +
    "                                                   tol.t = tol.r = tol.b = tol.l = ar[ 0 ];\n" +
    "                                           }\n" +
    "                                           break;\n" +
    "\n" +
    "                                   // The first value denotes top/bottom tolerance, and the second value denotes left/right tolerance\n" +
    "                                   case 2:\n" +
    "                                           if ( !isNaN( ar[ 0 ] ) ) {\n" +
    "                                                   tol.t = tol.b = ar[ 0 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 1 ] ) ) {\n" +
    "                                                   tol.l = tol.r = ar[ 1 ];\n" +
    "                                           }\n" +
    "                                           break;\n" +
    "\n" +
    "                                   // The array contains values in the order top, right, bottom, left\n" +
    "                                   case 4:\n" +
    "                                           if ( !isNaN( ar[ 0 ] ) ) {\n" +
    "                                                   tol.t = ar[ 0 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 1 ] ) ) {\n" +
    "                                                   tol.r = ar[ 1 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 2 ] ) ) {\n" +
    "                                                   tol.b = ar[ 2 ];\n" +
    "                                           }\n" +
    "                                           if ( !isNaN( ar[ 3 ] ) ) {\n" +
    "                                                   tol.l = ar[ 3 ];\n" +
    "                                           }\n" +
    "                                           break;\n" +
    "\n" +
    "                                   default:\n" +
    "                                           break;\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   this._tolerance = tol;\n" +
    "           },\n" +
    "\n" +
    "           _setOption: function( key, value ) {\n" +
    "                   var setter = \"_set\" + key.charAt( 0 ).toUpperCase() + key.slice( 1 );\n" +
    "\n" +
    "                   if ( this[ setter ] !== undefined ) {\n" +
    "                           this[ setter ]( value );\n" +
    "                   }\n" +
    "\n" +
    "                   this._super( key, value );\n" +
    "           },\n" +
    "\n" +
    "           _clampPopupWidth: function( infoOnly ) {\n" +
    "                   var menuSize,\n" +
    "                           winCoords = windowCoords(),\n" +
    "                           // rectangle within which the popup must fit\n" +
    "                           rc = {\n" +
    "                                   x: this._tolerance.l,\n" +
    "                                   y: winCoords.y + this._tolerance.t,\n" +
    "                                   cx: winCoords.cx - this._tolerance.l - this._tolerance.r,\n" +
    "                                   cy: winCoords.cy - this._tolerance.t - this._tolerance.b\n" +
    "                           },\n" +
    "                           ret;\n" +
    "\n" +
    "                   if ( !infoOnly ) {\n" +
    "                           // Clamp the width of the menu before grabbing its size\n" +
    "                           this._ui.container.css( \"max-width\", rc.cx );\n" +
    "                   }\n" +
    "\n" +
    "                   menuSize = {\n" +
    "                           cx: this._ui.container.outerWidth( true ),\n" +
    "                           cy: this._ui.container.outerHeight( true )\n" +
    "                   };\n" +
    "\n" +
    "                   return { rc: rc, menuSize: menuSize };\n" +
    "           },\n" +
    "\n" +
    "           _calculateFinalLocation: function( desired, clampInfo ) {\n" +
    "                   var ret,\n" +
    "                           rc = clampInfo.rc,\n" +
    "                           menuSize = clampInfo.menuSize;\n" +
    "\n" +
    "                   // Center the menu over the desired coordinates, while not going outside\n" +
    "                   // the window tolerances. This will center wrt. the window if the popup is too large.\n" +
    "                   ret = {\n" +
    "                           x: fitSegmentInsideSegment( rc.cx, menuSize.cx, rc.x, desired.x ),\n" +
    "                           y: fitSegmentInsideSegment( rc.cy, menuSize.cy, rc.y, desired.y )\n" +
    "                   };\n" +
    "\n" +
    "                   // Make sure the top of the menu is visible\n" +
    "                   ret.y = Math.max( 0, ret.y );\n" +
    "\n" +
    "                   // If the height of the menu is smaller than the height of the document\n" +
    "                   // align the bottom with the bottom of the document\n" +
    "\n" +
    "                   // fix for $.mobile.document.height() bug in core 1.7.2.\n" +
    "                   var docEl = document.documentElement, docBody = document.body,\n" +
    "                           docHeight = Math.max( docEl.clientHeight, docBody.scrollHeight, docBody.offsetHeight, docEl.scrollHeight, docEl.offsetHeight );\n" +
    "\n" +
    "                   ret.y -= Math.min( ret.y, Math.max( 0, ret.y + menuSize.cy - docHeight ) );\n" +
    "\n" +
    "                   return { left: ret.x, top: ret.y };\n" +
    "           },\n" +
    "\n" +
    "           // Try and center the overlay over the given coordinates\n" +
    "           _placementCoords: function( desired ) {\n" +
    "                   return this._calculateFinalLocation( desired, this._clampPopupWidth() );\n" +
    "           },\n" +
    "\n" +
    "           _createPrereqs: function( screenPrereq, containerPrereq, whenDone ) {\n" +
    "                   var self = this, prereqs;\n" +
    "\n" +
    "                   // It is important to maintain both the local variable prereqs and self._prereqs. The local variable remains in\n" +
    "                   // the closure of the functions which call the callbacks passed in. The comparison between the local variable and\n" +
    "                   // self._prereqs is necessary, because once a function has been passed to .animationComplete() it will be called\n" +
    "                   // next time an animation completes, even if that's not the animation whose end the function was supposed to catch\n" +
    "                   // (for example, if an abort happens during the opening animation, the .animationComplete handler is not called for\n" +
    "                   // that animation anymore, but the handler remains attached, so it is called the next time the popup is opened\n" +
    "                   // - making it stale. Comparing the local variable prereqs to the widget-level variable self._prereqs ensures that\n" +
    "                   // callbacks triggered by a stale .animationComplete will be ignored.\n" +
    "\n" +
    "                   prereqs = {\n" +
    "                           screen: $.Deferred(),\n" +
    "                           container: $.Deferred()\n" +
    "                   };\n" +
    "\n" +
    "                   prereqs.screen.then( function() {\n" +
    "                           if ( prereqs === self._prereqs ) {\n" +
    "                                   screenPrereq();\n" +
    "                           }\n" +
    "                   });\n" +
    "\n" +
    "                   prereqs.container.then( function() {\n" +
    "                           if ( prereqs === self._prereqs ) {\n" +
    "                                   containerPrereq();\n" +
    "                           }\n" +
    "                   });\n" +
    "\n" +
    "                   $.when( prereqs.screen, prereqs.container ).done( function() {\n" +
    "                           if ( prereqs === self._prereqs ) {\n" +
    "                                   self._prereqs = null;\n" +
    "                                   whenDone();\n" +
    "                           }\n" +
    "                   });\n" +
    "\n" +
    "                   self._prereqs = prereqs;\n" +
    "           },\n" +
    "\n" +
    "           _animate: function( args ) {\n" +
    "                   // NOTE before removing the default animation of the screen\n" +
    "                   //      this had an animate callback that would resolve the deferred\n" +
    "                   //      now the deferred is resolved immediately\n" +
    "                   // TODO remove the dependency on the screen deferred\n" +
    "                   this._ui.screen\n" +
    "                           .removeClass( args.classToRemove )\n" +
    "                           .addClass( args.screenClassToAdd );\n" +
    "\n" +
    "                   args.prereqs.screen.resolve();\n" +
    "\n" +
    "                   if ( args.transition && args.transition !== \"none\" ) {\n" +
    "                           if ( args.applyTransition ) {\n" +
    "                                   this._applyTransition( args.transition );\n" +
    "                           }\n" +
    "                           if ( this._fallbackTransition ) {\n" +
    "                                   this._ui.container\n" +
    "                                           .animationComplete( $.proxy( args.prereqs.container, \"resolve\" ) )\n" +
    "                                           .addClass( args.containerClassToAdd )\n" +
    "                                           .removeClass( args.classToRemove );\n" +
    "                                   return;\n" +
    "                           }\n" +
    "                   }\n" +
    "                   this._ui.container.removeClass( args.classToRemove );\n" +
    "                   args.prereqs.container.resolve();\n" +
    "           },\n" +
    "\n" +
    "           // The desired coordinates passed in will be returned untouched if no reference element can be identified via\n" +
    "           // desiredPosition.positionTo. Nevertheless, this function ensures that its return value always contains valid\n" +
    "           // x and y coordinates by specifying the center middle of the window if the coordinates are absent.\n" +
    "           // options: { x: coordinate, y: coordinate, positionTo: string: \"origin\", \"window\", or jQuery selector\n" +
    "           _desiredCoords: function( o ) {\n" +
    "                   var dst = null, offset, winCoords = windowCoords(), x = o.x, y = o.y, pTo = o.positionTo;\n" +
    "\n" +
    "                   // Establish which element will serve as the reference\n" +
    "                   if ( pTo && pTo !== \"origin\" ) {\n" +
    "                           if ( pTo === \"window\" ) {\n" +
    "                                   x = winCoords.cx / 2 + winCoords.x;\n" +
    "                                   y = winCoords.cy / 2 + winCoords.y;\n" +
    "                           } else {\n" +
    "                                   try {\n" +
    "                                           dst = $( pTo );\n" +
    "                                   } catch( e ) {\n" +
    "                                           dst = null;\n" +
    "                                   }\n" +
    "                                   if ( dst ) {\n" +
    "                                           dst.filter( \":visible\" );\n" +
    "                                           if ( dst.length === 0 ) {\n" +
    "                                                   dst = null;\n" +
    "                                           }\n" +
    "                                   }\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   // If an element was found, center over it\n" +
    "                   if ( dst ) {\n" +
    "                           offset = dst.offset();\n" +
    "                           x = offset.left + dst.outerWidth() / 2;\n" +
    "                           y = offset.top + dst.outerHeight() / 2;\n" +
    "                   }\n" +
    "\n" +
    "                   // Make sure x and y are valid numbers - center over the window\n" +
    "                   if ( $.type( x ) !== \"number\" || isNaN( x ) ) {\n" +
    "                           x = winCoords.cx / 2 + winCoords.x;\n" +
    "                   }\n" +
    "                   if ( $.type( y ) !== \"number\" || isNaN( y ) ) {\n" +
    "                           y = winCoords.cy / 2 + winCoords.y;\n" +
    "                   }\n" +
    "\n" +
    "                   return { x: x, y: y };\n" +
    "           },\n" +
    "\n" +
    "           _reposition: function( o ) {\n" +
    "                   // We only care about position-related parameters for repositioning\n" +
    "                   o = { x: o.x, y: o.y, positionTo: o.positionTo };\n" +
    "                   this._trigger( \"beforeposition\", o );\n" +
    "                   this._ui.container.offset( this._placementCoords( this._desiredCoords( o ) ) );\n" +
    "           },\n" +
    "\n" +
    "           reposition: function( o ) {\n" +
    "                   if ( this._isOpen ) {\n" +
    "                           this._reposition( o );\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _openPrereqsComplete: function() {\n" +
    "                   this._ui.container.addClass( \"ui-popup-active\" );\n" +
    "                   this._isOpen = true;\n" +
    "                   this._resizeScreen();\n" +
    "                   this._ui.container.attr( \"tabindex\", \"0\" ).focus();\n" +
    "                   this._ignoreResizeEvents();\n" +
    "                   this._trigger( \"afteropen\" );\n" +
    "           },\n" +
    "\n" +
    "           _open: function( options ) {\n" +
    "                   var o = $.extend( {}, this.options, options ),\n" +
    "                           // TODO move blacklist to private method\n" +
    "                           androidBlacklist = ( function() {\n" +
    "                                   var w = window,\n" +
    "                                           ua = navigator.userAgent,\n" +
    "                                           // Rendering engine is Webkit, and capture major version\n" +
    "                                           wkversion = !!wkmatch && wkmatch[ 1 ],\n" +
    "                                           androidmatch = ua.match( /Android (d+(?:.d+))/ ),\n" +
    "                                           andversion = !!androidmatch && androidmatch[ 1 ],\n" +
    "                                           chromematch = ua.indexOf( \"Chrome\" ) > -1;\n" +
    "\n" +
    "                                   // Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and not Chrome.\n" +
    "                                   if( androidmatch !== null && andversion === \"4.0\" && wkversion && wkversion > 534.13 && !chromematch ) {\n" +
    "                                           return true;\n" +
    "                                   }\n" +
    "                                   return false;\n" +
    "                           }());\n" +
    "\n" +
    "                   // Count down to triggering \"popupafteropen\" - we have two prerequisites:\n" +
    "                   // 1. The popup window animation completes (container())\n" +
    "                   // 2. The screen opacity animation completes (screen())\n" +
    "                   this._createPrereqs(\n" +
    "                           $.noop,\n" +
    "                           $.noop,\n" +
    "                           $.proxy( this, \"_openPrereqsComplete\" ) );\n" +
    "\n" +
    "                   this._currentTransition = o.transition;\n" +
    "                   this._applyTransition( o.transition );\n" +
    "\n" +
    "                   if ( !this.options.theme ) {\n" +
    "                           this._setTheme( this._page.jqmData( \"theme\" ) || $.mobile.getInheritedTheme( this._page, \"c\" ) );\n" +
    "                   }\n" +
    "\n" +
    "                   this._ui.screen.removeClass( \"ui-screen-hidden\" );\n" +
    "                   this._ui.container.removeClass( \"ui-popup-hidden\" );\n" +
    "\n" +
    "                   // Give applications a chance to modify the contents of the container before it appears\n" +
    "                   this._reposition( o );\n" +
    "\n" +
    "                   if ( this.options.overlayTheme && androidBlacklist ) {\n" +
    "                           /* TODO:\n" +
    "                           The native browser on Android 4.0.X (\"Ice Cream Sandwich\") suffers from an issue where the popup overlay appears to be z-indexed\n" +
    "                           above the popup itself when certain other styles exist on the same page -- namely, any element set to `position: fixed` and certain\n" +
    "                           types of input. These issues are reminiscent of previously uncovered bugs in older versions of Android's native browser:\n" +
    "                           https://github.com/scottjehl/Device-Bugs/issues/3\n" +
    "\n" +
    "                           This fix closes the following bugs ( I use \"closes\" with reluctance, and stress that this issue should be revisited as soon as possible ):\n" +
    "\n" +
    "                           https://github.com/jquery/jquery-mobile/issues/4816\n" +
    "                           https://github.com/jquery/jquery-mobile/issues/4844\n" +
    "                           https://github.com/jquery/jquery-mobile/issues/4874\n" +
    "                           */\n" +
    "\n" +
    "                           // TODO sort out why this._page isn't working\n" +
    "                           this.element.closest( \".ui-page\" ).addClass( \"ui-popup-open\" );\n" +
    "                   }\n" +
    "                   this._animate({\n" +
    "                           additionalCondition: true,\n" +
    "                           transition: o.transition,\n" +
    "                           classToRemove: \"\",\n" +
    "                           screenClassToAdd: \"in\",\n" +
    "                           containerClassToAdd: \"in\",\n" +
    "                           applyTransition: false,\n" +
    "                           prereqs: this._prereqs\n" +
    "                   });\n" +
    "           },\n" +
    "\n" +
    "           _closePrereqScreen: function() {\n" +
    "                   this._ui.screen\n" +
    "                           .removeClass( \"out\" )\n" +
    "                           .addClass( \"ui-screen-hidden\" );\n" +
    "           },\n" +
    "\n" +
    "           _closePrereqContainer: function() {\n" +
    "                   this._ui.container\n" +
    "                           .removeClass( \"reverse out\" )\n" +
    "                           .addClass( \"ui-popup-hidden\" )\n" +
    "                           .removeAttr( \"style\" );\n" +
    "           },\n" +
    "\n" +
    "           _closePrereqsDone: function() {\n" +
    "                   var opts = this.options;\n" +
    "\n" +
    "                   this._ui.container.removeAttr( \"tabindex\" );\n" +
    "\n" +
    "                   // remove the global mutex for popups\n" +
    "                   $.mobile.popup.active = undefined;\n" +
    "\n" +
    "                   // alert users that the popup is closed\n" +
    "                   this._trigger( \"afterclose\" );\n" +
    "           },\n" +
    "\n" +
    "           _close: function( immediate ) {\n" +
    "                   this._ui.container.removeClass( \"ui-popup-active\" );\n" +
    "                   this._page.removeClass( \"ui-popup-open\" );\n" +
    "\n" +
    "                   this._isOpen = false;\n" +
    "\n" +
    "                   // Count down to triggering \"popupafterclose\" - we have two prerequisites:\n" +
    "                   // 1. The popup window reverse animation completes (container())\n" +
    "                   // 2. The screen opacity animation completes (screen())\n" +
    "                   this._createPrereqs(\n" +
    "                           $.proxy( this, \"_closePrereqScreen\" ),\n" +
    "                           $.proxy( this, \"_closePrereqContainer\" ),\n" +
    "                           $.proxy( this, \"_closePrereqsDone\" ) );\n" +
    "\n" +
    "                   this._animate( {\n" +
    "                           additionalCondition: this._ui.screen.hasClass( \"in\" ),\n" +
    "                           transition: ( immediate ? \"none\" : ( this._currentTransition ) ),\n" +
    "                           classToRemove: \"in\",\n" +
    "                           screenClassToAdd: \"out\",\n" +
    "                           containerClassToAdd: \"reverse out\",\n" +
    "                           applyTransition: true,\n" +
    "                           prereqs: this._prereqs\n" +
    "                   });\n" +
    "           },\n" +
    "\n" +
    "           _unenhance: function() {\n" +
    "                   // Put the element back to where the placeholder was and remove the \"ui-popup\" class\n" +
    "                   this._setTheme( \"none\" );\n" +
    "                   this.element\n" +
    "                           // Cannot directly insertAfter() - we need to detach() first, because\n" +
    "                           // insertAfter() will do nothing if the payload div was not attached\n" +
    "                           // to the DOM at the time the widget was created, and so the payload\n" +
    "                           // will remain inside the container even after we call insertAfter().\n" +
    "                           // If that happens and we remove the container a few lines below, we\n" +
    "                           // will cause an infinite recursion - #5244\n" +
    "                           .detach()\n" +
    "                           .insertAfter( this._ui.placeholder )\n" +
    "                           .removeClass( \"ui-popup ui-overlay-shadow ui-corner-all\" );\n" +
    "                   this._ui.screen.remove();\n" +
    "                   this._ui.container.remove();\n" +
    "                   this._ui.placeholder.remove();\n" +
    "           },\n" +
    "\n" +
    "           _destroy: function() {\n" +
    "                   if ( $.mobile.popup.active === this ) {\n" +
    "                           this.element.one( \"popupafterclose\", $.proxy( this, \"_unenhance\" ) );\n" +
    "                           this.close();\n" +
    "                   } else {\n" +
    "                           this._unenhance();\n" +
    "                   }\n" +
    "           },\n" +
    "\n" +
    "           _closePopup: function( e, data ) {\n" +
    "                   var parsedDst, toUrl, o = this.options, immediate = false;\n" +
    "\n" +
    "                   if ( e && e.isDefaultPrevented() ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // restore location on screen\n" +
    "                   window.scrollTo( 0, this._scrollTop );\n" +
    "\n" +
    "                   if ( e && e.type === \"pagebeforechange\" && data ) {\n" +
    "                           // Determine whether we need to rapid-close the popup, or whether we can\n" +
    "                           // take the time to run the closing transition\n" +
    "                           if ( typeof data.toPage === \"string\" ) {\n" +
    "                                   parsedDst = data.toPage;\n" +
    "                           } else {\n" +
    "                                   parsedDst = data.toPage.jqmData( \"url\" );\n" +
    "                           }\n" +
    "                           parsedDst = $.mobile.path.parseUrl( parsedDst );\n" +
    "                           toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;\n" +
    "\n" +
    "                           if ( this._myUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ) {\n" +
    "                                   // Going to a different page - close immediately\n" +
    "                                   immediate = true;\n" +
    "                           } else {\n" +
    "                                   e.preventDefault();\n" +
    "                           }\n" +
    "                   }\n" +
    "\n" +
    "                   // remove nav bindings\n" +
    "                   $.mobile.window.off( o.closeEvents );\n" +
    "                   // unbind click handlers added when history is disabled\n" +
    "                   this.element.undelegate( o.closeLinkSelector, o.closeLinkEvents );\n" +
    "\n" +
    "                   this._close( immediate );\n" +
    "           },\n" +
    "\n" +
    "           // any navigation event after a popup is opened should close the popup\n" +
    "           // NOTE the pagebeforechange is bound to catch navigation events that don't\n" +
    "           //      alter the url (eg, dialogs from popups)\n" +
    "           _bindContainerClose: function() {\n" +
    "                   $.mobile.window\n" +
    "                           .on( this.options.closeEvents, $.proxy( this, \"_closePopup\" ) );\n" +
    "           },\n" +
    "\n" +
    "           // TODO no clear deliniation of what should be here and\n" +
    "           // what should be in _open. Seems to be \"visual\" vs \"history\" for now\n" +
    "           open: function( options ) {\n" +
    "                   var self = this, opts = this.options, url, hashkey, activePage, currentIsDialog, hasHash, urlHistory;\n" +
    "\n" +
    "                   // make sure open is idempotent\n" +
    "                   if( $.mobile.popup.active ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // set the global popup mutex\n" +
    "                   $.mobile.popup.active = this;\n" +
    "                   this._scrollTop = $.mobile.window.scrollTop();\n" +
    "\n" +
    "                   // if history alteration is disabled close on navigate events\n" +
    "                   // and leave the url as is\n" +
    "                   if( !( opts.history ) ) {\n" +
    "                           self._open( options );\n" +
    "                           self._bindContainerClose();\n" +
    "\n" +
    "                           // When histoy is disabled we have to grab the data-rel\n" +
    "                           // back link clicks so we can close the popup instead of\n" +
    "                           // relying on history to do it for us\n" +
    "                           self.element\n" +
    "                                   .delegate( opts.closeLinkSelector, opts.closeLinkEvents, function( e ) {\n" +
    "                                           self.close();\n" +
    "                                           e.preventDefault();\n" +
    "                                   });\n" +
    "\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // cache some values for min/readability\n" +
    "                   urlHistory = $.mobile.urlHistory;\n" +
    "                   hashkey = $.mobile.dialogHashKey;\n" +
    "                   activePage = $.mobile.activePage;\n" +
    "                   currentIsDialog = ( activePage ? activePage.hasClass( \"ui-dialog\" ) : false );\n" +
    "                   this._myUrl = url = urlHistory.getActive().url;\n" +
    "                   hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog && ( urlHistory.activeIndex > 0 );\n" +
    "\n" +
    "                   if ( hasHash ) {\n" +
    "                           self._open( options );\n" +
    "                           self._bindContainerClose();\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   // if the current url has no dialog hash key proceed as normal\n" +
    "                   // otherwise, if the page is a dialog simply tack on the hash key\n" +
    "                   if ( url.indexOf( hashkey ) === -1 && !currentIsDialog ){\n" +
    "                           url = url + (url.indexOf( \"#\" ) > -1 ? hashkey : \"#\" + hashkey);\n" +
    "                   } else {\n" +
    "                           url = $.mobile.path.parseLocation().hash + hashkey;\n" +
    "                   }\n" +
    "\n" +
    "                   // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash\n" +
    "                   if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {\n" +
    "                           url += hashkey;\n" +
    "                   }\n" +
    "\n" +
    "                   // swallow the the initial navigation event, and bind for the next\n" +
    "                   $.mobile.window.one( \"beforenavigate\", function( e ) {\n" +
    "                           e.preventDefault();\n" +
    "                           self._open( options );\n" +
    "                           self._bindContainerClose();\n" +
    "                   });\n" +
    "\n" +
    "                   this.urlAltered = true;\n" +
    "                   $.mobile.navigate( url, {role: \"dialog\"} );\n" +
    "           },\n" +
    "\n" +
    "           close: function() {\n" +
    "                   // make sure close is idempotent\n" +
    "                   if( $.mobile.popup.active !== this ) {\n" +
    "                           return;\n" +
    "                   }\n" +
    "\n" +
    "                   this._scrollTop = $.mobile.window.scrollTop();\n" +
    "\n" +
    "                   if( this.options.history && this.urlAltered ) {\n" +
    "                           $.mobile.back();\n" +
    "                           this.urlAltered = false;\n" +
    "                   } else {\n" +
    "                           // simulate the nav bindings having fired\n" +
    "                           this._closePopup();\n" +
    "                   }\n" +
    "           }\n" +
    "\n" +
    "  }});\n" +
    " window.someRandomVar = ";
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
1. Passing object
eval( window.theObjStr + ( window.theInc++ ) + ";" );
pending…
2. Passing function
eval( window.theFnStr + ( window.theInc++ ) + ";" );
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment