jquery_mobile_rails 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.