jquery_mobile_rails 1.1.0 → 1.1.1

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.
Files changed (39) hide show
  1. data/README.rdoc +1 -1
  2. data/lib/jquery_mobile_rails/version.rb +1 -1
  3. data/vendor/assets/images/jquery-mobile/ajax-loader.gif +0 -0
  4. data/vendor/assets/images/jquery-mobile/ajax-loader.png +0 -0
  5. data/vendor/assets/images/jquery-mobile/icons-18-black.png +0 -0
  6. data/vendor/assets/images/jquery-mobile/icons-18-white.png +0 -0
  7. data/vendor/assets/images/jquery-mobile/icons-36-black.png +0 -0
  8. data/vendor/assets/images/jquery-mobile/icons-36-white.png +0 -0
  9. data/vendor/assets/javascripts/jquery.mobile.js +366 -227
  10. data/vendor/assets/javascripts/jquery.mobile.min.js +180 -176
  11. data/vendor/assets/stylesheets/jquery.mobile.css.scss +303 -216
  12. data/vendor/assets/stylesheets/jquery.mobile.min.css.scss +2 -2
  13. data/vendor/assets/stylesheets/jquery.mobile.structure.css +175 -124
  14. data/vendor/assets/stylesheets/jquery.mobile.structure.min.css +2 -2
  15. data/vendor/assets/stylesheets/jquery.mobile.theme.css.scss +1203 -0
  16. data/vendor/assets/stylesheets/jquery.mobile.theme.min.css.scss +2 -0
  17. metadata +22 -53
  18. data/test/dummy/db/development.sqlite3 +0 -0
  19. data/test/dummy/log/development.log +0 -470
  20. data/test/dummy/tmp/cache/assets/C8E/8B0/sprockets%2F52156458f704751589bb6d29ec84a52e +0 -0
  21. data/test/dummy/tmp/cache/assets/CB1/D10/sprockets%2F5827a2184097ddeeb4158ef641f00085 +0 -0
  22. data/test/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  23. data/test/dummy/tmp/cache/assets/CDE/910/sprockets%2F8af59305547610b5be0e9e038bb58f21 +0 -0
  24. data/test/dummy/tmp/cache/assets/D20/330/sprockets%2Fe76bd3b1395773eefc19cb17743e5846 +0 -0
  25. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  26. data/test/dummy/tmp/cache/assets/D38/BD0/sprockets%2Ffe52d2d1bcf248d028108d0c513e6d79 +0 -0
  27. data/test/dummy/tmp/cache/assets/D40/4D0/sprockets%2F20f705b59cf5abf8a0a7938b887b11f1 +0 -0
  28. data/test/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  29. data/test/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  30. data/test/dummy/tmp/cache/assets/D5F/7B0/sprockets%2F1792255ae34105be22fabed9ba5e4d15 +0 -0
  31. data/test/dummy/tmp/cache/assets/D6C/7A0/sprockets%2Ff25aa7e262f1ff93154f29dc914afc20 +0 -0
  32. data/test/dummy/tmp/cache/assets/D9B/360/sprockets%2Fbb7052d2ba9883c28d4db137e1fb9b6c +0 -0
  33. data/test/dummy/tmp/cache/assets/DB7/390/sprockets%2Ff800b3c4a4a306067fea026fc7b2bbde +0 -0
  34. data/test/dummy/tmp/cache/assets/DDC/0B0/sprockets%2F63d8cfc5e184b372b7ec77cca69d4db7 +0 -0
  35. data/test/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  36. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  37. data/test/dummy/tmp/cache/assets/E08/030/sprockets%2F21255d26ce8eda2e2e0e69dfbea8cf99 +0 -0
  38. data/test/dummy/tmp/cache/assets/E1F/BB0/sprockets%2F222d4a2c95dbe3c58acdb1df2a04bff9 +0 -0
  39. data/test/dummy/tmp/cache/assets/E9D/620/sprockets%2Fcbde7d2f6466ed4a7dddefc773f63eae +0 -0
@@ -4,7 +4,7 @@ This gem adds the JQuery Mobile files to the rails assets pipeline.
4
4
 
5
5
  ==== Gem's JQuery Mobile Version
6
6
 
7
- 1.1.0
7
+ 1.1.1
8
8
 
9
9
  === Instalation
10
10
 
@@ -1,3 +1,3 @@
1
1
  module JqueryMobileRails
2
- VERSION = "1.1.0"
2
+ VERSION = "1.1.1"
3
3
  end
