jquery_mobile_rails 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d6e18cb902774ca95fddfa11669672ed633662d5
4
- data.tar.gz: 5f98cd7dcbb847e3e1a1171a9bcaf6da5540448f
3
+ metadata.gz: 7528d9103795a011147317b8c07b26f569d7ac4e
4
+ data.tar.gz: d85ac8bd198aad9a99a7109361ddbffb781d5d87
5
5
  SHA512:
6
- metadata.gz: 945f737f18203ac6681f51afc51a4df5de33522142f98c55f5fbe9ff0ef6aea3d27937da755f0baed47e0953c0e7c61faa2114ee177bd5994b2b28de8c334924
7
- data.tar.gz: 7571ba2aa56e081fd29e1051bc1d44980bb08ad2db2f5204cc0ec979e29a3b32b945f5cefd25083f41bd87679d3891f4818485aaed2dbda460f6f1198f46abc6
6
+ metadata.gz: 5ef1d023bdc554f242529cefd2d380eb827f8fff2692d3dcae246f0d29be73b17bc058234c34731499cf57422ec3a751d2c0ef7de0b675b9f76a409ca6e1a784
7
+ data.tar.gz: 08a64212692d1e9121635fcb48634023bdc095ba2052f5c81d2bfa7633c3fccb3c2818cc58c847d372057a28d619b68f641d23aa3c6bf5261137ee19739a5673
@@ -6,7 +6,7 @@ This gem adds the jQuery Mobile files to Rails' asset pipeline.
6
6
 
7
7
  === Gem's jQuery Mobile Version
8
8
 
9
- 1.3.1 (gem 1.3.1)
9
+ 1.3.2 (gem 1.3.2)
10
10
 
11
11
  === Installation
12
12
 
@@ -32,7 +32,7 @@ And the same in your application.css manifest:
32
32
 
33
33
  I built this gem for using with the Mobylette gem, but it will work with your standalone application, or with any other gem that filters your mobile requests.
34
34
 
35
- Please refer to jQuery Mobile's documentation for information on laying out your app and using all of the jQuery Mobile features: http://view.jquerymobile.com/1.3.1/dist/demos/
35
+ Please refer to jQuery Mobile's documentation for information on laying out your app and using all of the jQuery Mobile features: http://view.jquerymobile.com/1.3.2/dist/demos/
36
36
 
37
37
  === Example
38
38
 
@@ -1,3 +1,3 @@
1
1
  module JqueryMobileRails
2
- VERSION = "1.3.1"
2
+ VERSION = "1.3.2"
3
3
  end
