jquery_mobile_rails 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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