@@ -1,8 +1,8 @@
1
1
  /*
2
- * jQuery Mobile Framework 1.1.0 db342b1f315c282692791aa870455901fdb46a55
2
+ * jQuery Mobile Framework 1.1.1 1981b3f5ec22675ae47df8f0bdf9622e7780e90e
3
3
  * http://jquerymobile.com
4
4
  *
5
- * Copyright 2011 (c) jQuery Project
5
+ * Copyright 2012 jQuery Foundation and other contributors
6
6
  * Dual licensed under the MIT or GPL Version 2 licenses.
7
7
  * http://jquery.org/license
8
8
  *
@@ -18,7 +18,7 @@
18
18
  // Browser globals
19
19
  factory( root.jQuery, root, doc );
20
20
  }
21
- }( this, document, function ( $, window, document, undefined ) {
21
+ }( this, document, function ( jQuery, window, document, undefined ) {
22
22
 
23
23
 
24
24
  // This plugin is an experiment for abstracting away the touch and mouse
@@ -1240,7 +1240,7 @@ $.widget( "mobile.widget", {
1240
1240
  $.mobile = $.extend( {}, {
1241
1241
 
1242
1242
  // Version of the jQuery Mobile Framework
1243
- version: "1.1.0",
1243
+ version: "1.1.1",
1244
1244
 
1245
1245
  // Namespace used framework-wide for data-attrs. Default is no namespace
1246
1246
  ns: "",
@@ -1384,25 +1384,24 @@ $.widget( "mobile.widget", {
1384
1384
  return nsNormalizeDict[ prop ] || ( nsNormalizeDict[ prop ] = $.camelCase( $.mobile.ns + prop ) );
1385
1385
  },
1386
1386
 
1387
+ // Find the closest parent with a theme class on it. Note that
1388
+ // we are not using $.fn.closest() on purpose here because this
1389
+ // method gets called quite a bit and we need it to be as fast
1390
+ // as possible.
1387
1391
  getInheritedTheme: function( el, defaultTheme ) {
1388
-
1389
- // Find the closest parent with a theme class on it. Note that
1390
- // we are not using $.fn.closest() on purpose here because this
1391
- // method gets called quite a bit and we need it to be as fast
1392
- // as possible.
1393
-
1394
1392
  var e = el[ 0 ],
1395
1393
  ltr = "",
1396
1394
  re = /ui-(bar|body|overlay)-([a-z])\b/,
1397
1395
  c, m;
1398
1396
 
1399
1397
  while ( e ) {
1400
- var c = e.className || "";
1401
- if ( ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) {
1398
+ c = e.className || "";
1399
+ if ( c && ( m = re.exec( c ) ) && ( ltr = m[ 2 ] ) ) {
1402
1400
  // We found a parent with a theme class
1403
1401
  // on it so bail from this loop.
1404
1402
  break;
1405
1403
  }
1404
+
1406
1405
  e = e.parentNode;
1407
1406
  }
1408
1407
 
@@ -1464,6 +1463,12 @@ $.widget( "mobile.widget", {
1464
1463
  }
1465
1464
 
1466
1465
  return $newSet;
1466
+ },
1467
+
1468
+ getScreenHeight: function(){
1469
+ // Native innerHeight returns more accurate value for this across platforms,
1470
+ // jQuery version is here as a normalized fallback for platforms like Symbian
1471
+ return window.innerHeight || $( window ).height();
1467
1472
  }
1468
1473
  }, $.mobile );
1469
1474
 
@@ -1570,7 +1575,7 @@ var $window = $( window ),
1570
1575
  $.mobile.media = (function() {
1571
1576
  // TODO: use window.matchMedia once at least one UA implements it
1572
1577
  var cache = {},
1573
- testDiv = $( "<div id='jquery-mediatest'>" ),
1578
+ testDiv = $( "<div id='jquery-mediatest'></div>" ),
1574
1579
  fakeBody = $( "<body>" ).append( testDiv );
1575
1580
 
1576
1581
  return function( query ) {
@@ -1603,6 +1608,7 @@ var fakeBody = $( "<body>" ).prependTo( "html" ),
1603
1608
  fbCSS = fakeBody[ 0 ].style,
1604
1609
  vendors = [ "Webkit", "Moz", "O" ],
1605
1610
  webos = "palmGetResource" in window, //only used to rule out scrollTop
1611
+ opera = window.opera,
1606
1612
  operamini = window.operamini && ({}).toString.call( window.operamini ) === "[object OperaMini]",
1607
1613
  bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
1608
1614
 
@@ -1676,6 +1682,26 @@ function baseTagTest() {
1676
1682
  return rebase.indexOf( fauxBase ) === 0;
1677
1683
  }
1678
1684
 
1685
+ // Thanks Modernizr
1686
+ function cssPointerEventsTest() {
1687
+ var element = document.createElement('x'),
1688
+ documentElement = document.documentElement,
1689
+ getComputedStyle = window.getComputedStyle,
1690
+ supports;
1691
+
1692
+ if( !( 'pointerEvents' in element.style ) ){
1693
+ return false;
1694
+ }
1695
+
1696
+ element.style.pointerEvents = 'auto';
1697
+ element.style.pointerEvents = 'x';
1698
+ documentElement.appendChild(element);
1699
+ supports = getComputedStyle &&
1700
+ getComputedStyle( element, '' ).pointerEvents === 'auto';
1701
+ documentElement.removeChild( element );
1702
+ return !!supports;
1703
+ }
1704
+
1679
1705
 
1680
1706
  // non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683
1681
1707
  // allows for inclusion of IE 6+, including Windows Mobile 7
@@ -1696,7 +1722,7 @@ $.mobile.browser.ie = (function() {
1696
1722
  $.extend( $.support, {
1697
1723
  orientation: "orientation" in window && "onorientationchange" in window,
1698
1724
  touch: "ontouchend" in document,
1699
- cssTransitions: "WebKitTransitionEvent" in window || validStyle( 'transition', 'height 100ms linear' ),
1725
+ cssTransitions: "WebKitTransitionEvent" in window || validStyle( 'transition', 'height 100ms linear' ) && !opera,
1700
1726
  pushState: "pushState" in history && "replaceState" in history,
1701
1727
  mediaquery: $.mobile.media( "only all" ),
1702
1728
  cssPseudoElement: !!propExists( "content" ),
@@ -1704,7 +1730,8 @@ $.extend( $.support, {
1704
1730
  cssTransform3d: transform3dTest(),
1705
1731
  boxShadow: !!propExists( "boxShadow" ) && !bb,
1706
1732
  scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos && !operamini,
1707
- dynamicBaseTag: baseTagTest()
1733
+ dynamicBaseTag: baseTagTest(),
1734
+ cssPointerEvents: cssPointerEventsTest()
1708
1735
  });
1709
1736
 
1710
1737
  fakeBody.remove();
@@ -2003,7 +2030,7 @@ $.event.special.swipe = {
2003
2030
  win.bind( "throttledresize", handler );
2004
2031
  },
2005
2032
  teardown: function(){
2006
- // If the event is not supported natively, return false so that
2033
+ // If the event is supported natively, return false so that
2007
2034
  // jQuery will unbind the event using DOM methods.
2008
2035
  if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
2009
2036
  return false;
@@ -2192,7 +2219,8 @@ var createHandler = function( sequential ){
2192
2219
  toScroll = active.lastScroll || $.mobile.defaultHomeScroll,
2193
2220
  screenHeight = $.mobile.getScreenHeight(),
2194
2221
  maxTransitionOverride = $.mobile.maxTransitionWidth !== false && $( window ).width() > $.mobile.maxTransitionWidth,
2195
- none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none",
2222
+ none = !$.support.cssTransitions || maxTransitionOverride || !name || name === "none" || Math.max( $( window ).scrollTop(), toScroll ) > $.mobile.getMaxScrollForTransition(),
2223
+ toPreClass = " ui-page-pre-in",
2196
2224
  toggleViewportClass = function(){
2197
2225
  $.mobile.pageContainer.toggleClass( "ui-mobile-viewport-transitioning viewport-" + name );
2198
2226
  },
@@ -2301,7 +2329,10 @@ var createHandler = function( sequential ){
2301
2329
 
2302
2330
  // generate the handlers from the above
2303
2331
  var sequentialHandler = createHandler(),
2304
- simultaneousHandler = createHandler( false );
2332
+ simultaneousHandler = createHandler( false ),
2333
+ defaultGetMaxScrollForTransition = function() {
2334
+ return $.mobile.getScreenHeight() * 3;
2335
+ };
2305
2336
 
2306
2337
  // Make our transition handler the public default.
2307
2338
  $.mobile.defaultTransitionHandler = sequentialHandler;
@@ -2315,6 +2346,8 @@ $.mobile.transitionHandlers = {
2315
2346
 
2316
2347
  $.mobile.transitionFallbacks = {};
2317
2348
 
2349
+ // Set the getMaxScrollForTransition to default if no implementation was set by user
2350
+ $.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defaultGetMaxScrollForTransition;
2318
2351
  })( jQuery, this );
2319
2352
 
2320
2353
  ( function( $, undefined ) {
@@ -2472,7 +2505,7 @@ $.mobile.transitionFallbacks = {};
2472
2505
  // otherwise the Data Url won't match the id of the embedded Page.
2473
2506
  return u.hash.split( dialogHashKey )[0].replace( /^#/, "" );
2474
2507
  } else if ( path.isSameDomain( u, documentBase ) ) {
2475
- return u.hrefNoHash.replace( documentBase.domain, "" );
2508
+ return u.hrefNoHash.replace( documentBase.domain, "" ).split( dialogHashKey )[0];
2476
2509
  }
2477
2510
  return absUrl;
2478
2511
  },
@@ -2517,6 +2550,10 @@ $.mobile.transitionFallbacks = {};
2517
2550
  return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) );
2518
2551
  },
2519
2552
 
2553
+ isHashValid: function( hash ) {
2554
+ return /^#[^#]+$/.test(hash);
2555
+ },
2556
+
2520
2557
  //check whether a url is referencing the same domain, or an external domain or different protocol
2521
2558
  //could be mailto, etc
2522
2559
  isExternal: function( url ) {
@@ -2560,6 +2597,19 @@ $.mobile.transitionFallbacks = {};
2560
2597
  return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) );
2561
2598
  }
2562
2599
  return (/^#/).test( u.href );
2600
+ },
2601
+
2602
+
2603
+ // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR
2604
+ // requests if the document doing the request was loaded via the file:// protocol.
2605
+ // This is usually to allow the application to "phone home" and fetch app specific
2606
+ // data. We normally let the browser handle external/cross-domain urls, but if the
2607
+ // allowCrossDomainPages option is true, we will allow cross-domain http/https
2608
+ // requests to go through our page loading logic.
2609
+ isPermittedCrossDomainRequest: function( docUrl, reqUrl ) {
2610
+ return $.mobile.allowCrossDomainPages
2611
+ && docUrl.protocol === "file:"
2612
+ && reqUrl.search( /^https?:/ ) != -1;
2563
2613
  }
2564
2614
  },
2565
2615
 
@@ -2609,7 +2659,7 @@ $.mobile.transitionFallbacks = {};
2609
2659
  directHashChange: function( opts ) {
2610
2660
  var back , forward, newActiveIndex, prev = this.getActive();
2611
2661
 
2612
- // check if url isp in history and if it's ahead or behind current page
2662
+ // check if url is in history and if it's ahead or behind current page
2613
2663
  $.each( urlHistory.stack, function( i, historyEntry ) {
2614
2664
 
2615
2665
  //if the url is in the stack, it's a forward or a back
@@ -2659,7 +2709,9 @@ $.mobile.transitionFallbacks = {};
2659
2709
  documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl,
2660
2710
 
2661
2711
  //cache the comparison once.
2662
- documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash );
2712
+ documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash ),
2713
+
2714
+ getScreenHeight = $.mobile.getScreenHeight;
2663
2715
 
2664
2716
  //base element management, defined depending on dynamic base tag support
2665
2717
  var base = $.support.dynamicBaseTag ? {
@@ -2792,12 +2844,12 @@ $.mobile.transitionFallbacks = {};
2792
2844
 
2793
2845
  //clear page loader
2794
2846
  $.mobile.hidePageLoadingMsg();
2795
-
2847
+
2796
2848
  // If transition is defined, check if css 3D transforms are supported, and if not, if a fallback is specified
2797
2849
  if( transition && !$.support.cssTransform3d && $.mobile.transitionFallbacks[ transition ] ){
2798
2850
  transition = $.mobile.transitionFallbacks[ transition ];
2799
2851
  }
2800
-
2852
+
2801
2853
  //find the transition handler for the specified transition. If there
2802
2854
  //isn't one in our transitionHandlers dictionary, use the default one.
2803
2855
  //call the handler immediately to kick-off the transition.
@@ -2818,22 +2870,15 @@ $.mobile.transitionFallbacks = {};
2818
2870
  return promise;
2819
2871
  }
2820
2872
 
2821
- //simply set the active page's minimum height to screen height, depending on orientation
2822
- function getScreenHeight(){
2823
- // Native innerHeight returns more accurate value for this across platforms,
2824
- // jQuery version is here as a normalized fallback for platforms like Symbian
2825
- return window.innerHeight || $( window ).height();
2826
- }
2827
-
2828
- $.mobile.getScreenHeight = getScreenHeight;
2829
-
2830
2873
  //simply set the active page's minimum height to screen height, depending on orientation
2831
2874
  function resetActivePageHeight(){
2832
2875
  var aPage = $( "." + $.mobile.activePageClass ),
2833
2876
  aPagePadT = parseFloat( aPage.css( "padding-top" ) ),
2834
- aPagePadB = parseFloat( aPage.css( "padding-bottom" ) );
2835
-
2836
- aPage.css( "min-height", getScreenHeight() - aPagePadT - aPagePadB );
2877
+ aPagePadB = parseFloat( aPage.css( "padding-bottom" ) ),
2878
+ aPageBorderT = parseFloat( aPage.css( "border-top-width" ) ),
2879
+ aPageBorderB = parseFloat( aPage.css( "border-bottom-width" ) );
2880
+
2881
+ aPage.css( "min-height", getScreenHeight() - aPagePadT - aPagePadB - aPageBorderT - aPageBorderB );
2837
2882
  }
2838
2883
 
2839
2884
  //shared page enhancements
@@ -3308,6 +3353,16 @@ $.mobile.transitionFallbacks = {};
3308
3353
  if( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) {
3309
3354
  isPageTransitioning = false;
3310
3355
  mpc.trigger( "pagechange", triggerData );
3356
+
3357
+ // Even if there is no page change to be done, we should keep the urlHistory in sync with the hash changes
3358
+ if( settings.fromHashChange ) {
3359
+ urlHistory.directHashChange({
3360
+ currentUrl: url,
3361
+ isBack: function() {},
3362
+ isForward: function() {}
3363
+ });
3364
+ }
3365
+
3311
3366
  return;
3312
3367
  }
3313
3368
 
@@ -3339,6 +3394,9 @@ $.mobile.transitionFallbacks = {};
3339
3394
  }
3340
3395
  } catch(e) {}
3341
3396
 
3397
+ // Record whether we are at a place in history where a dialog used to be - if so, do not add a new history entry and do not change the hash either
3398
+ var alreadyThere = false;
3399
+
3342
3400
  // If we're displaying the page as a dialog, we don't want the url
3343
3401
  // for the dialog content to be used in the hash. Instead, we want
3344
3402
  // to append the dialogHashKey to the url of the current page.
@@ -3347,7 +3405,24 @@ $.mobile.transitionFallbacks = {};
3347
3405
  // be an empty string. Moving the undefined -> empty string back into
3348
3406
  // urlHistory.addNew seemed imprudent given undefined better represents
3349
3407
  // the url state
3408
+
3409
+ // If we are at a place in history that once belonged to a dialog, reuse
3410
+ // this state without adding to urlHistory and without modifying the hash.
3411
+ // However, if a dialog is already displayed at this point, and we're
3412
+ // about to display another dialog, then we must add another hash and
3413
+ // history entry on top so that one may navigate back to the original dialog
3414
+ if ( active.url.indexOf( dialogHashKey ) > -1 && !$.mobile.activePage.is( ".ui-dialog" ) ) {
3415
+ settings.changeHash = false;
3416
+ alreadyThere = true;
3417
+ }
3418
+
3350
3419
  url = ( active.url || "" ) + dialogHashKey;
3420
+
3421
+ // tack on another dialogHashKey if this is the same as the initial hash
3422
+ // this makes sure that a history entry is created for this dialog
3423
+ if ( urlHistory.activeIndex === 0 && url === urlHistory.initialDst ) {
3424
+ url += dialogHashKey;
3425
+ }
3351
3426
  }
3352
3427
 
3353
3428
  // Set the location hash.
@@ -3374,7 +3449,7 @@ $.mobile.transitionFallbacks = {};
3374
3449
  || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition );
3375
3450
 
3376
3451
  //add page to history stack if it's not back or forward
3377
- if( !historyDir ) {
3452
+ if( !historyDir && !alreadyThere ) {
3378
3453
  urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
3379
3454
  }
3380
3455
 
@@ -3458,11 +3533,10 @@ $.mobile.transitionFallbacks = {};
3458
3533
  return path.makeUrlAbsolute( url, base);
3459
3534
  }
3460
3535
 
3461
-
3462
3536
  //The following event bindings should be bound after mobileinit has been triggered
3463
- //the following function is called in the init file
3464
- $.mobile._registerInternalEvents = function(){
3465
-
3537
+ //the following deferred is resolved in the init file
3538
+ $.mobile.navreadyDeferred = $.Deferred();
3539
+ $.mobile.navreadyDeferred.done( function(){
3466
3540
  //bind to form submit events, handle with Ajax
3467
3541
  $( document ).delegate( "form", "submit", function( event ) {
3468
3542
  var $this = $( this );
@@ -3499,8 +3573,7 @@ $.mobile.transitionFallbacks = {};
3499
3573
 
3500
3574
  url = path.makeUrlAbsolute( url, getClosestBaseUrl($this) );
3501
3575
 
3502
- //external submits use regular HTTP
3503
- if( path.isExternal( url ) || target ) {
3576
+ if(( path.isExternal( url ) && !path.isPermittedCrossDomainRequest(documentUrl, url)) || target ) {
3504
3577
  return;
3505
3578
  }
3506
3579
 
@@ -3539,12 +3612,6 @@ $.mobile.transitionFallbacks = {};
3539
3612
  removeActiveLinkClass( true );
3540
3613
  $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
3541
3614
  $activeClickedLink.addClass( $.mobile.activeBtnClass );
3542
- $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
3543
-
3544
- // By caching the href value to data and switching the href to a #, we can avoid address bar showing in iOS. The click handler resets the href during its initial steps if this data is present
3545
- $( link )
3546
- .jqmData( "href", $( link ).attr( "href" ) )
3547
- .attr( "href", "#" );
3548
3615
  }
3549
3616
  }
3550
3617
  });
@@ -3570,11 +3637,6 @@ $.mobile.transitionFallbacks = {};
3570
3637
  window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 );
3571
3638
  };
3572
3639
 
3573
- // If there's data cached for the real href value, set the link's href back to it again. This pairs with an address bar workaround from the vclick handler
3574
- if( $link.jqmData( "href" ) ){
3575
- $link.attr( "href", $link.jqmData( "href" ) );
3576
- }
3577
-
3578
3640
  //if there's a data-rel=back attr, go back in history
3579
3641
  if( $link.is( ":jqmData(rel='back')" ) ) {
3580
3642
  window.history.back();
@@ -3627,12 +3689,11 @@ $.mobile.transitionFallbacks = {};
3627
3689
  // data. We normally let the browser handle external/cross-domain urls, but if the
3628
3690
  // allowCrossDomainPages option is true, we will allow cross-domain http/https
3629
3691
  // requests to go through our page loading logic.
3630
- isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ),
3631
3692
 
3632
3693
  //check for protocol or rel and its not an embedded page
3633
3694
  //TODO overlap in logic from isExternal, rel=external check should be
3634
3695
  // moved into more comprehensive isExternalLink
3635
- isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
3696
+ isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !path.isPermittedCrossDomainRequest(documentUrl, href) );
3636
3697
 
3637
3698
  if( isExternal ) {
3638
3699
  httpCleanup();
@@ -3683,6 +3744,10 @@ $.mobile.transitionFallbacks = {};
3683
3744
  fromHashChange: true
3684
3745
  };
3685
3746
 
3747
+ if ( 0 === urlHistory.stack.length ) {
3748
+ urlHistory.initialDst = to;
3749
+ }
3750
+
3686
3751
  //if listening is disabled (either globally or temporarily), or it's a dialog hash
3687
3752
  if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
3688
3753
  urlHistory.ignoreNextHashChange = false;
@@ -3690,7 +3755,7 @@ $.mobile.transitionFallbacks = {};
3690
3755
  }
3691
3756
 
3692
3757
  // special case for dialogs
3693
- if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) {
3758
+ if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 && urlHistory.initialDst !== to ) {
3694
3759
 
3695
3760
  // If current active page is not a dialog skip the dialog and continue
3696
3761
  // in the same direction
@@ -3754,7 +3819,7 @@ $.mobile.transitionFallbacks = {};
3754
3819
  $( document ).bind( "pageshow", resetActivePageHeight );
3755
3820
  $( window ).bind( "throttledresize", resetActivePageHeight );
3756
3821
 
3757
- };//_registerInternalEvents callback
3822
+ });//navreadyDeferred done callback
3758
3823
 
3759
3824
  })( jQuery );
3760
3825
 
@@ -3765,7 +3830,13 @@ $.mobile.transitionFallbacks = {};
3765
3830
  var pushStateHandler = {},
3766
3831
  self = pushStateHandler,
3767
3832
  $win = $( window ),
3768
- url = $.mobile.path.parseUrl( location.href );
3833
+ url = $.mobile.path.parseUrl( location.href ),
3834
+ mobileinitDeferred = $.Deferred(),
3835
+ domreadyDeferred = $.Deferred();
3836
+
3837
+ $( document ).ready( $.proxy( domreadyDeferred, "resolve" ) );
3838
+
3839
+ $( document ).one( "mobileinit", $.proxy( mobileinitDeferred, "resolve" ) );
3769
3840
 
3770
3841
  $.extend( pushStateHandler, {
3771
3842
  // TODO move to a path helper, this is rather common functionality
@@ -3773,6 +3844,10 @@ $.mobile.transitionFallbacks = {};
3773
3844
  return url.pathname + url.search;
3774
3845
  })(),
3775
3846
 
3847
+ hashChangeTimeout: 200,
3848
+
3849
+ hashChangeEnableTimer: undefined,
3850
+
3776
3851
  initialHref: url.hrefNoHash,
3777
3852
 
3778
3853
  state: function() {
@@ -3799,11 +3874,6 @@ $.mobile.transitionFallbacks = {};
3799
3874
  return url;
3800
3875
  },
3801
3876
 
3802
- hashValueAfterReset: function( url ) {
3803
- var resetUrl = self.resetUIKeys( url );
3804
- return $.mobile.path.parseUrl( resetUrl ).hash;
3805
- },
3806
-
3807
3877
  // TODO sort out a single barrier to hashchange functionality
3808
3878
  nextHashChangePrevented: function( value ) {
3809
3879
  $.mobile.urlHistory.ignoreNextHashChange = value;
@@ -3854,41 +3924,28 @@ $.mobile.transitionFallbacks = {};
3854
3924
  // cleaned up by the additional hash handling
3855
3925
  onPopState: function( e ) {
3856
3926
  var poppedState = e.originalEvent.state,
3857
- timeout, fromHash, toHash, hashChanged;
3927
+ fromHash, toHash, hashChanged;
3858
3928
 
3859
3929
  // if there's no state its not a popstate we care about, eg chrome's initial popstate
3860
3930
  if( poppedState ) {
3861
- // the active url in the history stack will still be from the previous state
3862
- // so we can use it to verify if a hashchange will be fired from the popstate
3863
- fromHash = self.hashValueAfterReset( $.mobile.urlHistory.getActive().url );
3864
-
3865
- // the hash stored in the state popped off the stack will be our currenturl or
3866
- // the url to which we wish to navigate
3867
- toHash = self.hashValueAfterReset( poppedState.hash.replace("#", "") );
3868
-
3869
- // if the hashes of the urls are different we must assume that the browser
3870
- // will fire a hashchange
3871
- hashChanged = fromHash !== toHash;
3872
-
3873
- // unlock hash handling once the hashchange caused be the popstate has fired
3874
- if( hashChanged ) {
3875
- $win.one( "hashchange.pushstate", function() {
3876
- self.nextHashChangePrevented( false );
3877
- });
3878
- }
3931
+ // if we get two pop states in under this.hashChangeTimeout
3932
+ // make sure to clear any timer set for the previous change
3933
+ clearTimeout( self.hashChangeEnableTimer );
3879
3934
 
3880
- // enable hash handling for the the _handleHashChange call
3935
+ // make sure to enable hash handling for the the _handleHashChange call
3881
3936
  self.nextHashChangePrevented( false );
3882
3937
 
3883
- // change the page based on the hash
3938
+ // change the page based on the hash in the popped state
3884
3939
  $.mobile._handleHashChange( poppedState.hash );
3885
3940
 
3886
- // only prevent another hash change handling if a hash change will be fired
3887
- // by the browser
3888
- if( hashChanged ) {
3889
- // disable hash handling until one of the above timers fires
3890
- self.nextHashChangePrevented( true );
3891
- }
3941
+ // prevent any hashchange in the next self.hashChangeTimeout
3942
+ self.nextHashChangePrevented( true );
3943
+
3944
+ // re-enable hash change handling after swallowing a possible hash
3945
+ // change event that comes on all popstates courtesy of browsers like Android
3946
+ self.hashChangeEnableTimer = setTimeout( function() {
3947
+ self.nextHashChangePrevented( false );
3948
+ }, self.hashChangeTimeout);
3892
3949
  }
3893
3950
  },
3894
3951
 
@@ -3905,7 +3962,8 @@ $.mobile.transitionFallbacks = {};
3905
3962
  }
3906
3963
  });
3907
3964
 
3908
- $( function() {
3965
+ // We need to init when "mobileinit", "domready", and "navready" have all happened
3966
+ $.when( domreadyDeferred, mobileinitDeferred, $.mobile.navreadyDeferred ).done( function() {
3909
3967
  if( $.mobile.pushStateEnabled && $.support.pushState ){
3910
3968
  pushStateHandler.init();
3911
3969
  }
@@ -4098,7 +4156,8 @@ $.widget( "mobile.dialog", $.mobile.widget, {
4098
4156
  }
4099
4157
  })
4100
4158
  .bind( "pagehide", function( e, ui ) {
4101
- $( this ).find( "." + $.mobile.activeBtnClass ).removeClass( $.mobile.activeBtnClass );
4159
+ self._isClosed = false;
4160
+ $( this ).find( "." + $.mobile.activeBtnClass ).not( ".ui-slider-bg" ).removeClass( $.mobile.activeBtnClass );
4102
4161
  })
4103
4162
  // Override the theme set by the page plugin on pageshow
4104
4163
  .bind( "pagebeforeshow", function(){
@@ -4112,7 +4171,15 @@ $.widget( "mobile.dialog", $.mobile.widget, {
4112
4171
 
4113
4172
  // Close method goes back in history
4114
4173
  close: function() {
4115
- window.history.back();
4174
+ if ( !this._isClosed ) {
4175
+ this._isClosed = true;
4176
+ if ( $.mobile.hashListeningEnabled ) {
4177
+ window.history.back();
4178
+ }
4179
+ else {
4180
+ $.mobile.changePage( $.mobile.urlHistory.getPrev().url );
4181
+ }
4182
+ }
4116
4183
  }
4117
4184
  });
4118
4185
 
@@ -4125,8 +4192,104 @@ $( document ).delegate( $.mobile.dialog.prototype.options.initSelector, "pagecre
4125
4192
 
4126
4193
  (function( $, undefined ) {
4127
4194
 
4195
+ $.mobile.page.prototype.options.backBtnText = "Back";
4196
+ $.mobile.page.prototype.options.addBackBtn = false;
4197
+ $.mobile.page.prototype.options.backBtnTheme = null;
4198
+ $.mobile.page.prototype.options.headerTheme = "a";
4199
+ $.mobile.page.prototype.options.footerTheme = "a";
4200
+ $.mobile.page.prototype.options.contentTheme = null;
4201
+
4202
+ // NOTE bind used to force this binding to run before the buttonMarkup binding
4203
+ // which expects .ui-footer top be applied in its gigantic selector
4204
+ // TODO remove the buttonMarkup giant selector and move it to the various modules
4205
+ // on which it depends
4206
+ $( document ).bind( "pagecreate", function( e ) {
4207
+ var $page = $( e.target ),
4208
+ o = $page.data( "page" ).options,
4209
+ pageRole = $page.jqmData( "role" ),
4210
+ pageTheme = o.theme;
4211
+
4212
+ $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", $page )
4213
+ .jqmEnhanceable()
4214
+ .each(function() {
4215
+
4216
+ var $this = $( this ),
4217
+ role = $this.jqmData( "role" ),
4218
+ theme = $this.jqmData( "theme" ),
4219
+ contentTheme = theme || o.contentTheme || ( pageRole === "dialog" && pageTheme ),
4220
+ $headeranchors,
4221
+ leftbtn,
4222
+ rightbtn,
4223
+ backBtn;
4224
+
4225
+ $this.addClass( "ui-" + role );
4226
+
4227
+ //apply theming and markup modifications to page,header,content,footer
4228
+ if ( role === "header" || role === "footer" ) {
4229
+
4230
+ var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
4231
+
4232
+ $this
4233
+ //add theme class
4234
+ .addClass( "ui-bar-" + thisTheme )
4235
+ // Add ARIA role
4236
+ .attr( "role", role === "header" ? "banner" : "contentinfo" );
4237
+
4238
+ if( role === "header") {
4239
+ // Right,left buttons
4240
+ $headeranchors = $this.children( "a" );
4241
+ leftbtn = $headeranchors.hasClass( "ui-btn-left" );
4242
+ rightbtn = $headeranchors.hasClass( "ui-btn-right" );
4243
+
4244
+ leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
4245
+
4246
+ rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
4247
+ }
4248
+
4249
+ // Auto-add back btn on pages beyond first view
4250
+ if ( o.addBackBtn &&
4251
+ role === "header" &&
4252
+ $( ".ui-page" ).length > 1 &&
4253
+ $page.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
4254
+ !leftbtn ) {
4255
+
4256
+ backBtn = $( "<a href='javascript:void(0);' class='ui-btn-left' data-"+ $.mobile.ns +"rel='back' data-"+ $.mobile.ns +"icon='arrow-l'>"+ o.backBtnText +"</a>" )
4257
+ // If theme is provided, override default inheritance
4258
+ .attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme )
4259
+ .prependTo( $this );
4260
+ }
4261
+
4262
+ // Page title
4263
+ $this.children( "h1, h2, h3, h4, h5, h6" )
4264
+ .addClass( "ui-title" )
4265
+ // Regardless of h element number in src, it becomes h1 for the enhanced page
4266
+ .attr({
4267
+ "role": "heading",
4268
+ "aria-level": "1"
4269
+ });
4270
+
4271
+ } else if ( role === "content" ) {
4272
+ if ( contentTheme ) {
4273
+ $this.addClass( "ui-body-" + ( contentTheme ) );
4274
+ }
4275
+
4276
+ // Add ARIA role
4277
+ $this.attr( "role", "main" );
4278
+ }
4279
+ });
4280
+ });
4281
+
4282
+ })( jQuery );
4283
+
4284
+ (function( $, undefined ) {
4285
+
4286
+ // filter function removes whitespace between label and form element so we can use inline-block (nodeType 3 = text)
4128
4287
  $.fn.fieldcontain = function( options ) {
4129
- return this.addClass( "ui-field-contain ui-body ui-br" );
4288
+ return this
4289
+ .addClass( "ui-field-contain ui-body ui-br" )
4290
+ .contents().filter( function() {
4291
+ return ( this.nodeType === 3 && !/\S/.test( this.nodeValue ) );
4292
+ }).remove();
4130
4293
  };
4131
4294
 
4132
4295
  //auto self-init widgets
@@ -4159,6 +4322,7 @@ $.fn.grid = function( options ) {
4159
4322
  }
4160
4323
  } else {
4161
4324
  grid = "a";
4325
+ $this.addClass( "ui-grid-duo" );
4162
4326
  }
4163
4327
  }
4164
4328
  iterator = gridCols[grid];
@@ -4434,94 +4598,6 @@ $( document ).bind( "pagecreate create", function( e ){
4434
4598
  })( jQuery );
4435
4599
 
4436
4600
 
4437
- (function( $, undefined ) {
4438
-
4439
- $.mobile.page.prototype.options.backBtnText = "Back";
4440
- $.mobile.page.prototype.options.addBackBtn = false;
4441
- $.mobile.page.prototype.options.backBtnTheme = null;
4442
- $.mobile.page.prototype.options.headerTheme = "a";
4443
- $.mobile.page.prototype.options.footerTheme = "a";
4444
- $.mobile.page.prototype.options.contentTheme = null;
4445
-
4446
- $( document ).delegate( ":jqmData(role='page'), :jqmData(role='dialog')", "pagecreate", function( e ) {
4447
-
4448
- var $page = $( this ),
4449
- o = $page.data( "page" ).options,
4450
- pageRole = $page.jqmData( "role" ),
4451
- pageTheme = o.theme;
4452
-
4453
- $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this )
4454
- .jqmEnhanceable()
4455
- .each(function() {
4456
-
4457
- var $this = $( this ),
4458
- role = $this.jqmData( "role" ),
4459
- theme = $this.jqmData( "theme" ),
4460
- contentTheme = theme || o.contentTheme || ( pageRole === "dialog" && pageTheme ),
4461
- $headeranchors,
4462
- leftbtn,
4463
- rightbtn,
4464
- backBtn;
4465
-
4466
- $this.addClass( "ui-" + role );
4467
-
4468
- //apply theming and markup modifications to page,header,content,footer
4469
- if ( role === "header" || role === "footer" ) {
4470
-
4471
- var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme;
4472
-
4473
- $this
4474
- //add theme class
4475
- .addClass( "ui-bar-" + thisTheme )
4476
- // Add ARIA role
4477
- .attr( "role", role === "header" ? "banner" : "contentinfo" );
4478
-
4479
- if( role === "header") {
4480
- // Right,left buttons
4481
- $headeranchors = $this.children( "a" );
4482
- leftbtn = $headeranchors.hasClass( "ui-btn-left" );
4483
- rightbtn = $headeranchors.hasClass( "ui-btn-right" );
4484
-
4485
- leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length;
4486
-
4487
- rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length;
4488
- }
4489
-
4490
- // Auto-add back btn on pages beyond first view
4491
- if ( o.addBackBtn &&
4492
- role === "header" &&
4493
- $( ".ui-page" ).length > 1 &&
4494
- $page.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
4495
- !leftbtn ) {
4496
-
4497
- backBtn = $( "<a href='#' class='ui-btn-left' data-"+ $.mobile.ns +"rel='back' data-"+ $.mobile.ns +"icon='arrow-l'>"+ o.backBtnText +"</a>" )
4498
- // If theme is provided, override default inheritance
4499
- .attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme )
4500
- .prependTo( $this );
4501
- }
4502
-
4503
- // Page title
4504
- $this.children( "h1, h2, h3, h4, h5, h6" )
4505
- .addClass( "ui-title" )
4506
- // Regardless of h element number in src, it becomes h1 for the enhanced page
4507
- .attr({
4508
- "role": "heading",
4509
- "aria-level": "1"
4510
- });
4511
-
4512
- } else if ( role === "content" ) {
4513
- if ( contentTheme ) {
4514
- $this.addClass( "ui-body-" + ( contentTheme ) );
4515
- }
4516
-
4517
- // Add ARIA role
4518
- $this.attr( "role", "main" );
4519
- }
4520
- });
4521
- });
4522
-
4523
- })( jQuery );
4524
-
4525
4601
  (function( $, undefined ) {
4526
4602
 
4527
4603
  $.widget( "mobile.collapsible", $.mobile.widget, {
@@ -4611,7 +4687,9 @@ $.widget( "mobile.collapsible", $.mobile.widget, {
4611
4687
  .end()
4612
4688
  .find( ".ui-icon" )
4613
4689
  .toggleClass( "ui-icon-minus", !isCollapse )
4614
- .toggleClass( "ui-icon-plus", isCollapse );
4690
+ .toggleClass( "ui-icon-plus", isCollapse )
4691
+ .end()
4692
+ .find( "a" ).first().removeClass( $.mobile.activeBtnClass );
4615
4693
 
4616
4694
  $this.toggleClass( "ui-collapsible-collapsed", isCollapse );
4617
4695
  collapsibleContent.toggleClass( "ui-collapsible-content-collapsed", isCollapse ).attr( "aria-hidden", isCollapse );
@@ -4628,6 +4706,9 @@ $.widget( "mobile.collapsible", $.mobile.widget, {
4628
4706
  .trigger( o.collapsed ? "collapse" : "expand" );
4629
4707
 
4630
4708
  collapsibleHeading
4709
+ .bind( "tap", function( event ) {
4710
+ collapsibleHeading.find( "a" ).first().addClass( $.mobile.activeBtnClass );
4711
+ })
4631
4712
  .bind( "click", function( event ) {
4632
4713
 
4633
4714
  var type = collapsibleHeading.is( ".ui-collapsible-heading-collapsed" ) ?
@@ -4636,6 +4717,7 @@ $.widget( "mobile.collapsible", $.mobile.widget, {
4636
4717
  collapsible.trigger( type );
4637
4718
 
4638
4719
  event.preventDefault();
4720
+ event.stopPropagation();
4639
4721
  });
4640
4722
  }
4641
4723
  });
@@ -4682,7 +4764,8 @@ $.widget( "mobile.collapsibleset", $.mobile.widget, {
4682
4764
  if ( contentTheme && collapsible.jqmData( "collapsible-last" ) ) {
4683
4765
  collapsible.find( widget.options.heading ).first()
4684
4766
  .find( "a" ).first()
4685
- .add( ".ui-btn-inner" )
4767
+ .toggleClass( "ui-corner-bottom", isCollapse )
4768
+ .find( ".ui-btn-inner" )
4686
4769
  .toggleClass( "ui-corner-bottom", isCollapse );
4687
4770
  collapsible.find( ".ui-collapsible-content" ).toggleClass( "ui-corner-bottom", !isCollapse );
4688
4771
  }
@@ -4711,7 +4794,8 @@ $.widget( "mobile.collapsibleset", $.mobile.widget, {
4711
4794
  collapsiblesInSet.each( function() {
4712
4795
  $( this ).find( $.mobile.collapsible.prototype.options.heading )
4713
4796
  .find( "a" ).first()
4714
- .add( ".ui-btn-inner" )
4797
+ .removeClass( "ui-corner-top ui-corner-bottom" )
4798
+ .find( ".ui-btn-inner" )
4715
4799
  .removeClass( "ui-corner-top ui-corner-bottom" );
4716
4800
  });
4717
4801
 
@@ -4755,16 +4839,12 @@ $.widget( "mobile.navbar", $.mobile.widget, {
4755
4839
  iconpos = $navbtns.filter( ":jqmData(icon)" ).length ?
4756
4840
  this.options.iconpos : undefined;
4757
4841
 
4758
- $navbar.addClass( "ui-navbar" )
4842
+ $navbar.addClass( "ui-navbar ui-mini" )
4759
4843
  .attr( "role","navigation" )
4760
4844
  .find( "ul" )
4761
4845
  .jqmEnhanceable()
4762
4846
  .grid({ grid: this.options.grid });
4763
4847
 
4764
- if ( !iconpos ) {
4765
- $navbar.addClass( "ui-navbar-noicons" );
4766
- }
4767
-
4768
4848
  $navbtns.buttonMarkup({
4769
4849
  corners: false,
4770
4850
  shadow: false,
@@ -4809,7 +4889,6 @@ $.widget( "mobile.listview", $.mobile.widget, {
4809
4889
  dividerTheme: "b",
4810
4890
  splitIcon: "arrow-r",
4811
4891
  splitTheme: "b",
4812
- mini: false,
4813
4892
  inset: false,
4814
4893
  initSelector: ":jqmData(role='listview')"
4815
4894
  },
@@ -4819,8 +4898,7 @@ $.widget( "mobile.listview", $.mobile.widget, {
4819
4898
  listviewClasses = "";
4820
4899
 
4821
4900
  listviewClasses += t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "";
4822
- listviewClasses += t.element.jqmData( "mini" ) || t.options.mini === true ? " ui-mini" : "";
4823
-
4901
+
4824
4902
  // create listview markup
4825
4903
  t.element.addClass(function( i, orig ) {
4826
4904
  return orig + " ui-listview " + listviewClasses;
@@ -4971,8 +5049,9 @@ $.widget( "mobile.listview", $.mobile.widget, {
4971
5049
  if ( create || !item.hasClass( "ui-li" ) ) {
4972
5050
  itemTheme = item.jqmData("theme") || o.theme;
4973
5051
  a = this._getChildrenByTagName( item[ 0 ], "a", "A" );
5052
+ var isDivider = ( item.jqmData( "role" ) === "list-divider" );
4974
5053
 
4975
- if ( a.length ) {
5054
+ if ( a.length && !isDivider ) {
4976
5055
  icon = item.jqmData("icon");
4977
5056
 
4978
5057
  item.buttonMarkup({
@@ -5006,7 +5085,7 @@ $.widget( "mobile.listview", $.mobile.widget, {
5006
5085
  corners: false,
5007
5086
  theme: itemTheme,
5008
5087
  icon: false,
5009
- iconpos: false
5088
+ iconpos: "notext"
5010
5089
  })
5011
5090
  .find( ".ui-btn-inner" )
5012
5091
  .append(
@@ -5020,7 +5099,7 @@ $.widget( "mobile.listview", $.mobile.widget, {
5020
5099
  })
5021
5100
  );
5022
5101
  }
5023
- } else if ( item.jqmData( "role" ) === "list-divider" ) {
5102
+ } else if ( isDivider ) {
5024
5103
 
5025
5104
  itemClass += " ui-li-divider ui-bar-" + dividertheme;
5026
5105
  item.attr( "role", "heading" );
@@ -5164,13 +5243,17 @@ $.widget( "mobile.listview", $.mobile.widget, {
5164
5243
  parentPage.data("page").options.domCache === false ) {
5165
5244
 
5166
5245
  var newRemove = function( e, ui ){
5167
- var nextPage = ui.nextPage, npURL;
5246
+ var nextPage = ui.nextPage, npURL,
5247
+ prEvent = new $.Event( "pageremove" );
5168
5248
 
5169
5249
  if( ui.nextPage ){
5170
5250
  npURL = nextPage.jqmData( "url" );
5171
5251
  if( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ){
5172
5252
  self.childPages().remove();
5173
- parentPage.remove();
5253
+ parentPage.trigger( prEvent );
5254
+ if( !prEvent.isDefaultPrevented() ){
5255
+ parentPage.removeWithDependents();
5256
+ }
5174
5257
  }
5175
5258
  }
5176
5259
  };
@@ -5443,6 +5526,12 @@ $.widget( "mobile.button", $.mobile.widget, {
5443
5526
  classes = "ui-btn-right";
5444
5527
  }
5445
5528
 
5529
+ if( $el.attr( "type" ) === "submit" || $el.attr( "type" ) === "reset" ) {
5530
+ classes ? classes += " ui-submit" : classes = "ui-submit";
5531
+ }
5532
+
5533
+ $( "label[for='" + $el.attr( "id" ) + "']" ).addClass( "ui-submit" );
5534
+
5446
5535
  // Add ARIA role
5447
5536
  this.button = $( "<div></div>" )
5448
5537
  .text( $el.text() || $el.val() )
@@ -5467,7 +5556,7 @@ $.widget( "mobile.button", $.mobile.widget, {
5467
5556
  // Add hidden input during submit if input type="submit" has a name.
5468
5557
  if ( type !== "button" && type !== "reset" && name ) {
5469
5558
  $el.bind( "vclick", function() {
5470
- // Add hidden input if it doesnt already exist.
5559
+ // Add hidden input if it doesn't already exist.
5471
5560
  if( $buttonPlaceholder === undefined ) {
5472
5561
  $buttonPlaceholder = $( "<input>", {
5473
5562
  type: "hidden",
@@ -5537,7 +5626,7 @@ $( document ).bind( "pagecreate create", function( e ){
5537
5626
 
5538
5627
  $.fn.controlgroup = function( options ) {
5539
5628
  function flipClasses( els, flCorners ) {
5540
- els.removeClass( "ui-btn-corner-all ui-shadow" )
5629
+ els.removeClass( "ui-btn-corner-all ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-controlgroup-last ui-shadow" )
5541
5630
  .eq( 0 ).addClass( flCorners[ 0 ] )
5542
5631
  .end()
5543
5632
  .last().addClass( flCorners[ 1 ] ).addClass( "ui-controlgroup-last" );
@@ -5554,10 +5643,11 @@ $.fn.controlgroup = function( options ) {
5554
5643
  groupheading = $el.children( "legend" ),
5555
5644
  flCorners = o.direction == "horizontal" ? [ "ui-corner-left", "ui-corner-right" ] : [ "ui-corner-top", "ui-corner-bottom" ],
5556
5645
  type = $el.find( "input" ).first().attr( "type" );
5646
+
5647
+ $el.wrapInner( "<div class='ui-controlgroup-controls'></div>" );
5557
5648
 
5558
5649
  // Replace legend with more stylable replacement div
5559
5650
  if ( groupheading.length ) {
5560
- $el.wrapInner( "<div class='ui-controlgroup-controls'></div>" );
5561
5651
  $( "<div role='heading' class='ui-controlgroup-label'>" + groupheading.html() + "</div>" ).insertBefore( $el.children(0) );
5562
5652
  groupheading.remove();
5563
5653
  }
@@ -5915,9 +6005,11 @@ $.widget( "mobile.slider", $.mobile.widget, {
5915
6005
 
5916
6006
  controlID = control.attr( "id" ),
5917
6007
 
5918
- labelID = controlID + "-label",
6008
+ $label = $( "[for='" + controlID + "']" ),
5919
6009
 
5920
- label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ),
6010
+ labelID = $label.attr( "id" ) || controlID + "-label",
6011
+
6012
+ label = $label.attr( "id", labelID ),
5921
6013
 
5922
6014
  val = function() {
5923
6015
  return cType == "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
@@ -5941,7 +6033,7 @@ $.widget( "mobile.slider", $.mobile.widget, {
5941
6033
 
5942
6034
  valuebg = control.jqmData("highlight") && cType != "select" ? (function() {
5943
6035
  var bg = document.createElement('div');
5944
- bg.className = 'ui-slider-bg ui-btn-active ui-btn-corner-all';
6036
+ bg.className = 'ui-slider-bg ' + $.mobile.activeBtnClass + ' ui-btn-corner-all';
5945
6037
  return $( bg ).prependTo( slider );
5946
6038
  })() : false,
5947
6039
 
@@ -6375,6 +6467,10 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
6375
6467
 
6376
6468
  options = this.options,
6377
6469
 
6470
+ inline = options.inline || this.select.jqmData( "inline" ),
6471
+ mini = options.mini || this.select.jqmData( "mini" ),
6472
+ iconpos = options.icon ? ( options.iconpos || this.select.jqmData( "iconpos" ) ) : false,
6473
+
6378
6474
  // IE throws an exception at options.item() function when
6379
6475
  // there is no selected item
6380
6476
  // select first in this case
@@ -6387,12 +6483,12 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
6387
6483
  .buttonMarkup( {
6388
6484
  theme: options.theme,
6389
6485
  icon: options.icon,
6390
- iconpos: options.iconpos,
6391
- inline: options.inline,
6486
+ iconpos: iconpos,
6487
+ inline: inline,
6392
6488
  corners: options.corners,
6393
6489
  shadow: options.shadow,
6394
6490
  iconshadow: options.iconshadow,
6395
- mini: options.mini
6491
+ mini: mini
6396
6492
  });
6397
6493
 
6398
6494
  // Opera does not properly support opacity on select elements
@@ -6400,8 +6496,8 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
6400
6496
  // On the desktop,it seems to do the opposite
6401
6497
  // for these reasons, using the nativeMenu option results in a full native select in Opera
6402
6498
  if ( options.nativeMenu && window.opera && window.opera.version ) {
6403
- this.select.addClass( "ui-select-nativeonly" );
6404
- }
6499
+ button.addClass( "ui-select-nativeonly" );
6500
+ }
6405
6501
 
6406
6502
  // Add counter for multi selects
6407
6503
  if ( this.isMultiple ) {
@@ -6659,8 +6755,13 @@ $( document ).bind( "pagecreate create", function( e ){
6659
6755
  self.select.trigger( "change" );
6660
6756
  }
6661
6757
 
6662
- //hide custom select for single selects only
6663
- if ( !self.isMultiple ) {
6758
+ // hide custom select for single selects only - otherwise focus clicked item
6759
+ // We need to grab the clicked item the hard way, because the list may have been rebuilt
6760
+ if ( self.isMultiple ) {
6761
+ self.list.find( "li:not(.ui-li-divider)" ).eq( newIndex )
6762
+ .addClass( "ui-btn-down-" + widget.options.theme ).find( "a" ).first().focus();
6763
+ }
6764
+ else {
6664
6765
  self.close();
6665
6766
  }
6666
6767
 
@@ -6774,19 +6875,22 @@ $( document ).bind( "pagecreate create", function( e ){
6774
6875
  return options.text() !== list.text();
6775
6876
  },
6776
6877
 
6878
+ selected: function() {
6879
+ return this._selectOptions().filter( ":selected:not(:jqmData(placeholder='true'))" );
6880
+ },
6881
+
6777
6882
  refresh: function( forceRebuild , foo ){
6778
6883
  var self = this,
6779
6884
  select = this.element,
6780
6885
  isMultiple = this.isMultiple,
6781
- options = this._selectOptions(),
6782
- selected = this.selected(),
6783
- // return an array of all selected index's
6784
- indicies = this.selectedIndices();
6886
+ indicies;
6785
6887
 
6786
6888
  if ( forceRebuild || this._isRebuildRequired() ) {
6787
6889
  self._buildList();
6788
6890
  }
6789
6891
 
6892
+ indicies = this.selectedIndices();
6893
+
6790
6894
  self.setButtonText();
6791
6895
  self.setButtonCount();
6792
6896
 
@@ -6864,7 +6968,11 @@ $( document ).bind( "pagecreate create", function( e ){
6864
6968
  }, 300);
6865
6969
 
6866
6970
  function focusMenuItem() {
6867
- self.list.find( "." + $.mobile.activeBtnClass + " a" ).focus();
6971
+ var selector = self.list.find( "." + $.mobile.activeBtnClass + " a" );
6972
+ if ( selector.length === 0 ) {
6973
+ selector = self.list.find( "li.ui-btn:not(:jqmData(placeholder='true')) a" );
6974
+ }
6975
+ selector.first().focus().closest( "li" ).addClass( "ui-btn-down-" + widget.options.theme );
6868
6976
  }
6869
6977
 
6870
6978
  if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
@@ -6965,10 +7073,12 @@ $( document ).bind( "pagecreate create", function( e ){
6965
7073
  dataIndexAttr = dataPrefix + 'option-index',
6966
7074
  dataIconAttr = dataPrefix + 'icon',
6967
7075
  dataRoleAttr = dataPrefix + 'role',
7076
+ dataPlaceholderAttr = dataPrefix + 'placeholder',
6968
7077
  fragment = document.createDocumentFragment(),
7078
+ isPlaceholderItem = false,
6969
7079
  optGroup;
6970
7080
 
6971
- for (var i = 0; i < numOptions;i++){
7081
+ for (var i = 0; i < numOptions;i++, isPlaceholderItem = false){
6972
7082
  var option = $options[i],
6973
7083
  $option = $(option),
6974
7084
  parent = option.parentNode,
@@ -6995,6 +7105,10 @@ $( document ).bind( "pagecreate create", function( e ){
6995
7105
 
6996
7106
  if (needPlaceholder && (!option.getAttribute( "value" ) || text.length == 0 || $option.jqmData( "placeholder" ))) {
6997
7107
  needPlaceholder = false;
7108
+ isPlaceholderItem = true;
7109
+
7110
+ // If we have identified a placeholder, mark it retroactively in the select as well
7111
+ option.setAttribute( dataPlaceholderAttr, true );
6998
7112
  if ( o.hidePlaceholderMenuItems ) {
6999
7113
  classes.push( "ui-selectmenu-placeholder" );
7000
7114
  }
@@ -7010,6 +7124,9 @@ $( document ).bind( "pagecreate create", function( e ){
7010
7124
  }
7011
7125
  item.setAttribute(dataIndexAttr,i);
7012
7126
  item.setAttribute(dataIconAttr,dataIcon);
7127
+ if ( isPlaceholderItem ) {
7128
+ item.setAttribute( dataPlaceholderAttr, true );
7129
+ }
7013
7130
  item.className = classes.join(" ");
7014
7131
  item.setAttribute('role','option');
7015
7132
  anchor.setAttribute('tabindex','-1');
@@ -7065,7 +7182,7 @@ $( document ).bind( "pagecreate create", function( e ){
7065
7182
  transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
7066
7183
  fullscreen: false,
7067
7184
  tapToggle: true,
7068
- tapToggleBlacklist: "a, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
7185
+ tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
7069
7186
  hideDuringFocus: "input, textarea, select",
7070
7187
  updatePagePadding: true,
7071
7188
  trackPersistentToolbars: true,
@@ -7179,15 +7296,17 @@ $( document ).bind( "pagecreate create", function( e ){
7179
7296
  }
7180
7297
  } )
7181
7298
  .bind( "webkitAnimationStart animationstart updatelayout", function(){
7299
+ var thisPage = this;
7182
7300
  if( o.updatePagePadding ){
7183
- self.updatePagePadding();
7301
+ self.updatePagePadding( thisPage );
7184
7302
  }
7185
7303
  })
7186
7304
  .bind( "pageshow", function(){
7187
- self.updatePagePadding();
7305
+ var thisPage = this;
7306
+ self.updatePagePadding( thisPage );
7188
7307
  if( o.updatePagePadding ){
7189
7308
  $( window ).bind( "throttledresize." + self.widgetName, function(){
7190
- self.updatePagePadding();
7309
+ self.updatePagePadding( thisPage );
7191
7310
  });
7192
7311
  }
7193
7312
  })
@@ -7222,14 +7341,15 @@ $( document ).bind( "pagecreate create", function( e ){
7222
7341
  _visible: true,
7223
7342
 
7224
7343
  // This will set the content element's top or bottom padding equal to the toolbar's height
7225
- updatePagePadding: function() {
7344
+ updatePagePadding: function( tbPage ) {
7226
7345
  var $el = this.element,
7227
7346
  header = $el.is( ".ui-header" );
7228
7347
 
7229
7348
  // This behavior only applies to "fixed", not "fullscreen"
7230
7349
  if( this.options.fullscreen ){ return; }
7231
7350
 
7232
- $el.closest( ".ui-page" ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() );
7351
+ tbPage = tbPage || $el.closest( ".ui-page" );
7352
+ $( tbPage ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() );
7233
7353
  },
7234
7354
 
7235
7355
  _useTransition: function( notransition ){
@@ -7370,7 +7490,7 @@ $( document ).bind( "pagecreate create", function( e ){
7370
7490
  $head = $( "head" ),
7371
7491
  $window = $( window );
7372
7492
 
7373
- // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
7493
+ // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
7374
7494
  $( window.document ).trigger( "mobileinit" );
7375
7495
 
7376
7496
  // support conditions
@@ -7500,8 +7620,14 @@ $( document ).bind( "pagecreate create", function( e ){
7500
7620
  //remove initial build class (only present on first pageshow)
7501
7621
  hideRenderingClass();
7502
7622
 
7503
- // if hashchange listening is disabled or there's no hash deeplink, change to the first page in the DOM
7504
- if ( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ) {
7623
+ // if hashchange listening is disabled, there's no hash deeplink,
7624
+ // the hash is not valid (contains more than one # or does not start with #)
7625
+ // or there is no page with that hash, change to the first page in the DOM
7626
+ // Remember, however, that the hash can also be a path!
7627
+ if ( ! ( $.mobile.hashListeningEnabled &&
7628
+ $.mobile.path.isHashValid( location.hash ) &&
7629
+ ( $( location.hash + ':jqmData(role="page")' ).length ||
7630
+ $.mobile.path.isPath( location.hash ) ) ) ) {
7505
7631
  $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true } );
7506
7632
  }
7507
7633
  // otherwise, trigger a hashchange to load a deeplink
@@ -7512,7 +7638,7 @@ $( document ).bind( "pagecreate create", function( e ){
7512
7638
  });
7513
7639
 
7514
7640
  // initialize events now, after mobileinit has occurred
7515
- $.mobile._registerInternalEvents();
7641
+ $.mobile.navreadyDeferred.resolve();
7516
7642
 
7517
7643
  // check which scrollTop value should be used by scrolling to 1 immediately at domready
7518
7644
  // then check what the scroll top is. Android will report 0... others 1
@@ -7544,6 +7670,19 @@ $( document ).bind( "pagecreate create", function( e ){
7544
7670
  // window load event
7545
7671
  // hide iOS browser chrome on load
7546
7672
  $window.load( $.mobile.silentScroll );
7673
+
7674
+ if ( !$.support.cssPointerEvents ) {
7675
+ // IE and Opera don't support CSS pointer-events: none that we use to disable link-based buttons
7676
+ // by adding the 'ui-disabled' class to them. Using a JavaScript workaround for those browser.
7677
+ // https://github.com/jquery/jquery-mobile/issues/3558
7678
+
7679
+ $( document ).delegate( ".ui-disabled", "vclick",
7680
+ function( e ) {
7681
+ e.preventDefault();
7682
+ e.stopImmediatePropagation();
7683
+ }
7684
+ );
7685
+ }
7547
7686
  });
7548
7687
  }( jQuery, this ));
7549
7688