@@ -1,6 +1,6 @@
1
- /*
2
- * jQuery Mobile 1.3.1
3
- * Git HEAD hash: 74b4bec049fd93e4fe40205e6157de16eb64eb46 <> Date: Wed Apr 10 2013 21:57:23 UTC
1
+ /*!
2
+ * jQuery Mobile 1.3.2
3
+ * Git HEAD hash: 528cf0e96940644ea644096bfeb913ed920ffaef <> Date: Fri Jul 19 2013 22:17:57 UTC
4
4
  * http://jquerymobile.com
5
5
  *
6
6
  * Copyright 2010, 2013 jQuery Foundation, Inc. and other contributors
@@ -32,7 +32,7 @@
32
32
  $.mobile = $.extend($.mobile, {
33
33
 
34
34
  // Version of the jQuery Mobile Framework
35
- version: "1.3.1",
35
+ version: "1.3.2",
36
36
 
37
37
  // Namespace used framework-wide for data-attrs. Default is no namespace
38
38
  ns: "",
@@ -4280,8 +4280,8 @@ $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defau
4280
4280
  };
4281
4281
  }
4282
4282
  // Reset base to the default document base.
4283
- // only reset if we are not prefetching
4284
- if ( base && typeof options.prefetch === "undefined" ) {
4283
+ // only reset if we are not prefetching
4284
+ if ( base && ( typeof options === "undefined" || typeof options.prefetch === "undefined" ) ) {
4285
4285
  base.reset();
4286
4286
  }
4287
4287
 
@@ -4317,7 +4317,7 @@ $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defau
4317
4317
  url = fileUrl = path.getFilePath( $( "<div>" + RegExp.$1 + "</div>" ).text() );
4318
4318
  }
4319
4319
  //dont update the base tag if we are prefetching
4320
- if ( base && typeof options.prefetch === "undefined") {
4320
+ if ( base && ( typeof options === "undefined" || typeof options.prefetch === "undefined" )) {
4321
4321
  base.set( fileUrl );
4322
4322
  }
4323
4323
 
@@ -5307,6 +5307,10 @@ $.widget( "mobile.dialog", $.mobile.widget, {
5307
5307
  }
5308
5308
  },
5309
5309
 
5310
+ _handlePageBeforeHide: function() {
5311
+ this._isCloseable = false;
5312
+ },
5313
+
5310
5314
  _create: function() {
5311
5315
  var self = this,
5312
5316
  $el = this.element,
@@ -5341,7 +5345,8 @@ $.widget( "mobile.dialog", $.mobile.widget, {
5341
5345
  });
5342
5346
 
5343
5347
  this._on( $el, {
5344
- pagebeforeshow: "_handlePageBeforeShow"
5348
+ pagebeforeshow: "_handlePageBeforeShow",
5349
+ pagebeforehide: "_handlePageBeforeHide"
5345
5350
  });
5346
5351
 
5347
5352
  $.extend( this, {
@@ -5565,11 +5570,6 @@ $.fn.buttonMarkup = function( options ) {
5565
5570
  }
5566
5571
  }
5567
5572
 
5568
- if ( getAttrFixed( e, nsKey + "rel" ) === "popup" && el.attr( "href" ) ) {
5569
- e.setAttribute( "aria-haspopup", true );
5570
- e.setAttribute( "aria-owns", el.attr( "href" ) );
5571
- }
5572
-
5573
5573
  // Check if this element is already enhanced
5574
5574
  buttonElements = $.data( ( ( e.tagName === "INPUT" || e.tagName === "BUTTON" ) ? e.parentNode : e ), "buttonElements" );
5575
5575
 
@@ -7588,7 +7588,7 @@ $.widget( "mobile.slider", $.mobile.widget, $.extend( {
7588
7588
  _sliderVMouseDown: function( event ) {
7589
7589
  // NOTE: we don't do this in refresh because we still want to
7590
7590
  // support programmatic alteration of disabled inputs
7591
- if ( this.options.disabled || !( event.which === 1 || event.which === 0 ) ) {
7591
+ if ( this.options.disabled || !( event.which === 1 || event.which === 0 || event.which === undefined ) ) {
7592
7592
  return false;
7593
7593
  }
7594
7594
  if ( this._trigger( "beforestart", event ) === false ) {
@@ -8346,882 +8346,874 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
8346
8346
 
8347
8347
  (function( $, undefined ) {
8348
8348
 
8349
- function fitSegmentInsideSegment( winSize, segSize, offset, desired ) {
8350
- var ret = desired;
8351
-
8352
- if ( winSize < segSize ) {
8353
- // Center segment if it's bigger than the window
8354
- ret = offset + ( winSize - segSize ) / 2;
8355
- } else {
8356
- // Otherwise center it at the desired coordinate while keeping it completely inside the window
8357
- ret = Math.min( Math.max( offset, desired - segSize / 2 ), offset + winSize - segSize );
8358
- }
8359
-
8360
- return ret;
8361
- }
8362
-
8363
- function windowCoords() {
8364
- var $win = $.mobile.window;
8349
+ function fitSegmentInsideSegment( winSize, segSize, offset, desired ) {
8350
+ var ret = desired;
8365
8351
 
8366
- return {
8367
- x: $win.scrollLeft(),
8368
- y: $win.scrollTop(),
8369
- cx: ( window.innerWidth || $win.width() ),
8370
- cy: ( window.innerHeight || $win.height() )
8371
- };
8352
+ if ( winSize < segSize ) {
8353
+ // Center segment if it's bigger than the window
8354
+ ret = offset + ( winSize - segSize ) / 2;
8355
+ } else {
8356
+ // Otherwise center it at the desired coordinate while keeping it completely inside the window
8357
+ ret = Math.min( Math.max( offset, desired - segSize / 2 ), offset + winSize - segSize );
8372
8358
  }
8373
8359
 
8374
- $.widget( "mobile.popup", $.mobile.widget, {
8375
- options: {
8376
- theme: null,
8377
- overlayTheme: null,
8378
- shadow: true,
8379
- corners: true,
8380
- transition: "none",
8381
- positionTo: "origin",
8382
- tolerance: null,
8383
- initSelector: ":jqmData(role='popup')",
8384
- closeLinkSelector: "a:jqmData(rel='back')",
8385
- closeLinkEvents: "click.popup",
8386
- navigateEvents: "navigate.popup",
8387
- closeEvents: "navigate.popup pagebeforechange.popup",
8388
- dismissible: true,
8389
-
8390
- // NOTE Windows Phone 7 has a scroll position caching issue that
8391
- // requires us to disable popup history management by default
8392
- // https://github.com/jquery/jquery-mobile/issues/4784
8393
- //
8394
- // NOTE this option is modified in _create!
8395
- history: !$.mobile.browser.oldIE
8396
- },
8360
+ return ret;
8361
+ }
8397
8362
 
8398
- _eatEventAndClose: function( e ) {
8399
- e.preventDefault();
8400
- e.stopImmediatePropagation();
8401
- if ( this.options.dismissible ) {
8402
- this.close();
8403
- }
8404
- return false;
8405
- },
8363
+ function windowCoords() {
8364
+ var $win = $.mobile.window;
8406
8365
 
8407
- // Make sure the screen size is increased beyond the page height if the popup's causes the document to increase in height
8408
- _resizeScreen: function() {
8409
- var popupHeight = this._ui.container.outerHeight( true );
8366
+ return {
8367
+ x: $win.scrollLeft(),
8368
+ y: $win.scrollTop(),
8369
+ cx: ( window.innerWidth || $win.width() ),
8370
+ cy: ( window.innerHeight || $win.height() )
8371
+ };
8372
+ }
8410
8373
 
8411
- this._ui.screen.removeAttr( "style" );
8412
- if ( popupHeight > this._ui.screen.height() ) {
8413
- this._ui.screen.height( popupHeight );
8414
- }
8415
- },
8374
+ $.widget( "mobile.popup", $.mobile.widget, {
8375
+ options: {
8376
+ theme: null,
8377
+ overlayTheme: null,
8378
+ shadow: true,
8379
+ corners: true,
8380
+ transition: "none",
8381
+ positionTo: "origin",
8382
+ tolerance: null,
8383
+ initSelector: ":jqmData(role='popup')",
8384
+ closeLinkSelector: "a:jqmData(rel='back')",
8385
+ closeLinkEvents: "click.popup",
8386
+ navigateEvents: "navigate.popup",
8387
+ closeEvents: "navigate.popup pagebeforechange.popup",
8388
+ dismissible: true,
8416
8389
 
8417
- _handleWindowKeyUp: function( e ) {
8418
- if ( this._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE ) {
8419
- return this._eatEventAndClose( e );
8420
- }
8421
- },
8390
+ // NOTE Windows Phone 7 has a scroll position caching issue that
8391
+ // requires us to disable popup history management by default
8392
+ // https://github.com/jquery/jquery-mobile/issues/4784
8393
+ //
8394
+ // NOTE this option is modified in _create!
8395
+ history: !$.mobile.browser.oldIE
8396
+ },
8422
8397
 
8423
- _expectResizeEvent: function() {
8424
- var winCoords = windowCoords();
8398
+ _eatEventAndClose: function( e ) {
8399
+ e.preventDefault();
8400
+ e.stopImmediatePropagation();
8401
+ if ( this.options.dismissible ) {
8402
+ this.close();
8403
+ }
8404
+ return false;
8405
+ },
8425
8406
 
8426
- if ( this._resizeData ) {
8427
- if ( winCoords.x === this._resizeData.winCoords.x &&
8428
- winCoords.y === this._resizeData.winCoords.y &&
8429
- winCoords.cx === this._resizeData.winCoords.cx &&
8430
- winCoords.cy === this._resizeData.winCoords.cy ) {
8431
- // timeout not refreshed
8432
- return false;
8433
- } else {
8434
- // clear existing timeout - it will be refreshed below
8435
- clearTimeout( this._resizeData.timeoutId );
8436
- }
8437
- }
8407
+ // Make sure the screen size is increased beyond the page height if the popup's causes the document to increase in height
8408
+ _resizeScreen: function() {
8409
+ var popupHeight = this._ui.container.outerHeight( true );
8438
8410
 
8439
- this._resizeData = {
8440
- timeoutId: setTimeout( $.proxy( this, "_resizeTimeout" ), 200 ),
8441
- winCoords: winCoords
8442
- };
8411
+ this._ui.screen.removeAttr( "style" );
8412
+ if ( popupHeight > this._ui.screen.height() ) {
8413
+ this._ui.screen.height( popupHeight );
8414
+ }
8415
+ },
8443
8416
 
8444
- return true;
8445
- },
8417
+ _handleWindowKeyUp: function( e ) {
8418
+ if ( this._isOpen && e.keyCode === $.mobile.keyCode.ESCAPE ) {
8419
+ return this._eatEventAndClose( e );
8420
+ }
8421
+ },
8446
8422
 
8447
- _resizeTimeout: function() {
8448
- if ( this._isOpen ) {
8449
- if ( !this._expectResizeEvent() ) {
8450
- if ( this._ui.container.hasClass( "ui-popup-hidden" ) ) {
8451
- // effectively rapid-open the popup while leaving the screen intact
8452
- this._ui.container.removeClass( "ui-popup-hidden" );
8453
- this.reposition( { positionTo: "window" } );
8454
- this._ignoreResizeEvents();
8455
- }
8423
+ _expectResizeEvent: function() {
8424
+ var winCoords = windowCoords();
8456
8425
 
8457
- this._resizeScreen();
8458
- this._resizeData = null;
8459
- this._orientationchangeInProgress = false;
8460
- }
8426
+ if ( this._resizeData ) {
8427
+ if ( winCoords.x === this._resizeData.winCoords.x &&
8428
+ winCoords.y === this._resizeData.winCoords.y &&
8429
+ winCoords.cx === this._resizeData.winCoords.cx &&
8430
+ winCoords.cy === this._resizeData.winCoords.cy ) {
8431
+ // timeout not refreshed
8432
+ return false;
8461
8433
  } else {
8462
- this._resizeData = null;
8463
- this._orientationchangeInProgress = false;
8434
+ // clear existing timeout - it will be refreshed below
8435
+ clearTimeout( this._resizeData.timeoutId );
8464
8436
  }
8465
- },
8437
+ }
8466
8438
 
8467
- _ignoreResizeEvents: function() {
8468
- var self = this;
8439
+ this._resizeData = {
8440
+ timeoutId: setTimeout( $.proxy( this, "_resizeTimeout" ), 200 ),
8441
+ winCoords: winCoords
8442
+ };
8469
8443
 
8470
- if ( this._ignoreResizeTo ) {
8471
- clearTimeout( this._ignoreResizeTo );
8472
- }
8473
- this._ignoreResizeTo = setTimeout( function() { self._ignoreResizeTo = 0; }, 1000 );
8474
- },
8444
+ return true;
8445
+ },
8475
8446
 
8476
- _handleWindowResize: function( e ) {
8477
- if ( this._isOpen && this._ignoreResizeTo === 0 ) {
8478
- if ( ( this._expectResizeEvent() || this._orientationchangeInProgress ) &&
8479
- !this._ui.container.hasClass( "ui-popup-hidden" ) ) {
8480
- // effectively rapid-close the popup while leaving the screen intact
8481
- this._ui.container
8482
- .addClass( "ui-popup-hidden" )
8483
- .removeAttr( "style" );
8447
+ _resizeTimeout: function() {
8448
+ if ( this._isOpen ) {
8449
+ if ( !this._expectResizeEvent() ) {
8450
+ if ( this._ui.container.hasClass( "ui-popup-hidden" ) ) {
8451
+ // effectively rapid-open the popup while leaving the screen intact
8452
+ this._ui.container.removeClass( "ui-popup-hidden" );
8453
+ this.reposition( { positionTo: "window" } );
8454
+ this._ignoreResizeEvents();
8484
8455
  }
8485
- }
8486
- },
8487
8456
 
8488
- _handleWindowOrientationchange: function( e ) {
8489
- if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) {
8490
- this._expectResizeEvent();
8491
- this._orientationchangeInProgress = true;
8457
+ this._resizeScreen();
8458
+ this._resizeData = null;
8459
+ this._orientationchangeInProgress = false;
8492
8460
  }
8493
- },
8461
+ } else {
8462
+ this._resizeData = null;
8463
+ this._orientationchangeInProgress = false;
8464
+ }
8465
+ },
8494
8466
 
8495
- // When the popup is open, attempting to focus on an element that is not a
8496
- // child of the popup will redirect focus to the popup
8497
- _handleDocumentFocusIn: function( e ) {
8498
- var tgt = e.target, $tgt, ui = this._ui;
8467
+ _ignoreResizeEvents: function() {
8468
+ var self = this;
8499
8469
 
8500
- if ( !this._isOpen ) {
8501
- return;
8502
- }
8470
+ if ( this._ignoreResizeTo ) {
8471
+ clearTimeout( this._ignoreResizeTo );
8472
+ }
8473
+ this._ignoreResizeTo = setTimeout( function() { self._ignoreResizeTo = 0; }, 1000 );
8474
+ },
8503
8475
 
8504
- if ( tgt !== ui.container[ 0 ] ) {
8505
- $tgt = $( e.target );
8506
- if ( 0 === $tgt.parents().filter( ui.container[ 0 ] ).length ) {
8507
- $( document.activeElement ).one( "focus", function( e ) {
8508
- $tgt.blur();
8509
- });
8510
- ui.focusElement.focus();
8511
- e.preventDefault();
8512
- e.stopImmediatePropagation();
8513
- return false;
8514
- } else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) {
8515
- ui.focusElement = $tgt;
8516
- }
8476
+ _handleWindowResize: function( e ) {
8477
+ if ( this._isOpen && this._ignoreResizeTo === 0 ) {
8478
+ if ( ( this._expectResizeEvent() || this._orientationchangeInProgress ) &&
8479
+ !this._ui.container.hasClass( "ui-popup-hidden" ) ) {
8480
+ // effectively rapid-close the popup while leaving the screen intact
8481
+ this._ui.container
8482
+ .addClass( "ui-popup-hidden" )
8483
+ .removeAttr( "style" );
8517
8484
  }
8485
+ }
8486
+ },
8518
8487
 
8519
- this._ignoreResizeEvents();
8520
- },
8488
+ _handleWindowOrientationchange: function( e ) {
8489
+ if ( !this._orientationchangeInProgress && this._isOpen && this._ignoreResizeTo === 0 ) {
8490
+ this._expectResizeEvent();
8491
+ this._orientationchangeInProgress = true;
8492
+ }
8493
+ },
8521
8494
 
8522
- _create: function() {
8523
- var ui = {
8524
- screen: $( "<div class='ui-screen-hidden ui-popup-screen'></div>" ),
8525
- placeholder: $( "<div style='display: none;'><!-- placeholder --></div>" ),
8526
- container: $( "<div class='ui-popup-container ui-popup-hidden'></div>" )
8527
- },
8528
- thisPage = this.element.closest( ".ui-page" ),
8529
- myId = this.element.attr( "id" ),
8530
- self = this;
8495
+ // When the popup is open, attempting to focus on an element that is not a
8496
+ // child of the popup will redirect focus to the popup
8497
+ _handleDocumentFocusIn: function( e ) {
8498
+ var tgt = e.target, $tgt, ui = this._ui;
8531
8499
 
8532
- // We need to adjust the history option to be false if there's no AJAX nav.
8533
- // We can't do it in the option declarations because those are run before
8534
- // it is determined whether there shall be AJAX nav.
8535
- this.options.history = this.options.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;
8500
+ if ( !this._isOpen ) {
8501
+ return;
8502
+ }
8536
8503
 
8537
- if ( thisPage.length === 0 ) {
8538
- thisPage = $( "body" );
8504
+ if ( tgt !== ui.container[ 0 ] ) {
8505
+ $tgt = $( e.target );
8506
+ if ( 0 === $tgt.parents().filter( ui.container[ 0 ] ).length ) {
8507
+ $( document.activeElement ).one( "focus", function( e ) {
8508
+ $tgt.blur();
8509
+ });
8510
+ ui.focusElement.focus();
8511
+ e.preventDefault();
8512
+ e.stopImmediatePropagation();
8513
+ return false;
8514
+ } else if ( ui.focusElement[ 0 ] === ui.container[ 0 ] ) {
8515
+ ui.focusElement = $tgt;
8539
8516
  }
8517
+ }
8540
8518
 
8541
- // define the container for navigation event bindings
8542
- // TODO this would be nice at the the mobile widget level
8543
- this.options.container = this.options.container || $.mobile.pageContainer;
8519
+ this._ignoreResizeEvents();
8520
+ },
8544
8521
 
8545
- // Apply the proto
8546
- thisPage.append( ui.screen );
8547
- ui.container.insertAfter( ui.screen );
8548
- // Leave a placeholder where the element used to be
8549
- ui.placeholder.insertAfter( this.element );
8550
- if ( myId ) {
8551
- ui.screen.attr( "id", myId + "-screen" );
8552
- ui.container.attr( "id", myId + "-popup" );
8553
- ui.placeholder.html( "<!-- placeholder for " + myId + " -->" );
8554
- }
8555
- ui.container.append( this.element );
8556
- ui.focusElement = ui.container;
8557
-
8558
- // Add class to popup element
8559
- this.element.addClass( "ui-popup" );
8560
-
8561
- // Define instance variables
8562
- $.extend( this, {
8563
- _scrollTop: 0,
8564
- _page: thisPage,
8565
- _ui: ui,
8566
- _fallbackTransition: "",
8567
- _currentTransition: false,
8568
- _prereqs: null,
8569
- _isOpen: false,
8570
- _tolerance: null,
8571
- _resizeData: null,
8572
- _ignoreResizeTo: 0,
8573
- _orientationchangeInProgress: false
8574
- });
8522
+ _create: function() {
8523
+ var ui = {
8524
+ screen: $( "<div class='ui-screen-hidden ui-popup-screen'></div>" ),
8525
+ placeholder: $( "<div style='display: none;'><!-- placeholder --></div>" ),
8526
+ container: $( "<div class='ui-popup-container ui-popup-hidden'></div>" )
8527
+ },
8528
+ thisPage = this.element.closest( ".ui-page" ),
8529
+ myId = this.element.attr( "id" ),
8530
+ o = this.options,
8531
+ key, value;
8575
8532
 
8576
- $.each( this.options, function( key, value ) {
8577
- // Cause initial options to be applied by their handler by temporarily setting the option to undefined
8578
- // - the handler then sets it to the initial value
8579
- self.options[ key ] = undefined;
8580
- self._setOption( key, value, true );
8581
- });
8533
+ // We need to adjust the history option to be false if there's no AJAX nav.
8534
+ // We can't do it in the option declarations because those are run before
8535
+ // it is determined whether there shall be AJAX nav.
8536
+ o.history = o.history && $.mobile.ajaxEnabled && $.mobile.hashListeningEnabled;
8582
8537
 
8583
- ui.screen.bind( "vclick", $.proxy( this, "_eatEventAndClose" ) );
8538
+ if ( thisPage.length === 0 ) {
8539
+ thisPage = $( "body" );
8540
+ }
8584
8541
 
8585
- this._on( $.mobile.window, {
8586
- orientationchange: $.proxy( this, "_handleWindowOrientationchange" ),
8587
- resize: $.proxy( this, "_handleWindowResize" ),
8588
- keyup: $.proxy( this, "_handleWindowKeyUp" )
8589
- });
8590
- this._on( $.mobile.document, {
8591
- focusin: $.proxy( this, "_handleDocumentFocusIn" )
8592
- });
8593
- },
8542
+ // define the container for navigation event bindings
8543
+ // TODO this would be nice at the the mobile widget level
8544
+ o.container = o.container || $.mobile.pageContainer || thisPage;
8594
8545
 
8595
- _applyTheme: function( dst, theme, prefix ) {
8596
- var classes = ( dst.attr( "class" ) || "").split( " " ),
8597
- alreadyAdded = true,
8598
- currentTheme = null,
8599
- matches,
8600
- themeStr = String( theme );
8601
-
8602
- while ( classes.length > 0 ) {
8603
- currentTheme = classes.pop();
8604
- matches = ( new RegExp( "^ui-" + prefix + "-([a-z])$" ) ).exec( currentTheme );
8605
- if ( matches && matches.length > 1 ) {
8606
- currentTheme = matches[ 1 ];
8607
- break;
8608
- } else {
8609
- currentTheme = null;
8610
- }
8611
- }
8546
+ // Apply the proto
8547
+ thisPage.append( ui.screen );
8548
+ ui.container.insertAfter( ui.screen );
8549
+ // Leave a placeholder where the element used to be
8550
+ ui.placeholder.insertAfter( this.element );
8551
+ if ( myId ) {
8552
+ ui.screen.attr( "id", myId + "-screen" );
8553
+ ui.container.attr( "id", myId + "-popup" );
8554
+ ui.placeholder.html( "<!-- placeholder for " + myId + " -->" );
8555
+ }
8556
+ ui.container.append( this.element );
8557
+ ui.focusElement = ui.container;
8612
8558
 
8613
- if ( theme !== currentTheme ) {
8614
- dst.removeClass( "ui-" + prefix + "-" + currentTheme );
8615
- if ( ! ( theme === null || theme === "none" ) ) {
8616
- dst.addClass( "ui-" + prefix + "-" + themeStr );
8617
- }
8618
- }
8619
- },
8559
+ // Add class to popup element
8560
+ this.element.addClass( "ui-popup" );
8620
8561
 
8621
- _setTheme: function( value ) {
8622
- this._applyTheme( this.element, value, "body" );
8623
- },
8562
+ // Define instance variables
8563
+ $.extend( this, {
8564
+ _scrollTop: 0,
8565
+ _page: thisPage,
8566
+ _ui: ui,
8567
+ _fallbackTransition: "",
8568
+ _currentTransition: false,
8569
+ _prereqs: null,
8570
+ _isOpen: false,
8571
+ _tolerance: null,
8572
+ _resizeData: null,
8573
+ _ignoreResizeTo: 0,
8574
+ _orientationchangeInProgress: false
8575
+ });
8624
8576
 
8625
- _setOverlayTheme: function( value ) {
8626
- this._applyTheme( this._ui.screen, value, "overlay" );
8577
+ // This duplicates the code from the various option setters below for
8578
+ // better performance. It must be kept in sync with those setters.
8579
+ this._applyTheme( this.element, o.theme, "body" );
8580
+ this._applyTheme( this._ui.screen, o.overlayTheme, "overlay" );
8581
+ this._applyTransition( o.transition );
8582
+ this.element
8583
+ .toggleClass( "ui-overlay-shadow", o.shadow )
8584
+ .toggleClass( "ui-corner-all", o.corners );
8585
+ this._setTolerance( o.tolerance );
8627
8586
 
8628
- if ( this._isOpen ) {
8629
- this._ui.screen.addClass( "in" );
8630
- }
8631
- },
8587
+ ui.screen.bind( "vclick", $.proxy( this, "_eatEventAndClose" ) );
8632
8588
 
8633
- _setShadow: function( value ) {
8634
- this.element.toggleClass( "ui-overlay-shadow", value );
8635
- },
8636
-
8637
- _setCorners: function( value ) {
8638
- this.element.toggleClass( "ui-corner-all", value );
8639
- },
8589
+ this._on( $.mobile.window, {
8590
+ orientationchange: $.proxy( this, "_handleWindowOrientationchange" ),
8591
+ resize: $.proxy( this, "_handleWindowResize" ),
8592
+ keyup: $.proxy( this, "_handleWindowKeyUp" )
8593
+ });
8594
+ this._on( $.mobile.document, {
8595
+ focusin: $.proxy( this, "_handleDocumentFocusIn" )
8596
+ });
8597
+ },
8640
8598
 
8641
- _applyTransition: function( value ) {
8642
- this._ui.container.removeClass( this._fallbackTransition );
8643
- if ( value && value !== "none" ) {
8644
- this._fallbackTransition = $.mobile._maybeDegradeTransition( value );
8645
- if ( this._fallbackTransition === "none" ) {
8646
- this._fallbackTransition = "";
8647
- }
8648
- this._ui.container.addClass( this._fallbackTransition );
8599
+ _applyTheme: function( dst, theme, prefix ) {
8600
+ var classes = ( dst.attr( "class" ) || "").split( " " ),
8601
+ alreadyAdded = true,
8602
+ currentTheme = null,
8603
+ matches,
8604
+ themeStr = String( theme );
8605
+
8606
+ while ( classes.length > 0 ) {
8607
+ currentTheme = classes.pop();
8608
+ matches = ( new RegExp( "^ui-" + prefix + "-([a-z])$" ) ).exec( currentTheme );
8609
+ if ( matches && matches.length > 1 ) {
8610
+ currentTheme = matches[ 1 ];
8611
+ break;
8612
+ } else {
8613
+ currentTheme = null;
8649
8614
  }
8650
- },
8615
+ }
8651
8616
 
8652
- _setTransition: function( value ) {
8653
- if ( !this._currentTransition ) {
8654
- this._applyTransition( value );
8617
+ if ( theme !== currentTheme ) {
8618
+ dst.removeClass( "ui-" + prefix + "-" + currentTheme );
8619
+ if ( ! ( theme === null || theme === "none" ) ) {
8620
+ dst.addClass( "ui-" + prefix + "-" + themeStr );
8655
8621
  }
8656
- },
8657
-
8658
- _setTolerance: function( value ) {
8659
- var tol = { t: 30, r: 15, b: 30, l: 15 };
8622
+ }
8623
+ },
8660
8624
 
8661
- if ( value !== undefined ) {
8662
- var ar = String( value ).split( "," );
8625
+ _setTheme: function( value ) {
8626
+ this._applyTheme( this.element, value, "body" );
8627
+ },
8663
8628
 
8664
- $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } );
8629
+ _setOverlayTheme: function( value ) {
8630
+ this._applyTheme( this._ui.screen, value, "overlay" );
8665
8631
 
8666
- switch( ar.length ) {
8667
- // All values are to be the same
8668
- case 1:
8669
- if ( !isNaN( ar[ 0 ] ) ) {
8670
- tol.t = tol.r = tol.b = tol.l = ar[ 0 ];
8671
- }
8672
- break;
8632
+ if ( this._isOpen ) {
8633
+ this._ui.screen.addClass( "in" );
8634
+ }
8635
+ },
8673
8636
 
8674
- // The first value denotes top/bottom tolerance, and the second value denotes left/right tolerance
8675
- case 2:
8676
- if ( !isNaN( ar[ 0 ] ) ) {
8677
- tol.t = tol.b = ar[ 0 ];
8678
- }
8679
- if ( !isNaN( ar[ 1 ] ) ) {
8680
- tol.l = tol.r = ar[ 1 ];
8681
- }
8682
- break;
8637
+ _setShadow: function( value ) {
8638
+ this.element.toggleClass( "ui-overlay-shadow", value );
8639
+ },
8683
8640
 
8684
- // The array contains values in the order top, right, bottom, left
8685
- case 4:
8686
- if ( !isNaN( ar[ 0 ] ) ) {
8687
- tol.t = ar[ 0 ];
8688
- }
8689
- if ( !isNaN( ar[ 1 ] ) ) {
8690
- tol.r = ar[ 1 ];
8691
- }
8692
- if ( !isNaN( ar[ 2 ] ) ) {
8693
- tol.b = ar[ 2 ];
8694
- }
8695
- if ( !isNaN( ar[ 3 ] ) ) {
8696
- tol.l = ar[ 3 ];
8697
- }
8698
- break;
8641
+ _setCorners: function( value ) {
8642
+ this.element.toggleClass( "ui-corner-all", value );
8643
+ },
8699
8644
 
8700
- default:
8701
- break;
8702
- }
8645
+ _applyTransition: function( value ) {
8646
+ this._ui.container.removeClass( this._fallbackTransition );
8647
+ if ( value && value !== "none" ) {
8648
+ this._fallbackTransition = $.mobile._maybeDegradeTransition( value );
8649
+ if ( this._fallbackTransition === "none" ) {
8650
+ this._fallbackTransition = "";
8703
8651
  }
8652
+ this._ui.container.addClass( this._fallbackTransition );
8653
+ }
8654
+ },
8704
8655
 
8705
- this._tolerance = tol;
8706
- },
8656
+ _setTransition: function( value ) {
8657
+ if ( !this._currentTransition ) {
8658
+ this._applyTransition( value );
8659
+ }
8660
+ },
8707
8661
 
8708
- _setOption: function( key, value ) {
8709
- var exclusions, setter = "_set" + key.charAt( 0 ).toUpperCase() + key.slice( 1 );
8662
+ _setTolerance: function( value ) {
8663
+ var tol = { t: 30, r: 15, b: 30, l: 15 };
8710
8664
 
8711
- if ( this[ setter ] !== undefined ) {
8712
- this[ setter ]( value );
8713
- }
8665
+ if ( value !== undefined ) {
8666
+ var ar = String( value ).split( "," );
8714
8667
 
8715
- // TODO REMOVE FOR 1.2.1 by moving them out to a default options object
8716
- exclusions = [
8717
- "initSelector",
8718
- "closeLinkSelector",
8719
- "closeLinkEvents",
8720
- "navigateEvents",
8721
- "closeEvents",
8722
- "history",
8723
- "container"
8724
- ];
8668
+ $.each( ar, function( idx, val ) { ar[ idx ] = parseInt( val, 10 ); } );
8725
8669
 
8726
- $.mobile.widget.prototype._setOption.apply( this, arguments );
8727
- if ( $.inArray( key, exclusions ) === -1 ) {
8728
- // Record the option change in the options and in the DOM data-* attributes
8729
- this.element.attr( "data-" + ( $.mobile.ns || "" ) + ( key.replace( /([A-Z])/, "-$1" ).toLowerCase() ), value );
8730
- }
8731
- },
8670
+ switch( ar.length ) {
8671
+ // All values are to be the same
8672
+ case 1:
8673
+ if ( !isNaN( ar[ 0 ] ) ) {
8674
+ tol.t = tol.r = tol.b = tol.l = ar[ 0 ];
8675
+ }
8676
+ break;
8732
8677
 
8733
- // Try and center the overlay over the given coordinates
8734
- _placementCoords: function( desired ) {
8735
- // rectangle within which the popup must fit
8736
- var
8737
- winCoords = windowCoords(),
8738
- rc = {
8739
- x: this._tolerance.l,
8740
- y: winCoords.y + this._tolerance.t,
8741
- cx: winCoords.cx - this._tolerance.l - this._tolerance.r,
8742
- cy: winCoords.cy - this._tolerance.t - this._tolerance.b
8743
- },
8744
- menuSize, ret;
8678
+ // The first value denotes top/bottom tolerance, and the second value denotes left/right tolerance
8679
+ case 2:
8680
+ if ( !isNaN( ar[ 0 ] ) ) {
8681
+ tol.t = tol.b = ar[ 0 ];
8682
+ }
8683
+ if ( !isNaN( ar[ 1 ] ) ) {
8684
+ tol.l = tol.r = ar[ 1 ];
8685
+ }
8686
+ break;
8745
8687
 
8746
- // Clamp the width of the menu before grabbing its size
8747
- this._ui.container.css( "max-width", rc.cx );
8748
- menuSize = {
8749
- cx: this._ui.container.outerWidth( true ),
8750
- cy: this._ui.container.outerHeight( true )
8751
- };
8688
+ // The array contains values in the order top, right, bottom, left
8689
+ case 4:
8690
+ if ( !isNaN( ar[ 0 ] ) ) {
8691
+ tol.t = ar[ 0 ];
8692
+ }
8693
+ if ( !isNaN( ar[ 1 ] ) ) {
8694
+ tol.r = ar[ 1 ];
8695
+ }
8696
+ if ( !isNaN( ar[ 2 ] ) ) {
8697
+ tol.b = ar[ 2 ];
8698
+ }
8699
+ if ( !isNaN( ar[ 3 ] ) ) {
8700
+ tol.l = ar[ 3 ];
8701
+ }
8702
+ break;
8752
8703
 
8753
- // Center the menu over the desired coordinates, while not going outside
8754
- // the window tolerances. This will center wrt. the window if the popup is too large.
8755
- ret = {
8756
- x: fitSegmentInsideSegment( rc.cx, menuSize.cx, rc.x, desired.x ),
8757
- y: fitSegmentInsideSegment( rc.cy, menuSize.cy, rc.y, desired.y )
8758
- };
8704
+ default:
8705
+ break;
8706
+ }
8707
+ }
8759
8708
 
8760
- // Make sure the top of the menu is visible
8761
- ret.y = Math.max( 0, ret.y );
8709
+ this._tolerance = tol;
8710
+ },
8762
8711
 
8763
- // If the height of the menu is smaller than the height of the document
8764
- // align the bottom with the bottom of the document
8712
+ _setOption: function( key, value ) {
8713
+ var setter = "_set" + key.charAt( 0 ).toUpperCase() + key.slice( 1 );
8765
8714
 
8766
- // fix for $.mobile.document.height() bug in core 1.7.2.
8767
- var docEl = document.documentElement, docBody = document.body,
8768
- docHeight = Math.max( docEl.clientHeight, docBody.scrollHeight, docBody.offsetHeight, docEl.scrollHeight, docEl.offsetHeight );
8715
+ if ( this[ setter ] !== undefined ) {
8716
+ this[ setter ]( value );
8717
+ }
8769
8718
 
8770
- ret.y -= Math.min( ret.y, Math.max( 0, ret.y + menuSize.cy - docHeight ) );
8719
+ this._super( key, value );
8720
+ },
8771
8721
 
8772
- return { left: ret.x, top: ret.y };
8773
- },
8722
+ // Try and center the overlay over the given coordinates
8723
+ _placementCoords: function( desired ) {
8724
+ // rectangle within which the popup must fit
8725
+ var
8726
+ winCoords = windowCoords(),
8727
+ rc = {
8728
+ x: this._tolerance.l,
8729
+ y: winCoords.y + this._tolerance.t,
8730
+ cx: winCoords.cx - this._tolerance.l - this._tolerance.r,
8731
+ cy: winCoords.cy - this._tolerance.t - this._tolerance.b
8732
+ },
8733
+ menuSize, ret;
8774
8734
 
8775
- _createPrereqs: function( screenPrereq, containerPrereq, whenDone ) {
8776
- var self = this, prereqs;
8777
-
8778
- // It is important to maintain both the local variable prereqs and self._prereqs. The local variable remains in
8779
- // the closure of the functions which call the callbacks passed in. The comparison between the local variable and
8780
- // self._prereqs is necessary, because once a function has been passed to .animationComplete() it will be called
8781
- // next time an animation completes, even if that's not the animation whose end the function was supposed to catch
8782
- // (for example, if an abort happens during the opening animation, the .animationComplete handler is not called for
8783
- // that animation anymore, but the handler remains attached, so it is called the next time the popup is opened
8784
- // - making it stale. Comparing the local variable prereqs to the widget-level variable self._prereqs ensures that
8785
- // callbacks triggered by a stale .animationComplete will be ignored.
8786
-
8787
- prereqs = {
8788
- screen: $.Deferred(),
8789
- container: $.Deferred()
8790
- };
8735
+ // Clamp the width of the menu before grabbing its size
8736
+ this._ui.container.css( "max-width", rc.cx );
8737
+ menuSize = {
8738
+ cx: this._ui.container.outerWidth( true ),
8739
+ cy: this._ui.container.outerHeight( true )
8740
+ };
8791
8741
 
8792
- prereqs.screen.then( function() {
8793
- if ( prereqs === self._prereqs ) {
8794
- screenPrereq();
8795
- }
8796
- });
8742
+ // Center the menu over the desired coordinates, while not going outside
8743
+ // the window tolerances. This will center wrt. the window if the popup is too large.
8744
+ ret = {
8745
+ x: fitSegmentInsideSegment( rc.cx, menuSize.cx, rc.x, desired.x ),
8746
+ y: fitSegmentInsideSegment( rc.cy, menuSize.cy, rc.y, desired.y )
8747
+ };
8797
8748
 
8798
- prereqs.container.then( function() {
8799
- if ( prereqs === self._prereqs ) {
8800
- containerPrereq();
8801
- }
8802
- });
8749
+ // Make sure the top of the menu is visible
8750
+ ret.y = Math.max( 0, ret.y );
8803
8751
 
8804
- $.when( prereqs.screen, prereqs.container ).done( function() {
8805
- if ( prereqs === self._prereqs ) {
8806
- self._prereqs = null;
8807
- whenDone();
8808
- }
8809
- });
8752
+ // If the height of the menu is smaller than the height of the document
8753
+ // align the bottom with the bottom of the document
8810
8754
 
8811
- self._prereqs = prereqs;
8812
- },
8755
+ // fix for $.mobile.document.height() bug in core 1.7.2.
8756
+ var docEl = document.documentElement, docBody = document.body,
8757
+ docHeight = Math.max( docEl.clientHeight, docBody.scrollHeight, docBody.offsetHeight, docEl.scrollHeight, docEl.offsetHeight );
8813
8758
 
8814
- _animate: function( args ) {
8815
- // NOTE before removing the default animation of the screen
8816
- // this had an animate callback that would resolve the deferred
8817
- // now the deferred is resolved immediately
8818
- // TODO remove the dependency on the screen deferred
8819
- this._ui.screen
8820
- .removeClass( args.classToRemove )
8821
- .addClass( args.screenClassToAdd );
8759
+ ret.y -= Math.min( ret.y, Math.max( 0, ret.y + menuSize.cy - docHeight ) );
8822
8760
 
8823
- args.prereqs.screen.resolve();
8761
+ return { left: ret.x, top: ret.y };
8762
+ },
8824
8763
 
8825
- if ( args.transition && args.transition !== "none" ) {
8826
- if ( args.applyTransition ) {
8827
- this._applyTransition( args.transition );
8828
- }
8829
- if ( this._fallbackTransition ) {
8830
- this._ui.container
8831
- .animationComplete( $.proxy( args.prereqs.container, "resolve" ) )
8832
- .addClass( args.containerClassToAdd )
8833
- .removeClass( args.classToRemove );
8834
- return;
8835
- }
8836
- }
8837
- this._ui.container.removeClass( args.classToRemove );
8838
- args.prereqs.container.resolve();
8839
- },
8764
+ _createPrereqs: function( screenPrereq, containerPrereq, whenDone ) {
8765
+ var self = this, prereqs;
8766
+
8767
+ // It is important to maintain both the local variable prereqs and self._prereqs. The local variable remains in
8768
+ // the closure of the functions which call the callbacks passed in. The comparison between the local variable and
8769
+ // self._prereqs is necessary, because once a function has been passed to .animationComplete() it will be called
8770
+ // next time an animation completes, even if that's not the animation whose end the function was supposed to catch
8771
+ // (for example, if an abort happens during the opening animation, the .animationComplete handler is not called for
8772
+ // that animation anymore, but the handler remains attached, so it is called the next time the popup is opened
8773
+ // - making it stale. Comparing the local variable prereqs to the widget-level variable self._prereqs ensures that
8774
+ // callbacks triggered by a stale .animationComplete will be ignored.
8775
+
8776
+ prereqs = {
8777
+ screen: $.Deferred(),
8778
+ container: $.Deferred()
8779
+ };
8840
8780
 
8841
- // The desired coordinates passed in will be returned untouched if no reference element can be identified via
8842
- // desiredPosition.positionTo. Nevertheless, this function ensures that its return value always contains valid
8843
- // x and y coordinates by specifying the center middle of the window if the coordinates are absent.
8844
- // options: { x: coordinate, y: coordinate, positionTo: string: "origin", "window", or jQuery selector
8845
- _desiredCoords: function( o ) {
8846
- var dst = null, offset, winCoords = windowCoords(), x = o.x, y = o.y, pTo = o.positionTo;
8847
-
8848
- // Establish which element will serve as the reference
8849
- if ( pTo && pTo !== "origin" ) {
8850
- if ( pTo === "window" ) {
8851
- x = winCoords.cx / 2 + winCoords.x;
8852
- y = winCoords.cy / 2 + winCoords.y;
8853
- } else {
8854
- try {
8855
- dst = $( pTo );
8856
- } catch( e ) {
8857
- dst = null;
8858
- }
8859
- if ( dst ) {
8860
- dst.filter( ":visible" );
8861
- if ( dst.length === 0 ) {
8862
- dst = null;
8863
- }
8864
- }
8865
- }
8781
+ prereqs.screen.then( function() {
8782
+ if ( prereqs === self._prereqs ) {
8783
+ screenPrereq();
8866
8784
  }
8785
+ });
8867
8786
 
8868
- // If an element was found, center over it
8869
- if ( dst ) {
8870
- offset = dst.offset();
8871
- x = offset.left + dst.outerWidth() / 2;
8872
- y = offset.top + dst.outerHeight() / 2;
8787
+ prereqs.container.then( function() {
8788
+ if ( prereqs === self._prereqs ) {
8789
+ containerPrereq();
8873
8790
  }
8791
+ });
8874
8792
 
8875
- // Make sure x and y are valid numbers - center over the window
8876
- if ( $.type( x ) !== "number" || isNaN( x ) ) {
8877
- x = winCoords.cx / 2 + winCoords.x;
8878
- }
8879
- if ( $.type( y ) !== "number" || isNaN( y ) ) {
8880
- y = winCoords.cy / 2 + winCoords.y;
8793
+ $.when( prereqs.screen, prereqs.container ).done( function() {
8794
+ if ( prereqs === self._prereqs ) {
8795
+ self._prereqs = null;
8796
+ whenDone();
8881
8797
  }
8798
+ });
8882
8799
 
8883
- return { x: x, y: y };
8884
- },
8885
-
8886
- _reposition: function( o ) {
8887
- // We only care about position-related parameters for repositioning
8888
- o = { x: o.x, y: o.y, positionTo: o.positionTo };
8889
- this._trigger( "beforeposition", o );
8890
- this._ui.container.offset( this._placementCoords( this._desiredCoords( o ) ) );
8891
- },
8800
+ self._prereqs = prereqs;
8801
+ },
8892
8802
 
8893
- reposition: function( o ) {
8894
- if ( this._isOpen ) {
8895
- this._reposition( o );
8803
+ _animate: function( args ) {
8804
+ // NOTE before removing the default animation of the screen
8805
+ // this had an animate callback that would resolve the deferred
8806
+ // now the deferred is resolved immediately
8807
+ // TODO remove the dependency on the screen deferred
8808
+ this._ui.screen
8809
+ .removeClass( args.classToRemove )
8810
+ .addClass( args.screenClassToAdd );
8811
+
8812
+ args.prereqs.screen.resolve();
8813
+
8814
+ if ( args.transition && args.transition !== "none" ) {
8815
+ if ( args.applyTransition ) {
8816
+ this._applyTransition( args.transition );
8817
+ }
8818
+ if ( this._fallbackTransition ) {
8819
+ this._ui.container
8820
+ .animationComplete( $.proxy( args.prereqs.container, "resolve" ) )
8821
+ .addClass( args.containerClassToAdd )
8822
+ .removeClass( args.classToRemove );
8823
+ return;
8896
8824
  }
8897
- },
8825
+ }
8826
+ this._ui.container.removeClass( args.classToRemove );
8827
+ args.prereqs.container.resolve();
8828
+ },
8898
8829
 
8899
- _openPrereqsComplete: function() {
8900
- this._ui.container.addClass( "ui-popup-active" );
8901
- this._isOpen = true;
8902
- this._resizeScreen();
8903
- this._ui.container.attr( "tabindex", "0" ).focus();
8904
- this._ignoreResizeEvents();
8905
- this._trigger( "afteropen" );
8906
- },
8830
+ // The desired coordinates passed in will be returned untouched if no reference element can be identified via
8831
+ // desiredPosition.positionTo. Nevertheless, this function ensures that its return value always contains valid
8832
+ // x and y coordinates by specifying the center middle of the window if the coordinates are absent.
8833
+ // options: { x: coordinate, y: coordinate, positionTo: string: "origin", "window", or jQuery selector
8834
+ _desiredCoords: function( o ) {
8835
+ var dst = null, offset, winCoords = windowCoords(), x = o.x, y = o.y, pTo = o.positionTo;
8907
8836
 
8908
- _open: function( options ) {
8909
- var o = $.extend( {}, this.options, options ),
8910
- // TODO move blacklist to private method
8911
- androidBlacklist = ( function() {
8912
- var w = window,
8913
- ua = navigator.userAgent,
8914
- // Rendering engine is Webkit, and capture major version
8915
- wkmatch = ua.match( /AppleWebKit\/([0-9\.]+)/ ),
8916
- wkversion = !!wkmatch && wkmatch[ 1 ],
8917
- androidmatch = ua.match( /Android (\d+(?:\.\d+))/ ),
8918
- andversion = !!androidmatch && androidmatch[ 1 ],
8919
- chromematch = ua.indexOf( "Chrome" ) > -1;
8920
-
8921
- // Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and not Chrome.
8922
- if( androidmatch !== null && andversion === "4.0" && wkversion && wkversion > 534.13 && !chromematch ) {
8923
- return true;
8837
+ // Establish which element will serve as the reference
8838
+ if ( pTo && pTo !== "origin" ) {
8839
+ if ( pTo === "window" ) {
8840
+ x = winCoords.cx / 2 + winCoords.x;
8841
+ y = winCoords.cy / 2 + winCoords.y;
8842
+ } else {
8843
+ try {
8844
+ dst = $( pTo );
8845
+ } catch( e ) {
8846
+ dst = null;
8847
+ }
8848
+ if ( dst ) {
8849
+ dst.filter( ":visible" );
8850
+ if ( dst.length === 0 ) {
8851
+ dst = null;
8924
8852
  }
8925
- return false;
8926
- }());
8853
+ }
8854
+ }
8855
+ }
8927
8856
 
8928
- // Count down to triggering "popupafteropen" - we have two prerequisites:
8929
- // 1. The popup window animation completes (container())
8930
- // 2. The screen opacity animation completes (screen())
8931
- this._createPrereqs(
8932
- $.noop,
8933
- $.noop,
8934
- $.proxy( this, "_openPrereqsComplete" ) );
8857
+ // If an element was found, center over it
8858
+ if ( dst ) {
8859
+ offset = dst.offset();
8860
+ x = offset.left + dst.outerWidth() / 2;
8861
+ y = offset.top + dst.outerHeight() / 2;
8862
+ }
8935
8863
 
8936
- this._currentTransition = o.transition;
8937
- this._applyTransition( o.transition );
8864
+ // Make sure x and y are valid numbers - center over the window
8865
+ if ( $.type( x ) !== "number" || isNaN( x ) ) {
8866
+ x = winCoords.cx / 2 + winCoords.x;
8867
+ }
8868
+ if ( $.type( y ) !== "number" || isNaN( y ) ) {
8869
+ y = winCoords.cy / 2 + winCoords.y;
8870
+ }
8938
8871
 
8939
- if ( !this.options.theme ) {
8940
- this._setTheme( this._page.jqmData( "theme" ) || $.mobile.getInheritedTheme( this._page, "c" ) );
8941
- }
8872
+ return { x: x, y: y };
8873
+ },
8942
8874
 
8943
- this._ui.screen.removeClass( "ui-screen-hidden" );
8944
- this._ui.container.removeClass( "ui-popup-hidden" );
8875
+ _reposition: function( o ) {
8876
+ // We only care about position-related parameters for repositioning
8877
+ o = { x: o.x, y: o.y, positionTo: o.positionTo };
8878
+ this._trigger( "beforeposition", undefined, o );
8879
+ this._ui.container.offset( this._placementCoords( this._desiredCoords( o ) ) );
8880
+ },
8945
8881
 
8946
- // Give applications a chance to modify the contents of the container before it appears
8882
+ reposition: function( o ) {
8883
+ if ( this._isOpen ) {
8947
8884
  this._reposition( o );
8885
+ }
8886
+ },
8948
8887
 
8949
- if ( this.options.overlayTheme && androidBlacklist ) {
8950
- /* TODO:
8951
- The native browser on Android 4.0.X ("Ice Cream Sandwich") suffers from an issue where the popup overlay appears to be z-indexed
8952
- above the popup itself when certain other styles exist on the same page -- namely, any element set to `position: fixed` and certain
8953
- types of input. These issues are reminiscent of previously uncovered bugs in older versions of Android's native browser:
8954
- https://github.com/scottjehl/Device-Bugs/issues/3
8955
-
8956
- This fix closes the following bugs ( I use "closes" with reluctance, and stress that this issue should be revisited as soon as possible ):
8957
-
8958
- https://github.com/jquery/jquery-mobile/issues/4816
8959
- https://github.com/jquery/jquery-mobile/issues/4844
8960
- https://github.com/jquery/jquery-mobile/issues/4874
8961
- */
8962
-
8963
- // TODO sort out why this._page isn't working
8964
- this.element.closest( ".ui-page" ).addClass( "ui-popup-open" );
8965
- }
8966
- this._animate({
8967
- additionalCondition: true,
8968
- transition: o.transition,
8969
- classToRemove: "",
8970
- screenClassToAdd: "in",
8971
- containerClassToAdd: "in",
8972
- applyTransition: false,
8973
- prereqs: this._prereqs
8974
- });
8975
- },
8976
-
8977
- _closePrereqScreen: function() {
8978
- this._ui.screen
8979
- .removeClass( "out" )
8980
- .addClass( "ui-screen-hidden" );
8981
- },
8888
+ _openPrereqsComplete: function() {
8889
+ this._ui.container.addClass( "ui-popup-active" );
8890
+ this._isOpen = true;
8891
+ this._resizeScreen();
8892
+ this._ui.container.attr( "tabindex", "0" ).focus();
8893
+ this._ignoreResizeEvents();
8894
+ this._trigger( "afteropen" );
8895
+ },
8982
8896
 
