compass-jquery-plugin 0.3.2.0 → 0.3.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,129 @@
1
+ /*!
2
+ * jQuery replaceText - v1.1 - 11/21/2009
3
+ * http://benalman.com/projects/jquery-replacetext-plugin/
4
+ *
5
+ * Copyright (c) 2009 "Cowboy" Ben Alman
6
+ * Dual licensed under the MIT and GPL licenses.
7
+ * http://benalman.com/about/license/
8
+ */
9
+
10
+ // Script: jQuery replaceText: String replace for your jQueries!
11
+ //
12
+ // *Version: 1.1, Last updated: 11/21/2009*
13
+ //
14
+ // Project Home - http://benalman.com/projects/jquery-replacetext-plugin/
15
+ // GitHub - http://github.com/cowboy/jquery-replacetext/
16
+ // Source - http://github.com/cowboy/jquery-replacetext/raw/master/jquery.ba-replacetext.js
17
+ // (Minified) - http://github.com/cowboy/jquery-replacetext/raw/master/jquery.ba-replacetext.min.js (0.5kb)
18
+ //
19
+ // About: License
20
+ //
21
+ // Copyright (c) 2009 "Cowboy" Ben Alman,
22
+ // Dual licensed under the MIT and GPL licenses.
23
+ // http://benalman.com/about/license/
24
+ //
25
+ // About: Examples
26
+ //
27
+ // This working example, complete with fully commented code, illustrates one way
28
+ // in which this plugin can be used.
29
+ //
30
+ // replaceText - http://benalman.com/code/projects/jquery-replacetext/examples/replacetext/
31
+ //
32
+ // About: Support and Testing
33
+ //
34
+ // Information about what version or versions of jQuery this plugin has been
35
+ // tested with, and what browsers it has been tested in.
36
+ //
37
+ // jQuery Versions - 1.3.2, 1.4.1
38
+ // Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome, Opera 9.6-10.1.
39
+ //
40
+ // About: Release History
41
+ //
42
+ // 1.1 - (11/21/2009) Simplified the code and API substantially.
43
+ // 1.0 - (11/21/2009) Initial release
44
+
45
+ (function($){
46
+ '$:nomunge'; // Used by YUI compressor.
47
+
48
+ // Method: jQuery.fn.replaceText
49
+ //
50
+ // Replace text in specified elements. Note that only text content will be
51
+ // modified, leaving all tags and attributes untouched. The new text can be
52
+ // either text or HTML.
53
+ //
54
+ // Uses the String prototype replace method, full documentation on that method
55
+ // can be found here:
56
+ //
57
+ // https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/String/Replace
58
+ //
59
+ // Usage:
60
+ //
61
+ // > jQuery('selector').replaceText( search, replace [, text_only ] );
62
+ //
63
+ // Arguments:
64
+ //
65
+ // search - (RegExp|String) A RegExp object or substring to be replaced.
66
+ // Because the String prototype replace method is used internally, this
67
+ // argument should be specified accordingly.
68
+ // replace - (String|Function) The String that replaces the substring received
69
+ // from the search argument, or a function to be invoked to create the new
70
+ // substring. Because the String prototype replace method is used internally,
71
+ // this argument should be specified accordingly.
72
+ // text_only - (Boolean) If true, any HTML will be rendered as text. Defaults
73
+ // to false.
74
+ //
75
+ // Returns:
76
+ //
77
+ // (jQuery) The initial jQuery collection of elements.
78
+
79
+ $.fn.replaceText = function( search, replace, text_only ) {
80
+ return this.each(function(){
81
+ var node = this.firstChild,
82
+ val,
83
+ new_val,
84
+
85
+ // Elements to be removed at the end.
86
+ remove = [];
87
+
88
+ // Only continue if firstChild exists.
89
+ if ( node ) {
90
+
91
+ // Loop over all childNodes.
92
+ do {
93
+
94
+ // Only process text nodes.
95
+ if ( node.nodeType === 3 ) {
96
+
97
+ // The original node value.
98
+ val = node.nodeValue;
99
+
100
+ // The new value.
101
+ new_val = val.replace( search, replace );
102
+
103
+ // Only replace text if the new value is actually different!
104
+ if ( new_val !== val ) {
105
+
106
+ if ( !text_only && /</.test( new_val ) ) {
107
+ // The new value contains HTML, set it in a slower but far more
108
+ // robust way.
109
+ $(node).before( new_val );
110
+
111
+ // Don't remove the node yet, or the loop will lose its place.
112
+ remove.push( node );
113
+ } else {
114
+ // The new value contains no HTML, so it can be set in this
115
+ // very fast, simple way.
116
+ node.nodeValue = new_val;
117
+ }
118
+ }
119
+ }
120
+
121
+ } while ( node = node.nextSibling );
122
+ }
123
+
124
+ // Time to remove those elements!
125
+ remove.length && $(remove).remove();
126
+ });
127
+ };
128
+
129
+ })(jQuery);
@@ -0,0 +1 @@
1
+ (function(c){c.fn.replaceText=function(f,g,h){return this.each(function(){var a=this.firstChild,d,b,e=[];if(a){do if(a.nodeType===3){d=a.nodeValue;b=d.replace(f,g);if(b!==d)if(!h&&/</.test(b)){c(a).before(b);e.push(a)}else a.nodeValue=b}while(a=a.nextSibling)}e.length&&c(e).remove()})}})(jQuery);
@@ -741,10 +741,12 @@ button.ui-button::-moz-focus-inner {
741
741
  ----------------------------------*/
742
742
  .ui-selectmenu {
743
743
  display: block;
744
+ display: inline-block;
744
745
  position: relative;
745
- height: 2em;
746
+ height: 2.2em;
746
747
  text-decoration: none;
747
- overflow: hidden; }
748
+ overflow: hidden;
749
+ zoom: 1; }
748
750
 
