qedproject 0.0.8 → 0.0.9

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * jQuery Mobile v1.0b2
2
+ * jQuery Mobile v1.0b3
3
3
  * http://jquerymobile.com/
4
4
  *
5
5
  * Copyright 2010, jQuery Project
@@ -356,7 +356,7 @@ $.mobile.media = (function() {
356
356
 
357
357
  var fakeBody = $( "<body>" ).prependTo( "html" ),
358
358
  fbCSS = fakeBody[ 0 ].style,
359
- vendors = [ "webkit", "moz", "o" ],
359
+ vendors = [ "Webkit", "Moz", "O" ],
360
360
  webos = "palmGetResource" in window, //only used to rule out scrollTop
361
361
  bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB
362
362
 
@@ -366,7 +366,7 @@ function propExists( prop ) {
366
366
  props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " );
367
367
 
368
368
  for ( var v in props ){
369
- if ( fbCSS[ v ] !== undefined ) {
369
+ if ( fbCSS[ props[ v ] ] !== undefined ) {
370
370
  return true;
371
371
  }
372
372
  }
@@ -415,9 +415,10 @@ $.extend( $.support, {
415
415
  orientation: "orientation" in window,
416
416
  touch: "ontouchend" in document,
417
417
  cssTransitions: "WebKitTransitionEvent" in window,
418
- pushState: !!history.pushState,
418
+ pushState: "pushState" in history && "replaceState" in history,
419
419
  mediaquery: $.mobile.media( "only all" ),
420
420
  cssPseudoElement: !!propExists( "content" ),
421
+ touchOverflow: !!propExists( "overflowScrolling" ),
421
422
  boxShadow: !!propExists( "boxShadow" ) && !bb,
422
423
  scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos,
423
424
  dynamicBaseTag: baseTagTest(),
@@ -631,8 +632,7 @@ function clearResetTimer() {
631
632
  }
632
633
 
633
634
  function triggerVirtualEvent( eventType, event, flags ) {
634
- var defaultPrevented = false,
635
- ve;
635
+ var ve;
636
636
 
637
637
  if ( ( flags && flags[ eventType ] ) ||
638
638
  ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
@@ -640,18 +640,27 @@ function triggerVirtualEvent( eventType, event, flags ) {
640
640
  ve = createVirtualEvent( event, eventType );
641
641
 
642
642
  $( event.target).trigger( ve );
643
-
644
- defaultPrevented = ve.isDefaultPrevented();
645
643
  }
646
644
 
647
- return defaultPrevented;
645
+ return ve;
648
646
  }
649
647
 
650
648
  function mouseEventCallback( event ) {
651
649
  var touchID = $.data(event.target, touchTargetPropertyName);
652
650
 
653
651
  if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){
654
- triggerVirtualEvent( "v" + event.type, event );
652
+ var ve = triggerVirtualEvent( "v" + event.type, event );
653
+ if ( ve ) {
654
+ if ( ve.isDefaultPrevented() ) {
655
+ event.preventDefault();
656
+ }
657
+ if ( ve.isPropagationStopped() ) {
658
+ event.stopPropagation();
659
+ }
660
+ if ( ve.isImmediatePropagationStopped() ) {
661
+ event.stopImmediatePropagation();
662
+ }
663
+ }
655
664
  }
656
665
  }
657
666
 
@@ -731,7 +740,8 @@ function handleTouchEnd( event ) {
731
740
  triggerVirtualEvent( "vmouseup", event, flags );
732
741
 
733
742
  if ( !didScroll ) {
734
- if ( triggerVirtualEvent( "vclick", event, flags ) ) {
743
+ var ve = triggerVirtualEvent( "vclick", event, flags );
744
+ if ( ve && ve.isDefaultPrevented() ) {
735
745
  // The target of the mouse events that follow the touchend
736
746
  // event don't necessarily match the target used during the
737
747
  // touch. This means we need to rely on coordinates for blocking
@@ -1028,16 +1038,19 @@ $.event.special.tap = {
1028
1038
  return false;
1029
1039
  }
1030
1040
 
1031
- var touching = true,
1032
- origTarget = event.target,
1041
+ var origTarget = event.target,
1033
1042
  origEvent = event.originalEvent,
1034
1043
  timer;
1035
1044
 
1045
+ function clearTapTimer() {
1046
+ clearTimeout( timer );
1047
+ }
1048
+
1036
1049
  function clearTapHandlers() {
1037
- touching = false;
1038
- clearTimeout(timer);
1050
+ clearTapTimer();
1039
1051
 
1040
1052
  $this.unbind( "vclick", clickHandler )
1053
+ .unbind( "vmouseup", clearTapTimer )
1041
1054
  .unbind( "vmousecancel", clearTapHandlers );
1042
1055
  }
1043
1056
 
@@ -1052,12 +1065,11 @@ $.event.special.tap = {
1052
1065
  }
1053
1066
 
1054
1067
  $this.bind( "vmousecancel", clearTapHandlers )
1068
+ .bind( "vmouseup", clearTapTimer )
1055
1069
  .bind( "vclick", clickHandler );
1056
1070
 
1057
1071
  timer = setTimeout(function() {
1058
- if ( touching ) {
1059
- triggerCustomEvent( thisObject, "taphold", event );
1060
- }
1072
+ triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
1061
1073
  }, 750 );
1062
1074
  });
1063
1075
  }
@@ -1657,14 +1669,12 @@ $.widget( "mobile.page", $.mobile.widget, {
1657
1669
  },
1658
1670
 
1659
1671
  _create: function() {
1660
- var $elem = this.element,
1661
- o = this.options;
1662
1672
 
1663
- if ( this._trigger( "beforeCreate" ) === false ) {
1664
- return;
1665
- }
1673
+ this._trigger( "beforecreate" );
1666
1674
 
1667
- $elem.addClass( "ui-page ui-body-" + o.theme );
1675
+ this.element
1676
+ .attr( "tabindex", "0" )
1677
+ .addClass( "ui-page ui-body-" + this.options.theme );
1668
1678
  }
1669
1679
  });
1670
1680
 
@@ -1707,7 +1717,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1707
1717
  defaultPageTransition: "slide",
1708
1718
 
1709
1719
  // Minimum scroll distance that will be remembered when returning to a page
1710
- minScrollBack: screen.height / 2,
1720
+ minScrollBack: 250,
1711
1721
 
1712
1722
  // Set default dialog transition - 'none' for no transitions
1713
1723
  defaultDialogTransition: "pop",
@@ -1718,10 +1728,12 @@ $.widget( "mobile.page", $.mobile.widget, {
1718
1728
 
1719
1729
  // Error response message - appears when an Ajax page request fails
1720
1730
  pageLoadErrorMessage: "Error Loading Page",
1721
-
1731
+
1722
1732
  //automatically initialize the DOM when it's ready
1723
1733
  autoInitializePage: true,
1724
1734
 
1735
+ pushStateEnabled: true,
1736
+
1725
1737
  // Support conditions that must be met in order to proceed
1726
1738
  // default enhanced qualifications are media query support OR IE 7+
1727
1739
  gradeA: function(){
@@ -1835,6 +1847,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1835
1847
  return $.find( expr, null, null, [ node ] ).length > 0;
1836
1848
  };
1837
1849
  })( jQuery, this );
1850
+
1838
1851
  /*
1839
1852
  * jQuery Mobile Framework : core utilities for auto ajax navigation, base tag mgmt,
1840
1853
  * Copyright (c) jQuery Project
@@ -1875,7 +1888,7 @@ $.widget( "mobile.page", $.mobile.widget, {
1875
1888
  // [15]: ?msg=1234&type=unread
1876
1889
  // [16]: #msg-content
1877
1890
  //
1878
- urlParseRE: /^(((([^:\/#\?]+:)?(?:\/\/((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?]+)(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
1891
+ urlParseRE: /^(((([^:\/#\?]+:)?(?:\/\/((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,
1879
1892
 
1880
1893
  //Parse a URL into a structure that allows easy access to
1881
1894
  //all of the URL components by name.
@@ -2053,6 +2066,26 @@ $.widget( "mobile.page", $.mobile.widget, {
2053
2066
  return ( /^(:?\w+:)/ ).test( url );
2054
2067
  },
2055
2068
 
2069
+ //check if the specified url refers to the first page in the main application document.
2070
+ isFirstPageUrl: function( url ) {
2071
+ // We only deal with absolute paths.
2072
+ var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ),
2073
+
2074
+ // Does the url have the same path as the document?
2075
+ samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ),
2076
+
2077
+ // Get the first page element.
2078
+ fp = $.mobile.firstPage,
2079
+
2080
+ // Get the id of the first page element if it has one.
2081
+ fpId = fp && fp[0] ? fp[0].id : undefined;
2082
+
2083
+ // The url refers to the first page if the path matches the document and
2084
+ // it either has no hash value, or the hash is exactly equal to the id of the
2085
+ // first page element.
2086
+ return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) );
2087
+ },
2088
+
2056
2089
  isEmbeddedPage: function( url ) {
2057
2090
  var u = path.parseUrl( url );
2058
2091
 
@@ -2074,7 +2107,7 @@ $.widget( "mobile.page", $.mobile.widget, {
2074
2107
  //urlHistory is purely here to make guesses at whether the back or forward button was clicked
2075
2108
  //and provide an appropriate transition
2076
2109
  urlHistory = {
2077
- // Array of pages that are visited during a single page load.
2110
+ // Array of pages that are visited during a single page load.
2078
2111
  // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs)
2079
2112
  stack: [],
2080
2113
 
@@ -2095,13 +2128,13 @@ $.widget( "mobile.page", $.mobile.widget, {
2095
2128
  },
2096
2129
 
2097
2130
  // addNew is used whenever a new page is added
2098
- addNew: function( url, transition, title, pageUrl ) {
2131
+ addNew: function( url, transition, title, pageUrl, role ) {
2099
2132
  //if there's forward history, wipe it
2100
2133
  if( urlHistory.getNext() ) {
2101
2134
  urlHistory.clearForward();
2102
2135
  }
2103
2136
 
2104
- urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl } );
2137
+ urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } );
2105
2138
 
2106
2139
  urlHistory.activeIndex = urlHistory.stack.length - 1;
2107
2140
  },
@@ -2112,7 +2145,7 @@ $.widget( "mobile.page", $.mobile.widget, {
2112
2145
  },
2113
2146
 
2114
2147
  directHashChange: function( opts ) {
2115
- var back , forward, newActiveIndex;
2148
+ var back , forward, newActiveIndex, prev = this.getActive();
2116
2149
 
2117
2150
  // check if url isp in history and if it's ahead or behind current page
2118
2151
  $.each( urlHistory.stack, function( i, historyEntry ) {
@@ -2130,9 +2163,9 @@ $.widget( "mobile.page", $.mobile.widget, {
2130
2163
  this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex;
2131
2164
 
2132
2165
  if( back ) {
2133
- opts.isBack();
2166
+ ( opts.either || opts.isBack )( true );
2134
2167
  } else if( forward ) {
2135
- opts.isForward();
2168
+ ( opts.either || opts.isForward )( false );
2136
2169
  }
2137
2170
  },
2138
2171
 
@@ -2191,20 +2224,13 @@ $.widget( "mobile.page", $.mobile.widget, {
2191
2224
 
2192
2225
  //direct focus to the page title, or otherwise first focusable element
2193
2226
  function reFocus( page ) {
2194
- var lastClicked = page.jqmData( "lastClicked" );
2227
+ var pageTitle = page.find( ".ui-title:eq(0)" );
2195
2228
 
2196
- if( lastClicked && lastClicked.length ) {
2197
- lastClicked.focus();
2229
+ if( pageTitle.length ) {
2230
+ pageTitle.focus();
2198
2231
  }
2199
- else {
2200
- var pageTitle = page.find( ".ui-title:eq(0)" );
2201
-
2202
- if( pageTitle.length ) {
2203
- pageTitle.focus();
2204
- }
2205
- else{
2206
- page.find( focusable ).eq( 0 ).focus();
2207
- }
2232
+ else{
2233
+ page.focus();
2208
2234
  }
2209
2235
  }
2210
2236
 
@@ -2222,41 +2248,94 @@ $.widget( "mobile.page", $.mobile.widget, {
2222
2248
  $.mobile.changePage.apply( null, pageTransitionQueue.pop() );
2223
2249
  }
2224
2250
  }
2251
+
2252
+ // Save the last scroll distance per page, before it is hidden
2253
+ var getLastScroll = (function( lastScrollEnabled ){
2254
+ return function(){
2255
+ if( !lastScrollEnabled ){
2256
+ lastScrollEnabled = true;
2257
+ return;
2258
+ }
2259
+
2260
+ lastScrollEnabled = false;
2261
+
2262
+ var active = $.mobile.urlHistory.getActive(),
2263
+ activePage = $( ".ui-page-active" ),
2264
+ scrollElem = $( window ),
2265
+ touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled;
2266
+
2267
+ if( touchOverflow ){
2268
+ scrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage;
2269
+ }
2270
+
2271
+ if( active ){
2272
+ var lastScroll = scrollElem.scrollTop();
2273
+
2274
+ // Set active page's lastScroll prop.
2275
+ // If the Y location we're scrolling to is less than minScrollBack, let it go.
2276
+ active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll;
2277
+ }
2278
+ };
2279
+ })( true );
2280
+
2281
+ // to get last scroll, we need to get scrolltop before the page change
2282
+ // using beforechangepage or popstate/hashchange (whichever comes first)
2283
+ $( document ).bind( "beforechangepage", getLastScroll );
2284
+ $( window ).bind( $.support.pushState ? "popstate" : "hashchange", getLastScroll );
2285
+
2286
+ // Make the iOS clock quick-scroll work again if we're using native overflow scrolling
2287
+ /*
2288
+ if( $.support.touchOverflow ){
2289
+ if( $.mobile.touchOverflowEnabled ){
2290
+ $( window ).bind( "scrollstop", function(){
2291
+ if( $( this ).scrollTop() === 0 ){
2292
+ $.mobile.activePage.scrollTop( 0 );
2293
+ }
2294
+ });
2295
+ }
2296
+ }
2297
+ */
2225
2298
 
2226
2299
  //function for transitioning between two existing pages
2227
2300
  function transitionPages( toPage, fromPage, transition, reverse ) {
2228
2301
 
2229
2302
  //get current scroll distance
2230
- var currScroll = $.support.scrollTop ? $window.scrollTop() : true,
2231
- toScroll = toPage.data( "lastScroll" ) || $.mobile.defaultHomeScroll,
2303
+ var active = $.mobile.urlHistory.getActive(),
2304
+ touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled,
2305
+ toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ),
2232
2306
  screenHeight = getScreenHeight();
2233
-
2234
- //if scrolled down, scroll to top
2235
- if( currScroll ){
2236
- window.scrollTo( 0, $.mobile.defaultHomeScroll );
2237
- }
2238
-
2239
- //if the Y location we're scrolling to is less than 10px, let it go for sake of smoothness
2240
- if( toScroll < $.mobile.minScrollBack ){
2241
- toScroll = 0;
2242
- }
2307
+
2308
+ // Scroll to top, hide addr bar
2309
+ window.scrollTo( 0, $.mobile.defaultHomeScroll );
2243
2310
 
2244
2311
  if( fromPage ) {
2245
- //set as data for returning to that spot
2246
- fromPage
2247
- .height( screenHeight + currScroll )
2248
- .jqmData( "lastScroll", currScroll )
2249
- .jqmData( "lastClicked", $activeClickedLink );
2250
-
2251
2312
  //trigger before show/hide events
2252
2313
  fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } );
2253
2314
  }
2254
- toPage
2255
- .height( screenHeight + toScroll )
2256
- .data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
2315
+
2316
+ if( !touchOverflow){
2317
+ toPage.height( screenHeight + toScroll );
2318
+ }
2319
+
2320
+ toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } );
2257
2321
 
2258
2322
  //clear page loader
2259
2323
  $.mobile.hidePageLoadingMsg();
2324
+
2325
+ if( touchOverflow && toScroll ){
2326
+
2327
+ toPage.addClass( "ui-mobile-pre-transition" );
2328
+ // Send focus to page as it is now display: block
2329
+ reFocus( toPage );
2330
+
2331
+ //set page's scrollTop to remembered distance
2332
+ if( toPage.is( ".ui-native-fixed" ) ){
2333
+ toPage.find( ".ui-content" ).scrollTop( toScroll );
2334
+ }
2335
+ else{
2336
+ toPage.scrollTop( toScroll );
2337
+ }
2338
+ }
2260
2339
 
2261
2340
  //find the transition handler for the specified transition. If there
2262
2341
  //isn't one in our transitionHandlers dictionary, use the default one.
@@ -2265,21 +2344,25 @@ $.widget( "mobile.page", $.mobile.widget, {
2265
2344
  promise = th( transition, reverse, toPage, fromPage );
2266
2345
 
2267
2346
  promise.done(function() {
2268
- //reset toPage height bac
2269
- toPage.height( "" );
2270
-
2271
- //jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
2272
- if( toScroll ){
2273
- $.mobile.silentScroll( toScroll );
2274
- $( document ).one( "silentscroll", function() { reFocus( toPage ); } );
2275
- }
2276
- else{
2347
+ //reset toPage height back
2348
+ if( !touchOverflow ){
2349
+ toPage.height( "" );
2350
+ // Send focus to the newly shown page
2277
2351
  reFocus( toPage );
2278
2352
  }
2353
+
2354
+ // Jump to top or prev scroll, sometimes on iOS the page has not rendered yet.
2355
+ if( !touchOverflow ){
2356
+ $.mobile.silentScroll( toScroll );
2357
+ }
2279
2358
 
2280
2359
  //trigger show/hide events
2281
2360
  if( fromPage ) {
2282
- fromPage.height("").data( "page" )._trigger( "hide", null, { nextPage: toPage } );
2361
+ if( !touchOverflow ){
2362
+ fromPage.height( "" );
2363
+ }
2364
+
2365
+ fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } );
2283
2366
  }
2284
2367
 
2285
2368
  //trigger pageshow, define prevPage as either fromPage or empty jQuery obj
@@ -2300,6 +2383,8 @@ $.widget( "mobile.page", $.mobile.widget, {
2300
2383
 
2301
2384
  return pageMin;
2302
2385
  }
2386
+
2387
+ $.mobile.getScreenHeight = getScreenHeight;
2303
2388
 
2304
2389
  //simply set the active page's minimum height to screen height, depending on orientation
2305
2390
  function resetActivePageHeight(){
@@ -2349,6 +2434,8 @@ $.widget( "mobile.page", $.mobile.widget, {
2349
2434
  //history stack
2350
2435
  $.mobile.urlHistory = urlHistory;
2351
2436
 
2437
+ $.mobile.dialogHashKey = dialogHashKey;
2438
+
2352
2439
  //default non-animation transition handler
2353
2440
  $.mobile.noneTransitionHandler = function( name, reverse, $toPage, $fromPage ) {
2354
2441
  if ( $fromPage ) {
@@ -2434,6 +2521,12 @@ $.widget( "mobile.page", $.mobile.widget, {
2434
2521
  // Check to see if the page already exists in the DOM.
2435
2522
  page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" );
2436
2523
 
2524
+ // If we failed to find a page in the DOM, check the URL to see if it
2525
+ // refers to the first page in the application.
2526
+ if ( page.length === 0 && $.mobile.firstPage && path.isFirstPageUrl( absUrl ) ) {
2527
+ page = $( $.mobile.firstPage );
2528
+ }
2529
+
2437
2530
  // Reset base to the default document base.
2438
2531
  if ( base ) {
2439
2532
  base.reset();
@@ -2453,18 +2546,18 @@ $.widget( "mobile.page", $.mobile.widget, {
2453
2546
  }
2454
2547
 
2455
2548
  if ( settings.showLoadMsg ) {
2456
-
2549
+
2457
2550
  // This configurable timeout allows cached pages a brief delay to load without showing a message
2458
2551
  var loadMsgDelay = setTimeout(function(){
2459
2552
  $.mobile.showPageLoadingMsg();
2460
2553
  }, settings.loadMsgDelay ),
2461
-
2554
+
2462
2555
  // Shared logic for clearing timeout and removing message.
2463
2556
  hideMsg = function(){
2464
-
2557
+
2465
2558
  // Stop message show timer
2466
2559
  clearTimeout( loadMsgDelay );
2467
-
2560
+
2468
2561
  // Hide loading message
2469
2562
  $.mobile.hidePageLoadingMsg();
2470
2563
  };
@@ -2582,7 +2675,7 @@ $.widget( "mobile.page", $.mobile.widget, {
2582
2675
 
2583
2676
  // Remove loading message.
2584
2677
  if ( settings.showLoadMsg ) {
2585
-
2678
+
2586
2679
  // Remove loading message.
2587
2680
  hideMsg();
2588
2681
 
@@ -2661,17 +2754,39 @@ $.widget( "mobile.page", $.mobile.widget, {
2661
2754
  return;
2662
2755
  }
2663
2756
 
2757
+ var settings = $.extend( {}, $.mobile.changePage.defaults, options );
2758
+
2759
+ // Make sure we have a pageContainer to work with.
2760
+ settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
2761
+
2762
+ // Make sure we have a fromPage.
2763
+ settings.fromPage = settings.fromPage || $.mobile.activePage;
2764
+
2765
+ var mpc = settings.pageContainer,
2766
+ pbcEvent = new $.Event( "pagebeforechange" ),
2767
+ triggerData = { toPage: toPage, options: settings };
2768
+
2769
+ // Let listeners know we're about to change the current page.
2770
+ mpc.trigger( pbcEvent, triggerData );
2771
+
2772
+ mpc.trigger( "beforechangepage", triggerData ); // XXX: DEPRECATED for 1.0
2773
+
2774
+ // If the default behavior is prevented, stop here!
2775
+ if( pbcEvent.isDefaultPrevented() ){
2776
+ return;
2777
+ }
2778
+
2779
+ // We allow "pagebeforechange" observers to modify the toPage in the trigger
2780
+ // data to allow for redirects. Make sure our toPage is updated.
2781
+
2782
+ toPage = triggerData.toPage;
2783
+
2664
2784
  // Set the isPageTransitioning flag to prevent any requests from
2665
2785
  // entering this method while we are in the midst of loading a page
2666
2786
  // or transitioning.
2667
2787
 
2668
2788
  isPageTransitioning = true;
2669
2789
 
2670
- var settings = $.extend( {}, $.mobile.changePage.defaults, options );
2671
-
2672
- // Make sure we have a pageContainer to work with.
2673
- settings.pageContainer = settings.pageContainer || $.mobile.pageContainer;
2674
-
2675
2790
  // If the caller passed us a url, call loadPage()
2676
2791
  // to make sure it is loaded into the DOM. We'll listen
2677
2792
  // to the promise object it returns so we know when
@@ -2692,16 +2807,16 @@ $.widget( "mobile.page", $.mobile.widget, {
2692
2807
 
2693
2808
  //release transition lock so navigation is free again
2694
2809
  releasePageTransitionLock();
2695
- settings.pageContainer.trigger("changepagefailed");
2810
+ settings.pageContainer.trigger( "pagechangefailed", triggerData );
2811
+ settings.pageContainer.trigger( "changepagefailed", triggerData ); // XXX: DEPRECATED for 1.0
2696
2812
  });
2697
2813
  return;
2698
2814
  }
2699
2815
 
2700
2816
  // The caller passed us a real page DOM element. Update our
2701
2817
  // internal state and then trigger a transition to the page.
2702
- var mpc = settings.pageContainer,
2703
- fromPage = $.mobile.activePage,
2704
- url = toPage.jqmData( "url" ),
2818
+ var fromPage = settings.fromPage,
2819
+ url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ),
2705
2820
  // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path
2706
2821
  pageUrl = url,
2707
2822
  fileUrl = path.getFilePath( url ),
@@ -2711,9 +2826,6 @@ $.widget( "mobile.page", $.mobile.widget, {
2711
2826
  pageTitle = document.title,
2712
2827
  isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog";
2713
2828
 
2714
- // Let listeners know we're about to change the current page.
2715
- mpc.trigger( "beforechangepage" );
2716
-
2717
2829
  // If we are trying to transition to the same page that we are currently on ignore the request.
2718
2830
  // an illegal same page request is defined by the current page being the same as the url, as long as there's history
2719
2831
  // and toPage is not an array or object (those are allowed to be "same")
@@ -2722,7 +2834,8 @@ $.widget( "mobile.page", $.mobile.widget, {
2722
2834
  // to the same page.
2723
2835
  if( fromPage && fromPage[0] === toPage[0] ) {
2724
2836
  isPageTransitioning = false;
2725
- mpc.trigger( "changepage" );
2837
+ mpc.trigger( "pagechange", triggerData );
2838
+ mpc.trigger( "changepage", triggerData ); // XXX: DEPRECATED for 1.0
2726
2839
  return;
2727
2840
  }
2728
2841
 
@@ -2754,7 +2867,11 @@ $.widget( "mobile.page", $.mobile.widget, {
2754
2867
  // for the dialog content to be used in the hash. Instead, we want
2755
2868
  // to append the dialogHashKey to the url of the current page.
2756
2869
  if ( isDialog && active ) {
2757
- url = active.url + dialogHashKey;
2870
+ // on the initial page load active.url is undefined and in that case should
2871
+ // be an empty string. Moving the undefined -> empty string back into
2872
+ // urlHistory.addNew seemed imprudent given undefined better represents
2873
+ // the url state
2874
+ url = ( active.url || "" ) + dialogHashKey;
2758
2875
  }
2759
2876
 
2760
2877
  // Set the location hash.
@@ -2773,7 +2890,7 @@ $.widget( "mobile.page", $.mobile.widget, {
2773
2890
 
2774
2891
  //add page to history stack if it's not back or forward
2775
2892
  if( !historyDir ) {
2776
- urlHistory.addNew( url, settings.transition, pageTitle, pageUrl );
2893
+ urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role );
2777
2894
  }
2778
2895
 
2779
2896
  //set page title
@@ -2805,7 +2922,9 @@ $.widget( "mobile.page", $.mobile.widget, {
2805
2922
  releasePageTransitionLock();
2806
2923
 
2807
2924
  // Let listeners know we're all done changing the current page.
2808
- mpc.trigger( "changepage" );
2925
+ mpc.trigger( "pagechange", triggerData );
2926
+
2927
+ mpc.trigger( "changepage", triggerData ); // XXX: DEPRECATED for 1.0
2809
2928
  });
2810
2929
  };
2811
2930
 
@@ -2817,7 +2936,9 @@ $.widget( "mobile.page", $.mobile.widget, {
2817
2936
  role: undefined, // By default we rely on the role defined by the @data-role attribute.
2818
2937
  duplicateCachedPage: undefined,
2819
2938
  pageContainer: undefined,
2820
- showLoadMsg: true //loading message shows by default when pages are being fetched during changePage
2939
+ showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage
2940
+ dataUrl: undefined,
2941
+ fromPage: undefined
2821
2942
  };
2822
2943
 
2823
2944
  /* Event Bindings - hashchange, submit, and click */
@@ -2905,7 +3026,9 @@ $.widget( "mobile.page", $.mobile.widget, {
2905
3026
  var link = findClosestLink( event.target );
2906
3027
  if ( link ) {
2907
3028
  if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) {
2908
- $( link ).closest( ".ui-btn" ).not( ".ui-disabled" ).addClass( $.mobile.activeBtnClass );
3029
+ removeActiveLinkClass( true );
3030
+ $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" );
3031
+ $activeClickedLink.addClass( $.mobile.activeBtnClass );
2909
3032
  $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur();
2910
3033
  }
2911
3034
  }
@@ -2983,8 +3106,6 @@ $.widget( "mobile.page", $.mobile.widget, {
2983
3106
  // moved into more comprehensive isExternalLink
2984
3107
  isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad );
2985
3108
 
2986
- $activeClickedLink = $link.closest( ".ui-btn" );
2987
-
2988
3109
  if( isExternal ) {
2989
3110
  httpCleanup();
2990
3111
  //use default click handling
@@ -3017,12 +3138,19 @@ $.widget( "mobile.page", $.mobile.widget, {
3017
3138
  });
3018
3139
  } );
3019
3140
 
3020
- //hashchange event handler
3021
- $window.bind( "hashchange", function( e, triggered ) {
3141
+ $.mobile._handleHashChange = function( hash ) {
3022
3142
  //find first page via hash
3023
- var to = path.stripHash( location.hash ),
3143
+ var to = path.stripHash( hash ),
3024
3144
  //transition is false if it's the first page, undefined otherwise (and may be overridden by default)
3025
- transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined;
3145
+ transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined,
3146
+
3147
+ // default options for the changPage calls made after examining the current state
3148
+ // of the page and the hash
3149
+ changePageOptions = {
3150
+ transition: transition,
3151
+ changeHash: false,
3152
+ fromHashChange: true
3153
+ };
3026
3154
 
3027
3155
  //if listening is disabled (either globally or temporarily), or it's a dialog hash
3028
3156
  if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) {
@@ -3031,8 +3159,7 @@ $.widget( "mobile.page", $.mobile.widget, {
3031
3159
  }
3032
3160
 
3033
3161
  // special case for dialogs
3034
- if( urlHistory.stack.length > 1 &&
3035
- to.indexOf( dialogHashKey ) > -1 ) {
3162
+ if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) {
3036
3163
 
3037
3164
  // If current active page is not a dialog skip the dialog and continue
3038
3165
  // in the same direction
@@ -3048,22 +3175,43 @@ $.widget( "mobile.page", $.mobile.widget, {
3048
3175
  // prevent changepage
3049
3176
  return;
3050
3177
  } else {
3051
- var setTo = function() { to = $.mobile.urlHistory.getActive().pageUrl; };
3052
3178
  // if the current active page is a dialog and we're navigating
3053
3179
  // to a dialog use the dialog objected saved in the stack
3054
- urlHistory.directHashChange({ currentUrl: to, isBack: setTo, isForward: setTo });
3180
+ urlHistory.directHashChange({
3181
+ currentUrl: to,
3182
+
3183
+ // regardless of the direction of the history change
3184
+ // do the following
3185
+ either: function( isBack ) {
3186
+ var active = $.mobile.urlHistory.getActive();
3187
+
3188
+ to = active.pageUrl;
3189
+
3190
+ // make sure to set the role, transition and reversal
3191
+ // as most of this is lost by the domCache cleaning
3192
+ $.extend( changePageOptions, {
3193
+ role: active.role,
3194
+ transition: active.transition,
3195
+ reverse: isBack
3196
+ });
3197
+ }
3198
+ });
3055
3199
  }
3056
3200
  }
3057
-
3201
+
3058
3202
  //if to is defined, load it
3059
3203
  if ( to ) {
3060
3204
  to = ( typeof to === "string" && !path.isPath( to ) ) ? ( '#' + to ) : to;
3061
- $.mobile.changePage( to, { transition: transition, changeHash: false, fromHashChange: true } );
3062
- }
3063
- //there's no hash, go to the first page in the dom
3064
- else {
3065
- $.mobile.changePage( $.mobile.firstPage, { transition: transition, changeHash: false, fromHashChange: true } );
3205
+ $.mobile.changePage( to, changePageOptions );
3206
+ } else {
3207
+ //there's no hash, go to the first page in the dom
3208
+ $.mobile.changePage( $.mobile.firstPage, changePageOptions );
3066
3209
  }
3210
+ };
3211
+
3212
+ //hashchange event handler
3213
+ $window.bind( "hashchange", function( e, triggered ) {
3214
+ $.mobile._handleHashChange( location.hash );
3067
3215
  });
3068
3216
 
3069
3217
  //set page min-heights to be device specific
@@ -3073,7 +3221,122 @@ $.widget( "mobile.page", $.mobile.widget, {
3073
3221
  };//_registerInternalEvents callback
3074
3222
 
3075
3223
  })( jQuery );
3076
- /*!
3224
+ /*
3225
+ * jQuery Mobile Framework : history.pushState support, layered on top of hashchange
3226
+ * Copyright (c) jQuery Project
3227
+ * Dual licensed under the MIT or GPL Version 2 licenses.
3228
+ * http://jquery.org/license
3229
+ */
3230
+ ( function( $, window ) {
3231
+ // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents
3232
+ // Scope self to pushStateHandler so we can reference it sanely within the
3233
+ // methods handed off as event handlers
3234
+ var pushStateHandler = {},
3235
+ self = pushStateHandler,
3236
+ $win = $( window ),
3237
+ url = $.mobile.path.parseUrl( location.href );
3238
+
3239
+ $.extend( pushStateHandler, {
3240
+ // TODO move to a path helper, this is rather common functionality
3241
+ initialFilePath: (function() {
3242
+ return url.pathname + url.search;
3243
+ })(),
3244
+
3245
+ initialHref: url.hrefNoHash,
3246
+
3247
+ // Flag for tracking if a Hashchange naturally occurs after each popstate + replace
3248
+ hashchangeFired: false,
3249
+
3250
+ state: function() {
3251
+ return {
3252
+ hash: location.hash || "#" + self.initialFilePath,
3253
+ title: document.title,
3254
+
3255
+ // persist across refresh
3256
+ initialHref: self.initialHref
3257
+ };
3258
+ },
3259
+
3260
+ resetUIKeys: function( url ) {
3261
+ var dialog = $.mobile.dialogHashKey,
3262
+ subkey = "&" + $.mobile.subPageUrlKey,
3263
+ dialogIndex = url.indexOf( dialog );
3264
+
3265
+ if( dialogIndex > -1 ) {
3266
+ url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex );
3267
+ } else if( url.indexOf( subkey ) > -1 ) {
3268
+ url = url.split( subkey ).join( "#" + subkey );
3269
+ }
3270
+
3271
+ return url;
3272
+ },
3273
+
3274
+ // on hash change we want to clean up the url
3275
+ // NOTE this takes place *after* the vanilla navigation hash change
3276
+ // handling has taken place and set the state of the DOM
3277
+ onHashChange: function( e ) {
3278
+ var href, state;
3279
+
3280
+ self.hashchangeFired = true;
3281
+
3282
+ // only replaceState when the hash doesn't represent an embeded page
3283
+ if( $.mobile.path.isPath(location.hash) ) {
3284
+
3285
+ // propulate the hash when its not available
3286
+ state = self.state();
3287
+
3288
+ // make the hash abolute with the current href
3289
+ href = $.mobile.path.makeUrlAbsolute( state.hash.replace("#", ""), location.href );
3290
+
3291
+ href = self.resetUIKeys( href );
3292
+
3293
+ // replace the current url with the new href and store the state
3294
+ history.replaceState( state, document.title, href );
3295
+ }
3296
+ },
3297
+
3298
+ // on popstate (ie back or forward) we need to replace the hash that was there previously
3299
+ // cleaned up by the additional hash handling
3300
+ onPopState: function( e ) {
3301
+ var poppedState = e.originalEvent.state, holdnexthashchange = false;
3302
+
3303
+ // if there's no state its not a popstate we care about, ie chrome's initial popstate
3304
+ // or forward popstate
3305
+ if( poppedState ) {
3306
+ // disable any hashchange triggered by the browser
3307
+ $.mobile.urlHistory.ignoreNextHashChange = true;
3308
+
3309
+ // defer our manual hashchange until after the browser fired
3310
+ // version has come and gone
3311
+ setTimeout(function() {
3312
+ // make sure that the manual hash handling takes place
3313
+ $.mobile.urlHistory.ignoreNextHashChange = false;
3314
+
3315
+ // change the page based on the hash
3316
+ $.mobile._handleHashChange( poppedState.hash );
3317
+ }, 100);
3318
+ }
3319
+ },
3320
+
3321
+ init: function() {
3322
+ $win.bind( "hashchange", self.onHashChange );
3323
+
3324
+ // Handle popstate events the occur through history changes
3325
+ $win.bind( "popstate", self.onPopState );
3326
+
3327
+ // if there's no hash, we need to replacestate for returning to home
3328
+ if ( location.hash === "" ) {
3329
+ history.replaceState( self.state(), document.title, location.href );
3330
+ }
3331
+ }
3332
+ });
3333
+
3334
+ $( function() {
3335
+ if( $.mobile.pushStateEnabled && $.support.pushState ){
3336
+ pushStateHandler.init();
3337
+ }
3338
+ });
3339
+ })( jQuery, this );/*!
3077
3340
  * jQuery Mobile v@VERSION
3078
3341
  * http://jquerymobile.com/
3079
3342
  *
@@ -3141,7 +3404,7 @@ $.mobile.page.prototype.options.degradeInputs = {
3141
3404
  month: false,
3142
3405
  number: false,
3143
3406
  range: "number",
3144
- search: true,
3407
+ search: "text",
3145
3408
  tel: false,
3146
3409
  time: false,
3147
3410
  url: false,
@@ -3324,7 +3587,9 @@ $( ":jqmData(role='page'), :jqmData(role='dialog')" ).live( "pagecreate", functi
3324
3587
 
3325
3588
  } else if ( role === "content" ) {
3326
3589
 
3327
- $this.addClass( "ui-body-" + ( theme || pageTheme || o.contentTheme ) );
3590
+ if (theme || o.contentTheme) {
3591
+ $this.addClass( "ui-body-" + ( theme || o.contentTheme ) );
3592
+ }
3328
3593
 
3329
3594
  // Add ARIA role
3330
3595
  $this.attr( "role", "main" );
@@ -3345,7 +3610,7 @@ $.widget( "mobile.collapsible", $.mobile.widget, {
3345
3610
  options: {
3346
3611
  expandCueText: " click to expand contents",
3347
3612
  collapseCueText: " click to collapse contents",
3348
- collapsed: false,
3613
+ collapsed: true,
3349
3614
  heading: ">:header,>legend",
3350
3615
  theme: null,
3351
3616
  iconTheme: "d",
@@ -3653,22 +3918,26 @@ $.widget( "mobile.listview", $.mobile.widget, {
3653
3918
  return orig + " ui-listview " + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
3654
3919
  });
3655
3920
 
3656
- t.refresh();
3921
+ t.refresh( true );
3657
3922
  },
3658
3923
 
3659
3924
  _itemApply: function( $list, item ) {
3925
+ var $countli = item.find( ".ui-li-count" );
3926
+ if ( $countli.length ) {
3927
+ item.addClass( "ui-li-has-count" );
3928
+ }
3929
+ $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
3930
+
3660
3931
  // TODO class has to be defined in markup
3661
- item.find( ".ui-li-count" )
3662
- .addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" ).end()
3663
- .find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
3664
- .find( "p, dl" ).addClass( "ui-li-desc" ).end()
3665
- .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() {
3666
- item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
3667
- }).end()
3668
- .find( ".ui-li-aside" ).each(function() {
3669
- var $this = $(this);
3670
- $this.prependTo( $this.parent() ); //shift aside to front for css float
3671
- });
3932
+ item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
3933
+ .find( "p, dl" ).addClass( "ui-li-desc" ).end()
3934
+ .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function() {
3935
+ item.addClass( $(this).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
3936
+ }).end()
3937
+ .find( ".ui-li-aside" ).each(function() {
3938
+ var $this = $(this);
3939
+ $this.prependTo( $this.parent() ); //shift aside to front for css float
3940
+ });
3672
3941
  },
3673
3942
 
3674
3943
  _removeCorners: function( li, which ) {
@@ -3686,6 +3955,43 @@ $.widget( "mobile.listview", $.mobile.widget, {
3686
3955
  }
3687
3956
  },
3688
3957
 
3958
+ _refreshCorners: function( create ) {
3959
+ var $li,
3960
+ $visibleli,
3961
+ $topli,
3962
+ $bottomli;
3963
+
3964
+ if ( this.options.inset ) {
3965
+ $li = this.element.children( "li" );
3966
+ // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
3967
+ $visibleli = create?$li.not( ".ui-screen-hidden" ):$li.filter( ":visible" );
3968
+
3969
+ this._removeCorners( $li );
3970
+
3971
+ // Select the first visible li element
3972
+ $topli = $visibleli.first()
3973
+ .addClass( "ui-corner-top" );
3974
+
3975
+ $topli.add( $topli.find( ".ui-btn-inner" ) )
3976
+ .find( ".ui-li-link-alt" )
3977
+ .addClass( "ui-corner-tr" )
3978
+ .end()
3979
+ .find( ".ui-li-thumb" )
3980
+ .addClass( "ui-corner-tl" );
3981
+
3982
+ // Select the last visible li element
3983
+ $bottomli = $visibleli.last()
3984
+ .addClass( "ui-corner-bottom" );
3985
+
3986
+ $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
3987
+ .find( ".ui-li-link-alt" )
3988
+ .addClass( "ui-corner-br" )
3989
+ .end()
3990
+ .find( ".ui-li-thumb" )
3991
+ .addClass( "ui-corner-bl" );
3992
+ }
3993
+ },
3994
+
3689
3995
  refresh: function( create ) {
3690
3996
  this.parentPage = this.element.closest( ".ui-page" );
3691
3997
  this._createSubPages();
@@ -3726,6 +4032,10 @@ $.widget( "mobile.listview", $.mobile.widget, {
3726
4032
  theme: itemTheme
3727
4033
  });
3728
4034
 
4035
+ if ( ( icon != false ) && ( a.length == 1 ) ) {
4036
+ item.addClass( "ui-li-has-arrow" );
4037
+ }
4038
+
3729
4039
  a.first().addClass( "ui-link-inherit" );
3730
4040
 
3731
4041
  if ( a.length > 1 ) {
@@ -3771,40 +4081,6 @@ $.widget( "mobile.listview", $.mobile.widget, {
3771
4081
  }
3772
4082
  }
3773
4083
 
3774
- if ( o.inset ) {
3775
- if ( pos === 0 ) {
3776
- itemClass += " ui-corner-top";
3777
-
3778
- item.add( item.find( ".ui-btn-inner" ) )
3779
- .find( ".ui-li-link-alt" )
3780
- .addClass( "ui-corner-tr" )
3781
- .end()
3782
- .find( ".ui-li-thumb" )
3783
- .addClass( "ui-corner-tl" );
3784
-
3785
- if ( item.next().next().length ) {
3786
- self._removeCorners( item.next() );
3787
- }
3788
- }
3789
-
3790
- if ( pos === li.length - 1 ) {
3791
- itemClass += " ui-corner-bottom";
3792
-
3793
- item.add( item.find( ".ui-btn-inner" ) )
3794
- .find( ".ui-li-link-alt" )
3795
- .addClass( "ui-corner-br" )
3796
- .end()
3797
- .find( ".ui-li-thumb" )
3798
- .addClass( "ui-corner-bl" );
3799
-
3800
- if ( item.prev().prev().length ) {
3801
- self._removeCorners( item.prev() );
3802
- } else if ( item.prev().length ) {
3803
- self._removeCorners( item.prev(), "bottom" );
3804
- }
3805
- }
3806
- }
3807
-
3808
4084
  if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
3809
4085
  countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
3810
4086
 
@@ -3814,10 +4090,10 @@ $.widget( "mobile.listview", $.mobile.widget, {
3814
4090
 
3815
4091
  item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
3816
4092
 
3817
- if ( !create ) {
3818
- self._itemApply( $list, item );
3819
- }
4093
+ self._itemApply( $list, item );
3820
4094
  }
4095
+
4096
+ this._refreshCorners( create );
3821
4097
  },
3822
4098
 
3823
4099
  //create a string for ID/subpage url creation
@@ -3926,6 +4202,9 @@ $( document ).bind( "pagecreate create", function( e ){
3926
4202
  $.mobile.listview.prototype.options.filter = false;
3927
4203
  $.mobile.listview.prototype.options.filterPlaceholder = "Filter items...";
3928
4204
  $.mobile.listview.prototype.options.filterTheme = "c";
4205
+ $.mobile.listview.prototype.options.filterCallback = function( text, searchValue ){
4206
+ return text.toLowerCase().indexOf( searchValue ) === -1;
4207
+ };
3929
4208
 
3930
4209
  $( ":jqmData(role='listview')" ).live( "listviewcreate", function() {
3931
4210
 
@@ -3953,7 +4232,7 @@ $( ":jqmData(role='listview')" ).live( "listviewcreate", function() {
3953
4232
  lastval = $this.jqmData( "lastval" ) + "",
3954
4233
  childItems = false,
3955
4234
  itemtext = "",
3956
- item;
4235
+ item, change;
3957
4236
 
3958
4237
  // Change val as lastval for next execution
3959
4238
  $this.jqmData( "lastval" , val );
@@ -3986,7 +4265,7 @@ $( ":jqmData(role='listview')" ).live( "listviewcreate", function() {
3986
4265
  // New bucket!
3987
4266
  childItems = false;
3988
4267
 
3989
- } else if ( itemtext.toLowerCase().indexOf( val ) === -1 ) {
4268
+ } else if ( listview.options.filterCallback( itemtext, val ) ) {
3990
4269
 
3991
4270
  //mark to be hidden
3992
4271
  item.toggleClass( "ui-filter-hidequeue" , true );
@@ -4013,6 +4292,7 @@ $( ":jqmData(role='listview')" ).live( "listviewcreate", function() {
4013
4292
  //filtervalue is empty => show all
4014
4293
  listItems.toggleClass( "ui-screen-hidden", false );
4015
4294
  }
4295
+ listview._refreshCorners();
4016
4296
  })
4017
4297
  .appendTo( wrapper )
4018
4298
  .textinput();
@@ -4400,7 +4680,8 @@ $.widget( "mobile.slider", $.mobile.widget, {
4400
4680
  slider: slider,
4401
4681
  handle: handle,
4402
4682
  dragging: false,
4403
- beforeStart: null
4683
+ beforeStart: null,
4684
+ userModified: false
4404
4685
  });
4405
4686
 
4406
4687
  if ( cType == "select" ) {
@@ -4413,7 +4694,7 @@ $.widget( "mobile.slider", $.mobile.widget, {
4413
4694
 
4414
4695
  var side = !i ? "b":"a",
4415
4696
  corners = !i ? "right" :"left",
4416
- theme = !i ? " ui-btn-down-" + trackTheme :" ui-btn-active";
4697
+ theme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass );
4417
4698
 
4418
4699
  $( "<div class='ui-slider-labelbg ui-slider-labelbg-" + side + theme + " ui-btn-corner-" + corners + "'></div>" )
4419
4700
  .prependTo( slider );
@@ -4442,12 +4723,14 @@ $.widget( "mobile.slider", $.mobile.widget, {
4442
4723
  $( document ).bind( "vmousemove", function( event ) {
4443
4724
  if ( self.dragging ) {
4444
4725
  self.refresh( event );
4726
+ self.userModified = self.userModified || self.beforeStart !== control[0].selectedIndex;
4445
4727
  return false;
4446
4728
  }
4447
4729
  });
4448
4730
 
4449
4731
  slider.bind( "vmousedown", function( event ) {
4450
4732
  self.dragging = true;
4733
+ self.userModified = false;
4451
4734
 
4452
4735
  if ( cType === "select" ) {
4453
4736
  self.beforeStart = control[0].selectedIndex;
@@ -4464,18 +4747,11 @@ $.widget( "mobile.slider", $.mobile.widget, {
4464
4747
 
4465
4748
  if ( cType === "select" ) {
4466
4749
 
4467
- if ( self.beforeStart === control[ 0 ].selectedIndex ) {
4750
+ if ( !self.userModified ) {
4468
4751
  //tap occurred, but value didn't change. flip it!
4752
+ handle.addClass( "ui-slider-handle-snapping" );
4469
4753
  self.refresh( !self.beforeStart ? 1 : 0 );
4470
4754
  }
4471
- var curval = val();
4472
- var snapped = Math.round( curval / ( max - min ) * 100 );
4473
- handle
4474
- .addClass( "ui-slider-handle-snapping" )
4475
- .css( "left", snapped + "%" )
4476
- .animationComplete( function() {
4477
- handle.removeClass( "ui-slider-handle-snapping" );
4478
- });
4479
4755
  }
4480
4756
  return false;
4481
4757
  }
@@ -4652,7 +4928,8 @@ $( document ).bind( "pagecreate create", function( e ){
4652
4928
 
4653
4929
  });
4654
4930
 
4655
- })( jQuery );/*
4931
+ })( jQuery );
4932
+ /*
4656
4933
  * jQuery Mobile Framework : "textinput" plugin for text inputs, textareas
4657
4934
  * Copyright (c) jQuery Project
4658
4935
  * Dual licensed under the MIT or GPL Version 2 licenses.
@@ -4788,619 +5065,683 @@ $( document ).bind( "pagecreate create", function( e ){
4788
5065
 
4789
5066
  })( jQuery );
4790
5067
  /*
4791
- * jQuery Mobile Framework : "selectmenu" plugin
5068
+ * jQuery Mobile Framework : custom "selectmenu" plugin
4792
5069
  * Copyright (c) jQuery Project
4793
5070
  * Dual licensed under the MIT or GPL Version 2 licenses.
4794
5071
  * http://jquery.org/license
4795
5072
  */
4796
5073
 
4797
5074
  (function( $, undefined ) {
5075
+ var extendSelect = function( widget ){
5076
+
5077
+ var select = widget.select,
5078
+ selectID = widget.selectID,
5079
+ label = widget.label,
5080
+ thisPage = widget.select.closest( ".ui-page" ),
5081
+ screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"} ).appendTo( thisPage ),
5082
+ selectOptions = widget.select.find("option"),
5083
+ isMultiple = widget.isMultiple = widget.select[ 0 ].multiple,
5084
+ buttonId = selectID + "-button",
5085
+ menuId = selectID + "-menu",
5086
+ menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' data-" +$.mobile.ns + "theme='"+ widget.options.menuPageTheme +"'>" +
5087
+ "<div data-" + $.mobile.ns + "role='header'>" +
5088
+ "<div class='ui-title'>" + label.text() + "</div>"+
5089
+ "</div>"+
5090
+ "<div data-" + $.mobile.ns + "role='content'></div>"+
5091
+ "</div>" ).appendTo( $.mobile.pageContainer ).page(),
5092
+
5093
+ listbox = $("<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + widget.options.overlayTheme + " " + $.mobile.defaultDialogTransition } ).insertAfter(screen),
5094
+
5095
+ list = $( "<ul>", {
5096
+ "class": "ui-selectmenu-list",
5097
+ "id": menuId,
5098
+ "role": "listbox",
5099
+ "aria-labelledby": buttonId
5100
+ }).attr( "data-" + $.mobile.ns + "theme", widget.options.theme ).appendTo( listbox ),
5101
+
5102
+ header = $( "<div>", {
5103
+ "class": "ui-header ui-bar-" + widget.options.theme
5104
+ }).prependTo( listbox ),
5105
+
5106
+ headerTitle = $( "<h1>", {
5107
+ "class": "ui-title"
5108
+ }).appendTo( header ),
5109
+
5110
+ headerClose = $( "<a>", {
5111
+ "text": widget.options.closeText,
5112
+ "href": "#",
5113
+ "class": "ui-btn-left"
5114
+ }).attr( "data-" + $.mobile.ns + "iconpos", "notext" ).attr( "data-" + $.mobile.ns + "icon", "delete" ).appendTo( header ).buttonMarkup(),
5115
+
5116
+ menuPageContent = menuPage.find( ".ui-content" ),
5117
+
5118
+ menuPageClose = menuPage.find( ".ui-header a" );
5119
+
5120
+
5121
+ $.extend( widget, {
5122
+ select: widget.select,
5123
+ selectID: selectID,
5124
+ buttonId: buttonId,
5125
+ menuId: menuId,
5126
+ thisPage: thisPage,
5127
+ menuPage: menuPage,
5128
+ label: label,
5129
+ screen: screen,
5130
+ selectOptions: selectOptions,
5131
+ isMultiple: isMultiple,
5132
+ theme: widget.options.theme,
5133
+ listbox: listbox,
5134
+ list: list,
5135
+ header: header,
5136
+ headerTitle: headerTitle,
5137
+ headerClose: headerClose,
5138
+ menuPageContent: menuPageContent,
5139
+ menuPageClose: menuPageClose,
5140
+ placeholder: "",
4798
5141
 
4799
- $.widget( "mobile.selectmenu", $.mobile.widget, {
4800
- options: {
4801
- theme: null,
4802
- disabled: false,
4803
- icon: "arrow-d",
4804
- iconpos: "right",
4805
- inline: null,
4806
- corners: true,
4807
- shadow: true,
4808
- iconshadow: true,
4809
- menuPageTheme: "b",
4810
- overlayTheme: "a",
4811
- hidePlaceholderMenuItems: true,
4812
- closeText: "Close",
4813
- nativeMenu: true,
4814
- initSelector: "select:not(:jqmData(role='slider'))"
4815
- },
4816
- _create: function() {
5142
+ build: function() {
5143
+ var self = this;
4817
5144
 
4818
- var self = this,
5145
+ // Create list from select, update state
5146
+ self.refresh();
4819
5147
 
4820
- o = this.options,
5148
+ self.select.attr( "tabindex", "-1" ).focus(function() {
5149
+ $( this ).blur();
5150
+ self.button.focus();
5151
+ });
4821
5152
 
4822
- select = this.element
4823
- .wrap( "<div class='ui-select'>" ),
5153
+ // Button events
5154
+ self.button.bind( "vclick keydown" , function( event ) {
5155
+ if ( event.type == "vclick" ||
5156
+ event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
5157
+ event.keyCode === $.mobile.keyCode.SPACE ) ) {
4824
5158
 
4825
- selectID = select.attr( "id" ),
5159
+ self.open();
5160
+ event.preventDefault();
5161
+ }
5162
+ });
4826
5163
 
4827
- label = $( "label[for='"+ selectID +"']" ).addClass( "ui-select" ),
5164
+ // Events for list items
5165
+ self.list.attr( "role", "listbox" )
5166
+ .delegate( ".ui-li>a", "focusin", function() {
5167
+ $( this ).attr( "tabindex", "0" );
5168
+ })
5169
+ .delegate( ".ui-li>a", "focusout", function() {
5170
+ $( this ).attr( "tabindex", "-1" );
5171
+ })
5172
+ .delegate( "li:not(.ui-disabled, .ui-li-divider)", "click", function( event ) {
4828
5173
 
4829
- // IE throws an exception at options.item() function when
4830
- // there is no selected item
4831
- // select first in this case
4832
- selectedIndex = select[ 0 ].selectedIndex == -1 ? 0 : select[ 0 ].selectedIndex,
5174
+ // index of option tag to be selected
5175
+ var oldIndex = self.select[ 0 ].selectedIndex,
5176
+ newIndex = self.list.find( "li:not(.ui-li-divider)" ).index( this ),
5177
+ option = self.selectOptions.eq( newIndex )[ 0 ];
5178
+
5179
+ // toggle selected status on the tag for multi selects
5180
+ option.selected = self.isMultiple ? !option.selected : true;
5181
+
5182
+ // toggle checkbox class for multiple selects
5183
+ if ( self.isMultiple ) {
5184
+ $( this ).find( ".ui-icon" )
5185
+ .toggleClass( "ui-icon-checkbox-on", option.selected )
5186
+ .toggleClass( "ui-icon-checkbox-off", !option.selected );
5187
+ }
4833
5188
 
4834
- button = ( self.options.nativeMenu ? $( "<div/>" ) : $( "<a>", {
4835
- "href": "#",
4836
- "role": "button",
4837
- "id": buttonId,
4838
- "aria-haspopup": "true",
4839
- "aria-owns": menuId
4840
- }) )
4841
- .text( $( select[ 0 ].options.item( selectedIndex ) ).text() )
4842
- .insertBefore( select )
4843
- .buttonMarkup({
4844
- theme: o.theme,
4845
- icon: o.icon,
4846
- iconpos: o.iconpos,
4847
- inline: o.inline,
4848
- corners: o.corners,
4849
- shadow: o.shadow,
4850
- iconshadow: o.iconshadow
4851
- }),
5189
+ // trigger change if value changed
5190
+ if ( self.isMultiple || oldIndex !== newIndex ) {
5191
+ self.select.trigger( "change" );
5192
+ }
4852
5193
 
4853
- // Multi select or not
4854
- isMultiple = self.isMultiple = select[ 0 ].multiple;
5194
+ //hide custom select for single selects only
5195
+ if ( !self.isMultiple ) {
5196
+ self.close();
5197
+ }
4855
5198
 
4856
- // Opera does not properly support opacity on select elements
4857
- // In Mini, it hides the element, but not its text
4858
- // On the desktop,it seems to do the opposite
4859
- // for these reasons, using the nativeMenu option results in a full native select in Opera
4860
- if ( o.nativeMenu && window.opera && window.opera.version ) {
4861
- select.addClass( "ui-select-nativeonly" );
4862
- }
4863
-
4864
- //vars for non-native menus
4865
- if ( !o.nativeMenu ) {
4866
- var options = select.find("option"),
4867
-
4868
- buttonId = selectID + "-button",
4869
-
4870
- menuId = selectID + "-menu",
5199
+ event.preventDefault();
5200
+ })
5201
+ .keydown(function( event ) { //keyboard events for menu items
5202
+ var target = $( event.target ),
5203
+ li = target.closest( "li" ),
5204
+ prev, next;
5205
+
5206
+ // switch logic based on which key was pressed
5207
+ switch ( event.keyCode ) {
5208
+ // up or left arrow keys
5209
+ case 38:
5210
+ prev = li.prev();
5211
+
5212
+ // if there's a previous option, focus it
5213
+ if ( prev.length ) {
5214
+ target
5215
+ .blur()
5216
+ .attr( "tabindex", "-1" );
5217
+
5218
+ prev.find( "a" ).first().focus();
5219
+ }
4871
5220
 
4872
- thisPage = select.closest( ".ui-page" ),
5221
+ return false;
5222
+ break;
4873
5223
 
4874
- //button theme
4875
- theme = /ui-btn-up-([a-z])/.exec( button.attr( "class" ) )[1],
5224
+ // down or right arrow keys
5225
+ case 40:
5226
+ next = li.next();
4876
5227
 
4877
- menuPage = $( "<div data-" + $.mobile.ns + "role='dialog' data-" +$.mobile.ns + "theme='"+ o.menuPageTheme +"'>" +
4878
- "<div data-" + $.mobile.ns + "role='header'>" +
4879
- "<div class='ui-title'>" + label.text() + "</div>"+
4880
- "</div>"+
4881
- "<div data-" + $.mobile.ns + "role='content'></div>"+
4882
- "</div>" )
4883
- .appendTo( $.mobile.pageContainer )
4884
- .page(),
5228
+ // if there's a next option, focus it
5229
+ if ( next.length ) {
5230
+ target
5231
+ .blur()
5232
+ .attr( "tabindex", "-1" );
4885
5233
 
4886
- menuPageContent = menuPage.find( ".ui-content" ),
5234
+ next.find( "a" ).first().focus();
5235
+ }
4887
5236
 
4888
- menuPageClose = menuPage.find( ".ui-header a" ),
5237
+ return false;
5238
+ break;
4889
5239
 
4890
- screen = $( "<div>", {"class": "ui-selectmenu-screen ui-screen-hidden"})
4891
- .appendTo( thisPage ),
5240
+ // If enter or space is pressed, trigger click
5241
+ case 13:
5242
+ case 32:
5243
+ target.trigger( "click" );
4892
5244
 
4893
- listbox = $("<div>", { "class": "ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-" + o.overlayTheme + " " + $.mobile.defaultDialogTransition })
4894
- .insertAfter(screen),
5245
+ return false;
5246
+ break;
5247
+ }
5248
+ });
4895
5249
 
4896
- list = $( "<ul>", {
4897
- "class": "ui-selectmenu-list",
4898
- "id": menuId,
4899
- "role": "listbox",
4900
- "aria-labelledby": buttonId
4901
- })
4902
- .attr( "data-" + $.mobile.ns + "theme", theme )
4903
- .appendTo( listbox ),
5250
+ // button refocus ensures proper height calculation
5251
+ // by removing the inline style and ensuring page inclusion
5252
+ self.menuPage.bind( "pagehide", function() {
5253
+ self.list.appendTo( self.listbox );
5254
+ self._focusButton();
5255
+ });
4904
5256
 
4905
- header = $( "<div>", {
4906
- "class": "ui-header ui-bar-" + theme
4907
- })
4908
- .prependTo( listbox ),
5257
+ // Events on "screen" overlay
5258
+ self.screen.bind( "vclick", function( event ) {
5259
+ self.close();
5260
+ });
4909
5261
 
4910
- headerTitle = $( "<h1>", {
4911
- "class": "ui-title"
4912
- })
4913
- .appendTo( header ),
5262
+ // Close button on small overlays
5263
+ self.headerClose.click( function() {
5264
+ if ( self.menuType == "overlay" ) {
5265
+ self.close();
5266
+ return false;
5267
+ }
5268
+ });
5269
+ },
4914
5270
 
4915
- headerClose = $( "<a>", {
4916
- "text": o.closeText,
4917
- "href": "#",
4918
- "class": "ui-btn-left"
4919
- })
4920
- .attr( "data-" + $.mobile.ns + "iconpos", "notext" )
4921
- .attr( "data-" + $.mobile.ns + "icon", "delete" )
4922
- .appendTo( header )
4923
- .buttonMarkup(),
5271
+ refresh: function( forceRebuild ){
5272
+ var self = this,
5273
+ select = this.element,
5274
+ isMultiple = this.isMultiple,
5275
+ options = this.selectOptions = select.find( "option" ),
5276
+ selected = this.selected(),
5277
+ // return an array of all selected index's
5278
+ indicies = this.selectedIndices();
5279
+
5280
+ if ( forceRebuild || select[0].options.length != self.list.find( "li" ).length ) {
5281
+ self._buildList();
5282
+ }
4924
5283
 
4925
- menuType;
4926
- } // End non native vars
5284
+ self.setButtonText();
5285
+ self.setButtonCount();
4927
5286
 
4928
- // Add counter for multi selects
4929
- if ( isMultiple ) {
4930
- self.buttonCount = $( "<span>" )
4931
- .addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" )
4932
- .hide()
4933
- .appendTo( button );
4934
- }
5287
+ self.list.find( "li:not(.ui-li-divider)" )
5288
+ .removeClass( $.mobile.activeBtnClass )
5289
+ .attr( "aria-selected", false )
5290
+ .each(function( i ) {
4935
5291
 
4936
- // Disable if specified
4937
- if ( o.disabled ) {
4938
- this.disable();
4939
- }
5292
+ if ( $.inArray( i, indicies ) > -1 ) {
5293
+ var item = $( this );
4940
5294
 
4941
- // Events on native select
4942
- select.change(function() {
4943
- self.refresh();
4944
- });
5295
+ // Aria selected attr
5296
+ item.attr( "aria-selected", true );
4945
5297
 
4946
- // Expose to other methods
4947
- $.extend( self, {
4948
- select: select,
4949
- optionElems: options,
4950
- selectID: selectID,
4951
- label: label,
4952
- buttonId: buttonId,
4953
- menuId: menuId,
4954
- thisPage: thisPage,
4955
- button: button,
4956
- menuPage: menuPage,
4957
- menuPageContent: menuPageContent,
4958
- screen: screen,
4959
- listbox: listbox,
4960
- list: list,
4961
- menuType: menuType,
4962
- header: header,
4963
- headerClose: headerClose,
4964
- headerTitle: headerTitle,
4965
- placeholder: ""
4966
- });
5298
+ // Multiple selects: add the "on" checkbox state to the icon
5299
+ if ( self.isMultiple ) {
5300
+ item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
5301
+ } else {
5302
+ item.addClass( $.mobile.activeBtnClass );
5303
+ }
5304
+ }
5305
+ });
5306
+ },
4967
5307
 
4968
- // Support for using the native select menu with a custom button
4969
- if ( o.nativeMenu ) {
5308
+ close: function() {
5309
+ if ( this.options.disabled || !this.isOpen ) {
5310
+ return;
5311
+ }
4970
5312
 
4971
- select.appendTo( button )
4972
- .bind( "vmousedown", function() {
4973
- // Add active class to button
4974
- button.addClass( $.mobile.activeBtnClass );
4975
- })
4976
- .bind( "focus vmouseover", function() {
4977
- button.trigger( "vmouseover" );
4978
- })
4979
- .bind( "vmousemove", function() {
4980
- // Remove active class on scroll/touchmove
4981
- button.removeClass( $.mobile.activeBtnClass );
4982
- })
4983
- .bind( "change blur vmouseout", function() {
5313
+ var self = this;
5314
+
5315
+ if ( self.menuType == "page" ) {
5316
+ // TODO centralize page removal binding / handling in the page plugin.
5317
+ // Suggestion from @jblas to do refcounting
5318
+ //
5319
+ // rebind the page remove that was unbound in the open function
5320
+ // to allow for the parent page removal from actions other than the use
5321
+ // of a dialog sized custom select
5322
+ if( !self.thisPage.data("page").options.domCache ){
5323
+ self.thisPage.bind( "pagehide.remove", function() {
5324
+ $(self).remove();
5325
+ });
5326
+ }
4984
5327
 
4985
- button.trigger( "vmouseout" )
4986
- .removeClass( $.mobile.activeBtnClass );
4987
- });
5328
+ // doesn't solve the possible issue with calling change page
5329
+ // where the objects don't define data urls which prevents dialog key
5330
+ // stripping - changePage has incoming refactor
5331
+ window.history.back();
5332
+ } else {
5333
+ self.screen.addClass( "ui-screen-hidden" );
5334
+ self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
5335
+ self.list.appendTo( self.listbox );
5336
+ self._focusButton();
5337
+ }
4988
5338
 
5339
+ // allow the dialog to be closed again
5340
+ self.isOpen = false;
5341
+ },
4989
5342
 
4990
- } else {
5343
+ open: function() {
5344
+ if ( this.options.disabled ) {
5345
+ return;
5346
+ }
4991
5347
 
4992
- // Create list from select, update state
4993
- self.refresh();
5348
+ var self = this,
5349
+ menuHeight = self.list.parent().outerHeight(),
5350
+ menuWidth = self.list.parent().outerWidth(),
5351
+ scrollTop = $( window ).scrollTop(),
5352
+ btnOffset = self.button.offset().top,
5353
+ screenHeight = window.innerHeight,
5354
+ screenWidth = window.innerWidth;
4994
5355
 
4995
- select.attr( "tabindex", "-1" )
4996
- .focus(function() {
4997
- $(this).blur();
4998
- button.focus();
4999
- });
5356
+ //add active class to button
5357
+ self.button.addClass( $.mobile.activeBtnClass );
5000
5358
 
5001
- // Button events
5002
- button.bind( "vclick keydown" , function( event ) {
5003
- if ( event.type == "vclick" ||
5004
- event.keyCode && ( event.keyCode === $.mobile.keyCode.ENTER ||
5005
- event.keyCode === $.mobile.keyCode.SPACE ) ) {
5359
+ //remove after delay
5360
+ setTimeout( function() {
5361
+ self.button.removeClass( $.mobile.activeBtnClass );
5362
+ }, 300);
5006
5363
 
5007
- self.open();
5008
- event.preventDefault();
5364
+ function focusMenuItem() {
5365
+ self.list.find( $.mobile.activeBtnClass ).focus();
5009
5366
  }
5010
- });
5011
-
5012
- // Events for list items
5013
- list.attr( "role", "listbox" )
5014
- .delegate( ".ui-li>a", "focusin", function() {
5015
- $( this ).attr( "tabindex", "0" );
5016
- })
5017
- .delegate( ".ui-li>a", "focusout", function() {
5018
- $( this ).attr( "tabindex", "-1" );
5019
- })
5020
- .delegate( "li:not(.ui-disabled, .ui-li-divider)", "vclick", function( event ) {
5021
5367
 
5022
- var $this = $( this ),
5023
- // index of option tag to be selected
5024
- oldIndex = select[ 0 ].selectedIndex,
5025
- newIndex = $this.jqmData( "option-index" ),
5026
- option = self.optionElems[ newIndex ];
5027
-
5028
- // toggle selected status on the tag for multi selects
5029
- option.selected = isMultiple ? !option.selected : true;
5030
-
5031
- // toggle checkbox class for multiple selects
5032
- if ( isMultiple ) {
5033
- $this.find( ".ui-icon" )
5034
- .toggleClass( "ui-icon-checkbox-on", option.selected )
5035
- .toggleClass( "ui-icon-checkbox-off", !option.selected );
5036
- }
5368
+ if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
5369
+ // prevent the parent page from being removed from the DOM,
5370
+ // otherwise the results of selecting a list item in the dialog
5371
+ // fall into a black hole
5372
+ self.thisPage.unbind( "pagehide.remove" );
5037
5373
 
5038
- // trigger change if value changed
5039
- if ( isMultiple || oldIndex !== newIndex ) {
5040
- select.trigger( "change" );
5374
+ //for webos (set lastscroll using button offset)
5375
+ if ( scrollTop == 0 && btnOffset > screenHeight ) {
5376
+ self.thisPage.one( "pagehide", function() {
5377
+ $( this ).jqmData( "lastScroll", btnOffset );
5378
+ });
5041
5379
  }
5042
5380
 
5043
- //hide custom select for single selects only
5044
- if ( !isMultiple ) {
5045
- self.close();
5046
- }
5381
+ self.menuPage.one( "pageshow", function() {
5382
+ // silentScroll() is called whenever a page is shown to restore
5383
+ // any previous scroll position the page may have had. We need to
5384
+ // wait for the "silentscroll" event before setting focus to avoid
5385
+ // the browser"s "feature" which offsets rendering to make sure
5386
+ // whatever has focus is in view.
5387
+ $( window ).one( "silentscroll", function() {
5388
+ focusMenuItem();
5389
+ });
5047
5390
 
5048
- event.preventDefault();
5049
- })
5050
- //keyboard events for menu items
5051
- .keydown(function( event ) {
5052
- var target = $( event.target ),
5053
- li = target.closest( "li" ),
5054
- prev, next;
5391
+ self.isOpen = true;
5392
+ });
5055
5393
 
5056
- // switch logic based on which key was pressed
5057
- switch ( event.keyCode ) {
5058
- // up or left arrow keys
5059
- case 38:
5060
- prev = li.prev();
5394
+ self.menuType = "page";
5395
+ self.menuPageContent.append( self.list );
5396
+ $.mobile.changePage( self.menuPage, {
5397
+ transition: $.mobile.defaultDialogTransition
5398
+ });
5399
+ } else {
5400
+ self.menuType = "overlay";
5061
5401
 
5062
- // if there's a previous option, focus it
5063
- if ( prev.length ) {
5064
- target
5065
- .blur()
5066
- .attr( "tabindex", "-1" );
5402
+ self.screen.height( $(document).height() )
5403
+ .removeClass( "ui-screen-hidden" );
5067
5404
 
5068
- prev.find( "a" ).first().focus();
5069
- }
5405
+ // Try and center the overlay over the button
5406
+ var roomtop = btnOffset - scrollTop,
5407
+ roombot = scrollTop + screenHeight - btnOffset,
5408
+ halfheight = menuHeight / 2,
5409
+ maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
5410
+ newtop, newleft;
5070
5411
 
5071
- return false;
5072
- break;
5412
+ if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
5413
+ newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
5414
+ } else {
5415
+ // 30px tolerance off the edges
5416
+ newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
5417
+ }
5073
5418
 
5074
- // down or right arrow keys
5075
- case 40:
5076
- next = li.next();
5419
+ // If the menuwidth is smaller than the screen center is
5420
+ if ( menuWidth < maxwidth ) {
5421
+ newleft = ( screenWidth - menuWidth ) / 2;
5422
+ } else {
5077
5423
 
5078
- // if there's a next option, focus it
5079
- if ( next.length ) {
5080
- target
5081
- .blur()
5082
- .attr( "tabindex", "-1" );
5424
+ //otherwise insure a >= 30px offset from the left
5425
+ newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
5083
5426
 
5084
- next.find( "a" ).first().focus();
5427
+ // 30px tolerance off the edges
5428
+ if ( newleft < 30 ) {
5429
+ newleft = 30;
5430
+ } else if ( (newleft + menuWidth) > screenWidth ) {
5431
+ newleft = screenWidth - menuWidth - 30;
5085
5432
  }
5433
+ }
5086
5434
 
5087
- return false;
5088
- break;
5435
+ self.listbox.append( self.list )
5436
+ .removeClass( "ui-selectmenu-hidden" )
5437
+ .css({
5438
+ top: newtop,
5439
+ left: newleft
5440
+ })
5441
+ .addClass( "in" );
5089
5442
 
5090
- // If enter or space is pressed, trigger click
5091
- case 13:
5092
- case 32:
5093
- target.trigger( "vclick" );
5443
+ focusMenuItem();
5094
5444
 
5095
- return false;
5096
- break;
5445
+ // duplicate with value set in page show for dialog sized selects
5446
+ self.isOpen = true;
5097
5447
  }
5098
- });
5448
+ },
5099
5449
 
5100
- // button refocus ensures proper height calculation
5101
- // by removing the inline style and ensuring page inclusion
5102
- self.menuPage.bind( "pagehide", function(){
5103
- self.list.appendTo( self.listbox );
5104
- self._focusButton();
5105
- });
5450
+ _buildList: function() {
5451
+ var self = this,
5452
+ o = this.options,
5453
+ placeholder = this.placeholder,
5454
+ optgroups = [],
5455
+ lis = [],
5456
+ dataIcon = self.isMultiple ? "checkbox-off" : "false";
5106
5457
 
5107
- // Events on "screen" overlay
5108
- screen.bind( "vclick", function( event ) {
5109
- self.close();
5110
- });
5458
+ self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
5111
5459
 
5112
- // Close button on small overlays
5113
- self.headerClose.click(function() {
5114
- if ( self.menuType == "overlay" ) {
5115
- self.close();
5116
- return false;
5117
- }
5118
- });
5119
- }
5120
- },
5460
+ // Populate menu with options from select element
5461
+ self.select.find( "option" ).each( function( i ) {
5462
+ var $this = $( this ),
5463
+ $parent = $this.parent(),
5464
+ text = $this.text(),
5465
+ anchor = "<a href='#'>"+ text +"</a>",
5466
+ classes = [],
5467
+ extraAttrs = [];
5468
+
5469
+ // Are we inside an optgroup?
5470
+ if ( $parent.is( "optgroup" ) ) {
5471
+ var optLabel = $parent.attr( "label" );
5472
+
5473
+ // has this optgroup already been built yet?
5474
+ if ( $.inArray( optLabel, optgroups ) === -1 ) {
5475
+ lis.push( "<li data-" + $.mobile.ns + "role='list-divider'>"+ optLabel +"</li>" );
5476
+ optgroups.push( optLabel );
5477
+ }
5478
+ }
5121
5479
 
5122
- _buildList: function() {
5123
- var self = this,
5124
- o = this.options,
5125
- placeholder = this.placeholder,
5126
- optgroups = [],
5127
- lis = [],
5128
- dataIcon = self.isMultiple ? "checkbox-off" : "false";
5480
+ // Find placeholder text
5481
+ // TODO: Are you sure you want to use getAttribute? ^RW
5482
+ if ( !this.getAttribute( "value" ) || text.length == 0 || $this.jqmData( "placeholder" ) ) {
5483
+ if ( o.hidePlaceholderMenuItems ) {
5484
+ classes.push( "ui-selectmenu-placeholder" );
5485
+ }
5486
+ placeholder = self.placeholder = text;
5487
+ }
5129
5488
 
5130
- self.list.empty().filter( ".ui-listview" ).listview( "destroy" );
5489
+ // support disabled option tags
5490
+ if ( this.disabled ) {
5491
+ classes.push( "ui-disabled" );
5492
+ extraAttrs.push( "aria-disabled='true'" );
5493
+ }
5131
5494
 
5132
- // Populate menu with options from select element
5133
- self.select.find( "option" ).each(function( i ) {
5134
- var $this = $( this ),
5135
- $parent = $this.parent(),
5136
- text = $this.text(),
5137
- anchor = "<a href='#'>"+ text +"</a>",
5138
- classes = [],
5139
- extraAttrs = [];
5140
-
5141
- // Are we inside an optgroup?
5142
- if ( $parent.is( "optgroup" ) ) {
5143
- var optLabel = $parent.attr( "label" );
5144
-
5145
- // has this optgroup already been built yet?
5146
- if ( $.inArray( optLabel, optgroups ) === -1 ) {
5147
- lis.push( "<li data-" + $.mobile.ns + "role='list-divider'>"+ optLabel +"</li>" );
5148
- optgroups.push( optLabel );
5495
+ lis.push( "<li data-" + $.mobile.ns + "option-index='" + i + "' data-" + $.mobile.ns + "icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" );
5496
+ });
5497
+
5498
+ self.list.html( lis.join(" ") );
5499
+
5500
+ self.list.find( "li" )
5501
+ .attr({ "role": "option", "tabindex": "-1" })
5502
+ .first().attr( "tabindex", "0" );
5503
+
5504
+ // Hide header close link for single selects
5505
+ if ( !this.isMultiple ) {
5506
+ this.headerClose.hide();
5149
5507
  }
5150
- }
5151
5508
 
5152
- // Find placeholder text
5153
- // TODO: Are you sure you want to use getAttribute? ^RW
5154
- if ( !this.getAttribute( "value" ) || text.length == 0 || $this.jqmData( "placeholder" ) ) {
5155
- if ( o.hidePlaceholderMenuItems ) {
5156
- classes.push( "ui-selectmenu-placeholder" );
5509
+ // Hide header if it's not a multiselect and there's no placeholder
5510
+ if ( !this.isMultiple && !placeholder.length ) {
5511
+ this.header.hide();
5512
+ } else {
5513
+ this.headerTitle.text( this.placeholder );
5157
5514
  }
5158
- placeholder = self.placeholder = text;
5159
- }
5160
5515
 
5161
- // support disabled option tags
5162
- if ( this.disabled ) {
5163
- classes.push( "ui-disabled" );
5164
- extraAttrs.push( "aria-disabled='true'" );
5165
- }
5516
+ // Now populated, create listview
5517
+ self.list.listview();
5518
+ },
5166
5519
 
5167
- lis.push( "<li data-" + $.mobile.ns + "option-index='" + i + "' data-" + $.mobile.ns + "icon='"+ dataIcon +"' class='"+ classes.join(" ") + "' " + extraAttrs.join(" ") +">"+ anchor +"</li>" )
5168
- });
5520
+ _button: function(){
5521
+ return $( "<a>", {
5522
+ "href": "#",
5523
+ "role": "button",
5524
+ // TODO value is undefined at creation
5525
+ "id": this.buttonId,
5526
+ "aria-haspopup": "true",
5169
5527
 
5170
- self.list.html( lis.join(" ") );
5528
+ // TODO value is undefined at creation
5529
+ "aria-owns": this.menuId
5530
+ });
5531
+ }
5532
+ });
5533
+ };
5171
5534
 
5172
- self.list.find( "li" )
5173
- .attr({ "role": "option", "tabindex": "-1" })
5174
- .first().attr( "tabindex", "0" );
5535
+ $( "select" ).live( "selectmenubeforecreate", function(){
5536
+ var selectmenuWidget = $( this ).data( "selectmenu" );
5175
5537
 
5176
- // Hide header close link for single selects
5177
- if ( !this.isMultiple ) {
5178
- this.headerClose.hide();
5538
+ if( !selectmenuWidget.options.nativeMenu ){
5539
+ extendSelect( selectmenuWidget );
5179
5540
  }
5541
+ });
5542
+ })( jQuery );
5543
+ /*
5544
+ * jQuery Mobile Framework : "selectmenu" plugin
5545
+ * Copyright (c) jQuery Project
5546
+ * Dual licensed under the MIT or GPL Version 2 licenses.
5547
+ * http://jquery.org/license
5548
+ */
5180
5549
 
5181
- // Hide header if it's not a multiselect and there's no placeholder
5182
- if ( !this.isMultiple && !placeholder.length ) {
5183
- this.header.hide();
5184
- } else {
5185
- this.headerTitle.text( this.placeholder );
5186
- }
5550
+ (function( $, undefined ) {
5187
5551
 
5188
- // Now populated, create listview
5189
- self.list.listview();
5552
+ $.widget( "mobile.selectmenu", $.mobile.widget, {
5553
+ options: {
5554
+ theme: null,
5555
+ disabled: false,
5556
+ icon: "arrow-d",
5557
+ iconpos: "right",
5558
+ inline: null,
5559
+ corners: true,
5560
+ shadow: true,
5561
+ iconshadow: true,
5562
+ menuPageTheme: "b",
5563
+ overlayTheme: "a",
5564
+ hidePlaceholderMenuItems: true,
5565
+ closeText: "Close",
5566
+ nativeMenu: true,
5567
+ initSelector: "select:not(:jqmData(role='slider'))"
5190
5568
  },
5191
5569
 
5192
- refresh: function( forceRebuild ) {
5193
- var self = this,
5194
- select = this.element,
5195
- isMultiple = this.isMultiple,
5196
- options = this.optionElems = select.find( "option" ),
5197
- selected = options.filter( ":selected" ),
5570
+ _button: function(){
5571
+ return $( "<div/>" );
5572
+ },
5198
5573
 
5199
- // return an array of all selected index's
5200
- indicies = selected.map(function() {
5201
- return options.index( this );
5202
- }).get();
5574
+ _theme: function(){
5575
+ var themedParent, theme;
5576
+ // if no theme is defined, try to find closest theme container
5577
+ // TODO move to core as something like findCurrentTheme
5578
+ themedParent = this.select.closest( "[class*='ui-bar-'], [class*='ui-body-']" );
5579
+ theme = themedParent.length ?
5580
+ /ui-(bar|body)-([a-z])/.exec( themedParent.attr( "class" ) )[2] :
5581
+ "c";
5203
5582
 
5204
- if ( !self.options.nativeMenu &&
5205
- ( forceRebuild || select[0].options.length != self.list.find( "li" ).length ) ) {
5583
+ return theme;
5584
+ },
5206
5585
 
5207
- self._buildList();
5208
- }
5586
+ _setDisabled: function( value ) {
5587
+ this.element.attr( "disabled", value );
5588
+ this.button.attr( "aria-disabled", value );
5589
+ return this._setOption( "disabled", value );
5590
+ },
5209
5591
 
5210
- self.button.find( ".ui-btn-text" )
5211
- .text(function() {
5592
+ _focusButton : function() {
5593
+ var self = this;
5212
5594
 
5213
- if ( !isMultiple ) {
5214
- return selected.text();
5215
- }
5595
+ setTimeout( function() {
5596
+ self.button.focus();
5597
+ }, 40);
5598
+ },
5216
5599
 
5217
- return selected.length ? selected.map(function() {
5218
- return $( this ).text();
5219
- }).get().join( ", " ) : self.placeholder;
5220
- });
5600
+ // setup items that are generally necessary for select menu extension
5601
+ _preExtension: function(){
5602
+ this.select = this.element.wrap( "<div class='ui-select'>" );
5603
+ this.selectID = this.select.attr( "id" );
5604
+ this.label = $( "label[for='"+ this.selectID +"']" ).addClass( "ui-select" );
5605
+ this.isMultiple = this.select[ 0 ].multiple;
5606
+ this.options.theme = this._theme();
5607
+ this.selectOptions = this.select.find( "option" );
5608
+ },
5221
5609
 
5222
- // multiple count inside button
5223
- if ( isMultiple ) {
5224
- self.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
5225
- }
5610
+ _create: function() {
5611
+ this._preExtension();
5226
5612
 
5227
- if ( !self.options.nativeMenu ) {
5613
+ // Allows for extension of the native select for custom selects and other plugins
5614
+ // see select.custom for example extension
5615
+ // TODO explore plugin registration
5616
+ this._trigger( "beforeCreate" );
5228
5617
 
5229
- self.list.find( "li:not(.ui-li-divider)" )
5230
- .removeClass( $.mobile.activeBtnClass )
5231
- .attr( "aria-selected", false )
5232
- .each(function( i ) {
5618
+ this.button = this._button();
5233
5619
 
5234
- if ( $.inArray( i, indicies ) > -1 ) {
5235
- var item = $( this ).addClass( $.mobile.activeBtnClass );
5620
+ var self = this,
5236
5621
 
5237
- // Aria selected attr
5238
- item.find( "a" ).attr( "aria-selected", true );
5622
+ options = this.options,
5239
5623
 
5240
- // Multiple selects: add the "on" checkbox state to the icon
5241
- if ( isMultiple ) {
5242
- item.find( ".ui-icon" ).removeClass( "ui-icon-checkbox-off" ).addClass( "ui-icon-checkbox-on" );
5243
- }
5244
- }
5624
+ // IE throws an exception at options.item() function when
5625
+ // there is no selected item
5626
+ // select first in this case
5627
+ selectedIndex = this.select[ 0 ].selectedIndex == -1 ? 0 : this.select[ 0 ].selectedIndex,
5628
+
5629
+ // TODO values buttonId and menuId are undefined here
5630
+ button = this.button
5631
+ .text( $( this.select[ 0 ].options.item( selectedIndex ) ).text() )
5632
+ .insertBefore( this.select )
5633
+ .buttonMarkup( {
5634
+ theme: options.theme,
5635
+ icon: options.icon,
5636
+ iconpos: options.iconpos,
5637
+ inline: options.inline,
5638
+ corners: options.corners,
5639
+ shadow: options.shadow,
5640
+ iconshadow: options.iconshadow
5245
5641
  });
5246
- }
5247
- },
5248
5642
 
5249
- open: function() {
5250
- if ( this.options.disabled || this.options.nativeMenu ) {
5251
- return;
5643
+ // Opera does not properly support opacity on select elements
5644
+ // In Mini, it hides the element, but not its text
5645
+ // On the desktop,it seems to do the opposite
5646
+ // for these reasons, using the nativeMenu option results in a full native select in Opera
5647
+ if ( options.nativeMenu && window.opera && window.opera.version ) {
5648
+ this.select.addClass( "ui-select-nativeonly" );
5252
5649
  }
5253
5650
 
5254
- var self = this,
5255
- menuHeight = self.list.parent().outerHeight(),
5256
- menuWidth = self.list.parent().outerWidth(),
5257
- scrollTop = $( window ).scrollTop(),
5258
- btnOffset = self.button.offset().top,
5259
- screenHeight = window.innerHeight,
5260
- screenWidth = window.innerWidth;
5261
-
5262
- //add active class to button
5263
- self.button.addClass( $.mobile.activeBtnClass );
5264
-
5265
- //remove after delay
5266
- setTimeout(function() {
5267
- self.button.removeClass( $.mobile.activeBtnClass );
5268
- }, 300);
5269
-
5270
- function focusMenuItem() {
5271
- self.list.find( ".ui-btn-active" ).focus();
5651
+ // Add counter for multi selects
5652
+ if ( this.isMultiple ) {
5653
+ this.buttonCount = $( "<span>" )
5654
+ .addClass( "ui-li-count ui-btn-up-c ui-btn-corner-all" )
5655
+ .hide()
5656
+ .appendTo( button );
5272
5657
  }
5273
5658
 
5274
- if ( menuHeight > screenHeight - 80 || !$.support.scrollTop ) {
5275
- // prevent the parent page from being removed from the DOM,
5276
- // otherwise the results of selecting a list item in the dialog
5277
- // fall into a black hole
5278
- self.thisPage.unbind( "pagehide.remove" );
5659
+ // Disable if specified
5660
+ if ( options.disabled ) {
5661
+ this.disable();
5662
+ }
5279
5663
 
5280
- //for webos (set lastscroll using button offset)
5281
- if ( scrollTop == 0 && btnOffset > screenHeight ) {
5282
- self.thisPage.one( "pagehide", function() {
5283
- $( this ).jqmData( "lastScroll", btnOffset );
5284
- });
5285
- }
5664
+ // Events on native select
5665
+ this.select.change( function() {
5666
+ self.refresh();
5667
+ });
5286
5668
 
5287
- self.menuPage.one( "pageshow", function() {
5288
- // silentScroll() is called whenever a page is shown to restore
5289
- // any previous scroll position the page may have had. We need to
5290
- // wait for the "silentscroll" event before setting focus to avoid
5291
- // the browser"s "feature" which offsets rendering to make sure
5292
- // whatever has focus is in view.
5293
- $( window ).one( "silentscroll", function() {
5294
- focusMenuItem();
5295
- });
5669
+ this.build();
5670
+ },
5296
5671
 
5297
- self.isOpen = true;
5298
- });
5672
+ build: function() {
5673
+ var self = this;
5299
5674
 
5300
- self.menuType = "page";
5301
- self.menuPageContent.append( self.list );
5302
- $.mobile.changePage( self.menuPage, {
5303
- transition: $.mobile.defaultDialogTransition
5675
+ this.select
5676
+ .appendTo( self.button )
5677
+ .bind( "vmousedown", function() {
5678
+ // Add active class to button
5679
+ self.button.addClass( $.mobile.activeBtnClass );
5680
+ })
5681
+ .bind( "focus vmouseover", function() {
5682
+ self.button.trigger( "vmouseover" );
5683
+ })
5684
+ .bind( "vmousemove", function() {
5685
+ // Remove active class on scroll/touchmove
5686
+ self.button.removeClass( $.mobile.activeBtnClass );
5687
+ })
5688
+ .bind( "change blur vmouseout", function() {
5689
+ self.button.trigger( "vmouseout" )
5690
+ .removeClass( $.mobile.activeBtnClass );
5691
+ })
5692
+ .bind( "change blur", function() {
5693
+ self.button.removeClass( "ui-btn-down-" + self.options.theme );
5304
5694
  });
5305
- } else {
5306
-
5307
- self.menuType = "overlay";
5308
-
5309
- self.screen.height( $(document).height() )
5310
- .removeClass( "ui-screen-hidden" );
5695
+ },
5311
5696
 
5312
- // Try and center the overlay over the button
5313
- var roomtop = btnOffset - scrollTop,
5314
- roombot = scrollTop + screenHeight - btnOffset,
5315
- halfheight = menuHeight / 2,
5316
- maxwidth = parseFloat( self.list.parent().css( "max-width" ) ),
5317
- newtop, newleft;
5697
+ selected: function() {
5698
+ return this.selectOptions.filter( ":selected" );
5699
+ },
5318
5700
 
5319
- if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
5320
- newtop = btnOffset + ( self.button.outerHeight() / 2 ) - halfheight;
5321
- } else {
5322
- // 30px tolerance off the edges
5323
- newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
5324
- }
5701
+ selectedIndices: function() {
5702
+ var self = this;
5325
5703
 
5326
- // If the menuwidth is smaller than the screen center is
5327
- if ( menuWidth < maxwidth ) {
5328
- newleft = ( screenWidth - menuWidth ) / 2;
5329
- } else {
5704
+ return this.selected().map( function() {
5705
+ return self.selectOptions.index( this );
5706
+ }).get();
5707
+ },
5330
5708
 
5331
- //otherwise insure a >= 30px offset from the left
5332
- newleft = self.button.offset().left + self.button.outerWidth() / 2 - menuWidth / 2;
5709
+ setButtonText: function() {
5710
+ var self = this, selected = this.selected();
5333
5711
 
5334
- // 30px tolerance off the edges
5335
- if ( newleft < 30 ) {
5336
- newleft = 30;
5337
- } else if ( ( newleft + menuWidth ) > screenWidth ) {
5338
- newleft = screenWidth - menuWidth - 30;
5339
- }
5712
+ this.button.find( ".ui-btn-text" ).text( function() {
5713
+ if ( !self.isMultiple ) {
5714
+ return selected.text();
5340
5715
  }
5341
5716
 
5342
- self.listbox.append( self.list )
5343
- .removeClass( "ui-selectmenu-hidden" )
5344
- .css({
5345
- top: newtop,
5346
- left: newleft
5347
- })
5348
- .addClass( "in" );
5349
-
5350
- focusMenuItem();
5351
-
5352
- // duplicate with value set in page show for dialog sized selects
5353
- self.isOpen = true;
5354
- }
5355
- },
5356
-
5357
- _focusButton : function(){
5358
- var self = this;
5359
- setTimeout(function() {
5360
- self.button.focus();
5361
- }, 40);
5717
+ return selected.length ? selected.map( function() {
5718
+ return $( this ).text();
5719
+ }).get().join( ", " ) : self.placeholder;
5720
+ });
5362
5721
  },
5363
5722
 
5364
- close: function() {
5365
- if ( this.options.disabled || !this.isOpen || this.options.nativeMenu ) {
5366
- return;
5367
- }
5368
-
5369
- var self = this;
5723
+ setButtonCount: function() {
5724
+ var selected = this.selected();
5370
5725
 
5371
- if ( self.menuType == "page" ) {
5372
- // rebind the page remove that was unbound in the open function
5373
- // to allow for the parent page removal from actions other than the use
5374
- // of a dialog sized custom select
5375
- self.thisPage.bind( "pagehide.remove", function(){
5376
- $(this).remove();
5377
- });
5378
-
5379
- // doesn't solve the possible issue with calling change page
5380
- // where the objects don't define data urls which prevents dialog key
5381
- // stripping - changePage has incoming refactor
5382
- window.history.back();
5383
- } else{
5384
- self.screen.addClass( "ui-screen-hidden" );
5385
- self.listbox.addClass( "ui-selectmenu-hidden" ).removeAttr( "style" ).removeClass( "in" );
5386
- self.list.appendTo( self.listbox );
5387
- self._focusButton();
5726
+ // multiple count inside button
5727
+ if ( this.isMultiple ) {
5728
+ this.buttonCount[ selected.length > 1 ? "show" : "hide" ]().text( selected.length );
5388
5729
  }
5730
+ },
5389
5731
 
5390
- // allow the dialog to be closed again
5391
- this.isOpen = false;
5732
+ refresh: function() {
5733
+ this.setButtonText();
5734
+ this.setButtonCount();
5392
5735
  },
5393
5736
 
5394
5737
  disable: function() {
5395
- this.element.attr( "disabled", true );
5396
- this.button.addClass( "ui-disabled" ).attr( "aria-disabled", true );
5397
- return this._setOption( "disabled", true );
5738
+ this._setDisabled( true );
5739
+ this.button.addClass( "ui-disabled" );
5398
5740
  },
5399
5741
 
5400
5742
  enable: function() {
5401
- this.element.attr( "disabled", false );
5402
- this.button.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
5403
- return this._setOption( "disabled", false );
5743
+ this._setDisabled( false );
5744
+ this.button.removeClass( "ui-disabled" );
5404
5745
  }
5405
5746
  });
5406
5747
 
@@ -5410,9 +5751,7 @@ $( document ).bind( "pagecreate create", function( e ){
5410
5751
  .not( ":jqmData(role='none'), :jqmData(role='nojs')" )
5411
5752
  .selectmenu();
5412
5753
  });
5413
-
5414
5754
  })( jQuery );
5415
-
5416
5755
  /*
5417
5756
  * jQuery Mobile Framework : plugin for making button-like links
5418
5757
  * Copyright (c) jQuery Project
@@ -5675,7 +6014,7 @@ $.fn.fixHeaderFooter = function( options ) {
5675
6014
  // single controller for all showing,hiding,toggling
5676
6015
  $.mobile.fixedToolbars = (function() {
5677
6016
 
5678
- if ( !$.support.scrollTop ) {
6017
+ if ( !$.support.scrollTop || $.support.touchOverflow ) {
5679
6018
  return;
5680
6019
  }
5681
6020
 
@@ -5999,7 +6338,7 @@ $( document ).bind( "pagecreate create", function( event ) {
5999
6338
 
6000
6339
  $( event.target ).each(function() {
6001
6340
 
6002
- if ( !$.support.scrollTop ) {
6341
+ if ( !$.support.scrollTop || $.support.touchOverflow ) {
6003
6342
  return this;
6004
6343
  }
6005
6344
 
@@ -6020,6 +6359,65 @@ $( document ).bind( "pagecreate create", function( event ) {
6020
6359
  }
6021
6360
  });
6022
6361
 
6362
+ })( jQuery );
6363
+ /*
6364
+ * jQuery Mobile Framework : "fixHeaderFooter" native plugin - Behavior for "fixed" headers,footers, and scrolling inner content
6365
+ * Copyright (c) jQuery Project
6366
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6367
+ * http://jquery.org/license
6368
+ */
6369
+
6370
+ (function( $, undefined ) {
6371
+
6372
+ $.mobile.touchOverflowEnabled = false;
6373
+
6374
+ $( document ).bind( "pagecreate", function( event ) {
6375
+ if( $.support.touchOverflow && $.mobile.touchOverflowEnabled ){
6376
+
6377
+ var $target = $( event.target ),
6378
+ scrollStartY = 0;
6379
+
6380
+ if( $target.is( ":jqmData(role='page')" ) ){
6381
+
6382
+ $target.each(function() {
6383
+ var $page = $( this ),
6384
+ $fixies = $page.find( ":jqmData(role='header'), :jqmData(role='footer')" ).filter( ":jqmData(position='fixed')" ),
6385
+ fullScreen = $page.jqmData( "fullscreen" ),
6386
+ $scrollElem = $fixies.length ? $page.find( ".ui-content" ) : $page;
6387
+
6388
+ $page.addClass( "ui-mobile-touch-overflow" );
6389
+
6390
+ $scrollElem.bind( "scrollstop", function(){
6391
+ if( $scrollElem.scrollTop() > 0 ){
6392
+ window.scrollTo( 0, $.mobile.defaultHomeScroll );
6393
+ }
6394
+ });
6395
+
6396
+ if( $fixies.length ){
6397
+
6398
+ $page.addClass( "ui-native-fixed" );
6399
+
6400
+ if( fullScreen ){
6401
+
6402
+ $page.addClass( "ui-native-fullscreen" );
6403
+
6404
+ $fixies.addClass( "fade in" );
6405
+
6406
+ $( document ).bind( "vclick", function(){
6407
+ $fixies
6408
+ .removeClass( "ui-native-bars-hidden" )
6409
+ .toggleClass( "in out" )
6410
+ .animationComplete(function(){
6411
+ $(this).not( ".in" ).addClass( "ui-native-bars-hidden" );
6412
+ });
6413
+ });
6414
+ }
6415
+ }
6416
+ });
6417
+ }
6418
+ }
6419
+ });
6420
+
6023
6421
  })( jQuery );
6024
6422
  /*
6025
6423
  * jQuery Mobile Framework : resolution and CSS media query related helpers and behavior
@@ -6100,7 +6498,7 @@ $( document ).bind( "mobileinit.htmlclass", function() {
6100
6498
 
6101
6499
  var ev = $.support.orientation;
6102
6500
 
6103
- $window.bind( "orientationchange.htmlclass throttledResize.htmlclass", function( event ) {
6501
+ $window.bind( "orientationchange.htmlclass throttledresize.htmlclass", function( event ) {
6104
6502
 
6105
6503
  // add orientation class to HTML element on flip/resize.
6106
6504
  if ( event.orientation ) {
@@ -6136,47 +6534,47 @@ $(function() {
6136
6534
  $head = $( "head" ),
6137
6535
  $window = $( window );
6138
6536
 
6139
- //trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
6537
+ // trigger mobileinit event - useful hook for configuring $.mobile settings before they're used
6140
6538
  $( window.document ).trigger( "mobileinit" );
6141
6539
 
6142
- //support conditions
6143
- //if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
6144
- //otherwise, proceed with the enhancements
6540
+ // support conditions
6541
+ // if device support condition(s) aren't met, leave things as they are -> a basic, usable experience,
6542
+ // otherwise, proceed with the enhancements
6145
6543
  if ( !$.mobile.gradeA() ) {
6146
6544
  return;
6147
6545
  }
6148
-
6149
- // override ajaxEnabled on platforms that have known conflicts with hash history updates
6546
+
6547
+ // override ajaxEnabled on platforms that have known conflicts with hash history updates
6150
6548
  // or generally work better browsing in regular http for full page refreshes (BB5, Opera Mini)
6151
- if( $.mobile.ajaxBlacklist ){
6549
+ if ( $.mobile.ajaxBlacklist ) {
6152
6550
  $.mobile.ajaxEnabled = false;
6153
6551
  }
6154
6552
 
6155
- //add mobile, initial load "rendering" classes to docEl
6553
+ // add mobile, initial load "rendering" classes to docEl
6156
6554
  $html.addClass( "ui-mobile ui-mobile-rendering" );
6157
6555
 
6158
- //loading div which appears during Ajax requests
6159
- //will not appear if $.mobile.loadingMessage is false
6556
+ // loading div which appears during Ajax requests
6557
+ // will not appear if $.mobile.loadingMessage is false
6160
6558
  var $loader = $( "<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1></h1></div>" );
6161
6559
 
6162
6560
  $.extend($.mobile, {
6163
6561
  // turn on/off page loading message.
6164
6562
  showPageLoadingMsg: function() {
6165
- if( $.mobile.loadingMessage ){
6563
+ if ( $.mobile.loadingMessage ) {
6166
6564
  var activeBtn = $( "." + $.mobile.activeBtnClass ).first();
6167
-
6565
+
6168
6566
  $loader
6169
6567
  .find( "h1" )
6170
6568
  .text( $.mobile.loadingMessage )
6171
6569
  .end()
6172
6570
  .appendTo( $.mobile.pageContainer )
6173
- //position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
6174
- .css( {
6175
- top: $.support.scrollTop && $(window).scrollTop() + $(window).height() / 2 ||
6571
+ // position at y center (if scrollTop supported), above the activeBtn (if defined), or just 100px from top
6572
+ .css({
6573
+ top: $.support.scrollTop && $window.scrollTop() + $window.height() / 2 ||
6176
6574
  activeBtn.length && activeBtn.offset().top || 100
6177
- } );
6575
+ });
6178
6576
  }
6179
-
6577
+
6180
6578
  $html.addClass( "ui-loading" );
6181
6579
  },
6182
6580
 
@@ -6194,36 +6592,36 @@ $(function() {
6194
6592
  },
6195
6593
 
6196
6594
  // find and enhance the pages in the dom and transition to the first page.
6197
- initializePage: function(){
6198
- //find present pages
6595
+ initializePage: function() {
6596
+ // find present pages
6199
6597
  var $pages = $( ":jqmData(role='page')" );
6200
-
6201
- //if no pages are found, create one with body's inner html
6202
- if( !$pages.length ){
6598
+
6599
+ // if no pages are found, create one with body's inner html
6600
+ if ( !$pages.length ) {
6203
6601
  $pages = $( "body" ).wrapInner( "<div data-" + $.mobile.ns + "role='page'></div>" ).children( 0 );
6204
6602
  }
6205
6603
 
6206
- //add dialogs, set data-url attrs
6207
- $pages.add( ":jqmData(role='dialog')" ).each(function(){
6604
+ // add dialogs, set data-url attrs
6605
+ $pages.add( ":jqmData(role='dialog')" ).each(function() {
6208
6606
  var $this = $(this);
6209
6607
 
6210
- // unless the data url is already set set it to the id
6211
- if( !$this.jqmData('url') ){
6212
- $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) );
6608
+ // unless the data url is already set set it to the pathname
6609
+ if ( !$this.jqmData("url") ) {
6610
+ $this.attr( "data-" + $.mobile.ns + "url", $this.attr( "id" ) || location.pathname + location.search );
6213
6611
  }
6214
6612
  });
6215
6613
 
6216
- //define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
6614
+ // define first page in dom case one backs out to the directory root (not always the first page visited, but defined as fallback)
6217
6615
  $.mobile.firstPage = $pages.first();
6218
6616
 
6219
- //define page container
6617
+ // define page container
6220
6618
  $.mobile.pageContainer = $pages.first().parent().addClass( "ui-mobile-viewport" );
6221
6619
 
6222
- //cue page loading message
6620
+ // cue page loading message
6223
6621
  $.mobile.showPageLoadingMsg();
6224
6622
 
6225
6623
  // if hashchange listening is disabled or there's no hash deeplink, change to the first page in the DOM
6226
- if( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ){
6624
+ if ( !$.mobile.hashListeningEnabled || !$.mobile.path.stripHash( location.hash ) ) {
6227
6625
  $.mobile.changePage( $.mobile.firstPage, { transition: "none", reverse: true, changeHash: false, fromHashChange: true } );
6228
6626
  }
6229
6627
  // otherwise, trigger a hashchange to load a deeplink
@@ -6232,28 +6630,28 @@ $(function() {
6232
6630
  }
6233
6631
  }
6234
6632
  });
6235
-
6236
- //initialize events now, after mobileinit has occurred
6633
+
6634
+ // initialize events now, after mobileinit has occurred
6237
6635
  $.mobile._registerInternalEvents();
6238
-
6239
- //check which scrollTop value should be used by scrolling to 1 immediately at domready
6240
- //then check what the scroll top is. Android will report 0... others 1
6241
- //note that this initial scroll won't hide the address bar. It's just for the check.
6242
- $(function(){
6636
+
6637
+ // check which scrollTop value should be used by scrolling to 1 immediately at domready
6638
+ // then check what the scroll top is. Android will report 0... others 1
6639
+ // note that this initial scroll won't hide the address bar. It's just for the check.
6640
+ $(function() {
6243
6641
  window.scrollTo( 0, 1 );
6244
6642
 
6245
- //if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
6246
- //it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
6247
- //so if it's 1, use 0 from now on
6643
+ // if defaultHomeScroll hasn't been set yet, see if scrollTop is 1
6644
+ // it should be 1 in most browsers, but android treats 1 as 0 (for hiding addr bar)
6645
+ // so if it's 1, use 0 from now on
6248
6646
  $.mobile.defaultHomeScroll = ( !$.support.scrollTop || $(window).scrollTop() === 1 ) ? 0 : 1;
6249
-
6647
+
6250
6648
  //dom-ready inits
6251
6649
  if( $.mobile.autoInitializePage ){
6252
- $( $.mobile.initializePage );
6650
+ $.mobile.initializePage();
6253
6651
  }
6254
-
6255
- //window load event
6256
- //hide iOS browser chrome on load
6652
+
6653
+ // window load event
6654
+ // hide iOS browser chrome on load
6257
6655
  $window.load( $.mobile.silentScroll );
6258
6656
  });
6259
6657
  })( jQuery, this );