8983
- _closePrereqContainer: function() {
8984
- this._ui.container
8985
- .removeClass( "reverse out" )
8986
- .addClass( "ui-popup-hidden" )
8987
- .removeAttr( "style" );
8988
- },
8897
+ _open: function( options ) {
8898
+ var o = $.extend( {}, this.options, options ),
8899
+ // TODO move blacklist to private method
8900
+ androidBlacklist = ( function() {
8901
+ var w = window,
8902
+ ua = navigator.userAgent,
8903
+ // Rendering engine is Webkit, and capture major version
8904
+ wkmatch = ua.match( /AppleWebKit\/([0-9\.]+)/ ),
8905
+ wkversion = !!wkmatch && wkmatch[ 1 ],
8906
+ androidmatch = ua.match( /Android (\d+(?:\.\d+))/ ),
8907
+ andversion = !!androidmatch && androidmatch[ 1 ],
8908
+ chromematch = ua.indexOf( "Chrome" ) > -1;
8909
+
8910
+ // Platform is Android, WebKit version is greater than 534.13 ( Android 3.2.1 ) and not Chrome.
8911
+ if( androidmatch !== null && andversion === "4.0" && wkversion && wkversion > 534.13 && !chromematch ) {
8912
+ return true;
8913
+ }
8914
+ return false;
8915
+ }());
8989
8916
 
8990
- _closePrereqsDone: function() {
8991
- var opts = this.options;
8917
+ // Count down to triggering "popupafteropen" - we have two prerequisites:
8918
+ // 1. The popup window animation completes (container())
8919
+ // 2. The screen opacity animation completes (screen())
8920
+ this._createPrereqs(
8921
+ $.noop,
8922
+ $.noop,
8923
+ $.proxy( this, "_openPrereqsComplete" ) );
8992
8924
 
8993
- this._ui.container.removeAttr( "tabindex" );
8925
+ this._currentTransition = o.transition;
8926
+ this._applyTransition( o.transition );
8994
8927
 
8995
- // remove the global mutex for popups
8996
- $.mobile.popup.active = undefined;
8928
+ if ( !this.options.theme ) {
8929
+ this._setTheme( this._page.jqmData( "theme" ) || $.mobile.getInheritedTheme( this._page, "c" ) );
8930
+ }
8997
8931
 
8998
- // alert users that the popup is closed
8999
- this._trigger( "afterclose" );
9000
- },
8932
+ this._ui.screen.removeClass( "ui-screen-hidden" );
8933
+ this._ui.container.removeClass( "ui-popup-hidden" );
9001
8934
 
9002
- _close: function( immediate ) {
9003
- this._ui.container.removeClass( "ui-popup-active" );
9004
- this._page.removeClass( "ui-popup-open" );
9005
-
9006
- this._isOpen = false;
9007
-
9008
- // Count down to triggering "popupafterclose" - we have two prerequisites:
9009
- // 1. The popup window reverse animation completes (container())
9010
- // 2. The screen opacity animation completes (screen())
9011
- this._createPrereqs(
9012
- $.proxy( this, "_closePrereqScreen" ),
9013
- $.proxy( this, "_closePrereqContainer" ),
9014
- $.proxy( this, "_closePrereqsDone" ) );
9015
-
9016
- this._animate( {
9017
- additionalCondition: this._ui.screen.hasClass( "in" ),
9018
- transition: ( immediate ? "none" : ( this._currentTransition ) ),
9019
- classToRemove: "in",
9020
- screenClassToAdd: "out",
9021
- containerClassToAdd: "reverse out",
9022
- applyTransition: true,
9023
- prereqs: this._prereqs
9024
- });
9025
- },
8935
+ // Give applications a chance to modify the contents of the container before it appears
8936
+ this._reposition( o );
9026
8937
 
9027
- _unenhance: function() {
9028
- // Put the element back to where the placeholder was and remove the "ui-popup" class
9029
- this._setTheme( "none" );
9030
- this.element
9031
- // Cannot directly insertAfter() - we need to detach() first, because
9032
- // insertAfter() will do nothing if the payload div was not attached
9033
- // to the DOM at the time the widget was created, and so the payload
9034
- // will remain inside the container even after we call insertAfter().
9035
- // If that happens and we remove the container a few lines below, we
9036
- // will cause an infinite recursion - #5244
9037
- .detach()
9038
- .insertAfter( this._ui.placeholder )
9039
- .removeClass( "ui-popup ui-overlay-shadow ui-corner-all" );
9040
- this._ui.screen.remove();
9041
- this._ui.container.remove();
9042
- this._ui.placeholder.remove();
9043
- },
8938
+ if ( this.options.overlayTheme && androidBlacklist ) {
8939
+ /* TODO:
8940
+ The native browser on Android 4.0.X ("Ice Cream Sandwich") suffers from an issue where the popup overlay appears to be z-indexed
8941
+ above the popup itself when certain other styles exist on the same page -- namely, any element set to `position: fixed` and certain
8942
+ types of input. These issues are reminiscent of previously uncovered bugs in older versions of Android's native browser:
8943
+ https://github.com/scottjehl/Device-Bugs/issues/3
9044
8944
 
9045
- _destroy: function() {
9046
- if ( $.mobile.popup.active === this ) {
9047
- this.element.one( "popupafterclose", $.proxy( this, "_unenhance" ) );
9048
- this.close();
9049
- } else {
9050
- this._unenhance();
9051
- }
9052
- },
8945
+ This fix closes the following bugs ( I use "closes" with reluctance, and stress that this issue should be revisited as soon as possible ):
9053
8946
 
9054
- _closePopup: function( e, data ) {
9055
- var parsedDst, toUrl, o = this.options, immediate = false;
8947
+ https://github.com/jquery/jquery-mobile/issues/4816
8948
+ https://github.com/jquery/jquery-mobile/issues/4844
8949
+ https://github.com/jquery/jquery-mobile/issues/4874
8950
+ */
9056
8951
 
9057
- // restore location on screen
9058
- window.scrollTo( 0, this._scrollTop );
8952
+ // TODO sort out why this._page isn't working
8953
+ this.element.closest( ".ui-page" ).addClass( "ui-popup-open" );
8954
+ }
8955
+ this._animate({
8956
+ additionalCondition: true,
8957
+ transition: o.transition,
8958
+ classToRemove: "",
8959
+ screenClassToAdd: "in",
8960
+ containerClassToAdd: "in",
8961
+ applyTransition: false,
8962
+ prereqs: this._prereqs
8963
+ });
8964
+ },
9059
8965
 
