jquery_mobile_rails 1.4.3 → 1.4.4

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: 41288fb39a3f897327288165d9df98e09a056211
4
- data.tar.gz: 666adc5a0c798e89a09e4a94cf66166b57a686cb
3
+ metadata.gz: 8cbe6522e4a9a1c19592f34fc5ae46e52940bf12
4
+ data.tar.gz: ad30e18021b1c8f16dbaa23eddb6f04913d948c6
5
5
  SHA512:
6
- metadata.gz: faad4d3fce16352441b499348d6b735a5c74bdec6293049b169def214ba7702e2bf6ead315c882e9c39452fdb117414246f2f778b878e8e95374bb68b5267179
7
- data.tar.gz: 32fcc9568c668b4dc7efe018dd46113edc764568bb656e97882896c16749b87f99483a2477b62c8f7575a7efd059762d8ef601bef471a24ce667008350312d6f
6
+ metadata.gz: 711664dc44d04c14a969aec91d21aaa95fe75a466d4ad89810f3fb5a5ed5f04d0fbd42ed9624df9d3a8f33a9f6be588af34a1c4860695e6a22a4f8e7e366d232
7
+ data.tar.gz: 51471bf8aad1ce38df478dd9dd9175da59cec885ac499c567ca8b80b1fc3007972f95147cdc7c5c8719a671aeee68b83fecc572e68b259ba4b0be4f9e01cbd39
data/README.md CHANGED
@@ -4,7 +4,7 @@ This gem adds the jQuery Mobile files to Rails' asset pipeline.
4
4
 
5
5
  ## Gem's jQuery Mobile Version
6
6
 
7
- 1.4.3 (gem 1.4.3)
7
+ 1.4.4 (gem 1.4.4)
8
8
 
9
9
  ## Installation
10
10
 
