compass-jquery-plugin 0.3.2.0 → 0.3.2.1

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