9060
- if ( e && e.type === "pagebeforechange" && data ) {
9061
- // Determine whether we need to rapid-close the popup, or whether we can
9062
- // take the time to run the closing transition
9063
- if ( typeof data.toPage === "string" ) {
9064
- parsedDst = data.toPage;
9065
- } else {
9066
- parsedDst = data.toPage.jqmData( "url" );
9067
- }
9068
- parsedDst = $.mobile.path.parseUrl( parsedDst );
9069
- toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;
8966
+ _closePrereqScreen: function() {
8967
+ this._ui.screen
8968
+ .removeClass( "out" )
8969
+ .addClass( "ui-screen-hidden" );
8970
+ },
9070
8971
 
9071
- if ( this._myUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ) {
9072
- // Going to a different page - close immediately
9073
- immediate = true;
9074
- } else {
9075
- e.preventDefault();
9076
- }
9077
- }
8972
+ _closePrereqContainer: function() {
8973
+ this._ui.container
8974
+ .removeClass( "reverse out" )
8975
+ .addClass( "ui-popup-hidden" )
8976
+ .removeAttr( "style" );
8977
+ },
9078
8978
 
9079
- // remove nav bindings
9080
- o.container.unbind( o.closeEvents );
9081
- // unbind click handlers added when history is disabled
9082
- this.element.undelegate( o.closeLinkSelector, o.closeLinkEvents );
8979
+ _closePrereqsDone: function() {
8980
+ var container = this._ui.container;
9083
8981
 
9084
- this._close( immediate );
9085
- },
8982
+ container.removeAttr( "tabindex" );
9086
8983
 