@@ -32,7 +32,7 @@ __NOTE:__ You should probably remove Turbolinks from your `application.js` manif
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](http://view.jquerymobile.com/1.4.3/dist/demos/) for information on laying out your app and using all of the jQuery Mobile features.
35
+ Please refer to [jQuery Mobile's documentation](http://demos.jquerymobile.com/1.4.4/) for information on laying out your app and using all of the jQuery Mobile features.
36
36
 
37
37
  ## Example
38
38
 
@@ -1,3 +1,3 @@
1
1
  module JqueryMobileRails
2
- VERSION = "1.4.3"
2
+ VERSION = "1.4.4"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * jQuery Mobile 1.4.3
3
- * Git HEAD hash: b9c6473e3d90af26570e6f14e5a0307897ab385c <> Date: Tue Jul 1 2014 15:37:36 UTC
2
+ * jQuery Mobile 1.4.4
3
+ * Git HEAD hash: b4150fb1c561b614da796c210877fb25e74cf622 <> Date: Fri Sep 12 2014 16:43:26 UTC
4
4
  * http://jquerymobile.com
5
5
  *
6
6
  * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors
@@ -30,7 +30,7 @@
30
30
  $.extend( $.mobile, {
31
31
 
32
32
  // Version of the jQuery Mobile Framework
33
- version: "1.4.3",
33
+ version: "1.4.4",
34
34
 
35
35
  // Deprecated and no longer used in 1.4 remove in 1.5
36
36
  // Define the url parameter used for referencing widget-generated sub-pages.
@@ -2476,7 +2476,8 @@ if ( !$.support.boxShadow ) {
2476
2476
 
2477
2477
  //Returns true if both urls have the same domain.
2478
2478
  isSameDomain: function( absUrl1, absUrl2 ) {
2479
- return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain;
2479
+ return path.parseUrl( absUrl1 ).domain.toLowerCase() ===
2480
+ path.parseUrl( absUrl2 ).domain.toLowerCase();
2480
2481
  },
2481
2482
 
2482
2483
  //Returns true for any relative variant.
@@ -2523,19 +2524,21 @@ if ( !$.support.boxShadow ) {
2523
2524
  },
2524
2525
 
2525
2526
  convertUrlToDataUrl: function( absUrl ) {
2526
- var u = path.parseUrl( absUrl );
2527
+ var result = absUrl,
2528
+ u = path.parseUrl( absUrl );
2529
+
2527
2530
  if ( path.isEmbeddedPage( u ) ) {
2528
2531
  // For embedded pages, remove the dialog hash key as in getFilePath(),
2529
2532
  // and remove otherwise the Data Url won't match the id of the embedded Page.
2530
- return u.hash
2533
+ result = u.hash
2531
2534
  .split( dialogHashKey )[0]
2532
2535
  .replace( /^#/, "" )
2533
2536
  .replace( /\?.*$/, "" );
2534
2537
  } else if ( path.isSameDomain( u, this.documentBase ) ) {
2535
- return u.hrefNoHash.replace( this.documentBase.domain, "" ).split( dialogHashKey )[0];
2538
+ result = u.hrefNoHash.replace( this.documentBase.domain, "" ).split( dialogHashKey )[0];
2536
2539
  }
2537
2540
 
2538
- return window.decodeURIComponent(absUrl);
2541
+ return window.decodeURIComponent( result );
2539
2542
  },
2540
2543
 
2541
2544
  //get path from current hash, or from a file path
@@ -2584,7 +2587,9 @@ if ( !$.support.boxShadow ) {
2584
2587
  //could be mailto, etc
2585
2588
  isExternal: function( url ) {
2586
2589
  var u = path.parseUrl( url );
2587
- return u.protocol && u.domain !== this.documentUrl.domain ? true : false;
2590
+
2591
+ return !!( u.protocol &&
2592
+ ( u.domain.toLowerCase() !== this.documentUrl.domain.toLowerCase() ) );
2588
2593
  },
2589
2594
 
2590
2595
  hasProtocol: function( url ) {
@@ -2606,14 +2611,25 @@ if ( !$.support.boxShadow ) {
2606
2611
  },
2607
2612
 
2608
2613
  squash: function( url, resolutionUrl ) {
2609
- var href, cleanedUrl, search, stateIndex,
2614
+ var href, cleanedUrl, search, stateIndex, docUrl,
2610
2615
  isPath = this.isPath( url ),
2611
2616
  uri = this.parseUrl( url ),
2612
2617
  preservedHash = uri.hash,
2613
2618
  uiState = "";
2614
2619
 
2615
- // produce a url against which we can resole the provided path
2616
- resolutionUrl = resolutionUrl || (path.isPath(url) ? path.getLocation() : path.getDocumentUrl());
2620
+ // produce a url against which we can resolve the provided path
2621
+ if ( !resolutionUrl ) {
2622
+ if ( isPath ) {
2623
+ resolutionUrl = path.getLocation();
2624
+ } else {
2625
+ docUrl = path.getDocumentUrl( true );
2626
+ if ( path.isPath( docUrl.hash ) ) {
2627
+ resolutionUrl = path.squash( docUrl.href );
2628
+ } else {
2629
+ resolutionUrl = docUrl.href;
2630
+ }
2631
+ }
2632
+ }
2617
2633
 
2618
2634
  // If the url is anything but a simple string, remove any preceding hash
2619
2635
  // eg #foo/bar -> foo/bar
@@ -2682,11 +2698,10 @@ if ( !$.support.boxShadow ) {
2682
2698
  return ( hasHash ? "#" : "" ) + hash.replace( /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g, "\\$1" );
2683
2699
  },
2684
2700
 
2685
- // return the substring of a filepath before the sub-page key, for making
2686
- // a server request
2701
+ // return the substring of a filepath before the dialogHashKey, for making a server
2702
+ // request
2687
2703
  getFilePath: function( path ) {
2688
- var splitkey = "&" + $.mobile.subPageUrlKey;
2689
- return path && path.split( splitkey )[0].split( dialogHashKey )[0];
2704
+ return path && path.split( dialogHashKey )[0];
2690
2705
  },
2691
2706
 
2692
2707
  // check if the specified url refers to the first page in the main
@@ -4023,7 +4038,7 @@ if ( eventCaptureSupported ) {
4023
4038
  emitted = false;
4024
4039
 
4025
4040
  context.move = function( event ) {
4026
- if ( !start ) {
4041
+ if ( !start || event.isDefaultPrevented() ) {
4027
4042
  return;
4028
4043
  }
4029
4044
 
@@ -4670,9 +4685,8 @@ $.widget( "mobile.page", {
4670
4685
  return $.mobile.navigate.history;
4671
4686
  },
4672
4687
 
4673
- // TODO use _getHistory
4674
4688
  _getActiveHistory: function() {
4675
- return $.mobile.navigate.history.getActive();
4689
+ return this._getHistory().getActive();
4676
4690
  },
4677
4691
 
4678
4692
  // TODO the document base should be determined at creation
@@ -4732,28 +4746,26 @@ $.widget( "mobile.page", {
4732
4746
  //
4733
4747
  // TODO move check to history object or path object?
4734
4748
  to = !$.mobile.path.isPath( to ) ? ( $.mobile.path.makeUrlAbsolute( "#" + to, this._getDocumentBase() ) ) : to;
4735
-
4736
- // If we're about to go to an initial URL that contains a
4737
- // reference to a non-existent internal page, go to the first
4738
- // page instead. We know that the initial hash refers to a
4739
- // non-existent page, because the initial hash did not end
4740
- // up in the initial history entry
4741
- // TODO move check to history object?
4742
- if ( to === $.mobile.path.makeUrlAbsolute( "#" + history.initialDst, this._getDocumentBase() ) &&
4743
- history.stack.length &&
4744
- history.stack[0].url !== history.initialDst.replace( $.mobile.dialogHashKey, "" ) ) {
4745
- to = this._getInitialContent();
4746
- }
4747
4749
  }
4748
4750
  return to || this._getInitialContent();
4749
4751
  },
4750
4752
 
4753
+ _transitionFromHistory: function( direction, defaultTransition ) {
4754
+ var history = this._getHistory(),
4755
+ entry = ( direction === "back" ? history.getLast() : history.getActive() );
4756
+
4757
+ return ( entry && entry.transition ) || defaultTransition;
4758
+ },
4759
+
4751
4760
  _handleDialog: function( changePageOptions, data ) {
4752
4761
  var to, active, activeContent = this.getActivePage();
4753
4762
 
4754
4763
  // If current active page is not a dialog skip the dialog and continue
4755
4764
  // in the same direction
4756
- if ( activeContent && !activeContent.hasClass( "ui-dialog" ) ) {
4765
+ // Note: The dialog widget is deprecated as of 1.4.0 and will be removed in 1.5.0.
4766
+ // Thus, as of 1.5.0 activeContent.data( "mobile-dialog" ) will always evaluate to
4767
+ // falsy, so the second condition in the if-statement below can be removed altogether.
4768
+ if ( activeContent && !activeContent.data( "mobile-dialog" ) ) {
4757
4769
  // determine if we're heading forward or backward and continue
4758
4770
  // accordingly past the current dialog
4759
4771
  if ( data.direction === "back" ) {
@@ -4774,7 +4786,9 @@ $.widget( "mobile.page", {
4774
4786
  // as most of this is lost by the domCache cleaning
4775
4787
  $.extend( changePageOptions, {
4776
4788
  role: active.role,
4777
- transition: active.transition,
4789
+ transition: this._transitionFromHistory(
4790
+ data.direction,
4791
+ changePageOptions.transition ),
4778
4792
  reverse: data.direction === "back"
4779
4793
  });
4780
4794
  }
@@ -4789,7 +4803,8 @@ $.widget( "mobile.page", {
4789
4803
 
4790
4804
  // transition is false if it's the first page, undefined
4791
4805
  // otherwise (and may be overridden by default)
4792
- transition = history.stack.length === 0 ? "none" : undefined,
4806
+ transition = history.stack.length === 0 ? "none" :
4807
+ this._transitionFromHistory( data.direction ),
4793
4808
 
4794
4809
  // default options for the changPage calls made after examining
4795
4810
  // the current state of the page and the hash, NOTE that the
@@ -4801,7 +4816,7 @@ $.widget( "mobile.page", {
4801
4816
  };
4802
4817
 
4803
4818
  $.extend( changePageOptions, data, {
4804
- transition: ( history.getLast() || {} ).transition || transition
4819
+ transition: transition
4805
4820
  });
4806
4821
 
4807
4822
  // TODO move to _handleDestination ?
@@ -4809,8 +4824,7 @@ $.widget( "mobile.page", {
4809
4824
  // key, and the initial destination isn't equal to the current target
4810
4825
  // page, use the special dialog handling
4811
4826
  if ( history.activeIndex > 0 &&
4812
- to.indexOf( $.mobile.dialogHashKey ) > -1 &&
4813
- history.initialDst !== to ) {
4827
+ to.indexOf( $.mobile.dialogHashKey ) > -1 ) {
4814
4828
 
4815
4829
  to = this._handleDialog( changePageOptions, data );
4816
4830
 
@@ -4861,7 +4875,8 @@ $.widget( "mobile.page", {
4861
4875
  // NOTE do _not_ use the :jqmData pseudo selector because parenthesis
4862
4876
  // are a valid url char and it breaks on the first occurence
4863
4877
  page = this.element
4864
- .children( "[data-" + this._getNs() +"url='" + dataUrl + "']" );
4878
+ .children( "[data-" + this._getNs() +
4879
+ "url='" + $.mobile.path.hashToSelector( dataUrl ) + "']" );
4865
4880
 
4866
4881
  // If we failed to find the page, check to see if the url is a
4867
4882
  // reference to an embedded page. If so, it may have been dynamically
@@ -4946,7 +4961,7 @@ $.widget( "mobile.page", {
4946
4961
  // TODO tagging a page with external to make sure that embedded pages aren't
4947
4962
  // removed by the various page handling code is bad. Having page handling code
4948
4963
  // in many places is bad. Solutions post 1.0
4949
- page.attr( "data-" + this._getNs() + "url", $.mobile.path.convertUrlToDataUrl(fileUrl) )
4964
+ page.attr( "data-" + this._getNs() + "url", this._createDataUrl( fileUrl ) )
4950
4965
  .attr( "data-" + this._getNs() + "external-page", true );
4951
4966
 
4952
4967
  return page;
@@ -4995,8 +5010,7 @@ $.widget( "mobile.page", {
4995
5010
  // or require ordering such that other bits are sprinkled in between parts that
4996
5011
  // could be abstracted out as a group
4997
5012
  _loadSuccess: function( absUrl, triggerData, settings, deferred ) {
4998
- var fileUrl = this._createFileUrl( absUrl ),
4999
- dataUrl = this._createDataUrl( absUrl );
5013
+ var fileUrl = this._createFileUrl( absUrl );
5000
5014
 
5001
5015
  return $.proxy(function( html, textStatus, xhr ) {
5002
5016
  //pre-parse html to check for a data-url,
@@ -5016,6 +5030,11 @@ $.widget( "mobile.page", {
5016
5030
  dataUrlRegex.test( RegExp.$1 ) &&
5017
5031
  RegExp.$1 ) {
5018
5032
  fileUrl = $.mobile.path.getFilePath( $("<div>" + RegExp.$1 + "</div>").text() );
5033
+
5034
+ // We specify that, if a data-url attribute is given on the page div, its value
5035
+ // must be given non-URL-encoded. However, in this part of the code, fileUrl is
5036
+ // assumed to be URL-encoded, so we URL-encode the retrieved value here
5037
+ fileUrl = this.window[ 0 ].encodeURIComponent( fileUrl );
5019
5038
  }
5020
5039
 
5021
5040
  //dont update the base tag if we are prefetching
@@ -5042,7 +5061,7 @@ $.widget( "mobile.page", {
5042
5061
  // Note that it is the responsibility of the listener/handler
5043
5062
  // that called preventDefault(), to resolve/reject the
5044
5063
  // deferred object within the triggerData.
5045
- if ( this._triggerWithDeprecated( "load" ).event.isDefaultPrevented() ) {
5064
+ if ( this._triggerWithDeprecated( "load", triggerData ).event.isDefaultPrevented() ) {
5046
5065
  return;
5047
5066
  }
5048
5067
 
@@ -5053,13 +5072,6 @@ $.widget( "mobile.page", {
5053
5072
 
5054
5073
  this._include( content, settings );
5055
5074
 
5056
- // Enhancing the content may result in new dialogs/sub content being inserted
5057
- // into the DOM. If the original absUrl refers to a sub-content, that is the
5058
- // real content we are interested in.
5059
- if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) {
5060
- content = this.element.children( "[data-" + this._getNs() +"url='" + dataUrl + "']" );
5061
- }
5062
-
5063
5075
  // Remove loading message.
5064
5076
  if ( settings.showLoadMsg ) {
5065
5077
  this._hideLoading();
@@ -5587,12 +5599,6 @@ $.widget( "mobile.page", {
5587
5599
  } else {
5588
5600
  url += "#" + $.mobile.dialogHashKey;
5589
5601
  }
5590
-
5591
- // tack on another dialogHashKey if this is the same as the initial hash
5592
- // this makes sure that a history entry is created for this dialog
5593
- if ( $.mobile.navigate.history.activeIndex === 0 && url === $.mobile.navigate.history.initialDst ) {
5594
- url += $.mobile.dialogHashKey;
5595
- }
5596
5602
  }
5597
5603
 
5598
5604
  // if title element wasn't found, try the page div data attr too
@@ -5635,7 +5641,7 @@ $.widget( "mobile.page", {
5635
5641
  };
5636
5642
 
5637
5643
  if ( settings.changeHash !== false && $.mobile.hashListeningEnabled ) {
5638
- $.mobile.navigate( url, params, true);
5644
+ $.mobile.navigate( this.window[ 0 ].encodeURI( url ), params, true);
5639
5645
  } else if ( toPage[ 0 ] !== $.mobile.firstPage[ 0 ] ) {
5640
5646
  $.mobile.navigate.history.add( url, params );
5641
5647
  }
@@ -6048,9 +6054,10 @@ $.widget( "mobile.page", {
6048
6054
  // lists and select dialogs, just write a hash in the link they
6049
6055
  // create. This means the actual URL path is based on whatever
6050
6056
  // the current value of the base tag is at the time this code
6051
- // is called. For now we are just assuming that any url with a
6052
- // hash in it is an application page reference.
6053
- if ( href.search( "#" ) !== -1 ) {
6057
+ // is called.
6058
+ if ( href.search( "#" ) !== -1 &&
6059
+ !( $.mobile.path.isExternal( href ) && $.mobile.path.isAbsoluteUrl( href ) ) ) {
6060
+
6054
6061
  href = href.replace( /[^#]*#/, "" );
6055
6062
  if ( !href ) {
6056
6063
  //link was an empty hash meant purely
@@ -6831,9 +6838,9 @@ $.widget( "mobile.collapsible", {
6831
6838
  this._renderedOptions = this._getOptions( this.options );
6832
6839
 
6833
6840
  if ( this.options.enhanced ) {
6834
- ui.heading = $( ".ui-collapsible-heading", this.element[ 0 ] );
6841
+ ui.heading = this.element.children( ".ui-collapsible-heading" );
6835
6842
  ui.content = ui.heading.next();
6836
- ui.anchor = $( "a", ui.heading[ 0 ] ).first();
6843
+ ui.anchor = ui.heading.children();
6837
6844
  ui.status = ui.anchor.children( ".ui-collapsible-heading-status" );
6838
6845
  } else {
6839
6846
  this._enhance( elem, ui );
@@ -7367,7 +7374,7 @@ $.widget( "mobile.navbar", {
7367
7374
  _create: function() {
7368
7375
 
7369
7376
  var $navbar = this.element,
7370
- $navbtns = $navbar.find( "a" ),
7377
+ $navbtns = $navbar.find( "a, button" ),
7371
7378
  iconpos = $navbtns.filter( ":jqmData(icon)" ).length ? this.options.iconpos : undefined;
7372
7379
 
7373
7380
  $navbar.addClass( "ui-navbar" )
@@ -8023,14 +8030,13 @@ $.widget( "mobile.checkboxradio", $.extend( {
8023
8030
  },
8024
8031
 
8025
8032
  refresh: function() {
8026
- var hasIcon = this._hasIcon(),
8027
- isChecked = this.element[ 0 ].checked,
8033
+ var isChecked = this.element[ 0 ].checked,
8028
8034
  active = $.mobile.activeBtnClass,
8029
8035
  iconposClass = "ui-btn-icon-" + this.options.iconpos,
8030
8036
  addClasses = [],
8031
8037
  removeClasses = [];
8032
8038
 
8033
- if ( hasIcon ) {
8039
+ if ( this._hasIcon() ) {
8034
8040
  removeClasses.push( active );
8035
8041
  addClasses.push( iconposClass );
8036
8042
  } else {
@@ -8046,6 +8052,8 @@ $.widget( "mobile.checkboxradio", $.extend( {
8046
8052
  removeClasses.push( this.checkedClass );
8047
8053
  }
8048
8054
 
8055
+ this.widget().toggleClass( "ui-state-disabled", this.element.prop( "disabled" ) );
8056
+
8049
8057
  this.label
8050
8058
  .addClass( addClasses.join( " " ) )
8051
8059
  .removeClass( removeClasses.join( " " ) );
@@ -8161,8 +8169,8 @@ $.widget( "mobile.button", {
8161
8169
  },
8162
8170
 
8163
8171
  _destroy: function() {
8164
- this.element.insertBefore( this.button );
8165
- this.button.remove();
8172
+ this.element.insertBefore( this.wrapper );
8173
+ this.wrapper.remove();
8166
8174
  },
8167
8175
 
8168
8176
  _getIconClasses: function( options ) {
@@ -8959,7 +8967,7 @@ $.widget( "mobile.slider", $.extend( {
8959
8967
 
8960
8968
  // update control"s value
8961
8969
  if ( isInput ) {
8962
- valueChanged = control.val() !== newval;
8970
+ valueChanged = parseFloat( control.val() ) !== newval;
8963
8971
  control.val( newval );
8964
8972
  } else {
8965
8973
  valueChanged = control[ 0 ].selectedIndex !== newval;
@@ -9030,6 +9038,8 @@ $.widget( "mobile.slider", $.extend( {
9030
9038
  this.slider
9031
9039
  .toggleClass( "ui-state-disabled", value )
9032
9040
  .attr( "aria-disabled", value );
9041
+
9042
+ this.element.toggleClass( "ui-state-disabled", value );
9033
9043
  }
9034
9044
 
9035
9045
  }, $.mobile.behaviors.formReset ) );
@@ -9453,6 +9463,7 @@ $.widget( "mobile.flipswitch", $.extend({
9453
9463
  //if the first handle is dragged send the event to the first slider
9454
9464
  $.data( this._inputFirst.get(0), "mobile-slider" ).dragging = true;
9455
9465
  $.data( this._inputFirst.get(0), "mobile-slider" ).refresh( event );
9466
+ $.data( this._inputFirst.get(0), "mobile-slider" )._trigger( "start" );
9456
9467
  return false;
9457
9468
  },
9458
9469
 
@@ -9505,6 +9516,11 @@ $.widget( "mobile.flipswitch", $.extend({
9505
9516
  if ( options.highlight !== undefined ) {
9506
9517
  this._setHighlight( options.highlight );
9507
9518
  }
9519
+
9520
+ if ( options.disabled !== undefined ) {
9521
+ this._setDisabled( options.disabled );
9522
+ }
9523
+
9508
9524
  this._super( options );
9509
9525
  this.refresh();
9510
9526
  },
@@ -9611,6 +9627,11 @@ $.widget( "mobile.flipswitch", $.extend({
9611
9627
  this._inputLast.slider( "option", "highlight", value );
9612
9628
  },
9613
9629
 
9630
+ _setDisabled: function( value ) {
9631
+ this._inputFirst.prop( "disabled", value );
9632
+ this._inputLast.prop( "disabled", value );
9633
+ },
9634
+
9614
9635
  _destroy: function() {
9615
9636
  this._label.prependTo( this.element );
9616
9637
  this.element.removeClass( "ui-rangeslider ui-mini" );
@@ -9635,16 +9656,21 @@ $.widget( "mobile.flipswitch", $.extend({
9635
9656
  _create: function() {
9636
9657
  this._super();
9637
9658
 
9638
- if ( !!this.options.clearBtn || this.isSearch ) {
9659
+ if ( this.isSearch ) {
9660
+ this.options.clearBtn = true;
9661
+ }
9662
+
9663
+ if ( !!this.options.clearBtn && this.inputNeedsWrap ) {
9639
9664
  this._addClearBtn();
9640
9665
  }
9641
9666
  },
9642
9667
 
9643
9668
  clearButton: function() {
9644
-
9645
- return $( "<a href='#' class='ui-input-clear ui-btn ui-icon-delete ui-btn-icon-notext ui-corner-all" +
9646
- "' title='" + this.options.clearBtnText + "'>" + this.options.clearBtnText + "</a>" );
9647
-
9669
+ return $( "<a href='#' tabindex='-1' aria-hidden='true' " +
9670
+ "class='ui-input-clear ui-btn ui-icon-delete ui-btn-icon-notext ui-corner-all'>" +
9671
+ "</a>" )
9672
+ .attr( "title", this.options.clearBtnText )
9673
+ .text( this.options.clearBtnText );
9648
9674
  },
9649
9675
 
9650
9676
  _clearBtnClick: function( event ) {
@@ -9737,7 +9763,9 @@ $.widget( "mobile.flipswitch", $.extend({
9737
9763
 
9738
9764
  _destroy: function() {
9739
9765
  this._super();
9740
- this._destroyClear();
9766
+ if ( this.options.clearBtn ) {
9767
+ this._destroyClear();
9768
+ }
9741
9769
  }
9742
9770
 
9743
9771
  });
@@ -9803,7 +9831,7 @@ $.widget( "mobile.flipswitch", $.extend({
9803
9831
  }, this ),
9804
9832
  "transition" );
9805
9833
  }
9806
- this._timeout();
9834
+ this._prepareHeightUpdate();
9807
9835
  }
9808
9836
  },
9809
9837
 
@@ -10496,7 +10524,9 @@ $.widget( "mobile.popup", {
10496
10524
  target = $( targetElement );
10497
10525
  if ( 0 === target.parents().filter( ui.container[ 0 ] ).length ) {
10498
10526
  $( this.document[ 0 ].activeElement ).one( "focus", function(/* theEvent */) {
10499
- target.blur();
10527
+ if ( targetElement.nodeName.toLowerCase() !== "body" ) {
10528
+ target.blur();
10529
+ }
10500
10530
  });
10501
10531
  ui.focusElement.focus();
10502
10532
  theEvent.preventDefault();
@@ -11100,11 +11130,6 @@ $.widget( "mobile.popup", {
11100
11130
  url = $.mobile.path.parseLocation().hash + hashkey;
11101
11131
  }
11102
11132
 
11103
- // Tack on an extra hashkey if this is the first page and we've just reconstructed the initial hash
11104
- if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
11105
- url += hashkey;
11106
- }
11107
-
11108
11133
  // swallow the the initial navigation event, and bind for the next
11109
11134
  this.window.one( "beforenavigate", function( theEvent ) {
11110
11135
  theEvent.preventDefault();
@@ -11618,10 +11643,15 @@ $.widget( "mobile.selectmenu", $.mobile.selectmenu, {
11618
11643
  }
11619
11644
 
11620
11645
  parent = option.parentNode;
11621
- text = $option.getEncodedText();
11622
- anchor = document.createElement( "a" );
11623
11646
  classes = [];
11624
11647
 
11648
+ // Although using .text() here raises the risk that, when we later paste this into the
11649
+ // list item we end up pasting possibly malicious things like <script> tags, that risk
11650
+ // only arises if we do something like $( "<li><a href='#'>" + text + "</a></li>" ). We
11651
+ // don't do that. We do document.createTextNode( text ) instead, which guarantees that
11652
+ // whatever we paste in will end up as text, with characters like <, > and & escaped.
11653
+ text = $option.text();
11654
+ anchor = document.createElement( "a" );
11625
11655
  anchor.setAttribute( "href", "#" );
11626
11656
  anchor.appendChild( document.createTextNode( text ) );
11627
11657
 
@@ -11992,7 +12022,7 @@ $.fn.buttonMarkup.defaults = {
11992
12022
  };
11993
12023
 
11994
12024
  $.extend( $.fn.buttonMarkup, {
11995
- initSelector: "a:jqmData(role='button'), .ui-bar > a, .ui-bar > :jqmData(role='controlgroup') > a, button"
12025
+ initSelector: "a:jqmData(role='button'), .ui-bar > a, .ui-bar > :jqmData(role='controlgroup') > a, button:not(:jqmData(role='navbar') button)"
11996
12026
  });
11997
12027
 
11998
12028
  })( jQuery );
@@ -12300,7 +12330,7 @@ $.widget( "mobile.controlgroup", $.extend( {
12300
12330
 
12301
12331
  // Skip back button creation if one is already present
12302
12332
  if ( !backButton.attached ) {
12303
- backButton.element = ( backButton.element ||
12333
+ this.backButton = backButton.element = ( backButton.element ||
12304
12334
  $( "<a role='button' href='javascript:void(0);' " +
12305
12335
  "class='ui-btn ui-corner-all ui-shadow ui-btn-left " +
12306
12336
  ( theme ? "ui-btn-" + theme + " " : "" ) +
@@ -12325,6 +12355,27 @@ $.widget( "mobile.controlgroup", $.extend( {
12325
12355
  "role": "heading",
12326
12356
  "aria-level": "1"
12327
12357
  });
12358
+ },
12359
+ _destroy: function() {
12360
+ var currentTheme;
12361
+
12362
+ this.element.children( "h1, h2, h3, h4, h5, h6" )
12363
+ .removeClass( "ui-title" )
12364
+ .removeAttr( "role" )
12365
+ .removeAttr( "aria-level" );
12366
+
12367
+ if ( this.role === "header" ) {
12368
+ this.element.children( "a, button" )
12369
+ .removeClass( "ui-btn-left ui-btn-right ui-btn ui-shadow ui-corner-all" );
12370
+ if ( this.backButton) {
12371
+ this.backButton.remove();
12372
+ }
12373
+ }
12374
+
12375
+ currentTheme = this.options.theme ? this.options.theme : "inherit";
12376
+ this.element.removeClass( "ui-bar-" + currentTheme );
12377
+
12378
+ this.element.removeClass( "ui-" + this.role ).removeAttr( "role" );
12328
12379
  }
12329
12380
  });
12330
12381
 
@@ -12358,6 +12409,7 @@ $.widget( "mobile.controlgroup", $.extend( {
12358
12409
 
12359
12410
  _create: function() {
12360
12411
  this._super();
12412
+ this.pagecontainer = $( ":mobile-pagecontainer" );
12361
12413
  if ( this.options.position === "fixed" && !this.options.supportBlacklist() ) {
12362
12414
  this._makeFixed();
12363
12415
  }
@@ -12600,12 +12652,30 @@ $.widget( "mobile.controlgroup", $.extend( {
12600
12652
  },
12601
12653
 
12602
12654
  _destroy: function() {
12603
- var $el = this.element,
12604
- header = $el.hasClass( "ui-header" );
12655
+ var pageClasses, toolbarClasses, hasFixed, header, hasFullscreen,
12656
+ page = this.pagecontainer.pagecontainer( "getActivePage" );
12605
12657
 
12606
- $el.closest( ".ui-page" ).css( "padding-" + ( header ? "top" : "bottom" ), "" );
12607
- $el.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
12608
- $el.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
12658
+ this._super();
12659
+ if ( this.options.position === "fixed" ) {
12660
+ hasFixed = $( "body>.ui-" + this.role + "-fixed" )
12661
+ .add( page.find( ".ui-" + this.options.role + "-fixed" ) )
12662
+ .not( this.element ).length > 0;
12663
+ hasFullscreen = $( "body>.ui-" + this.role + "-fixed" )
12664
+ .add( page.find( ".ui-" + this.options.role + "-fullscreen" ) )
12665
+ .not( this.element ).length > 0;
12666
+ toolbarClasses = "ui-header-fixed ui-footer-fixed ui-header-fullscreen in out" +
12667
+ " ui-footer-fullscreen fade slidedown slideup ui-fixed-hidden";
12668
+ this.element.removeClass( toolbarClasses );
12669
+ if ( !hasFullscreen ) {
12670
+ pageClasses = "ui-page-" + this.role + "-fullscreen";
12671
+ }
12672
+ if ( !hasFixed ) {
12673
+ header = this.role === "header";
12674
+ pageClasses += " ui-page-" + this.role + "-fixed";
12675
+ page.css( "padding-" + ( header ? "top" : "bottom" ), "" );
12676
+ }
12677
+ page.removeClass( pageClasses );
12678
+ }
12609
12679
  }
12610
12680
 
12611
12681
  });
@@ -13808,6 +13878,9 @@ $.widget( "mobile.table", $.mobile.table, {
13808
13878
  },
13809
13879
 
13810
13880
  _addLabels: function( cells, label, contents ) {
13881
+ if ( contents.length === 1 && contents[ 0 ].nodeName.toLowerCase() === "abbr" ) {
13882
+ contents = contents.eq( 0 ).attr( "title" );
13883
+ }
13811
13884
  // .not fixes #6006
13812
13885
  cells
13813
13886
  .not( ":has(b." + label + ")" )
@@ -14188,6 +14261,24 @@ $.widget( "mobile.filterable", $.mobile.filterable, {
14188
14261
  return ret;
14189
14262
  },
14190
14263
 
14264
+ // The listview implementation accompanying this filterable backcompat layer will call
14265
+ // filterable.refresh() after it's done refreshing the listview to make sure the filterable
14266
+ // filters out any new items added. However, when the listview refresh has been initiated by
14267
+ // the filterable itself, then such filtering has already taken place, and calling the
14268
+ // filterable's refresh() method will cause an infinite recursion. We stop this by setting a
14269
+ // flag that will cause the filterable's refresh() method to short-circuit.
14270
+ _refreshChildWidget: function() {
14271
+ this._refreshingChildWidget = true;
14272
+ this._superApply( arguments );
14273
+ this._refreshingChildWidget = false;
14274
+ },
14275
+
14276
+ refresh: function() {
14277
+ if ( !this._refreshingChildWidget ) {
14278
+ this._superApply( arguments );
14279
+ }
14280
+ },
14281
+
14191
14282
  _destroy: function() {
14192
14283
  if ( this._isSearchInternal() ) {
14193
14284
  this._search.remove();
@@ -14235,29 +14326,18 @@ $.widget( "mobile.listview", $.mobile.listview, {
14235
14326
  return this._super();
14236
14327
  },
14237
14328
 
14238
- _afterListviewRefresh: function() {
14239
- var filterable = this.element.data( "mobile-filterable" );
14240
-
14241
- if ( this.options.filter === true && filterable ) {
14242
- this._preventRefreshLoop = true;
14243
- filterable.refresh();
14244
- }
14245
- },
14246
-
14247
- // Eliminate infinite recursion caused by the fact that we call filterable.refresh() from
14248
- // _afterListviewRefresh() above, which, in turn, calls _refreshChildWidget(), which, in
14249
- // turn, calls listview refresh(), which would, in turn, calls _afterListviewRefresh()
14250
- // above, if we wouldn't prevent that right here.
14251
14329
  refresh: function() {
14252
- var returnValue;
14330
+ var filterable;
14253
14331
 
14254
- if ( !this._preventRefreshLoop ) {
14255
- returnValue = this._superApply( arguments );
14256
- }
14332
+ this._superApply( arguments );
14257
14333
 
14258
- this._preventRefreshLoop = false;
14334
+ if ( this.options.filter === true ) {
14335
+ filterable = this.element.data( "mobile-filterable" );
14259
14336
 
14260
- return returnValue;
14337
+ if ( filterable ) {
14338
+ filterable.refresh();
14339
+ }
14340
+ }
14261
14341
  }
14262
14342
  });
14263
14343
 
@@ -15196,7 +15276,7 @@ $.widget( "ui.tabs", {
15196
15276
  $pages = $( ":jqmData(role='page'), :jqmData(role='dialog')" ),
15197
15277
  hash = path.stripHash( path.stripQueryParams(path.parseLocation().hash) ),
15198
15278
  theLocation = $.mobile.path.parseLocation(),
15199
- hashPage = document.getElementById( hash );
15279
+ hashPage = hash ? document.getElementById( hash ) : undefined;
15200
15280
 
15201
15281
  // if no pages are found, create one with body's inner html
15202
15282
  if ( !$pages.length ) {
@@ -15210,7 +15290,7 @@ $.widget( "ui.tabs", {
15210
15290
  // unless the data url is already set set it to the pathname
15211
15291
  if ( !$this[ 0 ].getAttribute( "data-" + $.mobile.ns + "url" ) ) {
15212
15292
  $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) ||
15213
- theLocation.pathname + theLocation.search );
15293
+ path.convertUrlToDataUrl( theLocation.pathname + theLocation.search ) );
15214
15294
  }
15215
15295
  });
15216
15296
 
@@ -15247,11 +15327,6 @@ $.widget( "ui.tabs", {
15247
15327
  $.mobile.path.isPath( hash ) ||
15248
15328
  hash === $.mobile.dialogHashKey ) ) ) {
15249
15329
 
15250
- // Store the initial destination
15251
- if ( $.mobile.path.isHashValid( location.hash ) ) {
15252
- $.mobile.navigate.history.initialDst = hash.replace( "#", "" );
15253
- }
15254
-
15255
15330
  // make sure to set initial popstate state if it exists
15256
15331
  // so that navigation back to the initial page works properly
15257
15332
  if ( $.event.special.navigate.isPushStateEnabled() ) {