qedproject 0.0.8 → 0.0.9

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