9087
- // any navigation event after a popup is opened should close the popup
9088
- // NOTE the pagebeforechange is bound to catch navigation events that don't
9089
- // alter the url (eg, dialogs from popups)
9090
- _bindContainerClose: function() {
9091
- this.options.container
9092
- .one( this.options.closeEvents, $.proxy( this, "_closePopup" ) );
9093
- },
8984
+ // remove the global mutex for popups
8985
+ $.mobile.popup.active = undefined;
9094
8986
 
9095
- // TODO no clear deliniation of what should be here and
9096
- // what should be in _open. Seems to be "visual" vs "history" for now
9097
- open: function( options ) {
9098
- var self = this, opts = this.options, url, hashkey, activePage, currentIsDialog, hasHash, urlHistory;
8987
+ // Blur elements inside the container, including the container
8988
+ $( ":focus", container[ 0 ] ).add( container[ 0 ] ).blur();
9099
8989
 
9100
- // make sure open is idempotent
9101
- if( $.mobile.popup.active ) {
9102
- return;
9103
- }
8990
+ // alert users that the popup is closed
8991
+ this._trigger( "afterclose" );
8992
+ },
9104
8993
 
9105
- // set the global popup mutex
9106
- $.mobile.popup.active = this;
9107
- this._scrollTop = $.mobile.window.scrollTop();
8994
+ _close: function( immediate ) {
8995
+ this._ui.container.removeClass( "ui-popup-active" );
8996
+ this._page.removeClass( "ui-popup-open" );
8997
+
8998
+ this._isOpen = false;
8999
+
9000
+ // Count down to triggering "popupafterclose" - we have two prerequisites:
9001
+ // 1. The popup window reverse animation completes (container())
9002
+ // 2. The screen opacity animation completes (screen())
9003
+ this._createPrereqs(
9004
+ $.proxy( this, "_closePrereqScreen" ),
9005
+ $.proxy( this, "_closePrereqContainer" ),
9006
+ $.proxy( this, "_closePrereqsDone" ) );
9007
+
9008
+ this._animate( {
9009
+ additionalCondition: this._ui.screen.hasClass( "in" ),
9010
+ transition: ( immediate ? "none" : ( this._currentTransition ) ),
9011
+ classToRemove: "in",
9012
+ screenClassToAdd: "out",
9013
+ containerClassToAdd: "reverse out",
9014
+ applyTransition: true,
9015
+ prereqs: this._prereqs
9016
+ });
9017
+ },
9108
9018
 