749
751
  .ui-selectmenu-icon {
750
752
  position: absolute;
@@ -784,7 +786,7 @@ button.ui-button::-moz-focus-inner {
784
786
  .ui-selectmenu-menu li a,.ui-selectmenu-status {
785
787
  line-height: 1.4em;
786
788
  display: block;
787
- padding: .3em 1em;
789
+ padding: .405em 1em;
788
790
  outline: none;
789
791
  text-decoration: none; }
790
792
 
@@ -819,7 +821,7 @@ button.ui-button::-moz-focus-inner {
819
821
  .ui-selectmenu-menu li .ui-selectmenu-item-footer {
820
822
  opacity: .8; }
821
823
 
822
- /*for optgroups*/
824
+ /* for optgroups */
823
825
  .ui-selectmenu-menu .ui-selectmenu-group {
824
826
  font-size: 1em; }
825
827
 
@@ -833,6 +835,15 @@ button.ui-button::-moz-focus-inner {
833
835
  margin: 0;
834
836
  padding: 0; }
835
837
 
838
+ /* IE6 workaround (dotted transparent borders) */
839
+ * html .ui-selectmenu-menu li {
840
+ border-color: pink;
841
+ filter: chroma(color = pink);
842
+ width: 100%; }
843
+
844
+ * html .ui-selectmenu-menu li a {
845
+ position: relative; }
846
+
836
847
 
837
848
  /*
838
849
  * jQuery UI Slider 1.8.9
@@ -34,6 +34,8 @@ javascript 'jquery.offline.js'
34
34
  javascript 'jquery.offline.min.js'
35
35
  javascript 'jquery.pngFix.js'
36
36
  javascript 'jquery.pngFix.min.js'
37
+ javascript 'jquery.replacetext.js'
38
+ javascript 'jquery.replacetext.min.js'
37
39
  javascript 'jquery.themeswitchertool.js'
38
40
  javascript 'jquery.themeswitchertool.min.js'
39
41
  javascript 'jquery.tmpl.js'
@@ -1185,7 +1185,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1185
1185
 
1186
1186
  _create: function() {
1187
1187
  var $elem = this.element,
1188
- o = this.options;
1188
+ o = this.options;
1189
1189
 
1190
1190
  this.keepNative = "[data-role='none'], [data-role='nojs']" + (o.keepNative ? ", " + o.keepNative : "");
1191
1191
 
@@ -1236,7 +1236,8 @@ $.widget( "mobile.page", $.mobile.widget, {
1236
1236
 
1237
1237
  // auto-add back btn on pages beyond first view
1238
1238
  if ( o.addBackBtn && role === "header" &&
1239
- $.mobile.urlHistory.stack.length > 0 &&
1239
+ $( ".ui-page" ).length > 1 &&
1240
+ $elem.data( "url" ) !== $.mobile.path.stripHash( location.hash ) &&
1240
1241
  !leftbtn && $this.data( "backbtn" ) !== false ) {
1241
1242
 
1242
1243
  $( "<a href='#' class='ui-btn-left' data-rel='back' data-icon='arrow-l'>"+ o.backBtnText +"</a>" ).prependTo( $this );
@@ -1381,21 +1382,24 @@ $.widget( "mobile.page", $.mobile.widget, {
1381
1382
  //define the url parameter used for referencing widget-generated sub-pages.
1382
1383
  //Translates to to example.html&ui-page=subpageIdentifier
1383
1384
  //hash segment before &ui-page= is used to make Ajax request
1384
- subPageUrlKey: 'ui-page',
1385
+ subPageUrlKey: "ui-page",
1385
1386
 
1386
1387
  //anchor links with a data-rel, or pages with a data-role, that match these selectors will be untrackable in history
1387
1388
  //(no change in URL, not bookmarkable)
1388
- nonHistorySelectors: 'dialog',
1389
+ nonHistorySelectors: "dialog",
1389
1390
 
1390
1391
  //class assigned to page currently in view, and during transitions
1391
- activePageClass: 'ui-page-active',
1392
+ activePageClass: "ui-page-active",
1392
1393
 
1393
1394
  //class used for "active" button state, from CSS framework
1394
- activeBtnClass: 'ui-btn-active',
1395
+ activeBtnClass: "ui-btn-active",
1395
1396
 
1396
1397
  //automatically handle clicks and form submissions through Ajax, when same-domain
1397
1398
  ajaxEnabled: true,
1398
1399
 
1400
+ //automatically load and show pages based on location.hash
1401
+ hashListeningEnabled: true,
1402
+
1399
1403
  // TODO: deprecated - remove at 1.0
1400
1404
  //automatically handle link clicks through Ajax, when possible
1401
1405
  ajaxLinksEnabled: true,
@@ -1405,7 +1409,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1405
1409
  ajaxFormsEnabled: true,
1406
1410
 
1407
1411
  //set default transition - 'none' for no transitions
1408
- defaultTransition: 'slide',
1412
+ defaultTransition: "slide",
1409
1413
 
1410
1414
  //show loading message during Ajax requests
1411
1415
  //if false, message will not appear, but loading classes will still be toggled on html el
@@ -1421,6 +1425,9 @@ $.widget( "mobile.page", $.mobile.widget, {
1421
1425
  return $.support.mediaquery;
1422
1426
  },
1423
1427
 
1428
+ //automatically initialize first pages or not.
1429
+ autoInitialize: true,
1430
+
1424
1431
  //TODO might be useful upstream in jquery itself ?
1425
1432
  keyCode: {
1426
1433
  ALT: 18,
@@ -1460,7 +1467,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1460
1467
 
1461
1468
 
1462
1469
  //trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
1463
- $( window.document ).trigger('mobileinit');
1470
+ $( window.document ).trigger( "mobileinit" );
1464
1471
 
1465
1472
 
1466
1473
  //support conditions
@@ -1473,25 +1480,25 @@ $.widget( "mobile.page", $.mobile.widget, {
1473
1480
 
1474
1481
  //define vars for interal use
1475
1482
  var $window = $(window),
1476
- $html = $('html'),
1477
- $head = $('head'),
1483
+ $html = $( "html" ),
1484
+ $head = $( "head" ),
1478
1485
 
1479
1486
  //loading div which appears during Ajax requests
1480
1487
  //will not appear if $.mobile.loadingMessage is false
1481
1488
  $loader = $.mobile.loadingMessage ?
1482
- $('<div class="ui-loader ui-body-a ui-corner-all">'+
1483
- '<span class="ui-icon ui-icon-loading spin"></span>'+
1484
- '<h1>'+ $.mobile.loadingMessage +'</h1>'+
1485
- '</div>')
1489
+ $( "<div class='ui-loader ui-body-a ui-corner-all'>" +
1490
+ "<span class='ui-icon ui-icon-loading spin'></span>" +
1491
+ "<h1>" + $.mobile.loadingMessage + "</h1>" +
1492
+ "</div>" )
1486
1493
  : undefined;
1487
1494
 
1488
1495
 
1489
1496
  //add mobile, initial load "rendering" classes to docEl
1490
- $html.addClass('ui-mobile ui-mobile-rendering');
1497
+ $html.addClass( "ui-mobile ui-mobile-rendering" );
1491
1498
 
1492
1499
 
1493
1500
  //define & prepend meta viewport tag, if content is defined
1494
- $.mobile.metaViewportContent ? $("<meta>", { name: "viewport", content: $.mobile.metaViewportContent}).prependTo( $head ) : undefined;
1501
+ $.mobile.metaViewportContent ? $( "<meta>", { name: "viewport", content: $.mobile.metaViewportContent}).prependTo( $head ) : undefined;
1495
1502
 
1496
1503
 
1497
1504
  //expose some core utilities
@@ -1503,10 +1510,17 @@ $.widget( "mobile.page", $.mobile.widget, {
1503
1510
  $html.removeClass( "ui-loading" );
1504
1511
  } else {
1505
1512
  if( $.mobile.loadingMessage ){
1506
- var activeLink = $( "." + $.mobile.activeBtnClass ).eq(0),
1507
- yPos = activeLink.length ? activeLink.offset().top : $(window).scrollTop() + 75;
1508
- $loader.appendTo($.mobile.pageContainer).css({top: yPos});
1513
+ var activeBtn =$( "." + $.mobile.activeBtnClass ).first();
1514
+
1515
+ $loader
1516
+ .appendTo( $.mobile.pageContainer )
1517
+ //position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
1518
+ .css( {
1519
+ top: $.support.scrollTop && $(window).scrollTop() + $(window).height() / 2 ||
1520
+ activeBtn.length && activeBtn.offset().top || 100
1521
+ } );
1509
1522
  }
1523
+
1510
1524
  $html.addClass( "ui-loading" );
1511
1525
  }
1512
1526
  },
@@ -1516,46 +1530,52 @@ $.widget( "mobile.page", $.mobile.widget, {
1516
1530
  ypos = ypos || 0;
1517
1531
  // prevent scrollstart and scrollstop events
1518
1532
  $.event.special.scrollstart.enabled = false;
1533
+
1519
1534
  setTimeout(function() {
1520
1535
  window.scrollTo( 0, ypos );
1521
- $(document).trigger("silentscroll", { x: 0, y: ypos });
1536
+ $(document).trigger( "silentscroll", { x: 0, y: ypos });
1522
1537
  },20);
1538
+
1523
1539
  setTimeout(function() {
1524
1540
  $.event.special.scrollstart.enabled = true;
1525
1541
  }, 150 );
1526
- }
1527
- });
1528
-
1529
-
1530
- //dom-ready inits
1531
- $(function(){
1532
-
1533
- //find present pages
1534
- var $pages = $("[data-role='page']");
1535
-
1536
- $("[data-role='page'], [data-role='dialog']").each(function(){
1537
- $(this).attr('data-url', $(this).attr('id'));
1538
- });
1542
+ },
1539
1543
 
1540
- //set up active page
1541
- $.mobile.startPage = $.mobile.activePage = $pages.first();
1544
+ // find and enhance the pages in the dom and transition to the first page.
1545
+ initializePage: function(){
1546
+ //find present pages
1547
+ var $pages = $( "[data-role='page']" );
1542
1548
 
1543
- //set page container
1544
- $.mobile.pageContainer = $.mobile.startPage.parent().addClass('ui-mobile-viewport');
1549
+ //add dialogs, set data-url attrs
1550
+ $pages.add( "[data-role='dialog']" ).each(function(){
1551
+ $(this).attr( "data-url", $(this).attr( "id" ));
1552
+ });
1545
1553
 
1546
- //cue page loading message
1547
- $.mobile.pageLoading();
1554
+ //define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
1555
+ $.mobile.firstPage = $pages.first();
1548
1556
 
1549
- //initialize all pages present
1550
- $pages.page();
1557
+ //define page container
1558
+ $.mobile.pageContainer = $pages.first().parent().addClass( "ui-mobile-viewport" );
1551
1559
 
1552
- //trigger a new hashchange, hash or not
1553
- $window.trigger( "hashchange", [ true ] );
1560
+ //cue page loading message
1561
+ $.mobile.pageLoading();
1554
1562
 
1555
- //remove rendering class
1556
- $html.removeClass('ui-mobile-rendering');
1563
+ // if hashchange listening is disabled or there's no hash deeplink, change to the first page in the DOM
1564
+ if( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ){
1565
+ $.mobile.changePage( $.mobile.firstPage, false, true, false, true );
1566
+ }
1567
+ // otherwise, trigger a hashchange to load a deeplink
1568
+ else {
1569
+ $window.trigger( "hashchange", [ true ] );
1570
+ }
1571
+ }
1557
1572
  });
1558
1573
 
1574
+ //dom-ready inits
1575
+ if($.mobile.autoInitialize){
1576
+ $($.mobile.initializePage);
1577
+ }
1578
+
1559
1579
 
1560
1580
  //window load event
1561
1581
  //hide iOS browser chrome on load
@@ -1593,7 +1613,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1593
1613
  var splitkey = '&' + $.mobile.subPageUrlKey;
1594
1614
  return path && path.split( splitkey )[0].split( dialogHashKey )[0];
1595
1615
  },
1596
-
1616
+
1597
1617
  //set location hash to path
1598
1618
  set: function( path ){
1599
1619
  location.hash = path;
@@ -1605,83 +1625,93 @@ $.widget( "mobile.page", $.mobile.widget, {
1605
1625
  setOrigin: function(){
1606
1626
  path.origin = path.get( location.protocol + '//' + location.host + location.pathname );
1607
1627
  },
1608
-
1628
+
1609
1629
  //prefix a relative url with the current path
1610
1630
  makeAbsolute: function( url ){
1611
1631
  return path.get() + url;
1612
1632
  },
1613
-
1633
+
1614
1634
  //return a url path with the window's location protocol/hostname removed
1615
1635
  clean: function( url ){
1616
1636
  return url.replace( location.protocol + "//" + location.host, "");
1617
1637
  },
1618
-
1638
+
1619
1639
  //just return the url without an initial #
1620
1640
  stripHash: function( url ){
1621
1641
  return url.replace( /^#/, "" );
1622
1642
  },
1623
-
1643
+
1624
1644
  //check whether a url is referencing the same domain, or an external domain or different protocol
1625
1645
  //could be mailto, etc
1626
1646
  isExternal: function( url ){
1627
1647
  return path.hasProtocol( path.clean( url ) );
1628
1648
  },
1629
-
1649
+
1630
1650
  hasProtocol: function( url ){
1631
1651
  return /^(:?\w+:)/.test( url );
1632
1652
  },
1633
-
1653
+
1634
1654
  //check if the url is relative
1635
1655
  isRelative: function( url ){
1636
1656
  return /^[^\/|#]/.test( url ) && !path.hasProtocol( url );
1657
+ },
1658
+
1659
+ isEmbeddedPage: function( url ){
1660
+ return /^#/.test( url );
1637
1661
  }
1638
1662
  },
1639
1663
 
1640
1664
  //will be defined when a link is clicked and given an active class
1641
1665
  $activeClickedLink = null,
1642
-
1666
+
1643
1667
  //urlHistory is purely here to make guesses at whether the back or forward button was clicked
1644
1668
  //and provide an appropriate transition
1645
1669
  urlHistory = {
1646
1670
  //array of pages that are visited during a single page load. each has a url and optional transition
1647
1671
  stack: [],
1648
-
1672
+
1649
1673
  //maintain an index number for the active page in the stack
1650
1674
  activeIndex: 0,
1651
-
1675
+
1652
1676
  //get active
1653
1677
  getActive: function(){
1654
1678
  return urlHistory.stack[ urlHistory.activeIndex ];
1655
1679
  },
1656
-
1680
+
1657
1681
  getPrev: function(){
1658
1682
  return urlHistory.stack[ urlHistory.activeIndex - 1 ];
1659
1683
  },
1660
-
1684
+
1661
1685
  getNext: function(){
1662
1686
  return urlHistory.stack[ urlHistory.activeIndex + 1 ];
1663
1687
  },
1664
-
1688
+
1665
1689
  // addNew is used whenever a new page is added
1666
1690
  addNew: function( url, transition ){
1667
1691
  //if there's forward history, wipe it
1668
1692
  if( urlHistory.getNext() ){
1669
1693
  urlHistory.clearForward();
1670
1694
  }
1671
-
1695
+
1672
1696
  urlHistory.stack.push( {url : url, transition: transition } );
1673
-
1697
+
1674
1698
  urlHistory.activeIndex = urlHistory.stack.length - 1;
1675
1699
  },
1676
-
1700
+
1677
1701
  //wipe urls ahead of active index
1678
1702
  clearForward: function(){
1679
1703
  urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 );
1680
1704
  },
1681
1705
 
1682
- //enable/disable hashchange event listener
1706
+ //wipe all urls
1707
+ clear: function(){
1708
+ urlHistory.stack = [];
1709
+ urlHistory.activeIndex = 0;
1710
+ },
1711
+
1712
+ //disable hashchange event listener internally to ignore one change
1683
1713
  //toggled internally when location.hash is updated to match the url of a successful page load
1684
- listeningEnabled: true
1714
+ ignoreNextHashChange: true
1685
1715
  },
1686
1716
 
1687
1717
  //define first selector to receive focus when a page is shown
@@ -1689,7 +1719,13 @@ $.widget( "mobile.page", $.mobile.widget, {
1689
1719
 
1690
1720
  //contains role for next page, if defined on clicked link via data-rel
1691
1721
  nextPageRole = null,
1692
-
1722
+
1723
+ //queue to hold simultanious page transitions
1724
+ pageTransitionQueue = [],
1725
+
1726
+ // indicates whether or not page is in process of transitioning
1727
+ isPageTransitioning = false,
1728
+
1693
1729
  //nonsense hash change key for dialogs, so they create a history entry
1694
1730
  dialogHashKey = "&ui-state=dialog";
1695
1731
 
@@ -1779,7 +1815,8 @@ $.widget( "mobile.page", $.mobile.widget, {
1779
1815
  return $(this).one('webkitAnimationEnd', callback);
1780
1816
  }
1781
1817
  else{
1782
- callback();
1818
+ // defer execution for consistency between webkit/non webkit
1819
+ setTimeout(callback, 0);
1783
1820
  }
1784
1821
  };
1785
1822
 
@@ -1790,17 +1827,17 @@ $.widget( "mobile.page", $.mobile.widget, {
1790
1827
  //update location.hash, with or without triggering hashchange event
1791
1828
  //TODO - deprecate this one at 1.0
1792
1829
  $.mobile.updateHash = path.set;
1793
-
1830
+
1794
1831
  //expose path object on $.mobile
1795
1832
  $.mobile.path = path;
1796
-
1833
+
1797
1834
  //expose base object on $.mobile
1798
1835
  $.mobile.base = base;
1799
1836
 
1800
1837
  //url stack, useful when plugins need to be aware of previous pages viewed
1801
1838
  //TODO: deprecate this one at 1.0
1802
1839
  $.mobile.urlstack = urlHistory.stack;
1803
-
1840
+
1804
1841
  //history stack
1805
1842
  $.mobile.urlHistory = urlHistory;
1806
1843
 
@@ -1820,19 +1857,25 @@ $.widget( "mobile.page", $.mobile.widget, {
1820
1857
  currPage = urlHistory.getActive(),
1821
1858
  back = false,
1822
1859
  forward = false;
1823
-
1824
-
1860
+
1861
+
1825
1862
  // If we are trying to transition to the same page that we are currently on ignore the request.
1826
1863
  // an illegal same page request is defined by the current page being the same as the url, as long as there's history
1827
1864
  // and to is not an array or object (those are allowed to be "same")
1828
1865
  if( currPage && urlHistory.stack.length > 1 && currPage.url === url && !toIsArray && !toIsObject ) {
1829
1866
  return;
1830
- }
1831
-
1867
+ }
1868
+ else if(isPageTransitioning) {
1869
+ pageTransitionQueue.unshift(arguments);
1870
+ return;
1871
+ }
1872
+
1873
+ isPageTransitioning = true;
1874
+
1832
1875
  // if the changePage was sent from a hashChange event
1833
1876
  // guess if it came from the history menu
1834
1877
  if( fromHashChange ){
1835
-
1878
+
1836
1879
  // check if url is in history and if it's ahead or behind current page
1837
1880
  $.each( urlHistory.stack, function( i ){
1838
1881
  //if the url is in the stack, it's a forward or a back
@@ -1846,7 +1889,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1846
1889
  urlHistory.activeIndex = i;
1847
1890
  }
1848
1891
  });
1849
-
1892
+
1850
1893
  //if it's a back, use reverse animation
1851
1894
  if( back ){
1852
1895
  reverse = true;
@@ -1856,7 +1899,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1856
1899
  transition = transition || urlHistory.getActive().transition;
1857
1900
  }
1858
1901
  }
1859
-
1902
+
1860
1903
 
1861
1904
  if( toIsObject && to.url ){
1862
1905
  url = to.url,
@@ -1868,7 +1911,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1868
1911
  if($.type( data ) == "object" ){
1869
1912
  data = $.param(data);
1870
1913
  }
1871
-
1914
+
1872
1915
  url += "?" + data;
1873
1916
  data = undefined;
1874
1917
  }
@@ -1878,7 +1921,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1878
1921
  if(base){ base.reset(); }
1879
1922
 
1880
1923
  //kill the keyboard
1881
- $( window.document.activeElement ).add(':focus').blur();
1924
+ $( window.document.activeElement ).add( "input:focus, textarea:focus, select:focus" ).blur();
1882
1925
 
1883
1926
  function defaultTransition(){
1884
1927
  if(transition === undefined){
@@ -1892,52 +1935,65 @@ $.widget( "mobile.page", $.mobile.widget, {
1892
1935
 
1893
1936
  //get current scroll distance
1894
1937
  var currScroll = $window.scrollTop(),
1895
- perspectiveTransitions = ["flip"],
1938
+ perspectiveTransitions = [ "flip" ],
1896
1939
  pageContainerClasses = [];
1897
-
1898
- //support deep-links to generated sub-pages
1940
+
1941
+ //support deep-links to generated sub-pages
1899
1942
  if( url.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ){
1900
1943
  to = $( "[data-url='" + url + "']" );
1901
1944
  }
1902
-
1903
- //set as data for returning to that spot
1904
- from.data('lastScroll', currScroll);
1905
-
1906
- //trigger before show/hide events
1907
- from.data("page")._trigger("beforehide", {nextPage: to});
1908
- to.data("page")._trigger("beforeshow", {prevPage: from});
1945
+
1946
+ if( from ){
1947
+ //set as data for returning to that spot
1948
+ from.data( "lastScroll", currScroll);
1949
+ //trigger before show/hide events
1950
+ from.data( "page" )._trigger( "beforehide", { nextPage: to } );
1951
+ }
1952
+ to.data( "page" )._trigger( "beforeshow", { prevPage: from || $("") } );
1909
1953
 
1910
1954
  function loadComplete(){
1911
1955
 
1912
1956
  if( changeHash !== false && url ){
1913
- if( !back ){
1914
- urlHistory.listeningEnabled = false;
1915
- }
1957
+ //disable hash listening temporarily
1958
+ urlHistory.ignoreNextHashChange = false;
1959
+ //update hash and history
1916
1960
  path.set( url );
1917
- urlHistory.listeningEnabled = true;
1918
1961
  }
1919
-
1962
+
1920
1963
  //add page to history stack if it's not back or forward
1921
1964
  if( !back && !forward ){
1922
1965
  urlHistory.addNew( url, transition );
1923
1966
  }
1924
-
1967
+
1925
1968
  removeActiveLinkClass();
1926
1969
 
1927
1970
  //jump to top or prev scroll, sometimes on iOS the page has not rendered yet. I could only get by this with a setTimeout, but would like to avoid that.
1928
- $.mobile.silentScroll( to.data( 'lastScroll' ) );
1971
+ $.mobile.silentScroll( to.data( "lastScroll" ) );
1972
+
1929
1973
  reFocus( to );
1930
1974
 
1931
- //trigger show/hide events, allow preventing focus change through return false
1932
- from.data("page")._trigger("hide", null, {nextPage: to});
1933
- if( to.data("page")._trigger("show", null, {prevPage: from}) !== false ){
1934
- $.mobile.activePage = to;
1975
+ //trigger show/hide events
1976
+ if( from ){
1977
+ from.data( "page" )._trigger( "hide", null, { nextPage: to } );
1935
1978
  }
1979
+ //trigger pageshow, define prevPage as either from or empty jQuery obj
1980
+ to.data( "page" )._trigger( "show", null, { prevPage: from || $("") } );
1981
+
1982
+ //set "to" as activePage
1983
+ $.mobile.activePage = to;
1936
1984
 
1937
1985
  //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
1938
1986
  if (duplicateCachedPage != null) {
1939
1987
  duplicateCachedPage.remove();
1940
1988
  }
1989
+
1990
+ //remove initial build class (only present on first pageshow)
1991
+ $html.removeClass( "ui-mobile-rendering" );
1992
+
1993
+ isPageTransitioning = false
1994
+ if(pageTransitionQueue.length>0) {
1995
+ $.mobile.changePage.apply($.mobile, pageTransitionQueue.pop());
1996
+ }
1941
1997
  };
1942
1998
 
1943
1999
  function addContainerClass(className){
@@ -1952,8 +2008,8 @@ $.widget( "mobile.page", $.mobile.widget, {
1952
2008
 
1953
2009
  pageContainerClasses = [];
1954
2010
  };
1955
-
1956
-
2011
+
2012
+
1957
2013
 
1958
2014
  if(transition && (transition !== 'none')){
1959
2015
  $.mobile.pageLoading( true );
@@ -1963,26 +2019,27 @@ $.widget( "mobile.page", $.mobile.widget, {
1963
2019
 
1964
2020
  addContainerClass('ui-mobile-viewport-transitioning');
1965
2021
 
1966
- /* animate in / out
1967
- * This is in a setTimeout because we were often seeing pages in not animate across but rather go straight to
1968
- * the 'to' page. The loadComplete would still fire, so the browser thought it was applying the animation. From
1969
- * what I could tell this was a problem with the classes not being applied yet.
1970
- */
1971
- setTimeout(function() { from.addClass( transition + " out " + ( reverse ? "reverse" : "" ) );
2022
+ if( from ){
2023
+ from.addClass( transition + " out " + ( reverse ? "reverse" : "" ) );
2024
+ }
1972
2025
  to.addClass( $.mobile.activePageClass + " " + transition +
1973
- " in " + ( reverse ? "reverse" : "" ) ); } , 0);
2026
+ " in " + ( reverse ? "reverse" : "" ) );
1974
2027
 
1975
2028
  // callback - remove classes, etc
1976
2029
  to.animationComplete(function() {
1977
2030
  from.add( to ).removeClass("out in reverse " + transition );
1978
- from.removeClass( $.mobile.activePageClass );
2031
+ if( from ){
2032
+ from.removeClass( $.mobile.activePageClass );
2033
+ }
1979
2034
  loadComplete();
1980
2035
  removeContainerClasses();
1981
2036
  });
1982
2037
  }
1983
2038
  else{
1984
2039
  $.mobile.pageLoading( true );
1985
- from.removeClass( $.mobile.activePageClass );
2040
+ if( from ){
2041
+ from.removeClass( $.mobile.activePageClass );
2042
+ }
1986
2043
  to.addClass( $.mobile.activePageClass );
1987
2044
  loadComplete();
1988
2045
  }
@@ -2017,7 +2074,7 @@ $.widget( "mobile.page", $.mobile.widget, {
2017
2074
  fileUrl = toIDfileurl;
2018
2075
  }
2019
2076
  }
2020
-
2077
+
2021
2078
  // ensure a transition has been set where pop is undefined
2022
2079
  defaultTransition();
2023
2080
 
@@ -2025,7 +2082,7 @@ $.widget( "mobile.page", $.mobile.widget, {
2025
2082
  if ( to.length && !isFormRequest ) {
2026
2083
  if( fileUrl && base ){
2027
2084
  base.set( fileUrl );
2028
- }
2085
+ }
2029
2086
  enhancePage();
2030
2087
  transitionPages();
2031
2088
  } else {
@@ -2042,23 +2099,23 @@ $.widget( "mobile.page", $.mobile.widget, {
2042
2099
  type: type,
2043
2100
  data: data,
2044
2101
  success: function( html ) {
2045
-
2046
- //pre-parse html to check for a data-url,
2102
+
2103
+ //pre-parse html to check for a data-url,
2047
2104
  //use it as the new fileUrl, base path, etc
2048
2105
  var redirectLoc = / data-url="(.*)"/.test( html ) && RegExp.$1;
2049
2106
 
2050
2107
  if( redirectLoc ){
2051
2108
  if(base){
2052
2109
  base.set( redirectLoc );
2053
- }
2054
- url = fileUrl = path.makeAbsolute( path.getFilePath( redirectLoc ) );
2110
+ }
2111
+ url = fileUrl = path.getFilePath( redirectLoc );
2055
2112
  }
2056
2113
  else {
2057
2114
  if(base){
2058
2115
  base.set(fileUrl);
2059
- }
2116
+ }
2060
2117
  }
2061
-
2118
+
2062
2119
  var all = $("<div></div>");
2063
2120
  //workaround to allow scripts to execute when included in page divs
2064
2121
  all.get(0).innerHTML = html;
@@ -2079,14 +2136,14 @@ $.widget( "mobile.page", $.mobile.widget, {
2079
2136
  }
2080
2137
  });
2081
2138
  }
2082
-
2139
+
2083
2140
  //append to page and enhance
2084
2141
  to
2085
2142
  .attr( "data-url", fileUrl )
2086
2143
  .appendTo( $.mobile.pageContainer );
2087
2144
 
2088
2145
  enhancePage();
2089
- transitionPages();
2146
+ setTimeout(function() { transitionPages() }, 0);
2090
2147
  },
2091
2148
  error: function() {
2092
2149
  $.mobile.pageLoading( true );
@@ -2142,22 +2199,37 @@ $.widget( "mobile.page", $.mobile.widget, {
2142
2199
 
2143
2200
  //click routing - direct to HTTP or Ajax, accordingly
2144
2201
  $( "a" ).live( "click", function(event) {
2145
-
2202
+
2146
2203
  var $this = $(this),
2147
- //get href, remove same-domain protocol and host
2148
- url = path.clean( $this.attr( "href" ) ),
2149
-
2150
- //check if it's external
2151
- isExternal = path.isExternal( url ) || $this.is( "[rel='external']" ),
2204
+
2205
+ //get href, if defined, otherwise fall to null #
2206
+ href = $this.attr( "href" ) || "#",
2152
2207
 
2208
+ //get href, remove same-domain protocol and host
2209
+ url = path.clean( href ),
2210
+
2211
+ //rel set to external
2212
+ isRelExternal = $this.is( "[rel='external']" ),
2213
+
2214
+ //rel set to external
2215
+ isEmbeddedPage = path.isEmbeddedPage( url ),
2216
+
2217
+ //check for protocol or rel and its not an embedded page
2218
+ //TODO overlap in logic from isExternal, rel=external check should be
2219
+ // moved into more comprehensive isExternalLink
2220
+ isExternal = path.isExternal( url ) || isRelExternal && !isEmbeddedPage,
2221
+
2153
2222
  //if target attr is specified we mimic _blank... for now
2154
- hasTarget = $this.is( "[target]" );
2155
-
2223
+ hasTarget = $this.is( "[target]" ),
2224
+
2225
+ //if data-ajax attr is set to false, use the default behavior of a link
2226
+ hasAjaxDisabled = $this.is( "[data-ajax='false']" );
2227
+
2156
2228
  //if there's a data-rel=back attr, go back in history
2157
2229
  if( $this.is( "[data-rel='back']" ) ){
2158
2230
  window.history.back();
2159
2231
  return false;
2160
- }
2232
+ }
2161
2233
 
2162
2234
  if( url === "#" ){
2163
2235
  //for links created purely for interaction - ignore
@@ -2166,7 +2238,7 @@ $.widget( "mobile.page", $.mobile.widget, {
2166
2238
 
2167
2239
  $activeClickedLink = $this.closest( ".ui-btn" ).addClass( $.mobile.activeBtnClass );
2168
2240
 
2169
- if( isExternal || hasTarget || !$.mobile.ajaxEnabled ||
2241
+ if( isExternal || hasAjaxDisabled || hasTarget || !$.mobile.ajaxEnabled ||
2170
2242
  // TODO: deprecated - remove at 1.0
2171
2243
  !$.mobile.ajaxLinksEnabled ){
2172
2244
  //remove active link class if external (then it won't be there if you come back)
@@ -2176,6 +2248,9 @@ $.widget( "mobile.page", $.mobile.widget, {
2176
2248
  if( hasTarget ){
2177
2249
  window.open( url );
2178
2250
  }
2251
+ else if( hasAjaxDisabled ){
2252
+ return;
2253
+ }
2179
2254
  else{
2180
2255
  location.href = url;
2181
2256
  }
@@ -2184,11 +2259,11 @@ $.widget( "mobile.page", $.mobile.widget, {
2184
2259
  //use ajax
2185
2260
  var transition = $this.data( "transition" ),
2186
2261
  direction = $this.data("direction"),
2187
- reverse = direction && direction == "reverse" ||
2262
+ reverse = direction && direction == "reverse" ||
2188
2263
  // deprecated - remove by 1.0
2189
2264
  $this.data( "back" );
2190
-
2191
- //this may need to be more specific as we use data-rel more
2265
+
2266
+ //this may need to be more specific as we use data-rel more
2192
2267
  nextPageRole = $this.attr( "data-rel" );
2193
2268
 
2194
2269
  //if it's a relative href, prefix href with base url
@@ -2204,45 +2279,30 @@ $.widget( "mobile.page", $.mobile.widget, {
2204
2279
  });
2205
2280
 
2206
2281
 
2207
-
2208
2282
  //hashchange event handler
2209
- $window.bind( "hashchange", function(e, triggered) {
2210
- if( !triggered && ( !urlHistory.listeningEnabled || !$.mobile.ajaxEnabled ||
2211
- // TODO: deprecated - remove at 1.0
2212
- // only links need to be checked here, as forms don't trigger a hashchange event (they just silently update the hash)
2213
- !$.mobile.ajaxLinksEnabled ) ){
2214
- return;
2215
- }
2216
-
2283
+ $window.bind( "hashchange", function( e, triggered ) {
2284
+ //find first page via hash
2217
2285
  var to = path.stripHash( location.hash ),
2218
- transition = triggered ? false : undefined;
2219
-
2220
- //make sure that hash changes that produce a dialog url do nothing
2221
- if( urlHistory.stack.length > 1 &&
2222
- to.indexOf( dialogHashKey ) > -1 &&
2223
- !$.mobile.activePage.is( ".ui-dialog" ) ){
2286
+ //transition is false if it's the first page, undefined otherwise (and may be overridden by default)
2287
+ transition = $.mobile.urlHistory.stack.length === 0 ? false : undefined;
2288
+
2289
+ //if listening is disabled (either globally or temporarily), or it's a dialog hash
2290
+ if( !$.mobile.hashListeningEnabled || !urlHistory.ignoreNextHashChange ||
2291
+ urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 && !$.mobile.activePage.is( ".ui-dialog" )
2292
+ ){
2293
+ if( !urlHistory.ignoreNextHashChange ){
2294
+ urlHistory.ignoreNextHashChange = true;
2295
+ }
2224
2296
  return;
2225
- }
2297
+ }
2226
2298
 
2227
- //if to is defined, use it
2299
+ //if to is defined, load it
2228
2300
  if ( to ){
2229
2301
  $.mobile.changePage( to, transition, undefined, false, true );
2230
2302
  }
2231
- //there's no hash, the active page is not the start page, and it's not manually triggered hashchange
2232
- //we probably backed out to the first page visited
2233
- else if( $.mobile.activePage.length && $.mobile.startPage[0] !== $.mobile.activePage[0] && !triggered ) {
2234
- $.mobile.changePage( $.mobile.startPage, transition, true, false, true );
2235
- }
2236
- //probably the first page - show it
2237
- else{
2238
- urlHistory.addNew( "" );
2239
- $.mobile.startPage.trigger("pagebeforeshow", {prevPage: $('')});
2240
- $.mobile.startPage.addClass( $.mobile.activePageClass );
2241
- $.mobile.pageLoading( true );
2242
-
2243
- if( $.mobile.startPage.trigger("pageshow", {prevPage: $('')}) !== false ){
2244
- reFocus($.mobile.startPage);
2245
- }
2303
+ //there's no hash, go to the first page in the dom
2304
+ else {
2305
+ $.mobile.changePage( $.mobile.firstPage, transition, true, false, true );
2246
2306
  }
2247
2307
  });
2248
2308
  })( jQuery );
@@ -2333,11 +2393,11 @@ $.fn.buttonMarkup.defaults = {
2333
2393
 
2334
2394
  var attachEvents = function() {
2335
2395
  $(".ui-btn:not(.ui-disabled)").live({
2336
- mousedown: function() {
2396
+ "touchstart mousedown": function() {
2337
2397
  var theme = $(this).attr( "data-theme" );
2338
2398
  $(this).removeClass( "ui-btn-up-" + theme ).addClass( "ui-btn-down-" + theme );
2339
2399
  },
2340
- mouseup: function() {
2400
+ "touchmove touchend mouseup": function() {
2341
2401
  var theme = $(this).attr( "data-theme" );
2342
2402
  $(this).removeClass( "ui-btn-down-" + theme ).addClass( "ui-btn-up-" + theme );
2343
2403
  },
@@ -3108,7 +3168,7 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3108
3168
  overlayTheme: 'a',
3109
3169
  hidePlaceholderMenuItems: true,
3110
3170
  closeText: 'Close',
3111
- useNativeMenu: false
3171
+ nativeMenu: false
3112
3172
  },
3113
3173
  _create: function(){
3114
3174
 
@@ -3118,28 +3178,18 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3118
3178
 
3119
3179
  select = this.element
3120
3180
  .wrap( "<div class='ui-select'>" ),
3121
-
3122
- selectID = select.attr( "id" ),
3123
-
3124
- isMultiple = self.isMultiple = select[0].multiple,
3125
-
3126
- options = select.find("option"),
3127
-
3181
+
3182
+ selectID = select.attr( "id" ),
3183
+
3128
3184
  label = $( "label[for="+ selectID +"]" ).addClass( "ui-select" ),
3129
3185
 
3130
- buttonId = selectID + "-button",
3131
-
3132
- menuId = selectID + "-menu",
3133
-
3134
- thisPage = select.closest( ".ui-page" ),
3135
-
3136
- button = $( "<a>", {
3186
+ button = ( self.options.nativeMenu ? $( "<div/>" ) : $( "<a>", {
3137
3187
  "href": "#",
3138
3188
  "role": "button",
3139
3189
  "id": buttonId,
3140
3190
  "aria-haspopup": "true",
3141
3191
  "aria-owns": menuId
3142
- })
3192
+ }) )
3143
3193
  .text( $( select[0].options.item(select[0].selectedIndex) ).text() )
3144
3194
  .insertBefore( select )
3145
3195
  .buttonMarkup({
@@ -3151,61 +3201,81 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3151
3201
  shadow: o.shadow,
3152
3202
  iconshadow: o.iconshadow
3153
3203
  }),
3154
-
3155
- theme = /ui-btn-up-([a-z])/.exec( button.attr("class") )[1],
3156
-
3157
- menuPage = $( "<div data-role='dialog' data-theme='"+ o.menuPageTheme +"'>" +
3158
- "<div data-role='header'>" +
3159
- "<div class='ui-title'>" + label.text() + "</div>"+
3160
- "</div>"+
3161
- "<div data-role='content'></div>"+
3162
- "</div>" )
3163
- .appendTo( $.mobile.pageContainer )
3164
- .page(),
3165
-
3166
- menuPageContent = menuPage.find( ".ui-content" ),
3167
-
3168
- menuPageClose = menuPage.find( ".ui-header a" ),
3169
-
3170
- screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"})
3171
- .appendTo( thisPage ),
3172
-
3173
- listbox = $( "<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all pop ui-body-" + o.overlayTheme } )
3174
- .insertAfter(screen),
3175
-
3176
- list = $( "<ul>", {
3177
- "class": "ui-selectmenu-list",
3178
- "id": menuId,
3179
- "role": "listbox",
3180
- "aria-labelledby": buttonId,
3181
- "data-theme": theme
3182
- })
3183
- .appendTo( listbox ),
3184
-
3185
- header = $( "<div>", {
3186
- "class": "ui-header ui-bar-" + theme
3187
- })
3188
- .prependTo( listbox ),
3189
-
3190
- headerTitle = $( "<h1>", {
3191
- "class": "ui-title"
3192
- })
3193
- .appendTo( header ),
3194
-
3195
- headerClose = $( "<a>", {
3196
- "data-iconpos": "notext",
3197
- "data-icon": "delete",
3198
- "text": o.closeText,
3199
- "href": "#",
3200
- "class": "ui-btn-left"
3201
- })
3202
- .appendTo( header )
3203
- .buttonMarkup(),
3204
-
3205
- menuType;
3206
-
3207
- // set to native menu
3208
- o.useNativeMenu = $.mobile.nativeSelectMenus || select.is( "[data-native]" );
3204
+
3205
+ //multi select or not
3206
+ isMultiple = self.isMultiple = select[0].multiple;
3207
+
3208
+ //Opera does not properly support opacity on select elements
3209
+ //In Mini, it hides the element, but not its text
3210
+ //On the desktop,it seems to do the opposite
3211
+ //for these reasons, using the nativeMenu option results in a full native select in Opera
3212
+ if( o.nativeMenu && window.opera && window.opera.version ){
3213
+ select.addClass( "ui-select-nativeonly" );
3214
+ }
3215
+
3216
+ //vars for non-native menus
3217
+ if( !o.nativeMenu ){
3218
+ var options = select.find("option"),
3219
+
3220
+ buttonId = selectID + "-button",
3221
+
3222
+ menuId = selectID + "-menu",
3223
+
3224
+ thisPage = select.closest( ".ui-page" ),
3225
+
3226
+ //button theme
3227
+ theme = /ui-btn-up-([a-z])/.exec( button.attr("class") )[1],
3228
+
3229
+ menuPage = $( "<div data-role='dialog' data-theme='"+ o.menuPageTheme +"'>" +
3230
+ "<div data-role='header'>" +
3231
+ "<div class='ui-title'>" + label.text() + "</div>"+
3232
+ "</div>"+
3233
+ "<div data-role='content'></div>"+
3234
+ "</div>" )
3235
+ .appendTo( $.mobile.pageContainer )
3236
+ .page(),
3237
+
3238
+ menuPageContent = menuPage.find( ".ui-content" ),
3239
+
3240
+ menuPageClose = menuPage.find( ".ui-header a" ),
3241
+
3242
+ screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"})
3243
+ .appendTo( thisPage ),
3244
+
3245
+ listbox = $( "<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all pop ui-body-" + o.overlayTheme } )
3246
+ .insertAfter(screen),
3247
+
3248
+ list = $( "<ul>", {
3249
+ "class": "ui-selectmenu-list",
3250
+ "id": menuId,
3251
+ "role": "listbox",
3252
+ "aria-labelledby": buttonId,
3253
+ "data-theme": theme
3254
+ })
3255
+ .appendTo( listbox ),
3256
+
3257
+ header = $( "<div>", {
3258
+ "class": "ui-header ui-bar-" + theme
3259
+ })
3260
+ .prependTo( listbox ),
3261
+
3262
+ headerTitle = $( "<h1>", {
3263
+ "class": "ui-title"
3264
+ })
3265
+ .appendTo( header ),
3266
+
3267
+ headerClose = $( "<a>", {
3268
+ "data-iconpos": "notext",
3269
+ "data-icon": "delete",
3270
+ "text": o.closeText,
3271
+ "href": "#",
3272
+ "class": "ui-btn-left"
3273
+ })
3274
+ .appendTo( header )
3275
+ .buttonMarkup(),
3276
+
3277
+ menuType;
3278
+ } //end non native vars
3209
3279
 
3210
3280
  // add counter for multi selects
3211
3281
  if( isMultiple ){
@@ -3215,6 +3285,15 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3215
3285
  .appendTo( button );
3216
3286
  }
3217
3287
 
3288
+ //disable if specified
3289
+ if( o.disabled ){ this.disable(); }
3290
+
3291
+ //events on native select
3292
+ select
3293
+ .change(function(){
3294
+ self.refresh();
3295
+ });
3296
+
3218
3297
  //expose to other methods
3219
3298
  $.extend(self, {
3220
3299
  select: select,
@@ -3235,36 +3314,25 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3235
3314
  headerClose:headerClose,
3236
3315
  headerTitle:headerTitle,
3237
3316
  placeholder: ''
3238
- });
3239
-
3240
- //create list from select, update state
3241
- self.refresh();
3242
-
3243
- //disable if specified
3244
- if( o.disabled ){ this.disable(); }
3245
-
3246
- //events on native select
3247
- select
3248
- .change(function(){
3249
- self.refresh();
3250
- });
3317
+ });
3251
3318
 
3252
3319
  //support for using the native select menu with a custom button
3253
- if( o.useNativeMenu ){
3320
+ if( o.nativeMenu ){
3254
3321
 
3255
3322
  select
3256
3323
  .appendTo(button)
3257
3324
  .bind( "touchstart mousedown", function( e ){
3258
3325
  //add active class to button
3259
3326
  button.addClass( $.mobile.activeBtnClass );
3260
-
3261
- //ensure button isn't clicked
3262
- e.stopPropagation();
3263
3327
  })
3264
3328
  .bind( "focus mouseover", function(){
3265
3329
  button.trigger( "mouseover" );
3266
3330
  })
3267
- .bind( "blur mouseout", function(){
3331
+ .bind( "touchmove", function(){
3332
+ //remove active class on scroll/touchmove
3333
+ button.removeClass( $.mobile.activeBtnClass );
3334
+ })
3335
+ .bind( "change blur mouseout", function(){
3268
3336
  button
3269
3337
  .trigger( "mouseout" )
3270
3338
  .removeClass( $.mobile.activeBtnClass );
@@ -3272,6 +3340,9 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3272
3340
 
3273
3341
  button.attr( "tabindex", "-1" );
3274
3342
  } else {
3343
+
3344
+ //create list from select, update state
3345
+ self.refresh();
3275
3346
 
3276
3347
  select
3277
3348
  .attr( "tabindex", "-1" )
@@ -3306,62 +3377,67 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3306
3377
  $( this ).data( "moved", true );
3307
3378
  }
3308
3379
  });
3309
- }
3310
-
3311
- //events for list items
3312
- list.delegate("li:not(.ui-disabled, .ui-li-divider)", "click", function(event){
3313
- // clicking on the list item fires click on the link in listview.js.
3314
- // to prevent this handler from firing twice if the link isn't clicked on,
3315
- // short circuit unless the target is the link
3316
- if( !$(event.target).is("a") ){ return; }
3317
-
3318
- // index of option tag to be selected
3319
- var newIndex = list.find( "li:not(.ui-li-divider)" ).index( this ),
3320
- option = self.optionElems.eq( newIndex )[0];
3321
-
3322
- // toggle selected status on the tag for multi selects
3323
- option.selected = isMultiple ? !option.selected : true;
3324
-
3325
- // toggle checkbox class for multiple selects
3326
- if( isMultiple ){
3327
- $(this)
3328
- .find('.ui-icon')
3329
- .toggleClass('ui-icon-checkbox-on', option.selected)
3330
- .toggleClass('ui-icon-checkbox-off', !option.selected);
3331
- }
3332
-
3333
- // trigger change
3334
- select.trigger( "change" );
3335
-
3336
- //hide custom select for single selects only
3337
- if( !isMultiple ){
3338
- self.close();
3339
- }
3340
-
3341
- event.preventDefault();
3342
- });
3343
-
3344
- //events on "screen" overlay + close button
3345
- screen
3346
- .add( headerClose )
3347
- .add( menuPageClose )
3348
- .bind("click", function(event){
3349
- self.close();
3350
- event.preventDefault();
3351
-
3352
- // if the dialog's close icon was clicked, prevent the dialog's close
3353
- // handler from firing. selectmenu's should take precedence
3354
- if( $.contains(menuPageClose[0], event.target) ){
3355
- event.stopImmediatePropagation();
3380
+
3381
+
3382
+ //events for list items
3383
+ list.delegate("li:not(.ui-disabled, .ui-li-divider)", "click", function(event){
3384
+ // clicking on the list item fires click on the link in listview.js.
3385
+ // to prevent this handler from firing twice if the link isn't clicked on,
3386
+ // short circuit unless the target is the link
3387
+ if( !$(event.target).is("a") ){ return; }
3388
+
3389
+ // index of option tag to be selected
3390
+ var newIndex = list.find( "li:not(.ui-li-divider)" ).index( this ),
3391
+ option = self.optionElems.eq( newIndex )[0];
3392
+
3393
+ // toggle selected status on the tag for multi selects
3394
+ option.selected = isMultiple ? !option.selected : true;
3395
+
3396
+ // toggle checkbox class for multiple selects
3397
+ if( isMultiple ){
3398
+ $(this)
3399
+ .find('.ui-icon')
3400
+ .toggleClass('ui-icon-checkbox-on', option.selected)
3401
+ .toggleClass('ui-icon-checkbox-off', !option.selected);
3402
+ }
3403
+
3404
+ // trigger change
3405
+ select.trigger( "change" );
3406
+
3407
+ //hide custom select for single selects only
3408
+ if( !isMultiple ){
3409
+ self.close();
3356
3410
  }
3411
+
3412
+ event.preventDefault();
3357
3413
  });
3414
+
3415
+ //events on "screen" overlay + close button
3416
+ screen
3417
+ .add( headerClose )
3418
+ .add( menuPageClose )
3419
+ .bind("click", function(event){
3420
+ self.close();
3421
+ event.preventDefault();
3422
+
3423
+ // if the dialog's close icon was clicked, prevent the dialog's close
3424
+ // handler from firing. selectmenu's should take precedence
3425
+ if( $.contains(menuPageClose[0], event.target) ){
3426
+ event.stopImmediatePropagation();
3427
+ }
3428
+ });
3429
+ }
3430
+
3431
+
3358
3432
  },
3359
3433
 
3360
3434
  _buildList: function(){
3361
3435
  var self = this,
3362
- optgroups = [],
3363
3436
  o = this.options,
3364
- placeholder = this.placeholder;
3437
+ placeholder = this.placeholder,
3438
+ optgroups = [],
3439
+ lis = [],
3440
+ dataIcon = self.isMultiple ? "checkbox-off" : "false";
3365
3441
 
3366
3442
  self.list.empty().filter('.ui-listview').listview('destroy');
3367
3443
 
@@ -3369,7 +3445,10 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3369
3445
  self.select.find( "option" ).each(function( i ){
3370
3446
  var $this = $(this),
3371
3447
  $parent = $this.parent(),
3372
- text = $this.text();
3448
+ text = $this.text(),
3449
+ anchor = "<a href='#'>"+ text +"</a>",
3450
+ classes = [],
3451
+ extraAttrs = [];
3373
3452
 
3374
3453
  // are we inside an optgroup?
3375
3454
  if( $parent.is("optgroup") ){
@@ -3377,46 +3456,29 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3377
3456
 
3378
3457
  // has this optgroup already been built yet?
3379
3458
  if( $.inArray(optLabel, optgroups) === -1 ){
3380
- $("<li>", {
3381
- "data-role":"list-divider",
3382
- "text": optLabel
3383
- }).appendTo( self.list );
3384
-
3459
+ lis.push( "<li data-role='list-divider'>"+ optLabel +"</li>" );
3385
3460
  optgroups.push( optLabel );
3386
3461
  }
3387
3462
  }
3388
-
3389
- var anchor = $("<a>", {
3390
- "role": "",
3391
- "href": "#",
3392
- "text": text
3393
- }),
3394
-
3395
- item = $( "<li>", { "data-icon": false });
3396
-
3463
+
3464
+ //find placeholder text
3397
3465
  if( !this.getAttribute('value') || text.length == 0 || $this.data('placeholder') ){
3398
3466
  if( o.hidePlaceholderMenuItems ){
3399
- item.addClass('ui-selectmenu-placeholder');
3467
+ classes.push( "ui-selectmenu-placeholder" );
3400
3468
  }
3401
-
3402
3469
  placeholder = self.placeholder = text;
3403
3470
  }
3404
3471
 
3405
- // multiple select defaults
3406
- if( self.isMultiple ){
3407
- item.data('icon', 'checkbox-off');
3408
- }
3409
-
3410
3472
  // support disabled option tags
3411
3473
  if( this.disabled ){
3412
- item.addClass("ui-disabled")
3413
- .attr("aria-disabled", true);
3474
+ classes.push( "ui-disabled" );
3475
+ extraAttrs.push( "aria-disabled='true'" );
3414
3476
  }
3415
3477
 
3416
- item
3417
- .append( anchor )
3418
- .appendTo( self.list );
3478
+ lis.push( "<li data-icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" )
3419
3479
  });
3480
+
3481
+ self.list.html( lis.join(" ") );
3420
3482
 
3421
3483
  // hide header close link for single selects
3422
3484
  if( !this.isMultiple ){
@@ -3446,7 +3508,7 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3446
3508
  return options.index( this );
3447
3509
  }).get();
3448
3510
 
3449
- if( forceRebuild || select[0].options.length > self.list.find('li').length ){
3511
+ if( !self.options.nativeMenu && ( forceRebuild || select[0].options.length > self.list.find('li').length )){
3450
3512
  self._buildList();
3451
3513
  }
3452
3514
 
@@ -3466,28 +3528,30 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3466
3528
  if( isMultiple ){
3467
3529
  self.buttonCount[ selected.length > 1 ? 'show' : 'hide' ]().text( selected.length );
3468
3530
  }
3469
-
3470
- self.list
3471
- .find( 'li:not(.ui-li-divider)' )
3472
- .removeClass( $.mobile.activeBtnClass )
3473
- .attr( 'aria-selected', false )
3474
- .each(function( i ){
3475
- if( $.inArray(i, indicies) > -1 ){
3476
- var item = $(this).addClass( $.mobile.activeBtnClass );
3477
-
3478
- // aria selected attr
3479
- item.find( 'a' ).attr( 'aria-selected', true );
3480
-
3481
- // multiple selects: add the "on" checkbox state to the icon
3482
- if( isMultiple ){
3483
- item.find('.ui-icon').removeClass('ui-icon-checkbox-off').addClass('ui-icon-checkbox-on');
3531
+
3532
+ if( !self.options.nativeMenu ){
3533
+ self.list
3534
+ .find( 'li:not(.ui-li-divider)' )
3535
+ .removeClass( $.mobile.activeBtnClass )
3536
+ .attr( 'aria-selected', false )
3537
+ .each(function( i ){
3538
+ if( $.inArray(i, indicies) > -1 ){
3539
+ var item = $(this).addClass( $.mobile.activeBtnClass );
3540
+
3541
+ // aria selected attr
3542
+ item.find( 'a' ).attr( 'aria-selected', true );
3543
+
3544
+ // multiple selects: add the "on" checkbox state to the icon
3545
+ if( isMultiple ){
3546
+ item.find('.ui-icon').removeClass('ui-icon-checkbox-off').addClass('ui-icon-checkbox-on');
3547
+ }
3484
3548
  }
3485
- }
3486
- });
3549
+ });
3550
+ }
3487
3551
  },
3488
3552
 
3489
3553
  open: function(){
3490
- if( this.options.disabled ){ return; }
3554
+ if( this.options.disabled || this.options.nativeMenu ){ return; }
3491
3555
 
3492
3556
  var self = this,
3493
3557
  menuHeight = self.list.outerHeight(),
@@ -3516,11 +3580,18 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3516
3580
  });
3517
3581
  }
3518
3582
 
3519
- self.menuPage.one('pageshow',focusMenuItem);
3583
+ self.menuPage.one('pageshow', function() {
3584
+ // silentScroll() is called whenever a page is shown to restore
3585
+ // any previous scroll position the page may have had. We need to
3586
+ // wait for the "silentscroll" event before setting focus to avoid
3587
+ // the browser's "feature" which offsets rendering to make sure
3588
+ // whatever has focus is in view.
3589
+ $(window).one("silentscroll", function(){ focusMenuItem(); });
3590
+ });
3520
3591
 
3521
3592
  self.menuType = "page";
3522
3593
  self.menuPageContent.append( self.list );
3523
- $.mobile.changePage(self.menuPage, 'pop', false, false);
3594
+ $.mobile.changePage(self.menuPage, 'pop', false, true);
3524
3595
  }
3525
3596
  else {
3526
3597
  self.menuType = "overlay";
@@ -3565,7 +3636,7 @@ $.widget( "mobile.selectmenu", $.mobile.widget, {
3565
3636
  },
3566
3637
 
3567
3638
  close: function(){
3568
- if( this.options.disabled || !this.isOpen ){ return; }
3639
+ if( this.options.disabled || !this.isOpen || this.options.nativeMenu ){ return; }
3569
3640
  var self = this;
3570
3641
 
3571
3642
  function focusButton(){
@@ -4215,6 +4286,8 @@ $.widget( "mobile.listview", $.mobile.widget, {
4215
4286
  return;
4216
4287
  }
4217
4288
 
4289
+ var itemTheme = item.data("theme") || o.theme;
4290
+
4218
4291
  var a = item.find( "a" );
4219
4292
 
4220
4293
  if ( a.length ) {
@@ -4227,7 +4300,7 @@ $.widget( "mobile.listview", $.mobile.widget, {
4227
4300
  corners: false,
4228
4301
  iconpos: "right",
4229
4302
  icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
4230
- theme: o.theme
4303
+ theme: itemTheme
4231
4304
  });
4232
4305
 
4233
4306
  a.first().addClass( "ui-link-inherit" );
@@ -4245,7 +4318,7 @@ $.widget( "mobile.listview", $.mobile.widget, {
4245
4318
  .buttonMarkup({
4246
4319
  shadow: false,
4247
4320
  corners: false,
4248
- theme: o.theme,
4321
+ theme: itemTheme,
4249
4322
  icon: false,
4250
4323
  iconpos: false
4251
4324
  })
@@ -4269,7 +4342,7 @@ $.widget( "mobile.listview", $.mobile.widget, {
4269
4342
  }
4270
4343
 
4271
4344
  } else {
4272
- itemClass += " ui-li-static ui-btn-up-" + o.theme;
4345
+ itemClass += " ui-li-static ui-btn-up-" + itemTheme;
4273
4346
  }
4274
4347
 
4275
4348
 
@@ -4288,7 +4361,8 @@ $.widget( "mobile.listview", $.mobile.widget, {
4288
4361
  self._removeCorners( item.next() );
4289
4362
  }
4290
4363
 
4291
- } else if ( pos === li.length - 1 ) {
4364
+ }
4365
+ if ( pos === li.length - 1 ) {
4292
4366
  itemClass += " ui-corner-bottom";
4293
4367
 
4294
4368
  item