9109
- // if history alteration is disabled close on navigate events
9110
- // and leave the url as is
9111
- if( !( opts.history ) ) {
9112
- self._open( options );
9113
- self._bindContainerClose();
9019
+ _unenhance: function() {
9020
+ // Put the element back to where the placeholder was and remove the "ui-popup" class
9021
+ this._setTheme( "none" );
9022
+ this.element
9023
+ // Cannot directly insertAfter() - we need to detach() first, because
9024
+ // insertAfter() will do nothing if the payload div was not attached
9025
+ // to the DOM at the time the widget was created, and so the payload
9026
+ // will remain inside the container even after we call insertAfter().
9027
+ // If that happens and we remove the container a few lines below, we
9028
+ // will cause an infinite recursion - #5244
9029
+ .detach()
9030
+ .insertAfter( this._ui.placeholder )
9031
+ .removeClass( "ui-popup ui-overlay-shadow ui-corner-all" );
9032
+ this._ui.screen.remove();
9033
+ this._ui.container.remove();
9034
+ this._ui.placeholder.remove();
9035
+ },
9114
9036
 
9115
- // When histoy is disabled we have to grab the data-rel
9116
- // back link clicks so we can close the popup instead of
9117
- // relying on history to do it for us
9118
- self.element
9119
- .delegate( opts.closeLinkSelector, opts.closeLinkEvents, function( e ) {
9120
- self.close();
9121
- e.preventDefault();
9122
- });
9037
+ _destroy: function() {
9038
+ if ( $.mobile.popup.active === this ) {
9039
+ this.element.one( "popupafterclose", $.proxy( this, "_unenhance" ) );
9040
+ this.close();
9041
+ } else {
9042
+ this._unenhance();
9043
+ }
9044
+ },
9123
9045
 
9124
- return;
9125
- }
9046
+ _closePopup: function( e, data ) {
9047
+ var parsedDst, toUrl, o = this.options, immediate = false;
9126
9048
 
9127
- // cache some values for min/readability
9128
- urlHistory = $.mobile.urlHistory;
9129
- hashkey = $.mobile.dialogHashKey;
9130
- activePage = $.mobile.activePage;
9131
- currentIsDialog = activePage.is( ".ui-dialog" );
9132
- this._myUrl = url = urlHistory.getActive().url;
9133
- hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog && ( urlHistory.activeIndex > 0 );
9049
+ // restore location on screen
9050
+ window.scrollTo( 0, this._scrollTop );
9134
9051
 
9135
- if ( hasHash ) {
9136
- self._open( options );
9137
- self._bindContainerClose();
9138
- return;
9052
+ if ( e && e.type === "pagebeforechange" && data ) {
9053
+ // Determine whether we need to rapid-close the popup, or whether we can
9054
+ // take the time to run the closing transition
9055
+ if ( typeof data.toPage === "string" ) {
9056
+ parsedDst = data.toPage;
9057
+ } else {
9058
+ parsedDst = data.toPage.jqmData( "url" );
9139
9059
  }
9060
+ parsedDst = $.mobile.path.parseUrl( parsedDst );
9061
+ toUrl = parsedDst.pathname + parsedDst.search + parsedDst.hash;
9140
9062
 
9141
- // if the current url has no dialog hash key proceed as normal
9142
- // otherwise, if the page is a dialog simply tack on the hash key
9143
- if ( url.indexOf( hashkey ) === -1 && !currentIsDialog ){
9144
- url = url + (url.indexOf( "#" ) > -1 ? hashkey : "#" + hashkey);
9063
+ if ( this._myUrl !== $.mobile.path.makeUrlAbsolute( toUrl ) ) {
9064
+ // Going to a different page - close immediately
9065
+ immediate = true;
9145
9066
  } else {
9146
- url = $.mobile.path.parseLocation().hash + hashkey;
9067
+ e.preventDefault();
9147
9068
  }
9069
+ }
9148
9070
 
9149
- // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash
9150
- if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
9151
- url += hashkey;
9152
- }
9071
+ // remove nav bindings
9072
+ o.container.unbind( o.closeEvents );
9073
+ // unbind click handlers added when history is disabled
9074
+ this.element.undelegate( o.closeLinkSelector, o.closeLinkEvents );
9153
9075
 
9154
- // swallow the the initial navigation event, and bind for the next
9155
- $(window).one( "beforenavigate", function( e ) {
9156
- e.preventDefault();
9157
- self._open( options );
9158
- self._bindContainerClose();
9159
- });
9076
+ this._close( immediate );
9077
+ },
9160
9078
 
9161
- this.urlAltered = true;
9162
- $.mobile.navigate( url, {role: "dialog"} );
9163
- },
9079
+ // any navigation event after a popup is opened should close the popup
9080
+ // NOTE the pagebeforechange is bound to catch navigation events that don't
9081
+ // alter the url (eg, dialogs from popups)
9082
+ _bindContainerClose: function() {
9083
+ this.options.container
9084
+ .one( this.options.closeEvents, $.proxy( this, "_closePopup" ) );
9085
+ },
9164
9086
 
9165
- close: function() {
9166
- // make sure close is idempotent
9167
- if( $.mobile.popup.active !== this ) {
9168
- return;
9169
- }
9087
+ // TODO no clear deliniation of what should be here and
9088
+ // what should be in _open. Seems to be "visual" vs "history" for now
9089
+ open: function( options ) {
9090
+ var self = this, opts = this.options, url, hashkey, activePage, currentIsDialog, hasHash, urlHistory;
9170
9091
 
9171
- this._scrollTop = $.mobile.window.scrollTop();
9092
+ // make sure open is idempotent
9093
+ if( $.mobile.popup.active ) {
9094
+ return;
9095
+ }
9172
9096
 
9173
- if( this.options.history && this.urlAltered ) {
9174
- $.mobile.back();
9175
- this.urlAltered = false;
9176
- } else {
9177
- // simulate the nav bindings having fired
9178
- this._closePopup();
9179
- }
9097
+ // set the global popup mutex
9098
+ $.mobile.popup.active = this;
9099
+ this._scrollTop = $.mobile.window.scrollTop();
9100
+
9101
+ // if history alteration is disabled close on navigate events
9102
+ // and leave the url as is
9103
+ if( !( opts.history ) ) {
9104
+ self._open( options );
9105
+ self._bindContainerClose();
9106
+
9107
+ // When histoy is disabled we have to grab the data-rel
9108
+ // back link clicks so we can close the popup instead of
9109
+ // relying on history to do it for us
9110
+ self.element
9111
+ .delegate( opts.closeLinkSelector, opts.closeLinkEvents, function( e ) {
9112
+ self.close();
9113
+ e.preventDefault();
9114
+ });
9115
+
9116
+ return;
9180
9117
  }
9181
- });
9182
9118
 
9119
+ // cache some values for min/readability
9120
+ urlHistory = $.mobile.urlHistory;
9121
+ hashkey = $.mobile.dialogHashKey;
9122
+ activePage = $.mobile.activePage;
9123
+ currentIsDialog = activePage.is( ".ui-dialog" );
9124
+ this._myUrl = url = urlHistory.getActive().url;
9125
+ hasHash = ( url.indexOf( hashkey ) > -1 ) && !currentIsDialog && ( urlHistory.activeIndex > 0 );
9183
9126
 
9184
- // TODO this can be moved inside the widget
9185
- $.mobile.popup.handleLink = function( $link ) {
9186
- var closestPage = $link.closest( ":jqmData(role='page')" ),
9187
- scope = ( ( closestPage.length === 0 ) ? $( "body" ) : closestPage ),
9188
- // NOTE make sure to get only the hash, ie7 (wp7) return the absolute href
9189
- // in this case ruining the element selection
9190
- popup = $( $.mobile.path.parseUrl($link.attr( "href" )).hash, scope[0] ),
9191
- offset;
9192
-
9193
- if ( popup.data( "mobile-popup" ) ) {
9194
- offset = $link.offset();
9195
- popup.popup( "open", {
9196
- x: offset.left + $link.outerWidth() / 2,
9197
- y: offset.top + $link.outerHeight() / 2,
9198
- transition: $link.jqmData( "transition" ),
9199
- positionTo: $link.jqmData( "position-to" )
9200
- });
9127
+ if ( hasHash ) {
9128
+ self._open( options );
9129
+ self._bindContainerClose();
9130
+ return;
9201
9131
  }
9202
9132
 
9203
- //remove after delay
9204
- setTimeout( function() {
9205
- // Check if we are in a listview
9206
- var $parent = $link.parent().parent();
9207
- if ($parent.hasClass("ui-li")) {
9208
- $link = $parent.parent();
9209
- }
9210
- $link.removeClass( $.mobile.activeBtnClass );
9211
- }, 300 );
9212
- };
9133
+ // if the current url has no dialog hash key proceed as normal
9134
+ // otherwise, if the page is a dialog simply tack on the hash key
9135
+ if ( url.indexOf( hashkey ) === -1 && !currentIsDialog ){
9136
+ url = url + (url.indexOf( "#" ) > -1 ? hashkey : "#" + hashkey);
9137
+ } else {
9138
+ url = $.mobile.path.parseLocation().hash + hashkey;
9139
+ }
9213
9140
 
9214
- // TODO move inside _create
9215
- $.mobile.document.bind( "pagebeforechange", function( e, data ) {
9216
- if ( data.options.role === "popup" ) {
9217
- $.mobile.popup.handleLink( data.options.link );
9141
+ // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash
9142
+ if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
9143
+ url += hashkey;
9144
+ }
9145
+
9146
+ // swallow the the initial navigation event, and bind for the next
9147
+ $(window).one( "beforenavigate", function( e ) {
9218
9148
  e.preventDefault();
9149
+ self._open( options );
9150
+ self._bindContainerClose();
9151
+ });
9152
+
9153
+ this.urlAltered = true;
9154
+ $.mobile.navigate( url, {role: "dialog"} );
9155
+ },
9156
+
9157
+ close: function() {
9158
+ // make sure close is idempotent
9159
+ if( $.mobile.popup.active !== this ) {
9160
+ return;
9219
9161
  }
9220
- });
9221
9162
 
9222
- $.mobile.document.bind( "pagecreate create", function( e ) {
9223
- $.mobile.popup.prototype.enhanceWithin( e.target, true );
9224
- });
9163
+ this._scrollTop = $.mobile.window.scrollTop();
9164
+
9165
+ if( this.options.history && this.urlAltered ) {
9166
+ $.mobile.back();
9167
+ this.urlAltered = false;
9168
+ } else {
9169
+ // simulate the nav bindings having fired
9170
+ this._closePopup();
9171
+ }
9172
+ }
9173
+ });
9174
+
9175
+
9176
+ // TODO this can be moved inside the widget
9177
+ $.mobile.popup.handleLink = function( $link ) {
9178
+ var closestPage = $link.closest( ":jqmData(role='page')" ),
9179
+ scope = ( ( closestPage.length === 0 ) ? $( "body" ) : closestPage ),
9180
+ // NOTE make sure to get only the hash, ie7 (wp7) return the absolute href
9181
+ // in this case ruining the element selection
9182
+ popup = $( $.mobile.path.parseUrl($link.attr( "href" )).hash, scope[0] ),
9183
+ offset;
9184
+
9185
+ if ( popup.data( "mobile-popup" ) ) {
9186
+ offset = $link.offset();
9187
+ popup.popup( "open", {
9188
+ x: offset.left + $link.outerWidth() / 2,
9189
+ y: offset.top + $link.outerHeight() / 2,
9190
+ transition: $link.jqmData( "transition" ),
9191
+ positionTo: $link.jqmData( "position-to" )
9192
+ });
9193
+ }
9194
+
9195
+ //remove after delay
9196
+ setTimeout( function() {
9197
+ // Check if we are in a listview
9198
+ var $parent = $link.parent().parent();
9199
+ if ($parent.hasClass("ui-li")) {
9200
+ $link = $parent.parent();
9201
+ }
9202
+ $link.removeClass( $.mobile.activeBtnClass );
9203
+ }, 300 );
9204
+ };
9205
+
9206
+ // TODO move inside _create
9207
+ $.mobile.document.bind( "pagebeforechange", function( e, data ) {
9208
+ if ( data.options.role === "popup" ) {
9209
+ $.mobile.popup.handleLink( data.options.link );
9210
+ e.preventDefault();
9211
+ }
9212
+ });
9213
+
9214
+ $.mobile.document.bind( "pagecreate create", function( e ) {
9215
+ $.mobile.popup.prototype.enhanceWithin( e.target, true );
9216
+ });
9225
9217
 
9226
9218
  })( jQuery );
9227
9219
 
@@ -9306,7 +9298,10 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
9306
9298
  placeholder: "",
9307
9299
 
9308
9300
  build: function() {
9309
- var self = this;
9301
+ var self = this,
9302
+ escapeId = function( id ) {
9303
+ return id.replace( /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, "\\$1" );
9304
+ };
9310
9305
 
9311
9306
  // Create list from select, update state
9312
9307
  self.refresh();
@@ -9336,9 +9331,9 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
9336
9331
 
9337
9332
  self._decideFormat();
9338
9333
  if ( self.menuType === "overlay" ) {
9339
- self.button.attr( "href", "#" + self.popupID ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "popup" );
9334
+ self.button.attr( "href", "#" + escapeId( self.popupID ) ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "popup" );
9340
9335
  } else {
9341
- self.button.attr( "href", "#" + self.dialogID ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "dialog" );
9336
+ self.button.attr( "href", "#" + escapeId( self.dialogID ) ).attr( "data-" + ( $.mobile.ns || "" ) + "rel", "dialog" );
9342
9337
  }
9343
9338
  self.isOpen = true;
9344
9339
  // Do not prevent default, so the navigation may have a chance to actually open the chosen format
@@ -9745,6 +9740,9 @@ $.mobile.document.bind( "pagecreate create", function( e ) {
9745
9740
  // Remove the popup
9746
9741
  this.listbox.remove();
9747
9742
 
9743
+ // Remove the dialog
9744
+ this.menuPage.remove();
9745
+
9748
9746
  // Chain up
9749
9747
  origDestroy.apply( this, arguments );
9750
9748
  }
@@ -9874,6 +9872,25 @@ $( document ).bind( "pagecreate create", function( e ) {
9874
9872
  $( e.target )
9875
9873
  .find( "a" )
9876
9874
  .jqmEnhanceable()
9875
+ .filter( ":jqmData(rel='popup')[href][href!='']" )
9876
+ .each( function() {
9877
+ // Accessibility info for popups
9878
+ var e = this,
9879
+ href = $( this ).attr( "href" ),
9880
+ idref = href.substring( 1 );
9881
+
9882
+ e.setAttribute( "aria-haspopup", true );
9883
+ e.setAttribute( "aria-owns", idref );
9884
+ e.setAttribute( "aria-expanded", false );
9885
+ $( document )
9886
+ .on( "popupafteropen", href, function() {
9887
+ e.setAttribute( "aria-expanded", true );
9888
+ })
9889
+ .on( "popupafterclose", href, function() {
9890
+ e.setAttribute( "aria-expanded", false );
9891
+ });
9892
+ })
9893
+ .end()
9877
9894
  .not( ".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')" )
9878
9895
  .addClass( "ui-link" );
9879
9896
 
@@ -9938,7 +9955,7 @@ $( document ).bind( "pagecreate create", function( e ) {
9938
9955
  $.extend( this, {
9939
9956
  _thisPage: null
9940
9957
  });
9941
-
9958
+
9942
9959
  self._addTransitionClass();
9943
9960
  self._bindPageEvents();
9944
9961
  self._bindToggleHandlers();
@@ -10479,8 +10496,14 @@ $.widget( "mobile.panel", $.mobile.widget, {
10479
10496
  self._page.on( "click.panel" , "a", function( e ) {
10480
10497
  if ( this.href.split( "#" )[ 1 ] === self._panelID && self._panelID !== undefined ) {
10481
10498
  e.preventDefault();
10482
- var $link = $( this );
10499
+ var $link = $( this ),
10500
+ $parent;
10483
10501
  if ( ! $link.hasClass( "ui-link" ) ) {
10502
+ // Check if we are in a listview
10503
+ $parent = $link.parent().parent();
10504
+ if ( $parent.hasClass( "ui-li" ) ) {
10505
+ $link = $parent.parent();
10506
+ }
10484
10507
  $link.addClass( $.mobile.activeBtnClass );
10485
10508
  self.element.one( "panelopen panelclose", function() {
10486
10509
  $link.removeClass( $.mobile.activeBtnClass );
@@ -11097,6 +11120,10 @@ $.mobile.document.delegate( ":jqmData(role='table')", "tablecreate refresh", fun
11097
11120
  // define page container
11098
11121
  $.mobile.pageContainer = $.mobile.firstPage.parent().addClass( "ui-mobile-viewport" );
11099
11122
 
11123
+ // initialize navigation events now, after mobileinit has occurred and the page container
11124
+ // has been created but before the rest of the library is alerted to that fact
11125
+ $.mobile.navreadyDeferred.resolve();
11126
+
11100
11127
  // alert listeners that the pagecontainer has been determined for binding
11101
11128
  // to events triggered on it
11102
11129
  $window.trigger( "pagecontainercreate" );
@@ -11149,9 +11176,6 @@ $.mobile.document.delegate( ":jqmData(role='table')", "tablecreate refresh", fun
11149
11176
  }
11150
11177
  });
11151
11178
 
11152
- // initialize events now, after mobileinit has occurred
11153
- $.mobile.navreadyDeferred.resolve();
11154
-
11155
11179
  // check which scrollTop value should be used by scrolling to 1 immediately at domready
11156
11180
  // then check what the scroll top is. Android will report 0... others 1
11157
11181
  // note that this initial scroll won't hide the address bar. It's just for the check.