diff --git a/.gitignore b/.gitignore index acdb6593..a73ae101 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,9 @@ .DS_Store cache/ combined/ +combine/ +compiled/ +gitstatus.log +refreshCDN +*.swp +.gitignore diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 8672b19c..00000000 --- a/.htaccess +++ /dev/null @@ -1,10 +0,0 @@ -#thx askapache.com - -FileETag None - -Header unset ETag -Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate" -Header set Pragma "no-cache" -Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT" - - \ No newline at end of file diff --git a/Makefile b/Makefile old mode 100755 new mode 100644 index e97775a6..ab116292 --- a/Makefile +++ b/Makefile @@ -1,108 +1,216 @@ +# The system generated date in YYYYMMDD format +DATE = $(shell date "+%Y%m%d") +# The version according to the source file. If this is the nightly build, use a different version VER = $(shell cat version.txt) -SED_VER = sed "s/@VERSION/${VER}/" +# The command to replace the @VERSION in the files with the actual version +SED_VER = sed "s/@VERSION/$(shell git log -1 --format=format:" Git > Date: %cd Info SHA1: %H")/" +deploy: SED_VER = sed "s/@VERSION/${VER}/" + +# The version of jQuery core used +JQUERY = $(shell grep Library js/jquery.js | sed s'/ \* jQuery JavaScript Library v//') + +# The directory to create the zipped files in and also serves as the filenames DIR = jquery.mobile-${VER} -MAX = ${DIR}.js +STRUCTUREFILE = jquery.mobile.structure-${VER} +nightly: DIR = jquery.mobile + +# The output folder for the finished files +OUTPUT = compiled + +# Command to remove the latest directory from the CDN before uploading, only if using latest target +RMLATEST = echo "" + +# The output folder for the nightly files. +NIGHTLY_OUTPUT = nightlies/${DATE} +ifeq (${NIGHTLY_OUTPUT}, latest) + RMLATEST = ssh jqadmin@code.origin.jquery.com 'rm -rf /var/www/html/code.jquery.com/mobile/latest' +endif +NIGHTLY_WEBPATH = http://code.jquery.com/mobile/${NIGHTLY_OUTPUT} + +# The filenames +JS = ${DIR}.js MIN = ${DIR}.min.js CSS = ${DIR}.css CSSMIN = ${DIR}.min.css +CSSSTRUCTURE = ${STRUCTUREFILE}.css +CSSSTRUCTUREMIN = ${STRUCTUREFILE}.min.css +CSSTHEME = default -FILES = js/jquery.ui.widget.js \ - js/jquery.mobile.widget.js \ - js/jquery.mobile.media.js \ - js/jquery.mobile.support.js \ - js/jquery.mobile.event.js \ - js/jquery.mobile.hashchange.js \ - js/jquery.mobile.core.js \ - js/jquery.mobile.navigation.js \ - js/jquery.mobile.page.js \ - js/jquery.ui.position.js \ - js/jquery.mobile.fixHeaderFooter.js \ - js/jquery.mobile.forms.checkboxradio.js \ - js/jquery.mobile.forms.textinput.js \ - js/jquery.mobile.forms.select.js \ - js/jquery.mobile.buttonMarkup.js \ - js/jquery.mobile.forms.button.js \ - js/jquery.mobile.forms.slider.js \ - js/jquery.mobile.collapsible.js \ - js/jquery.mobile.controlGroup.js \ - js/jquery.mobile.fieldContain.js \ - js/jquery.mobile.listview.js \ - js/jquery.mobile.listview.filter.js \ - js/jquery.mobile.dialog.js \ - js/jquery.mobile.navbar.js \ - js/jquery.mobile.grid.js +# The files to include when compiling the JS files +JSFILES = js/jquery.ui.widget.js \ + js/jquery.mobile.widget.js \ + js/jquery.mobile.media.js \ + js/jquery.mobile.support.js \ + js/jquery.mobile.vmouse.js \ + js/jquery.mobile.event.js \ + js/jquery.mobile.hashchange.js \ + js/jquery.mobile.page.js \ + js/jquery.mobile.core.js \ + js/jquery.mobile.navigation.js \ + js/jquery.mobile.navigation.pushstate.js \ + js/jquery.mobile.transition.js \ + js/jquery.mobile.degradeInputs.js \ + js/jquery.mobile.dialog.js \ + js/jquery.mobile.page.sections.js \ + js/jquery.mobile.collapsible.js \ + js/jquery.mobile.fieldContain.js \ + js/jquery.mobile.grid.js \ + js/jquery.mobile.navbar.js \ + js/jquery.mobile.listview.js \ + js/jquery.mobile.listview.filter.js \ + js/jquery.mobile.nojs.js \ + js/jquery.mobile.forms.checkboxradio.js \ + js/jquery.mobile.forms.button.js \ + js/jquery.mobile.forms.slider.js \ + js/jquery.mobile.forms.textinput.js \ + js/jquery.mobile.forms.select.custom.js \ + js/jquery.mobile.forms.select.js \ + js/jquery.mobile.buttonMarkup.js \ + js/jquery.mobile.controlGroup.js \ + js/jquery.mobile.links.js \ + js/jquery.mobile.fixHeaderFooter.js \ + js/jquery.mobile.fixHeaderFooter.native.js \ + js/jquery.mobile.init.js -CSSFILES = themes/default/jquery.mobile.theme.css \ - themes/default/jquery.mobile.core.css \ - themes/default/jquery.mobile.transitions.css \ - themes/default/jquery.mobile.grids.css \ - themes/default/jquery.mobile.headerfooter.css \ - themes/default/jquery.mobile.navbar.css \ - themes/default/jquery.mobile.button.css \ - themes/default/jquery.mobile.collapsible.css \ - themes/default/jquery.mobile.controlgroup.css \ - themes/default/jquery.mobile.dialog.css \ - themes/default/jquery.mobile.forms.checkboxradio.css \ - themes/default/jquery.mobile.forms.fieldcontain.css \ - themes/default/jquery.mobile.forms.select.css \ - themes/default/jquery.mobile.forms.textinput.css \ - themes/default/jquery.mobile.listview.css \ - themes/default/jquery.mobile.forms.slider.css +CSSTHEMEFILES = css/themes/${CSSTHEME}/jquery.mobile.theme.css +CSSSTRUCTUREFILES = css/structure/jquery.mobile.core.css \ + css/structure/jquery.mobile.transitions.css \ + css/structure/jquery.mobile.grids.css \ + css/structure/jquery.mobile.headerfooter.css \ + css/structure/jquery.mobile.navbar.css \ + css/structure/jquery.mobile.button.css \ + css/structure/jquery.mobile.collapsible.css \ + css/structure/jquery.mobile.controlgroup.css \ + css/structure/jquery.mobile.dialog.css \ + css/structure/jquery.mobile.forms.checkboxradio.css \ + css/structure/jquery.mobile.forms.fieldcontain.css \ + css/structure/jquery.mobile.forms.select.css \ + css/structure/jquery.mobile.forms.textinput.css \ + css/structure/jquery.mobile.listview.css \ + css/structure/jquery.mobile.forms.slider.css -all: mobile min css cssmin -clean: - @@rm -rf ${DIR}* +# The files to include when compiling the CSS files +CSSFILES = ${CSSTHEMEFILES} ${CSSSTRUCTUREFILES} -css: - @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${CSS} - @@cat ${CSSFILES} >> ${CSS} +# By default, this is what get runs when make is called without any arguments. +# Min and un-min CSS and JS files are the only things built +all: init js min css cssmin notify -cssmin: css - @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${CSSMIN} - @@java -jar build/yuicompressor-2.4.2.jar --type css ${CSS} >> ${CSSMIN} +# Build the normal CSS file. +css: init + # Build the CSS file + @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${OUTPUT}/${CSS} + @@cat ${CSSFILES} >> ${OUTPUT}/${CSS} + @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${OUTPUT}/${CSSSTRUCTURE} + @@cat ${CSSSTRUCTUREFILES} >> ${OUTPUT}/${CSSSTRUCTURE} -mobile: - @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${MAX} - @@cat ${FILES} >> ${MAX} +# Build the minified CSS file +cssmin: init css + # Build the minified CSS file + @@java -jar build/yuicompressor-2.4.6.jar --type css ${OUTPUT}/${CSS} >> ${OUTPUT}/${CSSMIN} + @@java -jar build/yuicompressor-2.4.6.jar --type css ${OUTPUT}/${CSSSTRUCTURE} >> ${OUTPUT}/${CSSSTRUCTUREMIN} -min: mobile - @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${MIN} - @@java -jar build/google-compiler-20100917.jar --js ${MAX} --warning_level QUIET --js_output_file ${MIN}.tmp - @@cat ${MIN}.tmp >> ${MIN} +# Build the normal JS file +js: init + # Build the JavaScript file + @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${OUTPUT}/${JS} + @@cat ${JSFILES} >> ${OUTPUT}/${JS} + +# Create the output directory. This is in a separate step so its not dependant on other targets +init: + # Building jQuery Mobile in the "${OUTPUT}" folder + @@rm -rf ${OUTPUT} + @@mkdir ${OUTPUT} + +# Build the minified JS file +min: init js + # Build the minified JavaScript file + @@head -8 js/jquery.mobile.core.js | ${SED_VER} > ${OUTPUT}/${MIN} + @@java -jar build/google-compiler-20111003.jar --js ${OUTPUT}/${JS} --warning_level QUIET --js_output_file ${MIN}.tmp + @@cat ${MIN}.tmp >> ${OUTPUT}/${MIN} @@rm -f ${MIN}.tmp -zip: clean min cssmin +# Let the user know the files were built and where they are +notify: + @@echo "The files have been built and are in " $$(pwd)/${OUTPUT} + +# Pull the latest commits. This is used for the nightly build but can be used to save some keystrokes +pull: + @@git pull --quiet + +# Zip the 4 files and the theme images into one convenient package +zip: init js min css cssmin @@mkdir -p ${DIR} - @@cp ${DIR}*.js ${DIR}/ - @@cp ${DIR}*.css ${DIR}/ - @@cp -R themes/default/images ${DIR}/ - @@zip -r ${DIR}.zip ${DIR} + @@cp ${OUTPUT}/*.js ${DIR}/ + @@cp ${OUTPUT}/*.css ${DIR}/ + @@cp -R css/themes/${CSSTHEME}/images ${DIR}/ + @@zip -rq ${OUTPUT}/${DIR}.zip ${DIR} + @@rm -fr ${DIR} + + +# Used by the jQuery team to make the nightly builds +nightly: pull zip + # Create the folder to hold the files for the demos + @@mkdir -p ${VER} + + # Copy in the base stuff for the demos + @@cp -r index.html css experiments docs tools ${VER}/ + + # First change all the paths from super deep to the same level for JS files + @@find ${VER} -type f -name '*.html' -exec sed -i 's|src="../../../js|src="js|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i 's|src="../../js|src="js|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i 's|src="../js|src="js|g' {} \; + + # Then change all the paths from super deep to the same level for CSS files + @@find ${VER} -type f -name '*.html' -exec sed -i 's|media="only all"||g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i 's|rel="stylesheet" href="../../../|rel="stylesheet" href="|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i 's|rel="stylesheet" href="../../|rel="stylesheet" href="|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i 's|rel="stylesheet" href="../|rel="stylesheet" href="|g' {} \; + + # Change the empty paths to the location of this nightly file + @@find ${VER} -type f -name '*.html' -exec sed -i 's|href="css/themes/${CSSTHME}/"|href="${NIGHTLY_WEBPATH}/${DIR}.min.css"|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i 's|src="js/jquery.js"|src="http://code.jquery.com/jquery-${JQUERY}.min.js"|' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i 's|src="js/"|src="${NIGHTLY_WEBPATH}/${DIR}.min.js"|g' {} \; + + # Move the demos into the output folder + @@mv ${VER} ${OUTPUT}/demos + + # Copy the images as well + @@cp -R css/themes/${CSSTHME}/images ${OUTPUT} + + @@${RMLATEST} + @@scp -r ${OUTPUT} jqadmin@code.origin.jquery.com:/var/www/html/code.jquery.com/mobile/${NIGHTLY_OUTPUT} + @@rm -rf ${OUTPUT} # Used by the jQuery team to deploy a build to the CDN deploy: zip # Deploy to CDN - @@mv ${DIR} ${VER} - @@cp ${DIR}.zip ${VER}/ + @@mv ${OUTPUT} ${VER} @@scp -r ${VER} jqadmin@code.origin.jquery.com:/var/www/html/code.jquery.com/mobile/ - @@mv ${VER} ${DIR} + @@mv ${VER} ${OUTPUT} - # Deploy Demos + # Deploy Demos to the jQueryMobile.com site @@mkdir -p ${VER} - @@cp -r index.html themes experiments docs ${VER}/ + @@cp -r index.html css experiments docs tools ${VER}/ - @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|"text/javascript" src="../../../js|"text/javascript" src="js|g' {} \; - @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|"text/javascript" src="../../js|"text/javascript" src="js|g' {} \; - @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|"text/javascript" src="../js|"text/javascript" src="js|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|src="../../../js|src="js|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|src="../../js|src="js|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|src="../js|src="js|g' {} \; + @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|media="only all"||g' {} \; @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|rel="stylesheet" href="../../../|rel="stylesheet" href="|g' {} \; @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|rel="stylesheet" href="../../|rel="stylesheet" href="|g' {} \; @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|rel="stylesheet" href="../|rel="stylesheet" href="|g' {} \; - @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's|href="themes/default"|href="http://code.jquery.com/mobile/${VER}/${DIR}.min.css"|g' {} \; - @@find ${VER} -type f -name '*.html' -exec sed -i "" -e 's| + + + + -
+
-
+

Accessibility

+ Home
+ + +

Accessibility

jQuery Mobile is built upon standard, semantic HTML, allowing pages to be accessible to the broadest range of devices possible. For A-Grade browsers, many of the components in jQuery Mobile leverage techniques such as focus management, keyboard navigation, and HTML attributes specified in the W3C's WAI-ARIA specification.

@@ -24,7 +32,32 @@

While our accessibility implementation is currently a work in progress, we aim to provide a fully accessible suite of components for version 1.0.

- +
+ +
+ +
+ +

More in this section

+ + +
+
+ + +
+ + diff --git a/docs/about/features.html b/docs/about/features.html index 212e29d2..100b48ae 100755 --- a/docs/about/features.html +++ b/docs/about/features.html @@ -1,42 +1,74 @@ - - jQuery Mobile Docs - Intro - + + + jQuery Mobile Docs - Features + - + + + + -
+
-
-

Features

+
+

Features

+ Home
+ +

Key features:

    -
  • Built on jQuery core for familiar and consistent jQuery syntax and minimal learning curve
  • -
  • Compatible with all major mobile platforms - iOS, Android, Blackberry, Palm WebOS, Nokia/Symbian, Windows Mobile, bada, MeeGo with baseline support for all devices that understand HTML
  • -
  • Lightweight size (12k compressed for all mobile functionality) and minimal image dependencies for speed.
  • +
  • Built on jQuery core for familiar and consistent jQuery syntax and minimal learning curve and leverages jQuery UI code and patterns.
  • +
  • Compatible with all major mobile, tablet, e-reader & desktop platforms - iOS, Android, Blackberry, Palm WebOS, Nokia/Symbian, Windows Phone 7, MeeGo, Opera Mobile/Mini, Firefox Mobile, Kindle, Nook, and all modern browsers with graded levels of support.
  • +
  • Lightweight size and minimal image dependencies for speed.
  • +
  • Modular architecture for creating custom builds that are optimized to only the features needed for a particular application
  • HTML5 Markup-driven configuration of pages and behavior for fast development and minimal required scripting.
  • -
  • Progressive enhancement approach brings core content and functionality to all mobile, tablet and deskstop platforms and a rich, installed application-like experience on newer mobile platforms.
  • -
  • Automatic initialization by using HTML5 data-role attributes in the HTML markup to act as the trigger to automatically initialize all jQuery Mobile widgets found on a page.
  • +
  • Progressive enhancement approach brings core content and functionality to all mobile, tablet and desktop platforms and a rich, installed application-like experience on newer mobile platforms.
  • +
  • Responsive design techniques and tools allow the same underlying codebase to automatically scale from smartphone to desktop-sized screens
  • +
  • Powerful Ajax-powered navigation system to enable animated page transitions while maintaining back button, bookmarking and and clean URLs though pushState.
  • Accessibility features such as WAI-ARIA are also included to ensure that the pages work for screen readers (e.g. VoiceOver in iOS) and other assistive technologies.
  • -
  • New events streamline the process of supporting touch, mouse, and cursor focus-based user input methods with a simple API.
  • -
  • New plugins enhance native controls with touch-optimized, themable controls.
  • +
  • Touch and mouse event support streamline the process of supporting touch, mouse, and cursor focus-based user input methods with a simple API.
  • +
  • Unified UI widgets for common controls enhance native controls with touch-optimized, themable controls that are platform-agnostic and easy to use.
  • Powerful theming framework and ThemeRoller application make highly-branded experiences easy to build.
- +
+ +
+ +
+ +

More in this section

+ + +
+
+ + + +
- \ No newline at end of file + diff --git a/docs/about/index.html b/docs/about/index.html index 181b9fd5..567ee011 100755 --- a/docs/about/index.html +++ b/docs/about/index.html @@ -1,22 +1,28 @@ - - jQuery UI Mobile Framework - Documentation - + + + jQuery UI Mobile Framework - About + - + + + + -
-
+
+ +

About jQuery Mobile

+ Home
-
- \ No newline at end of file + diff --git a/docs/about/intro.html b/docs/about/intro.html index c9504a09..ab355974 100755 --- a/docs/about/intro.html +++ b/docs/about/intro.html @@ -1,36 +1,67 @@ - + + jQuery Mobile Docs - Intro - + - + + + + -
+
-
+

Introduction

+ + + Home
+
-

jQuery’s mobile strategy can be summarized simply: Delivering top-of-the-line JavaScript in a unified User Interface that works across the most-used smartphone web browsers and tablet form factors.

+

jQuery Mobile Overview

+ +

jQuery’s mobile strategy can be summarized simply: A unified user interface system that works seamlessly across all popular mobile device platforms, built on the rock-solid jQuery and jQuery UI foundation. Focused on a lightweight codebase built on progressive enhancement with a flexible, easily themeable design.

The critical difference with our approach is the wide variety of mobile platforms we’re targeting with jQuery Mobile. We’ve been working hard at bringing jQuery support to all mobile browsers that are sufficiently-capable and have at least a nominal amount of market share. In this way, we’re treating mobile web browsers exactly how we treat desktop web browsers.

To make this broad support possible, all pages in jQuery Mobile are built on a foundation of clean, semantic HTML to ensure compatibility with pretty much any web-enabled device. In devices that interpret CSS and JavaScript, jQuery Mobile applies progressive enhancement techniques to unobtrusively transform the semantic page into a rich, interactive experience that leverages the power of jQuery and CSS. Accessibility features such as WAI-ARIA are tightly integrated throughout the framework to provide support for screen readers and other assistive technologies.

- - Smartphone and tablet designs - +
+ +
+ +
+ +

More in this section

+ + +
+
+ + +
diff --git a/docs/about/platforms.html b/docs/about/platforms.html index 27692840..e9970b6f 100755 --- a/docs/about/platforms.html +++ b/docs/about/platforms.html @@ -1,44 +1,101 @@ - - jQuery Mobile Docs - Intro - + + + jQuery Mobile Docs - Supported platforms + - + + + + -
+
-
+

Supported platforms

+ Home
- -

Supported platforms

-

For the alpha release, the following devices have been tested and should have a fairly solid jQuery Mobile experience. That being said, there are still a fair amount of bugs and performance improvements to be tackled before the 1.0 release in January.

+ + +
+

Platform support in 1.0 RC2

+

We're excited to announce that as of 1.0 RC2, we've covered all our target platforms for the project. At this stage, we have broad support for the vast majority of all modern desktop, smartphone, tablet, and e-reader platforms. In addition, feature phones and older browsers are supported because of our progressive enhancement approach. We're very proud of our commitment to universal accessibility through our broad support for all popular platforms.

+ +

Our graded support matrix was created over a year ago based on our goals as a project and since that time, we've been refining our grading system based on real-world device testing and the quickly evolving mobile landscape. To provide a quick summary of our browser support in Beta 1, we've created a simple A (full), B (full minus Ajax), C (basic) grade system with notes of the actual devices and versions we've been testing on in our lab.

+ +

The visual fidelity of the experience is highly dependent on CSS rendering capabilities of the device and platform so not all A grade experience will be pixel-perfect but that's the nature of the web. 

+

A-grade - Full enhanced experience with Ajax-based animated page transitions.

    -
  • Apple iOS: iPhone, iPod Touch, iPad (all versions)
  • -
  • Android: all devices (all versions)
  • -
  • Blackberry Torch (version 6)
  • -
  • Palm WebOS Pre, Pixi
  • -
  • Nokia N900 (in progress)
  • +
  • Apple iOS 3.2-5.0 beta - Tested on the original iPad (3.2 / 4.3), iPad 2 (4.3), original iPhone (3.1), iPhone 3 (3.2), 3GS (4.3), and 4 (4.3 / 5.0 beta)
  • +
  • Android 2.1-2.3 - Tested on the HTC Incredible (2.2), original Droid (2.2), Nook Color (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5)
  • +
  • Android Honeycomb- Tested on the Samsung Galaxy Tab 10.1
  • +
  • Windows Phone 7 - Tested on the HTC 7 Surround
  • +
  • Blackberry 6.0 - Tested on the Torch 9800 and Style 9670
  • +
  • Blackberry 7 - Tested on BlackBerry® Torch 9810
  • +
  • Blackberry Playbook - Tested on PlayBook version 1.0.1 / 1.0.5
  • +
  • Palm WebOS (1.4-2.0) - Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0)
  • +
  • Palm WebOS 3.0 - Tested on HP TouchPad
  • +
  • Firebox Mobile (Beta) - Tested on Android 2.2
  • +
  • Opera Mobile 11.0: Tested on the iPhone 3GS and 4 (5.0/6.0), Android 2.2 (5.0/6.0), Windows Mobile 6.5 (5.0)
  • +
  • Meego 1.2 - Tested on Nokia 950
  • +
  • Kindle 3: Tested on the built-in WebKit browser included in the Kindle 3 device
  • +
  • Chrome Desktop 11-13 - Tested on OS X 10.6.7 and Windows 7
  • +
  • Firefox Desktop 3.6-4.0 - Tested on OS X 10.6.7 and Windows 7
  • +
  • Internet Explorer 7-9 - Tested on Windows XP, Vista and 7 (minor CSS issues)
  • +
  • Opera Desktop 10-11 - Tested on OS X 10.6.7 and Windows 7
  • +
+

B-grade - Enhanced experience except without Ajax navigation features.

+
    +
  • Blackberry 5.0: Tested on the Storm 2 9550, Bold 9770
  • +
  • Opera Mini (5.0-6.0) - Tested on iOS 3.2/4.3
  • +
  • Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1)
  • +
+

C-grade - Basic, non-enhanced HTML experience that is still functional

+
    +
  • Blackberry 4.x - Tested on the Curve 8330
  • +
  • Windows Mobile - Tested on the HTC Leo (WInMo 5.2)
  • +
  • All older smartphone platforms and featurephones - Any device that doesn't support media queries will receive the basic, C grade experience
  • +
+

Not Officially Supported - May work, but haven't been thoroughly tested or debugged

+
    +
  • Samsung Bada - The project doesn't currently have test devices or emulators, but current support is known to be fairly good. Support level undecided for 1.0
-

Older versions of Blackberry, Nokia/Symbian, and Windows Mobile may work but we're still seeing JavaScript errors and rendering bugs that need to be tracked down. We don't yet have phones to test Windows Mobile 7, bada, or MeeGo but these will be added as soon as we can get devices.

- -

Since jQuery Mobile is built on the jQuery core, all pages should also work great on most recent versions of desktop browsers too - Firefox, Chrome, Safari, Internet Explorer, Opera, etc.

- -

View supported browser matrix on jQuery Mobile

+
+ +
+ +
+ +

More in this section

+ + +
+
-
+
+ + +
- \ No newline at end of file + diff --git a/docs/api/events.html b/docs/api/events.html index 2c005b7b..b6bd9fa7 100755 --- a/docs/api/events.html +++ b/docs/api/events.html @@ -1,48 +1,111 @@ - - + + - - jQuery Mobile Docs - Events - + + + jQuery Mobile Docs - Events + - - - + + + + + + -
+
-
+

Events

+ Home
- +

jQuery Mobile offers several custom events that build upon native events to create useful hooks for development. Note that these events employ various touch, mouse, and window events, depending on event existence, so you can bind to them for use in both handheld and desktop environments. You can bind to these events like you would with other jQuery events, using live() or bind().

- + +
+

Important: Use pageInit(), not $(document).ready()

+ The first thing you learn in jQuery is to call code inside the $(document).ready() function so everything will execute as soon as the DOM is loaded. However, in jQuery Mobile, Ajax is used to load the contents of each page into the DOM as you navigate, and the DOM ready handler only executes for the first page. To execute code whenever a new page is loaded and created, you can bind to the pageinit event. This event is explained in detail at the bottom of this page.
+ +

+

+

Important: pageCreate() vs pageInit()

+ Prior to Beta 2 the recommendation to users wishing to manipulate jQuery Mobile enhanced page and child widget markup was to bind to the pagecreate event. In Beta 2 an internal change was made to decouple each of the widgets by binding to the pagecreate event in place of direct calls to the widget methods. As a result, users binding to the pagecreate in mobileinit would find their binding executing before the markup had been enhanced by each of the plugins. In keeping with the lifecycle of the jQuery UI Widget Factory, the initialization method is invoked after the create method, so the pageinit event provides the correct timing for post enhancement manipulation of the DOM and/or Javascript objects. + + In short, if you were previously using pagecreate to manipulate the enhanced markup before the page was shown its very likely you'll want to migrate to 'pageinit'. +

+

Touch events

tap
Triggers after a quick, complete touch event.
- +
taphold
Triggers after a held complete touch event (close to one second).
- +
swipe
-
Triggers when a horizontal drag of 30px or more (and less than 20px vertically) occurs within 1 second duration.
- +

Triggers when a horizontal drag of 30px or more (and less than 20px vertically) occurs within 1 second duration but these can be configured:

+
    +
  • scrollSupressionThreshold (default: 10px) – More than this horizontal displacement, and we will suppress scrolling
  • +
  • durationThreshold (default: 1000ms) – More time than this, and it isn’t a swipe
  • +
  • horizontalDistanceThreshold (default: 30px) – Swipe horizontal displacement must be more than this.
  • +
  • verticalDistanceThreshold (default: 75px) – Swipe vertical displacement must be less than this.
  • +
+
+
swipeleft
Triggers when a swipe event occurred moving in the left direction.
- +
swiperight
Triggers when a swipe event occurred moving in the right direction.
+ +

Virtual mouse events

+

We provide a set of "virtual" mouse events that attempt to abstract away mouse and touch events. This allows the developer to register listeners for the basic mouse events, such as mousedown, mousemove, mouseup, and click, and the plugin will take care of registering the correct listeners behind the scenes to invoke the listener at the fastest possible time for that device. In touch environments, the plugin retains the order of event firing that is seen in traditional mouse environments, so for example, vmouseup is always dispatched before vmousedown, and vmousedown before vclick, etc. The virtual mouse events also normalize how coordinate information is extracted from the event, so in touch based environments, coordinates are available from the pageX, pageY, screenX, screenY, clientX, and clientY properties, directly on the event object.

+
+
vmouseover
+
Normalized event for handling touch or mouseover events
+ +
vmousedown
+
Normalized event for handling touchstart or mousedown events
+ +
vmousemove
+
Normalized event for handling touchmove or mousemove events
+ +
vmouseup
+
Normalized event for handling touchend or mouseup events
+ +
vclick
+
Normalized event for handling touchend or mouse click events. On touch devices, this event is dispatched *AFTER* vmouseup.
+ +
vmousecancel
+
Normalized event for handling touch or mouse mousecancel events
+
+ +
+

Warning: Use vclick with caution

+

Use vclick with caution on touch devices. Webkit based browsers synthesize mousedown, mouseup, and click events roughly 300ms after the touchend event is dispatched. The target of the synthesized mouse events are calculated at the time they are dispatched and are based on the location of the touch events and, in some cases, implementation specific heuristics which leads to different target calculations on different devices and even different OS versions for the same device. This means the target element within the original touch events could be different from the target element within the synthesized mouse events.

+

We recommend using click instead of vclick anytime the action being triggered has the possibility of changing the content underneath the point that was touched on screen. This includes page transitions and other behaviors such as collapse/expand that could result in the screen shifting or content being completely replaced.

+
+

 

+
+

Canceling an elements default click behavior

+

Applications can call preventDefault() on a vclick event to cancel an element's default click behavior. On mouse based devices, calling preventDefault() on a vclick event equates to calling preventDefault() on the real click event during the bubble event phase. On touch based devices, it's a bit more complicated since the actual click event is dispatched about 300ms after the vclick event is dispatched. For touch devices, calling preventDefault() on a vclick event triggers some code in the vmouse plugin that attempts to catch the next click event that gets dispatched by the browser, during the capture event phase, and call preventDefault() and stopPropagation() on it. As mentioned in the warning above, it is sometimes difficult match up a touch event with its corresponding mouse event because the targets can differ. For this reason, the vmouse plugin also falls back to attempting to identify a corresponding click event by coordinates. There are still cases where both target and coordinate identification fail, which results in the click event being dispatched and either triggering the default action of the element, or in the case where content has been shifted or replaced, triggering a click on a different element. If this happens on a regular basis for a given element/control, we suggest you use click for triggering your action.

+
+

Orientation change event

orientationchange
-
Triggers when a device orientation changes (by turning it vertically or horizontally). When bound to this event, your callback function can leverage a second argument, which contains an orientation property equal to either "portrait" or "landscape". These values are also added as classes to the HTML element, allowing you to leverage them in your CSS selectors. Note that we currently bind to the resize event when orientationChange is not natively supported.
+
Triggers when a device orientation changes (by turning it vertically or horizontally). When bound to this event, your callback function can leverage a second argument, which contains an orientation property equal to either "portrait" or "landscape". These values are also added as classes to the HTML element, allowing you to leverage them in your CSS selectors. Note that we currently bind to the resize event when orientationChange is not natively supported, or when $.mobile.orientationChangeEnabled is set to false.
+
+

orientationchange timing

+ + The timing of the orientationchange with relation to the change of the client height and width is different between browsers, though the current implementation will give you the correct value for event.orientation derived from window.orientation. This means that if your bindings are dependent on the height and width values you may want to disable orientationchange all together with $.mobile.orientationChangeEnabled = false to let the fallback resize code trigger your bindings. +
- +

Scroll events

scrollstart
@@ -51,87 +114,373 @@
scrollstop
Triggers when a scroll finishes.
-
- -

Page show/hide events

-

Whenever a page is shown or hidden in jQuery Mobile, two events are triggered on that page. The events triggered depend on whether that page is being shown or hidden, so when a page transition occurs, there are actually 4 events triggered: 2 for each page.

+
+ +

Page load events

+

Whenever an external page is loaded into the application DOM, 2 events are fired. The first is pagebeforeload. The 2nd event will be either pageload or pageloadfailed.

+
+
pagebeforeload
+

Triggered before any load request is made. Callbacks bound to this event can call preventDefault() on the event to indicate that they are handling the load request. Callbacks that do this *MUST* make sure they call resolve() or reject() on the deferred object reference contained in the data object passed to the callback.

+

The data object, passed as the 2nd arg to the callback function contains the following properties:

+
    +
  • url (string) +
      +
    • The absolute or relative URL that was passed into $.mobile.loadPage() by the caller.
    • +
    +
  • +
  • absUrl (string) +
      +
    • The absolute version of the url. If url was relative, it is resolved against the url used to load the current active page.
    • +
    +
  • +
  • dataUrl (string) +
      +
    • The filtered version of absUrl to be used when identifying the page and updating the browser location when the page is made active.
    • +
    +
  • +
  • deferred (object) +
      +
    • Callbacks that call preventDefault() on the event, *MUST* call resolve() or reject() on this object so that changePage() requests resume processing. Deferred object observers expect the deferred object to be resolved like this:

      +
      
      +$( document ).bind( "pagebeforeload", function( event, data ){
      +
      +	// Let the framework know we're going to handle the load.
      +
      +	event.preventDefault();
      +
      +	// ... load the document then insert it into the DOM ...
      +	// at some point, either in this callback, or through
      +	// some other async means, call resolve, passing in
      +	// the following args, plus a jQuery collection object
      +	// containing the DOM element for the page.
      +
      +	data.deferred.resolve( data.absUrl, data.options, page );
      +
      +});
      +

      or rejected like this: +

      
      +$( document ).bind( "pagebeforeload", function( event, data ){
      +
      +	// Let the framework know we're going to handle the load.
      +
      +	event.preventDefault();
      +
      +	// ... load the document then insert it into the DOM ...
      +	// at some point, if the load fails, either in this
      +	// callback, or through some other async means, call
      +	// reject like this:
      +
      +	data.deferred.reject( data.absUrl, data.options );
      +
      +});
      +
    • +
    +
  • +
  • options (object) +
      +
    • This object contains the options that were passed into $.mobile.loadPage().
    • +
    +
  • +
+
+
pageload
+
Triggered after the page is successfully loaded and inserted into the DOM. Callbacks bound to this event will be passed a data object as its 2nd arg. This object contains the following information: +
    +
  • url (string) +
      +
    • The absolute or relative URL that was passed into $.mobile.loadPage() by the caller.
    • +
    +
  • +
  • absUrl (string) +
      +
    • The absolute version of the url. If url was relative, it is resolved against the url used to load the current active page.
    • +
    +
  • +
  • dataUrl (string) +
      +
    • The filtered version of absUrl to be used when identifying the page and updating the browser location when the page is made active.
    • +
    +
  • +
  • options (object) +
      +
    • This object contains the options that were passed into $.mobile.loadPage().
    • +
    +
  • +
+
+
pageloadfailed
+
Triggered if the page load request failed. By default, after dispatching this event, the framework will display a page failed message and call reject() on the deferred object contained within the event's data object. Callbacks can prevent this default behavior from executing by calling preventDefault() on the event. +

The data object, passed as the 2nd arg to the callback function contains the following properties:

+
    +
  • url (string) +
      +
    • The absolute or relative URL that was passed into $.mobile.loadPage() by the caller.
    • +
    +
  • +
  • absUrl (string) +
      +
    • The absolute version of the url. If url was relative, it is resolved against the url used to load the current active page.
    • +
    +
  • +
  • dataUrl (string) +
      +
    • The filtered version of absUrl to be used when identifying the page and updating the browser location when the page is made active.
    • +
    +
  • +
  • deferred (object) +
      +
    • Callbacks that call preventDefault() on the event, *MUST* call resolve() or reject() on this object so that changePage() requests resume processing. Deferred object observers expect the deferred object to be resolved like this:

      +
      
      +$( document ).bind( "pageloadfailed", function( event, data ){
      +
      +	// Let the framework know we're going to handle things.
      +
      +	event.preventDefault();
      +
      +	// ... attempt to load some other page ...
      +	// at some point, either in this callback, or through
      +	// some other async means, call resolve, passing in
      +	// the following args, plus a jQuery collection object
      +	// containing the DOM element for the page.
      +
      +	data.deferred.resolve( data.absUrl, data.options, page );
      +
      +});
      +

      or rejected like this: +

      
      +$( document ).bind( "pageloadfailed", function( event, data ){
      +
      +	// Let the framework know we're going to handle things.
      +
      +	event.preventDefault();
      +
      +	// ... attempt to load some other page ...
      +	// at some point, if the load fails, either in this
      +	// callback, or through some other async means, call
      +	// reject like this:
      +
      +	data.deferred.reject( data.absUrl, data.options );
      +
      +});
      +
    • +
    +
  • +
  • options (object) +
      +
    • This object contains the options that were passed into $.mobile.loadPage().
    • +
    +
  • +
+
+
+

Page change events

+

Navigating between pages in the application is usually accomplished through a call to $.mobile.changePage(). This function is responsible for making sure that the page we are navigating to is loaded and inserted into the DOM, and then kicking off the transition animations between the current active page, and the page the caller wants to to make active. During this process, which is usually asynchronous, changePage() will fire off 2 events. The first is pagebeforechange. The second event depends on the success or failure of the change request. It will either be pagechange or pagechangefailed.

+
+
pagebeforechange
+
This event is triggered prior to any page loading or transition. Callbacks can prevent execution of the changePage() function by calling preventDefault on the event object passed into the callback. The callback also recieves a data object as its 2nd arg. The data object has the following properties: +
    +
  • toPage (object or string) +
      +
    • This property represents the page the caller wishes to make active. It can be either a jQuery collection object containing the page DOM element, or an absolute/relative url to an internal or external page. The value exactly matches the 1st arg to the changePage() call that triggered the event.
    • +
    +
  • +
  • options (object) +
      +
    • This object contains the configuration options to be used for the current changePage() call.
    • +
    +
  • +
+

It should be noted that callbacks can modify both the toPage and options properties to alter the behavior of the current changePage() call. So for example, the toPage can be mapped to a different url from within a callback to do a sort of redirect.

+
+
pagechange
+
This event is triggered after the changePage() request has finished loading the page into the DOM and all page transition animations have completed. Note that any pageshow or pagehide events will have fired *BEFORE* this event is triggered. Callbacks for this particular event will be passed a data object as the 2nd arg. The properties for this object are as follows: +
    +
  • toPage (object or string) +
      +
    • This property represents the page the caller wishes to make active. It can be either a jQuery collection object containing the page DOM element, or an absolute/relative url to an internal or external page. The value exactly matches the 1st arg to the changePage() call that triggered the event.
    • +
    +
  • +
  • options (object) +
      +
    • This object contains the configuration options to be used for the current changePage() call.
    • +
    +
  • +
+
+
pagechangefailed
+
This event is triggered when the changePage() request fails to load the page. Callbacks for this particular event will be passed a data object as the 2nd arg. The properties for this object are as follows: +
    +
  • toPage (object or string) +
      +
    • This property represents the page the caller wishes to make active. It can be either a jQuery collection object containing the page DOM element, or an absolute/relative url to an internal or external page. The value exactly matches the 1st arg to the changePage() call that triggered the event.
    • +
    +
  • +
  • options (object) +
      +
    • This object contains the configuration options to be used for the current changePage() call.
    • +
    +
  • +
+
+
+

Page transition events

+

Page transitions are used to animate the change from the current active page (fromPage) to a new page (toPage). Events are triggered before and after these transitions so that observers can be notified whenever pages are shown or hidden. The events triggered are as follows:

pagebeforeshow
-
Triggered on the page being shown, before its transition begins.
- +
Triggered on the "toPage" we are transitioning to, before the actual transition animation is kicked off. Callbacks for this event will recieve a data object as their 2nd arg. This data object has the following properties on it: +
    +
  • prevPage (object) +
      +
    • A jQuery collection object that contains the page DOM element that we are transitioning away from. Note that this collection is empty when the first page is transitioned in during application startup.
    • +
    +
  • +
+
+
pagebeforehide
-
Triggered on the page being hidden, before its transition begins.
- +
Triggered on the "fromPage" we are transitioning away from, before the actual transition animation is kicked off. Callbacks for this event will recieve a data object as their 2nd arg. This data object has the following properties on it: +
    +
  • nextPage (object) +
      +
    • A jQuery collection object that contains the page DOM element that we are transitioning to.
    • +
    +
  • +
+

Note that this event will not be dispatched during the transition of the first page at application startup since there is no previously active page.

+
+
pageshow
-
Triggered on the page being shown, after its transition completes.
- +
Triggered on the "toPage" after the transition animation has completed. Callbacks for this event will recieve a data object as their 2nd arg. This data object has the following properties on it: +
    +
  • prevPage (object) +
      +
    • A jQuery collection object that contains the page DOM element that we just transitioned away from. Note that this collection is empty when the first page is transitioned in during application startup.
    • +
    +
  • +
+
+
pagehide
-
Triggered on the page being hidden, after its transition completes.
- +
Triggered on the "fromPage" after the transition animation has completed. Callbacks for this event will recieve a data object as their 2nd arg. This data object has the following properties on it: +
    +
  • nextPage (object) +
      +
    • A jQuery collection object that contains the page DOM element that we just transitioned to.
    • +
    +
  • +
+

Note that this event will not be dispatched during the transition of the first page at application startup since there is no previously active page.

+
+
- -

Note that all four of these events expose a reference to either the next page (nextPage) or previous page (prevPage), depending on whether the page is being shown or hidden, and whether that next or previous page exists (the first ever page shown does not have a previous page to reference, but an empty jQuery object is provided just the same). You can access this reference via the second argument of a bound callback function. For example:

-
-		
-$('div').live('pageshow',function(event, ui){
-  alert('This page was just hidden: '+ ui.prevPage);
+
+		

You can access the prevPage or nextPage properties via the second argument of a bound callback function. For example:

+

+$( 'div' ).live( 'pageshow',function(event, ui){
+  alert( 'This page was just hidden: '+ ui.prevPage);
 });
 
-$('div').live('pagehide',function(event, ui){
-  alert('This page was just shown: '+ ui.nextPage);
+$( 'div' ).live( 'pagehide',function(event, ui){
+  alert( 'This page was just shown: '+ ui.nextPage);
 });
-		
-		
+

Also, for these handlers to be invoked during the initial page load, you must bind them before jQuery Mobile executes. This can be done in the mobileinit handler, as described on the global config page.

Page initialization events

- +

Internally, jQuery Mobile auto-initializes plugins based on the markup conventions found in a given "page". For example, an input element with a type of range will automatically generate a custom slider control.

- -

This auto-initialization is controlled by the "page" plugin, which dispatches events before and after it executes, allowing you to manipulate a page either pre-or-post initialization, or even provide your own intialization behavior and prevent the auto-initializations from occuring. Note that these events will only fire once per "page", as opposed to the show/hide events, which fire every time a page is shown and hidden.

- + +

This auto-initialization is controlled by the "page" plugin, which dispatches events before and after it executes, allowing you to manipulate a page either pre-or-post initialization, or even provide your own intialization behavior and prevent the auto-initializations from occuring. Note that these events will only fire once per "page", as opposed to the show/hide events, which fire every time a page is shown and hidden.

+
pagebeforecreate
-
Triggered on the page being initialized, before initialization occurs.
+
+

Triggered on the page being initialized, before most plugin auto-initialization occurs.

+

+$( '#aboutPage' ).live( 'pagebeforecreate',function(event){
+  alert( 'This page was just inserted into the dom!' );
+});
+
+

Note that by binding to pagebeforecreate, you can manipulate markup before jQuery Mobile's default widgets are auto-initialized. For example, say you want to add data-attributes via JavaScript instead of in the HTML source, this is the event you'd use.

+ +

+$( '#aboutPage' ).live( 'pagebeforecreate',function(event){
+  // manipulate this page before its widgets are auto-initialized
+});
+
+
+
pagecreate
-
Triggered on the page being initialized, after initialization occurs.
-
- - -
-		
-$('#aboutPage').live('pagebeforecreate',function(event){
-  alert('This page was just inserted into the dom!');
+			
+

Triggered when the page has been created in the DOM (via ajax or other) but before all widgets have had an opportunity to enhance the contained markup. This event is most useful for user's wishing to create their own custom widgets for child markup enhancement as the jquery mobile widgets do.

+

+$( '#aboutPage' ).live( 'pagecreate',function(event){
+  ( ":jqmData(role='sweet-plugin')" ).sweetPlugin();
 });
+
+
-$('#aboutPage').live('pagecreate',function(event){ - alert('This page was just enhanced by jQuery Mobile!'); -}); -
-
- -

Note that by binding to pagebeforecreate and returning false, you can prevent the page plugin from making its manipulations.

- -
-		
-$('#aboutPage').live('pagebeforecreate',function(event){
-  //run your own enhancement scripting here...
-  return false;
+			
pageinit
+
+

Triggered on the page being initialized, after initialization occurs. We recommend binding to this event instead of DOM ready() because this will work regardless of whether the page is loaded directly or if the content is pulled into another page as part of the Ajax navigation system.

+

+$( '#aboutPage' ).live( 'pageinit',function(event){
+  alert( 'This page was just enhanced by jQuery Mobile!' );
 });
+
+
+ -
-
- -
-

Note on Page IDs in Alpha 2 release (no longer an issue): In jQuery Mobile Alpha 2 and older, page elements utilized the ID attribute for storing the location from which they came. When you place an ID attribute on a page that is brought into jQuery Mobile's single-page environment through Ajax, jQuery Mobile wraps that page with a new "page" div element, preserving any CSS references to your ID. However, this means that your ID attribute is no longer on the "page" element, so you must keep this in mind when binding to page events (pagebeforecreate, pagecreate, etc). To avoid issues, try using a class if possible.

-
- + + +

Page remove events

+

By default, the framework removes any non active dynamically loaded external pages from the DOM as soon as the user navigates away to a different page. The pageremove event is dispatched just before the framework attempts to remove the a page from the DOM.

+
+
pageremove
+
This event is triggered just before the framework attempts to remove an external page from the DOM. Event callbacks can call preventDefault on the event object to prevent the page from being removed. +
+
+ +

Layout events

+

Some components within the framework, such as collapsible and listview search, dynamically hide and show content based on user events. This hiding/showing of content affects the size of the page and may result in the browser adjusting/scrolling the viewport to accommodate the new page size. Since this has the potential to affect other components such as fixed headers and footers, components like collapsible and listview trigger a custom updatelayout event to notify other components that they may need to adjust their layouts in response to their content changes. Developers who are building dynamic applications that inject content into the current page can also manually trigger this updatelayout event to ensure components on the page update in response to the new content that was just added.

+
+
updatelayout
+
This event is triggered by components within the framework that dynamically show/hide content, and is meant as a generic mechanism to notify other components that they may need to update their size or position. Within the framework, this event is fired on the component element whose content was shown/hidden, and bubbles all the way up to the document element. +

+$( '#foo' ).hide().trigger( 'updatelayout' );
+
+
+

Animation Events

jQuery Mobile exposes the animationComplete plugin, which you can utilize after adding or removing a class that applies a CSS transition.

-
- +
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + +
- \ No newline at end of file + diff --git a/docs/api/globalconfig.html b/docs/api/globalconfig.html index 873c778b..b8ee7bdb 100755 --- a/docs/api/globalconfig.html +++ b/docs/api/globalconfig.html @@ -1,27 +1,34 @@ - - + + - - jQuery Mobile Docs - Configuring default settings - + + + jQuery Mobile Docs - Configuring default settings + - - - + + + + + + -
+
-
+

Configuring Defaults

+ Home
+
+

Working with jQuery Mobile's Auto-initialization

Unlike other jQuery projects, such as jQuery and jQuery UI, jQuery Mobile automatically applies many markup enhancements as soon as it loads (long before document.ready event fires). These enhancements are applied based on jQuery Mobile's default configuration, which is designed to work with common scenarios, but may or may not match your particular needs. Fortunately, these settings are easy to configure.

- +

The mobileinit event

When the jQuery Mobile starts to execute, it triggers a mobileinit event on the document object, to which you can bind to apply overrides to jQuery Mobile's defaults.

- +
 				
 $(document).bind("mobileinit", function(){
@@ -29,7 +36,7 @@ $(document).bind("mobileinit", function(){
 });
 				
 			
- +

Because the mobileinit event is triggered immediately upon execution, you'll need to bind your event handler before jQuery Mobile is loaded. Thus, we recommend linking to your JavaScript files in the following order:

@@ -38,10 +45,10 @@ $(document).bind("mobileinit", function(){
 <script src="custom-scripting.js"></script>
 <script src="jquery-mobile.js"></script>
 				
-			
- + +

Within this event binding, you can configure defaults either by extending the $.mobile object using jQuery's $.extend method:

- +
 				
 $(document).bind("mobileinit", function(){
@@ -51,7 +58,7 @@ $(document).bind("mobileinit", function(){
 });
 				
 			
- +

...or by setting them individually:

 				
@@ -60,50 +67,96 @@ $(document).bind("mobileinit", function(){
 });
 				
 			
- - + +

To quickly preview these global configuration options in action, check out the config test pages.

+

Configurable options

The following defaults are configurable via the $.mobile object:

- +
-
subPageUrlKey (string, default: "ui-page"):
+
ns string, default: ""
+
The namespace used in data- attributes, for example, data-role. Can be set to anything, including a blank string which is the default. When using, it's clearest if you include a trailing dash, such as "mynamespace-" which maps to data-mynamespace-foo="...". +

NOTE: if you're using data- namespacing, you'll need to manually update/override one selector in the theme CSS. The following data selectors should incorporate the namespace you're using: +


+.ui-mobile [data-mynamespace-role=page], .ui-mobile [data-mynamespace-role=dialog], .ui-page { ...
+		
+

+
+ +
autoInitializePage boolean, default: true
+
When the DOM is ready, the framework should automatically call $.mobile.initializePage. If false, page will not initialize, and will be visually hidden until until $.mobile.initializePage is manually called.
+ +
subPageUrlKey string, default: "ui-page"
The url parameter used for referencing widget-generated sub-pages (such as those generated by nested listviews). Translates to to example.html&ui-page=subpageIdentifier. The hash segment before &ui-page= is used by the framework for making an Ajax request to the URL where the sub-page exists.
- -
nonHistorySelectors (string, default: "dialog"):
-
Anchor links with a data-rel attribute value, or pages with a data-role value, that match these selectors will not be trackable in history (they won't update the location.hash and won't be bookmarkable).
- - -
activePageClass (string, default: "ui-page-active"):
+ +
activePageClass string, default: "ui-page-active"
The class assigned to page currently in view, and during transitions
- - -
activeBtnClass (string, default: "ui-page-active"):
+ + +
activeBtnClass string, default: "ui-btn-active"
The class used for "active" button state, from CSS framework.
-
ajaxLinksEnabled (boolean, default: true):
-
jQuery Mobile will automatically handle link clicks through Ajax, when possible.
+
ajaxEnabled boolean, default: true
+
jQuery Mobile will automatically handle link clicks and form submissions through Ajax, when possible. If false, url hash listening will be disabled as well, and urls will load as regular http requests.
-
ajaxFormsEnabled (boolean, default: true):
-
jQuery Mobile will automatically handle form submissions through Ajax, when possible.
+
linkBindingEnabled boolean, default: true
+
jQuery Mobile will automatically bind the clicks on anchor tags in your document. Setting this options to false will prevent all anchor click handling including the addition of active button state and alternate link bluring. This should only be used when attempting to delegate the click management to another library or custom code.
-
defaultTransition (string, default: 'slide'):
+
hashListeningEnabled boolean, default: true
+
jQuery Mobile will automatically listen and handle changes to the location.hash. Disabling this will prevent jQuery Mobile from handling hash changes, which allows you to handle them yourself, or simply to use simple deep-links within a document that scroll to a particular ID.
+ +
pushStateEnabled boolean, default: true
+
Enhancement to use history.replaceState in supported browsers, to convert the hash-based Ajax URL into the full document path. Note that we recommend disabling this feature if Ajax is disabled or if extensive use of external links are used.
+ +
defaultPageTransition string, default: 'slide'
Set the default transition for page changes that use Ajax. Set to 'none' for no transitions by default.
- -
loadingMessage (string, default: "loading"):
-
Set the text that appears when a page is loading. If set to false, the message will not appear at all.
- -
metaViewportContent (string, default: "width=device-width, minimum-scale=1, maximum-scale=1"):
-
Configure the auto-generated meta viewport tag's content attribute. If false, no meta tag will be appended to the DOM.
-
gradeA (function that returns a boolean, default: a function returning the value of $.support.mediaquery):
+
touchOverflowEnabled boolean, default: false
+
Enable smoother page transitions and true fixed toolbars in devices that support both the overflow: and overflow-scrolling: touch; CSS properties.
+ +
defaultDialogTransition string, default: 'pop'
+
Set the default transition for dialog changes that use Ajax. Set to 'none' for no transitions by default.
+ +
minScrollBack string, default: 150
+
Minimum scroll distance that will be remembered when returning to a page.
+ +
loadingMessage string, default: "loading"
+
Set the text that appears when a page is loading. If set to false, the message will not appear at all.
+ +
pageLoadErrorMessage string, default: "Error Loading Page"
+
Set the text that appears when a page fails to load through Ajax.
+ +
gradeA function that returns a boolean, default: a function returning the value of $.support.mediaquery
Any support conditions that must be met in order to proceed.
- - -
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + +
- \ No newline at end of file + diff --git a/docs/api/index.html b/docs/api/index.html index a82f55eb..181ba309 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -1,28 +1,32 @@ - + + jQuery UI Mobile Framework - API - + - + + + + -
+
-
+

API

+ Home
- \ No newline at end of file + diff --git a/docs/api/mediahelpers.html b/docs/api/mediahelpers.html index 63d41eb6..d5eff4fb 100755 --- a/docs/api/mediahelpers.html +++ b/docs/api/mediahelpers.html @@ -1,27 +1,36 @@ - + + jQuery Mobile Docs - Responsive Layout Helpers - + - + + + + -
+
-
+

Responsive Layout Helpers

+ Home
- +

Media Query Helper Classes

+

Note: This feature was deprecated in beta, and removed in 1.0rc1. We recommend using CSS3 Media Queries instead. To support older versions of Internet Explorer, check out respond.js, a fast & lightweight polyfill for min/max-width CSS3 Media Queries.

+

If you still need this feature, you can find the code here: jquery.mobile.media.classes.js

+

jQuery Mobile adds classes to the HTML element that mimic browser orientation and common min/max-width CSS media queries. These classes are updated on load, resize and orientationchange, allowing you to key off these classes in your CSS, to create responsive layouts - even in browsers that don't support media queries!

-

Orientation Classes

+

Orientation Classes

+

The HTML element will always have a class of either "portrait" or "landscape", depending on the orientation of the browser or device. You can utilize these in your CSS like this:

 			
@@ -89,8 +98,32 @@ $.mobile.media("screen and (-webkit-min-device-pixel-ratio: 2)");
 			
 
+
+ +
-
+
+ +

More in this section

+ + +
+
+ +
+ + +
diff --git a/docs/api/methods.html b/docs/api/methods.html index 26ffff54..e81b4ee0 100755 --- a/docs/api/methods.html +++ b/docs/api/methods.html @@ -1,46 +1,79 @@ - + + jQuery Mobile Docs - Methods - + - + + + + -
+
-
+

Methods

+ Home
- +
-

jQuery Mobile exposes several methods and properties on the $.mobile object for use in your applications.

+

jQuery Mobile exposes several methods and properties on the $.mobile object for use in your applications.

$.mobile.changePage (method)
-
Programmatically change from one page to another. This method is used internally for transitions that occur as a result of clicking a link or submitting a form, when those features are enabled.
+
Programmatically change from one page to another. This method is used internally for the page loading and transitioning that occurs as a result of clicking a link or submitting a form, when those features are enabled.
-
Arguments
-
to -
    -
  • String, url to transition to ("about/us.html")
  • -
  • jQuery object ($("#about"))
  • -
  • Array specifying two page references [from,to] for transitioning from a known page. From is otherwise assumed to be the current page in view (or $.mobile.activePage ).
  • -
  • Object for sending form data. ({to: url, data: serialized form data, type: "get" or "post"}
  • -
-
+
· Arguments
+
to (string or object, required) +
    +
  • String: Absolute or relative URL. ("about/us.html")
  • +
  • Object: jQuery collection object. ($("#about"))
  • +
+
-
transition (string, examples: "pop", "slide"," "none")
-
back (boolean, default: false). True will cause a reverse-direction transition.
-
changeHash (boolean, default: true). Update the hash to the to page's URL when page change is complete.
+
options (object, optional) +
    +
  • Properties: +
      +
    • allowSamePageTransition (boolean, default: false) By default, changePage() ignores requests to change to the current active page. Setting this option to true, allows the request to execute. Developers should note that some of the page transitions assume that the fromPage and toPage of a changePage request are different, so they may not animate as expected. Developers are responsible for either providing a proper transition, or turning it off for this specific case.
    • +
    • changeHash (boolean, default: true) Decides if the hash in the location bar should be updated.
    • +
    • data (object or string, default: undefined) The data to send with an Ajax page request. +
        +
      • Used only when the 'to' argument of changePage() is a URL.
      • +
      +
    • +
    • data-url (string, default: undefined) The URL to use when updating the browser location upon changePage completion. + If not specified, the value of the data-url attribute of the page element is used.
    • +
    • pageContainer (jQuery collection, default: $.mobile.pageContainer) Specifies the element that should contain the page.
    • +
    • reloadPage (boolean, default: false) Forces a reload of a page, even if it is already in the DOM of the page container. +
        +
      • Used only when the 'to' argument of changePage() is a URL.
      • +
      +
    • +
    • reverse (boolean, default: false) Decides what direction the transition will run when showing the page.
    • +
    • showLoadMsg (boolean, default: true) Decides whether or not to show the loading message when loading external pages.
    • +
    • role (string, default: undefined) The data-role value to be used when displaying the page. By default this is undefined which means rely on the value of the @data-role attribute defined on the element.
    • +
    • transition (string, default: $.mobile.defaultPageTransition) The transition to use when showing the page.
    • +
    • type (string, default: "get") Specifies the method ("get" or "post") to use when making a page request. +
        +
      • Used only when the 'to' argument of changePage() is a URL.
      • +
      +
    • +
    +
  • +
+
+
@@ -48,51 +81,434 @@
 			
 //transition to the "about us" page with a slideup transition 			
-$.mobile.changePage("about/us.html", "slideup");	
+$.mobile.changePage( "about/us.html", { transition: "slideup"} );	
 
 //transition to the "search results" page, using data from a form with an ID of "search"" 		
-$.mobile.changePage({
-	url: "searchresults.php", 
-	type: "get", 
+$.mobile.changePage( "searchresults.php", {
+	type: "post", 
 	data: $("form#search").serialize()
 });		
 
 //transition to the "confirm" page with a "pop" transition without tracking it in history			
-$.mobile.changePage("../alerts/confirm.html", "pop", false, false);	
+$.mobile.changePage( "../alerts/confirm.html", {
+	transition: "pop",
+	reverse: false,
+	changeHash: false
+});	
 		
 			
 			
-
$.mobile.pageLoading (method)
-
Show or hide the page loading message, which is configurable via $.mobile.loadingMessage.
+ +
$.mobile.loadPage (method)
+
Load an external page, enhance its content, and insert it into the DOM. This method is called internally by the changePage() function when its first argument is a URL. This function does not affect the current active page so it can be used to load pages in the background. The function returns a deferred promise object that gets resolved after the page has been enhanced and inserted into the document.
+
+
-
Arguments:
-
Done (boolean, defaults to false, meaning loading has started). True will hide the loading message.
+
· Arguments
+
url (string or object, required) A relative or absolute URL.
+ +
options (object, optional) +
    +
  • Properties: +
      +
    • data (object or string, default: undefined) The data to send with an Ajax page request.
    • +
    • loadMsgDelay (number (in ms), default: 50) Forced delay before the loading message is shown. This is meant to allow time for a page that has already been visited to be fetched from cache without a loading message.
    • +
    • pageContainer (jQuery collection, default: $.mobile.pageContainer) Specifies the element that should contain the page after it is loaded.
    • +
    • reloadPage (boolean, default: false) Forces a reload of a page, even if it is already in the DOM of the page container.
    • +
    • role (string, default: undefined) The data-role value to be used when displaying the page. By default this is undefined which means rely on the value of the @data-role attribute defined on the element.
    • +
    • type (string, default: "get") Specifies the method ("get" or "post") to use when making a page request. +
    • +
    +
  • +
+
+
Examples:
 			
-//cue the page loader 			
-$.mobile.pageLoading();	
+//load the "about us" page into the DOM			
+$.mobile.loadPage( "about/us.html" );	
 
-//hide the page loader 			
-$.mobile.pageLoading( true );	
+//load a "search results" page, using data from a form with an ID of "search"" 		
+$.mobile.loadPage( "searchresults.php", {
+	type: "post", 
+	data: $("form#search").serialize()
+});				
 			
 			
+
jqmData(), jqmRemoveData() (method)
+
When working with jQuery Mobile, jqmData and jqmRemoveData should be used in place of jQuery core's data and removeData methods (note that this includes $.fn.data, $.fn.removeData, and the $.data, $.removeData, and $.hasData utilities), as they automatically incorporate getting and setting of namespaced data attributes (even if no namespace is currently in use).
+
+
+
· Arguments:
+
See jQuery's data and removeData methods
+ Note: Calling jqmData() with no argument will return undefined. This behavior is subject to change in future versions. +
· Also:
+
When finding elements by their jQuery Mobile data attribute, please use the custom selector :jqmData(), as it automatically incorporates namespaced data attributes into the lookup when they are in use. For example, instead of calling $("div[data-role='page']"), you should use $("div:jqmData(role='page')"), which internally maps to $("div[data-"+ $.mobile.ns +"role='page']") without forcing you to concatenate a namespace into your selectors manually.
+
+
+ + + + +
$.mobile.showPageLoadingMsg ()
+
Show the page loading message, which is configurable via $.mobile.loadingMessage.
+ +
Example: +
+			
+//cue the page loader 			
+$.mobile.showPageLoadingMsg();	
+			
+			
+ +
+ + + + +
$.mobile.hidePageLoadingMsg ()
+
Hide the page loading message, which is configurable via $.mobile.loadingMessage.
+ +
Example: +
+			
+//cue the page loader 			
+$.mobile.hidePageLoadingMsg();	
+			
+			
+ +
+ +
$.mobile.fixedToolbars.show (method)
+
Utility method for displaying the fixed header and/or footer of the current active page within the viewport. Note that fixed headers/footers are never really hidden. Toggling the show/hide state of a toolbar is really toggling whether or not they are inline within the page content, or displayed within the viewport as if they were fixed.
+
+
+
· Arguments
+
immediately (boolean, optional) If true, any fixed header or footer for the current active page is displayed immediately within the viewport. If false or unspecified, the fixed header/footer will fade-in after a 100 millisecond delay. Note that other events such as a document resize or scroll event can result in an additional delay before the start of the header/footer display animation.
+
+
+
Example: +
+			
+// Show fixed header/footer with a fade animation. 			
+$.mobile.fixedToolbars.show();	
+
+// Show fixed header/footer immediately.
+$.mobile.fixedToolbars.show(true);	
+			
+			
+ +
+ +
$.mobile.fixedToolbars.hide (method)
+
Utility method for hiding the fixed header and/or footer of the current active page.
+
+
+
· Arguments
+
immediately (boolean, optional) If true, any fixed header or footer for the current active page is immediately placed inline (back in flow) with the page content, which means it will scroll along with the content and will only be visible when viewing the top or bottom of the page within the viewport. If false or unspecified, the fixed header/footer will fade-out after a 100 millisecond delay. Note that other events such as a document resize or scroll event can result in the header/footer being immediately hidden.
+
+
+
Example: +
+			
+// Hide fixed header/footer with a fade animation. 			
+$.mobile.fixedToolbars.hide();	
+
+// Hide fixed header/footer immediately.
+$.mobile.fixedToolbars.hide(true);	
+			
+			
+ +
+ +
$.mobile.path.parseUrl (method)
+
Utility method for parsing a URL and its relative variants into an object that makes accessing the components of the URL easy. When parsing relative variants, the resulting object will contain empty string values for missing components (like protocol, host, etc). Also, when parsing URLs that have no authority, such as tel: urls, the pathname property of the object will contain the data after the protocol/scheme colon.
+ +
+ +
+
· Arguments
+
url (string, required) A relative or absolute URL.
+ +
· Return Value
+
+

This function returns an object that contains the various components of the URL as strings. The properties on the object mimic the browser's location object:

+
+
hash
+
The fragment conponent of the URL, including the leading '#' character.
+
host
+
The host and port number of the URL.
+
hostname
+
The name of the host within the URL.
+
href
+
The original URL that was parsed.
+
pathname
+
The path of the file or directory referenced by the URL.
+
port
+
The port specified within the URL. Most URLs rely on the default port for the protocol used, so this may be an empty string most of the time.
+
protocol
+
The protocol for the URL including the trailing ':' character.
+
search
+
The query component of the URL including the leading '?' character.
+
+

But it also contains additional properties that provide access to additional components as well as some common forms of the URL developers access:

+
+
authority
+
The username, password, and host components of the URL
+
directory
+
The directory component of the pathname, minus any filename.
+
domain
+
The protocol and authority components of the URL.
+
filename
+
The filename within the pathname component, minus the directory.
+
hrefNoHash
+
The original URL minus the fragment (hash) components.
+
hrefNoSearch
+
The original URL minus the query (search) and fragment (hash) components.
+
password
+
The password contained within the authority component.
+
username
+
The username contained within the authority component.
+
+
+ +
+
+ +
Examples: +
+			
+// Parsing the Url below results an object that is returned with the
+// following properties:
+//
+//  obj.href:         http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content
+//  obj.hrefNoHash:   http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread
+//  obj.hrefNoSearch: http://jblas:password@mycompany.com:8080/mail/inbox
+//  obj.domain:       http://jblas:password@mycompany.com:8080
+//  obj.protocol:     http:
+//  obj.authority:    jblas:password@mycompany.com:8080
+//  obj.username:     jblas
+//  obj.password:     password
+//  obj.host:         mycompany.com:8080
+//  obj.hostname:     mycompany.com
+//  obj.port:         8080
+//  obj.pathname:     /mail/inbox
+//  obj.directory:    /mail/
+//  obj.filename:     inbox
+//  obj.search:       ?msg=1234&type=unread
+//  obj.hash:         #msg-content
+
+var obj = $.mobile.path.parseUrl("http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234");
+			
+			
+ +
+ + +
$.mobile.path.makePathAbsolute (method)
+
Utility method for converting a relative file or directory path into an absolute path.
+
+
+
· Arguments
+
relPath (string, required) A relative file or directory path.
+
absPath (string, required) An absolute file or relative path to resolve against.
+ +
· Return Value
+
This function returns a string that is an absolute version of the relative path passed in.
+ +
+
+
Examples: +
+			
+// Returns: /a/b/c/file.html
+var absPath = $.mobile.path.makePathAbsolute("file.html", "/a/b/c/bar.html");
+
+// Returns: /a/foo/file.html
+var absPath = $.mobile.path.makePathAbsolute("../../foo/file.html", "/a/b/c/bar.html");
+
+			
+			
+
+ + +
$.mobile.path.makeUrlAbsolute (method)
+
Utility method for converting a relative URL to an absolute URL.
+
+ +
+
Arguments
+
relUrl (string, required) A relative URL.
+
absUrl (string, required) An absolute URL to resolve against.
+ +
Return Value
+
This function returns a string that is an absolute version of the relative URL passed in.
+ +
+
+
Examples: +
+			
+// Returns: http://foo.com/a/b/c/file.html
+var absUrl = $.mobile.path.makeUrlAbsolute("file.html", "http://foo.com/a/b/c/test.html");
+
+// Returns: http://foo.com/a/foo/file.html
+var absUrl = $.mobile.path.makeUrlAbsolute("../../foo/file.html", "http://foo.com/a/b/c/test.html");
+
+// Returns: http://foo.com/bar/file.html
+var absUrl = $.mobile.path.makeUrlAbsolute("//foo.com/bar/file.html", "http://foo.com/a/b/c/test.html");
+
+// Returns: http://foo.com/a/b/c/test.html?a=1&b=2
+var absUrl = $.mobile.path.makeUrlAbsolute("?a=1&b=2", "http://foo.com/a/b/c/test.html");
+
+// Returns: http://foo.com/a/b/c/test.html#bar
+var absUrl = $.mobile.path.makeUrlAbsolute("#bar", "http://foo.com/a/b/c/test.html");
+
+			
+			
+ +
+ + +
$.mobile.path.isSameDomain (method)
+
Utility method for comparing the domain of 2 URLs.
+
+ +
+
· Arguments
+
url1 (string, required) A relative URL.
+
url2 (string, required) An absolute URL to resolve against.
+ +
Return Value
+
This function returns a boolean true if the domains match, false if they don't.
+ +
+
+
Examples: +
+			
+// Returns: true
+var same = $.mobile.path.isSameDomain("http://foo.com/a/file.html", "http://foo.com/a/b/c/test.html");
+
+// Returns: false
+var same = $.mobile.path.isSameDomain("file://foo.com/a/file.html", "http://foo.com/a/b/c/test.html");
+
+// Returns: false
+var same = $.mobile.path.isSameDomain("https://foo.com/a/file.html", "http://foo.com/a/b/c/test.html");
+
+// Returns: false
+var same = $.mobile.path.isSameDomain("http://foo.com/a/file.html", "http://bar.com/a/b/c/test.html");
+
+			
+			
+ +
+ + +
$.mobile.path.isRelativeUrl (method)
+
Utility method for determining if a URL is a relative variant.
+
+ +
+
· Arguments
+
url (string, required) A relative or absolute URL.
+ +
· Return Value
+
This function returns a boolean true if the URL is relative, false if it is absolute.
+ +
+
+
Examples: +
+			
+// Returns: false
+var isRel = $.mobile.path.isRelativeUrl("http://foo.com/a/file.html");
+
+// Returns: true
+var isRel = $.mobile.path.isRelativeUrl("//foo.com/a/file.html");
+
+// Returns: true
+var isRel = $.mobile.path.isRelativeUrl("/a/file.html");
+
+// Returns: true
+var isRel = $.mobile.path.isRelativeUrl("file.html");
+
+// Returns: true
+var isRel = $.mobile.path.isRelativeUrl("?a=1&b=2");
+
+// Returns: true
+var isRel = $.mobile.path.isRelativeUrl("#foo");
+
+
+			
+			
+ +
+ + +
$.mobile.path.isAbsoluteUrl (method)
+
Utility method for determining if a URL is absolute.
+
+ +
+
· Arguments
+
url (string, required) A relative or absolute URL.
+ +
· Return Value
+
This function returns a boolean true if the URL is absolute, false if it is absolute.
+ +
+
+
Examples: +
+			
+// Returns: true
+var isAbs = $.mobile.path.isAbsoluteUrl("http://foo.com/a/file.html");
+
+// Returns: false
+var isAbs = $.mobile.path.isAbsoluteUrl("//foo.com/a/file.html");
+
+// Returns: false
+var isAbs = $.mobile.path.isAbsoluteUrl("/a/file.html");
+
+// Returns: false
+var isAbs = $.mobile.path.isAbsoluteUrl("file.html");
+
+// Returns: false
+var isAbs = $.mobile.path.isAbsoluteUrl("?a=1&b=2");
+
+// Returns: false
+var isAbs = $.mobile.path.isAbsoluteUrl("#foo");
+
+
+			
+			
+ +
+ + +
$.mobile.base (methods, properties)
+
Utilities for working with generated base element. TODO: document as public API is finalized.
+ + +
$.mobile.silentScroll (method)
Scroll to a particular Y position without triggering scroll event listeners.
-
Arguments:
+
· Arguments:
yPos (number, defaults to 0). Pass any number to scroll to that Y location.
@@ -107,27 +523,7 @@ $.mobile.silentScroll(100); - -
$.mobile.addResolutionBreakpoints (method)
-
Add width breakpoints to the min/max width classes that are added to the HTML element.
-
-
-
Arguments:
-
values (number or array). Pass any number or array of numbers to add to the resolution classes. Read more about this feature here: Orientation & resolution targeting.
-
-
- -
Examples: -
-			
-//add a 400px breakpoint 			
-$.mobile.addResolutionBreakpoints(400);	
-//add 2 more breakpoints 			
-$.mobile.addResolutionBreakpoints([600,800]);	
-			
-			
- -
+ @@ -138,7 +534,30 @@ $.mobile.addResolutionBreakpoints([600,800]);
-
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ +
diff --git a/docs/api/themes.html b/docs/api/themes.html index 1e7f7805..cc4bd26a 100755 --- a/docs/api/themes.html +++ b/docs/api/themes.html @@ -1,22 +1,28 @@ - + + jQuery Mobile Framework - Static Containers, States - + - + + + + -
+
-
+

Themes

+ Home
+

Theming overview

Every layout and widget in jQuery Mobile is designed around a new object-oriented CSS framework that makes it possible to apply a complete unified visual design Theme to sites and applications. The theming system is similar to the ThemeRoller system in jQuery UI, but adds a few important improvements:

@@ -45,14 +51,14 @@

The default theme contains the following five Bar styles:

-
Bar A
-
Bar B
-
Bar C
-
Bar D
-
Bar E
+
Bar A - Link
+
Bar B - Link
+
Bar C - Link
+
Bar D - Link
+
Bar E - Link
-

By default, the framework assigns the "a" swatch to all headers and footers, because these are typically given high visual priority in an application. To set the color of a bar to a different swatch color, simply add the data-theme attribute to your header or footer and specify an alternate swatch letter ('b' or 'd', for example) and the specified theme swatch color will be applied. Learn more about toolbar theming.

+

By default, the framework assigns the "a" swatch to all headers and footers, because these are typically given high visual priority in an application. To set the color of a bar to a different swatch color, simply add the data-theme attribute to your header or footer and specify an alternate swatch letter ('b' or 'd', for example) and the specified theme swatch color will be applied. Learn more about toolbar theming.

@@ -60,11 +66,11 @@

The default theme also includes color swatch values for use in content blocks, designed to coordinate with the header color swatches in the theme.

-
Block A
-
Block B
-
Block C
-
Block D
-
Block E
+
Block A - Link
+
Block B - Link
+
Block C - Link
+
Block D - Link
+
Block E - Link
@@ -90,10 +96,11 @@
+

Learn more about content theming.

@@ -148,52 +155,52 @@

This default behavior makes it easy to ripple a theme change through a page by setting a theme swatch on a parent because you know the buttons will maintain the same relative visual weight across themes. Since form elements use the button styles, they will also adapt to their parent container too.

-

If you want to add visual emphasis to a button and help it stand out visually from its parent toolbar, an alternate swatch color can be set by adding a data-theme="a" to the anchor. Once an alternate swatch color is set on a button in the markup, the framework won't override that color if the parent theme is changed, because you made a conscious decision to set it.

+

If you want to add visual emphasis to a button and help it stand out visually from its parent toolbar, an alternate swatch color can be set by adding a data-theme="a" to the anchor. Once an alternate swatch color is set on a button in the markup, the framework won't override that color if the parent theme is changed, because you made a conscious decision to set it.

-
+
- A - B - C - D - E + A + B + C + D + E
-
+
- A - B - C - D - E + A + B + C + D + E
-
+
- A - B - C - D - E + A + B + C + D + E
-
+
- A - B - C - D - E + A + B + C + D + E
-
+
- A - B - C - D - E + A + B + C + D + E
@@ -216,20 +223,63 @@

Icons

There a core set of standard icons included in the framework that can be assigned to any button. To minimize the download size of the core icons, jQuery Mobile only includes these icons in white and automatically adds a semi-transparent black circle behind the icon to make sure it has good contrast on all background colors.

- +

Theme classes

+

Assigning color swatches through the data-theme attribute is one way to leverage the theme system, but it's also possible to apply any of the theme swatches directly to your markup through classes to apply the colors, textures and font formatting of your theme to any markup. This is especially useful when creating your own custom layout elements or UI widgets. Here are a few common theme classes, but many more are available in the theme stylesheet:

+
+
ui-bar-(a-z)
+
Applies the toolbar theme styles for the selected swatch letter. Commonly used in conjunction with ui-bar structural class to add the standard bar padding styles.
+
ui-body-(a-z)
+
Applies the content body theme styles for the selected swatch letter. Commonly used in conjunction with ui-body structural class to add the standard content block padding styles.
+
ui-btn-up-(a-z)
+
Applies the button/clickable element theme styles for the selected swatch letter. Commonly used in with the ui-btn-hover-(a-z) and ui-btn-down-(a-z) interaction class states to provide visual feedback and ui-btn-active to indicate the selected or "on" state.
+
ui-corner-all
+
Applies the theme's global border-radius for rounded corners and is used for container or grouped items in the framework (inset lists, radiobutton sets). There are additional classes for all the possible combinations of rounded corners, for example: ui-corner-tl (top left only), -top (both top corners), -left (both left corners), etc. A second full set of corner classes is provided for buttons so these can have a different corner radius. These use classes with a similar naming convention, but with "btn" instead of "corner", like this: .ui-btn-corner-all.
+
ui-shadow
+
Applies the theme's global drop shadow to any element using CSS box-shadow property.
+
ui-disabled
+
Applies the disabled look and feel which essentially reduces the opacity of any element with this class to 30%, hides the cursor, and sets pointer-events: none; which prevents any interaction in many modern browsers.
+
- +

Overriding themes

+

The themes are meant as a solid starting point, but are meant to be customized to add the custom design elements that make your site or app unique. Since everything is controlled by CSS, it's easy to use a web inspector tool to identify the style properties you want to modify. The set of of theme classes (global) and semantic structural classes (widget-specific) added to elements provide a rich set of possible selectors to target style overrides against. We recommend adding an external stylesheet to the head, placed after the structure and theme stylesheet references, that contain all your style overrides. This allows you to easily update to newer versions of the library because overrides are kept separate from the library code.

+ +

Learn more about theming individual components:

-
+
-
+
+ +
+ +

More in this section

+ + +
+
+ +
+ + + +
\ No newline at end of file diff --git a/docs/buttons/api-buttons.html b/docs/buttons/api-buttons.html index 282d5de8..f8e1dbe3 100755 --- a/docs/buttons/api-buttons.html +++ b/docs/buttons/api-buttons.html @@ -1,18 +1,23 @@ - + + jQuery Mobile Docs - Buttons - + - + + + + -
+
-
+

Button API

+ Home
diff --git a/docs/buttons/buttons-events.html b/docs/buttons/buttons-events.html new file mode 100644 index 00000000..92166036 --- /dev/null +++ b/docs/buttons/buttons-events.html @@ -0,0 +1,90 @@ + + + + + + jQuery Mobile Docs - Button events + + + + + + + + + +
+ +
+

Button basics

+ Home +
+ +
+
+ +
+ +

Button basics

+ + + +

Bind events directly to the a, input, or button element. Use jQuery Mobile's virtual events, or bind standard JavaScript events, like change, focus, blur, etc.:

+
 
+$( ".myButton" ).bind( "click", function(event, ui) {
+  ...
+});
+
+ +

The form button plugin has the following custom events:

+ +
+ +
create triggered when a form button is created
+
+ +

+$('[type='submit']').button({
+   create: function(event, ui) { ... }
+});		
+			
+
+ +
+ +
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + + +
+ + + + diff --git a/docs/buttons/buttons-grouped.html b/docs/buttons/buttons-grouped.html index 3d8342e7..ff6b9bcc 100755 --- a/docs/buttons/buttons-grouped.html +++ b/docs/buttons/buttons-grouped.html @@ -1,23 +1,29 @@ - - jQuery Mobile Docs - Buttons - + + + jQuery Mobile Docs - Grouped Buttons + - - + + + + -
+
-
+

Grouped buttons

+ Home
-

Occasionally, you may want to visually group a set of buttons together to form a single block that looks contained like a navigation component. To get this effect, wrap a set of buttons in a container with the data-role="controlgroup" attribute — the framework will create a vertical button group, remove all margins and drop shadows between the buttons, and only round the first and last buttons of the set to create the effect that they are grouped together.

+
+ +

Occasionally, you may want to visually group a set of buttons together to form a single block that looks contained like a navigation component. To get this effect, wrap a set of buttons in a container with the data-role="controlgroup" attribute — the framework will create a vertical button group, remove all margins and drop shadows between the buttons, and only round the first and last buttons of the set to create the effect that they are grouped together.


 <div data-role="controlgroup">
 <a href="index.html" data-role="button">Yes</a>
@@ -45,8 +51,7 @@
 			
 		

Horizontal grouped buttons with icons:

- Up - Down + Add Delete
@@ -58,9 +63,33 @@
- +
+ +
-
+
+ +

More in this section

+ + +
+
+ +
+ + +
diff --git a/docs/buttons/buttons-icons.html b/docs/buttons/buttons-icons.html index 53021dbe..00e416e8 100755 --- a/docs/buttons/buttons-icons.html +++ b/docs/buttons/buttons-icons.html @@ -1,31 +1,36 @@ - - jQuery Mobile Docs - Buttons - + + + jQuery Mobile Docs - Button icons + - - + + + + -
+
-
+

Button icons

+ Home
+

Adding Icons to Buttons

The jQuery Mobile framework includes a selected set of icons most often needed for mobile apps. To minimize download size, jQuery Mobile includes a single white icon sprite, and automatically adds a semi-transparent black circle behind the icon to ensure that it has good contrast on any background color.

-

An icon can be added to a button by adding a data-icon attribute on the anchor specifying the icon to display. For example, the following markup:

+

An icon can be added to a button by adding a data-icon attribute on the anchor specifying the icon to display. For example, the following markup:

- <a href="index.html" data-role="button" data-icon="delete">Delete</a> + <a href="index.html" data-role="button" data-icon="delete">Delete</a>

Creates this button with an icon:

@@ -33,7 +38,7 @@

Icon set

-

The following data-icon attributes can be referenced to create the icons shown below:

+

The following data-icon attributes can be referenced to create the icons shown below:

Left arrow - data-icon="arrow-l"

My button @@ -67,15 +72,17 @@ My button

Info - data-icon="info"

My button - - +

Home - data-icon="home"

+ My button +

Search - data-icon="search"

+ My button

Icon positioning

By default, all icons in buttons are placed to the left of the button text.

Delete -

This default may be overridden using the data-iconpos attribute to set the icon to the right, above (top) or below (bottom) the text. For example, the markup:

+

This default may be overridden using the data-iconpos attribute to set the icon to the right, above (top) or below (bottom) the text. For example, the markup:

<a href="index.html" data-role="button" data-icon="delete" data-iconpos="right">Delete</a> @@ -84,13 +91,13 @@

Creates this button with right-aligned icon:

Delete -

Icons can also be positioned above the text by specifying data-iconpos="top"

+

Icons can also be positioned above the text by specifying data-iconpos="top"

Delete -

Or icons can also be positioned below the text by specifying data-iconpos="bottom"

+

Or icons can also be positioned below the text by specifying data-iconpos="bottom"

Delete -

You can also create an icon-only button, by setting the data-iconpos attribute to notext. The button plugin will hide the text on-screen, but add it as a title attribute on the link to provide context for screen readers and devices that support tooltips. For example, replacing data-iconpos="right" on the previous example with data-iconpos="notext":

+

You can also create an icon-only button, by setting the data-iconpos attribute to notext. The button plugin will hide the text on-screen, but add it as a title attribute on the link to provide context for screen readers and devices that support tooltips. For example, replacing data-iconpos="right" on the previous example with data-iconpos="notext":

<a href="index.html" data-role="button" data-icon="delete" data-iconpos="notext">Delete</a> @@ -100,7 +107,7 @@ Delete

Custom Icons

-

To use custom icons, specify a data-icon value that has a unique name like myapp-email and the button plugin will generate a class by prefixing ui-icon- to the data-icon value and apply it to the button. You can then write a CSS rule that targets the ui-icon-myapp-email class to specify the icon background source. To maintain visual consistency, create a white icon 18x18 pixels saved as a PNG-8 with alpha transparency.

+

To use custom icons, specify a data-icon value that has a unique name like myapp-email and the button plugin will generate a class by prefixing ui-icon- to the data-icon value and apply it to the button. You can then write a CSS rule that targets the ui-icon-myapp-email class to specify the icon background source. To maintain visual consistency, create a white icon 18x18 pixels saved as a PNG-8 with alpha transparency.

Icons and themes

@@ -125,6 +132,8 @@ My button My button My button + My button + My button
@@ -146,6 +155,8 @@ My button My button My button + My button + My button
@@ -167,10 +178,38 @@ My button My button My button + My button + My button
-
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + +
- + \ No newline at end of file diff --git a/docs/buttons/buttons-inline.html b/docs/buttons/buttons-inline.html index 7991ea7f..b9662f17 100755 --- a/docs/buttons/buttons-inline.html +++ b/docs/buttons/buttons-inline.html @@ -1,43 +1,46 @@ - - jQuery Mobile Docs - Buttons - + + + jQuery Mobile Docs - Inline buttons + - - + + + + -
+
-
+

Inline buttons

+ Home
+

By default, all buttons in the body content are styled as block-level element so they fill the width of the screen:

Button -

However, if you want a more compact button that is only as wide as the text and icons inside, add the data-inline="true" attribute to the button:

+

However, if you want a more compact button that is only as wide as the text and icons inside, add the data-inline="true" attribute to the button:

Button -

If you have multiple buttons that should sit side-by-side on the same line, wrap the buttons in a container that has a data-inline="true" attribute. This will style the buttons to be the width of their content and float the buttons so they sit on the same line.

+

If you have multiple buttons that should sit side-by-side on the same line, add the data-inline="true" attribute to each button. This will style the buttons to be the width of their content and float the buttons so they sit on the same line.


-<div data-inline="true">
-	<a href="index.html" data-role="button">Cancel</a>
-	<a href="index.html" data-role="button" data-theme="b">Save</a>
-</div>
+<a href="index.html" data-role="button" data-inline="true">Cancel</a>
+<a href="index.html" data-role="button" data-inline="true" data-theme="b">Save</a>
 
-

This creates an inline button set:

+

The result is this:

Cancel Save @@ -45,10 +48,34 @@

If you want buttons to sit side-by-side but stretch to fill the width of the screen, you can use the content column grids to put normal full-width buttons into 2- or 3-columns.

- - -
-
+
- - \ No newline at end of file +
+ +
+ +

More in this section

+ + +
+
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/buttons/buttons-methods.html b/docs/buttons/buttons-methods.html new file mode 100644 index 00000000..cba405ab --- /dev/null +++ b/docs/buttons/buttons-methods.html @@ -0,0 +1,96 @@ + + + + + + jQuery Mobile Docs - Button methods + + + + + + + + + +
+ +
+

Button basics

+ Home +
+ +
+
+ +
+ +

Button basics

+ + + +

The following methods apply only to form buttons. Link-based buttons do not have any associated methods.

+ +
+ +
enable enable a disabled form button
+
+

+$('[type='submit']').button('enable');			
+				
+
+ +
disable disable a form button
+
+

+$('[type='submit']').button('disable');			
+				
+
+ +
refresh update the form button
+
+

If you manipulate a form button via JavaScript, you must call the refresh method on it to update the visual styling.

+ +
		
+$('[type='submit']').button('refresh');
+				
+
+ +
+ +
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + + +
+ + + + diff --git a/docs/buttons/buttons-options.html b/docs/buttons/buttons-options.html new file mode 100644 index 00000000..d7e66c81 --- /dev/null +++ b/docs/buttons/buttons-options.html @@ -0,0 +1,143 @@ + + + + + + jQuery Mobile Docs - Button options + + + + + + + + + +
+ +
+

Button basics

+ Home +
+ +
+
+ +
+ +

Button basics

+ + + +

The following options apply to all buttons:

+ +
+
corners boolean
+
+

default: true

+

Applies the theme button border-radius if set to true. This option is also exposed as a data attribute: data-corners="false"

+
$('a').buttonMarkup({ corners: "false" });
+ No rounded corners +
+
icon string
+
+

default: null

+

Applies an icon from the icon set. This option is also exposed as a data attribute: data-icon="star"

+
$('a').buttonMarkup({ icon: "star" });
+ Star icon +
+ +
iconpos string
+
+

default: "left"

+

Positions the icon in the button. Possible values: left, right, none, notext. The notext value will display an icon-only button with no text feedback. This option is also exposed as a data attribute: data-iconpos="left"

+
$('a').buttonMarkup({ iconpos: "right" });
+ Star icon +
+ +
iconshadow boolean
+
+

default: true

+

Applies the theme shadow to the button's icon if set to true. This option is also exposed as a data attribute: data-iconshadow="false"

+
$('a').buttonMarkup({ iconshadow: "false" });
+ No icon shadow +
+ +
inline boolean
+
+

default: null (false)

+

If set to true, this will make the button act like an inline button so the width is determined by the button's text. By default, this is null (false) so the button is full width, regardless of the feedback content. Possible values: true, false. This option is also exposed as a data attribute: data-inline="true"

+
$('a').buttonMarkup({ inline: "true" });
+ Inline +
+ +
shadow boolean
+
+

default: true

+

Applies the drop shadow style to the button if set to true. This option is also exposed as a data attribute: data-shadow="false"

+
$('a').buttonMarkup({ shadow: "false" });
+ No button shadow +
+ +
theme string
+
+

default: null, inherited from parent

+

Sets the color scheme (swatch) for all instances of this widget. It accepts a single letter from a-z that maps to the swatches included in your theme. By default, it will inherit the same swatch color as it's parent container if not explicitly set. This option is also exposed as a data attribute: data-theme="a"

+
$('a').buttonMarkup({ theme: "a" });
+ Theme A +
+
+ +
+

The following option applies only to form buttons, which are automatically initialized by the framework:

+
+ +
+
initSelector CSS selector string
+
+

default: "button, [type='button'], [type='submit'], [type='reset'], [type='image']"

+

This is used to define the selectors (element types, data roles, etc.) that will automatically be initialized as form buttons. To change which elements are initialized, bind this option to the mobileinit event:

+
$( document ).bind( "mobileinit", function(){
+   $.mobile.button.prototype.options.initSelector = ".myButtons";
+});
+
+
+
+ + +
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + + +
+ + + + diff --git a/docs/buttons/buttons-themes.html b/docs/buttons/buttons-themes.html old mode 100755 new mode 100644 index 4143c7cc..7659efe6 --- a/docs/buttons/buttons-themes.html +++ b/docs/buttons/buttons-themes.html @@ -1,41 +1,46 @@ - - jQuery Mobile Docs - Buttons - + + + jQuery Mobile Docs - Theming buttons + - - + + + + -
+
-
+

Theming buttons

+ Home
+
-

Theming

+

Theming buttons

-

jQuery Mobile has a rich theming system that gives you full control of how buttons are styled. When a link is added to a container, it is automatically assigned a theme swatch letter that matches it's parent bar or content box to visually integrate the button into the parent container, like a chameleon. So a button placed inside a content container with a theme of "a" (black in the default theme) will be automatically assigned the button theme of "a" (charcoal in the default theme). Here are examples of the button theme pairings in the default theme. All buttons have the same HTML markup:

+

jQuery Mobile has a rich theming system that gives you full control of how buttons are styled. When a link is added to a container, it is automatically assigned a theme swatch letter that matches its parent bar or content box to visually integrate the button into the parent container, like a chameleon. So a button placed inside a content container with a theme of "a" (black in the default theme) will be automatically assigned the button theme of "a" (charcoal in the default theme). Here are examples of the button theme pairings in the default theme. All buttons have the same HTML markup:

-

A swatch

Button
-

B swatch

Button
-

C swatch

Button
-

D swatch

Button
-

E swatch

Button
+

A swatch

Button
+

B swatch

Button
+

C swatch

Button
+

D swatch

Button
+

E swatch

Button

Assigning theme swatches

-

Button can be manually assigned any of the button color swatches from the theme to add visual contrast with the container they sit inside by adding the data-theme attribute on the button markup and specifying a swatch letter.

+

Buttons can be manually assigned any of the button color swatches from the theme to add visual contrast with the container they sit inside by adding the data-theme attribute on the button markup and specifying a swatch letter.

			
 <a href="index.html" data-role="button" data-theme="a">Theme a</a>			
 
-

Here are 4 buttons with icons that have a different swatch letter assigned via the data-theme attribute.

+

Here are 5 buttons with icons that have a different swatch letter assigned via the data-theme attribute.

Theme a Theme b @@ -46,7 +51,7 @@

Theme variations

"a" theme on container with themed buttons inside

-
+
Theme a Theme b Theme c @@ -55,7 +60,7 @@

"b" theme on container with themed buttons inside

-
+
Theme a Theme b Theme c @@ -64,7 +69,7 @@

"c" theme on container with themed buttons inside

-
+
Theme a Theme b Theme c @@ -73,7 +78,7 @@

"d" theme on container with themed buttons inside

-
+
Theme a Theme b Theme c @@ -81,8 +86,8 @@ Theme e
-

"d" theme on container with themed buttons inside

-
+

"e" theme on container with themed buttons inside

+
Theme a Theme b Theme c @@ -90,7 +95,33 @@ Theme e
-
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + +
diff --git a/docs/buttons/buttons-types.html b/docs/buttons/buttons-types.html index 343eb3f0..cd581afd 100755 --- a/docs/buttons/buttons-types.html +++ b/docs/buttons/buttons-types.html @@ -1,28 +1,43 @@ - - jQuery Mobile Docs - Lists - + + + jQuery Mobile Docs - Button types + - - + + + + -
+
-
-

Button markup options

+
+

Button basics

+ Home
- -

Buttons that are used for navigation should be coded as anchor links, and those that submit forms as button elements — each will be styled identically by the framework.

+
+ +

Button basics

+ + + + +

Buttons are coded with standard HTML anchor and input elements, then enhanced by jQuery Mobile to make them more attractive and useable on a mobile device. Use anchor links (a elements) to mark up navigation buttons, and input or button elements for form submission.

Styling links as buttons

-

In the main content block of a page, you can style any anchor link as a button by adding the data-role="button" to the link. The framework will add all necessary classes to style the link as a button. For example, this markup:

+

In the main content block of a page, you can style any anchor link as a button by adding the data-role="button" attribute. The framework will enhance the link with markup and classes to style the link as a button. For example, this markup:

<a href="index.html" data-role="button">Link button</a> @@ -31,27 +46,57 @@

Produces this link-based button:

Link button +

Links styled like buttons have all the same visual options as true form-based buttons below, but there are a few important differences. Link-based buttons aren't part of the button plugin and only just use the underlying buttonMarkup plugin to generate the button styles so the form button methods (enable, disable, refresh) aren't supported. If you need to disable a link-based button (or any element), it's possible to apply the disabled class ui-disabled yourself with JavaScript to achieve the same effect.

+

Form buttons

-

For ease of styling, the framework automatically converts any button element or input with a type of submit, reset, button, or image into a custom styled link-based button — there is no need to add the data-role="button" attribute.

-

The original form-based button is hidden, but remains in the markup. When a click event fires on a link button, it triggers a click on the original form button.

+

For ease of styling, the framework automatically converts any button or input element with a type of submit, reset, button, or image into a custom styled button — there is no need to add the data-role="button" attribute. However, if needed, you can directly call the button plugin on any selector, just like any jQuery plugin:

+ +$('[type='submit']').button(); + + +

To preserve events bound to the original button or input, the framework hides the original element by making it transparent and positioning it over the new button markup. When a user clicks on the the custom-styled button, they're actually clicking on the original element. To prevent a form button from being converted into an enhanced button, add the data-role="none" attribute and hte native control will be rendered.

Button based button:

Input type="button" based button:

- +

Input type="submit" based button:

- +

Input type="reset" based button:

- +

Input type="image" based button:

- + -
+
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + +
diff --git a/docs/buttons/index.html b/docs/buttons/index.html index 4e086a94..359e6c58 100755 --- a/docs/buttons/index.html +++ b/docs/buttons/index.html @@ -1,34 +1,41 @@ - + + jQuery Mobile Docs - Buttons - + - - + + + + -
+
-
+

Buttons

+ Home
-

Buttons are core widgets in jQuery Mobile, and are used within a wide range of other plugins.

- - -
-
+

Buttons are core widgets in jQuery Mobile, and are used within a wide range of other plugins.

- - + +
+
+ + + +
+ + + diff --git a/docs/config/dialogTransition.html b/docs/config/dialogTransition.html new file mode 100644 index 00000000..bf8acc70 --- /dev/null +++ b/docs/config/dialogTransition.html @@ -0,0 +1,40 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + +
+ +
+

Config applied

+ Home +
+ +
+ +

defaultDialogTransition is now "flip"

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + Or open a basic dialog + +
+
+ + + diff --git a/docs/config/iOSFullscreen.html b/docs/config/iOSFullscreen.html new file mode 100644 index 00000000..1df52a78 --- /dev/null +++ b/docs/config/iOSFullscreen.html @@ -0,0 +1,45 @@ + + + + + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + +
+ +
+

jQuery Mobile

+ Home +
+ +
+ +

Fullscreen docs in iOS

+

First, hit Add to Home Screen to create a new shortcut icon on the home screen. Next, open the new shortcut and hit the button below to browse the docs as a fullscreen web app.

+ Browse docs + +
+
+ + + diff --git a/docs/config/index.html b/docs/config/index.html new file mode 100644 index 00000000..d44354ca --- /dev/null +++ b/docs/config/index.html @@ -0,0 +1,90 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + + + diff --git a/docs/config/jq17b1.html b/docs/config/jq17b1.html new file mode 100644 index 00000000..e4583513 --- /dev/null +++ b/docs/config/jq17b1.html @@ -0,0 +1,34 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + +
+ +
+

jQuery version

+ Home +
+ +
+ +

jQuery core version 1.7 Beta 1

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + + +
+
+ + + diff --git a/docs/config/loadingMessage.html b/docs/config/loadingMessage.html new file mode 100644 index 00000000..e5987881 --- /dev/null +++ b/docs/config/loadingMessage.html @@ -0,0 +1,40 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + +
+ +
+

Config applied

+ Home +
+ +
+ +

loadingMessage is now disabled

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + + +
+
+ + + diff --git a/docs/config/minScrollBack.html b/docs/config/minScrollBack.html new file mode 100644 index 00000000..a2d3fbab --- /dev/null +++ b/docs/config/minScrollBack.html @@ -0,0 +1,40 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + +
+ +
+

Config applied

+ Home +
+ +
+ +

minScrollBack is now set to 999 (disabled)

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + + +
+
+ + + diff --git a/docs/config/pageLoadErrorMessage.html b/docs/config/pageLoadErrorMessage.html new file mode 100644 index 00000000..b0887b5a --- /dev/null +++ b/docs/config/pageLoadErrorMessage.html @@ -0,0 +1,41 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + +
+ +
+

Config applied

+ Home +
+ +
+ +

pageLoadErrorMessage is now "Yikes, we broke the internet!"

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + Or try this broken link + + +
+
+ + + diff --git a/docs/config/pageTransition.html b/docs/config/pageTransition.html new file mode 100644 index 00000000..9f4a6924 --- /dev/null +++ b/docs/config/pageTransition.html @@ -0,0 +1,40 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + + +
+ +
+

Config applied

+ Home +
+ +
+ +

defaultPageTransition is now "fade"

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + +
+
+ + + diff --git a/docs/config/pushState.html b/docs/config/pushState.html new file mode 100644 index 00000000..fd84f2fb --- /dev/null +++ b/docs/config/pushState.html @@ -0,0 +1,40 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + +
+ +
+

Config applied

+ Home +
+ +
+ +

pushStateEnabled is now disabled

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + + +
+
+ + + diff --git a/docs/config/touchOverflow.html b/docs/config/touchOverflow.html new file mode 100644 index 00000000..034d78b8 --- /dev/null +++ b/docs/config/touchOverflow.html @@ -0,0 +1,40 @@ + + + + + + jQuery Mobile Docs - Configuration + + + + + + + + + + + +
+ +
+

Config applied

+ Home +
+ +
+ +

touchOverflowEnabled is now active

+

To test, hit the button below and browse the docs. Note that if a link causes a refresh, this setting will be lost and the default settings will be seen.

+ Browse docs + + +
+
+ + + diff --git a/docs/content/api-content.html b/docs/content/api-content.html index 2306dad0..88389bf8 100755 --- a/docs/content/api-content.html +++ b/docs/content/api-content.html @@ -1,16 +1,20 @@ - + + jQuery Mobile Docs - Content formatting - - + + + + + -
+
-
+

Content formatting API

diff --git a/docs/content/content-collapsible-set.html b/docs/content/content-collapsible-set.html new file mode 100644 index 00000000..5e82855e --- /dev/null +++ b/docs/content/content-collapsible-set.html @@ -0,0 +1,151 @@ + + + + + + jQuery Mobile Docs - Collapsible Content + + + + + + + + + +
+ +
+

Collapsible sets (Accordions)

+ Home +
+ +
+
+ +

Collapsible set markup

+

Collapsible sets start with the exact same markup as individual collapsibles. By adding a parent wrapper with a data-role="collapsible-set" attribute around a number of collapsibles, the framework will style these to looks like a visually grouped widget and make it behave like an accordion so only one section can be open at a time.

+

By default, all the sections will be collapsed. To set a section to be open when the page loads, add the data-collapsed="false" attribute to the heading of the section you want expanded.

+ +
		
+<div data-role="collapsible-set">
+
+	<div data-role="collapsible" data-collapsed="false">
+	<h3>Section 1</h3>
+	<p>I'm the collapsible set content for section B.</p>
+	</div>
+	
+	<div data-role="collapsible">
+	<h3>Section 2</h3>
+	<p>I'm the collapsible set content for section B.</p>
+	</div>
+	
+</div>
+	
+ + +

Here is an example of a collapsible set with 5 sections.

+ +
+
+

Section 1

+

I'm the collapsible content in a set so this feels like an accordion. I'm open by default because I have the data-collapsed="false" attribute.

+
+
+

Section 2

+

I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

+ +
+
+

Section 3

+

I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

+ +
+
+

Section 4

+

I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

+ +
+
+

Section 5

+

I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

+ +
+
+ +

Theming collapsible content

+

The standard data-theme attribute can be used to set the color of each collapsible in a set. To provide a clearer visual grouping of the content with the headers, add the data-content-theme attribute with a swatch letter. This adds a themed background color and border to the content block. For consistent theming, add these attributes to the parent collapsible set.

+ + +
		
+<div data-role="collapsible-set" data-theme="c" data-content-theme="d">
+
+ + +
+
+

Section 1

+

Collapsible content

+
+
+

Section 2

+

Collapsible content

+ +
+
+

Section 3

+

Collapsible content

+
+
+ + +

Theming individual sections

+

To have individual sections in a group styled differently, add data-theme and data-content-theme attributes to specific collapsibles.

+ +
+
+

Section header, swatch B

+

Collapsible content, swatch B

+ +
+
+

Section header, swatch A

+

Collapsible content, swatch A

+
+
+

Section header, swatch E

+

Collapsible content, swatch D

+
+
+ +
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/content/content-collapsible.html b/docs/content/content-collapsible.html index 3c71f8ef..1ed2c040 100755 --- a/docs/content/content-collapsible.html +++ b/docs/content/content-collapsible.html @@ -1,138 +1,194 @@ - - jQuery Mobile Docs - Content formatting - + + + jQuery Mobile Docs - Collapsible Content + - - - + + + + -
+
-
+

Collapsible content

+ Home
+

Collapsible content markup

-

To create a collapsible blocks of content, create a container and add the data-role="collapsible" attribute.

- +

To create a collapsible block of content, create a container and add the data-role="collapsible" attribute.

+ +

Using data-content-theme attribute allows you to set a theme for the content of the collapsible.

+

Directly inside this container, add any header element (H1-H6). The framework will style the header to look like a clickable button and add a "+" icon to the left to indicate it's expandable.

After the header, add any HTML markup you want to be collapsible. The framework will wrap this markup in a container that will be hidden/shown when the heading is clicked.

- -
		
-	<div data-role="collapsible">
-	<h3>I'm a header</h3>
-	<p>I'm the collapsible content. By default I'm open and displayed on the page, but you can click the header to hide me.</p>
-	</div>
-	
+ +

By default, the content will be collapsed.

+
		
+<div data-role="collapsible">
+   <h3>I'm a header</h3>
+   <p>I'm the collapsible content. By default I'm closed, but you can click the header to open me.</p>
+</div>
+

I'm a header

-

I'm the collapsible content. By default I'm open and displayed on the page, but you can click the header to hide me.

+

I'm the collapsible content. By default I'm closed, but you can click the header to open me.

- -

As the example notes, by default the content will be expanded. To collapse the content when the page loads, add the data-collapsed="true" attribute to the wrapper.

+

Expanding collapsibles on load

+ +

To expand the content when the page loads, add the data-collapsed="false" attribute to the wrapper.

- <div data-role="collapsible" data-collapsed="true"> + <div data-role="collapsible" data-collapsed="false">

This code will create a collapsible widget like this:

-
+

I'm a header

-

I'm the collapsible content. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

+

I'm the collapsible content. I'm expanded by default because I have the "collapsed" state set to false.

-

Collapsible content is minimally styled — we add only a bit of margin between the bar and content, and the header adopts the default Theme styles of the container it sits within.

- -

Collapsible example

-

This page has 4 collapsible containers with different types of content inside.

- -
-

Section 1: Collapsed text block

-

I'm closed when the page loads because I have the data-collapsed="true" attribute on my container.

-

I'm the collapsible content. I'm the collapsible content. I'm the collapsible content. I'm the collapsible content. I'm the collapsible content. I'm the collapsible content. I'm the collapsible content.

-
- -
-

Section 2: Expanded on load

-

I'm open when the page loads because I don't have the data-collapsed="true" attribute on my container.

-

I'm the collapsible content. I'm the collapsible content. I'm the collapsible content. I'm the collapsible content. I'm the collapsible content. I'm the collapsible content.

-
- -
-

Section 3: Form elements

-
-
- - -
-
- - -
-
-
-
-
-
-
+

Theming collapsible content

-
-

Section 4: Collapsed list

- -
+ +

Collapsible content is minimally styled — we add only a bit of margin between the bar and content, and the header adopts the default Theme styles of the container it sits within.

+ +

To provide a stronger visual connection between the collapsible header and content, add the data-content-theme attribute to the wrapper and specify a theme swatch letter. This will apply the swatch's border and flat background color (not the gradient) to the content block and changes the corner rounding to square off the bottom of the header and round the bottom of the content block instead to visually group these elements.

+ +
		
+<div data-role="collapsible" data-content-theme="c">
+   <h3>Header swatch A</h3>
+   <p>I'm the collapsible content with a themed content block set to "C".</p>
+</div>
+
+ +
+

Header swatch

+

I'm the collapsible content with a themed content block set to "C".

+
+ +

Theming collapsible headers

+

To set the theme on a collapsible header button, add the data-theme attribute to the wrapper and specify a swatch letter. Note that you can mix and match swatch letters between the header and content with these theme attributes.

+ +
		
+<div data-role="collapsible" data-theme="a" data-content-theme="a">
+   <h3>Header swatch A</h3>
+   <p>I'm the collapsible content with a themed content block set to "A".</p>
+</div>
+
+ +
+

Header swatch A

+

I'm the collapsible content with a themed content block set to "A".

+
+ + + +
+

Header swatch B

+

I'm the collapsible content with a themed content block set to "D".

+
+ +

Nested Collapsibles

-
+ +

Collapsibles can be nested inside each other if needed. In this example, we're setting the content theme to provide clearer visual connection between the levels.

+

I'm a header

I'm the collapsible content. By default I'm open and displayed on the page, but you can click the header to hide me.

-
-

I'm a nested collapsible header

-

I'm the collapsible content. By default I'm open and displayed on the page, but you can click the header to hide me.

-
+ +
+

I'm a nested collapsible with a child collapsible

+

I'm a child collapsible.

+
+

Nested inside again.

+

Three levels deep now.

+
+
+ + +
+

Section 3: Form elements

+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ +
+

Section 4: Collapsed list

+

Here is an inset list:

+ +
-

Collapsible sets

-

By giving a parent element a data-role of collapsible-set, you can cause other collapsibles within that parent to close whenever a new one is opened, acting like an accordion widget:

-
-
-

I'm a header in a set of collapsibles

-

I'm the collapsible content. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

-
-
-

I'm a header in a set of collapsibles

-

I'm the collapsible content. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

-
-
-

I'm a header in a set of collapsibles

-

I'm the collapsible content. I'm hidden by default because I have the "collapsed" state; you need to expand the header to see me.

-
-
+

Collapsible sets (accordions)

+

It's possible to combine multiple collapsibles into a grouped sets that acts like an accordion widget. Learn more

+ + +
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + -
diff --git a/docs/content/content-grids.html b/docs/content/content-grids.html old mode 100755 new mode 100644 index 9758896c..35edb416 --- a/docs/content/content-grids.html +++ b/docs/content/content-grids.html @@ -1,30 +1,43 @@ - - jQuery Mobile Docs - Content formatting - + + + jQuery Mobile Docs - Content Grids + - - - - + + + + -
+
-
+

Layout grids

+ Home
+

Using multiple column layouts isn't generally recommended on a mobile device because of the narrow screen width, but there are times where you may need to place small elements side-by-side (like buttons or navigation tabs, for example).

The jQuery Mobile framework provides a simple way to build CSS-based columns through a block style class convention called ui-grid.

-

There are two preset configurations layouts — two-column (using the class of ui-grid-a), and three-column (using the class of ui-grid-b) — that can be used in any situation that requires columns. Grids are 100% width, completely invisible (no borders or backgrounds) and don't have padding or margins, so they shouldn't interfere with the styles of elements placed inside them.

+

There are four preset configurations layouts that can be used in any situation that requires columns:

+
    +
  • two-column (using the ui-grid-a class)
  • +
  • three-column (using the ui-grid-b class)
  • +
  • four-column (using the ui-grid-c class)
  • +
  • five-column (using the ui-grid-d class)
  • +
+ +

Grids are 100% width, completely invisible (no borders or backgrounds) and don't have padding or margins, so they shouldn't interfere with the styles of elements placed inside them.

+

Within the grid container, child elements are assigned ui-block-a/b/c/d in a sequential manner which makes each "block" element float side-by-side, forming the grid. The ui-block-a class essentially clears the floats which will start a new line (see multiple row grids, below).

+

Two column grids

To build a two-column (50/50%) layout, start with a container with a class of ui-grid-a, and add two child containers inside it classed with ui-block-a for the first column and ui-block-b for the second:

@@ -44,7 +57,7 @@
I'm Block B and text inside will wrap.
-

As you see above, by default grid blocks have no styles for appearance; they simply present content side-by-side.

+

As you see above, by default grid blocks have no visual styling; they simply present content side-by-side.

Grid classes can be applied to any container. In this next example, we add ui-grid-a to a fieldset, and apply the ui-block classes to the two buttons inside to stretch them each to 50% of the screen width:

@@ -61,7 +74,7 @@ -

And, grid blocks can adopt presentation styles from the theming system — by adding a height and color swatch reference to the grid blocks, we can achieve this style appearance:

+

Theme classes (not data-theme attributes) from the theming system can be added to an element, including grids. On the blocks below, we're adding two classes: ui-bar to add the default bar padding and ui-bar-e to apply the background gradient and font styling for the "e" toolbar theme swatch. For illustration purposes, an inline style="height:120px" attribute is also added to each grid to set each to a standard height.

Block A
@@ -69,14 +82,14 @@

Three-column grids

-

The other grid layout configuration uses class=ui-grid-b on the parent, and 3 child container elements, each with its respective ui-block-a/b/c class, to create a three-column layout (33/33/33%).

+

The other grid layout configuration uses class=ui-grid-b on the parent, and 3 child container elements, each with its respective ui-block-a/b/c class, to create a three-column layout (33/33/33%). Note: These blocks are also styled with theme classes so the grid layout is clearly visible.


 <div class="ui-grid-b">
 	<div class="ui-block-a">Block A</div>
 	<div class="ui-block-b">Block B</div>
 	<div class="ui-block-c">Block C</div>
-</div><!-- /grid-a -->
+</div><!-- /grid-b -->
 

This will produce a 33/33/33% grid for our content.

@@ -97,7 +110,7 @@

Four-column grids

-

A four-column, 25/25/25/25% grid is created by specifying class=ui-grid-c on the parent and adding a fourth block.

+

A four-column, 25/25/25/25% grid is created by specifying class=ui-grid-c on the parent and adding a fourth block. Note: These blocks are also styled with theme classes so the grid layout is clearly visible.

A
@@ -107,7 +120,7 @@

Five-column grids

-

A five-column, 20/20/20/20/20% grid is created by specifying class=ui-grid-d on the parent and adding a fourth block.

+

A five-column, 20/20/20/20/20% grid is created by specifying class=ui-grid-d on the parent and adding a fourth block. Note: These blocks are also styled with theme classes so the grid layout is clearly visible.

A
@@ -133,7 +146,45 @@
C
-
+ +

Grids in toolbars

+

Grids are helpful for creating layouts within a toolbar. Here's a footer with a 3 column grid.

+ +
+

Settings

+
+
+
+
+ + +
+ +
+ +
+ +

More in this section

+ + +
+
+ +
+ + +
diff --git a/docs/content/content-html.html b/docs/content/content-html.html old mode 100755 new mode 100644 index ff892783..708a3b23 --- a/docs/content/content-html.html +++ b/docs/content/content-html.html @@ -1,26 +1,28 @@ - - jQuery Mobile Docs - Content formatting - + + + jQuery Mobile Docs - HTML formatting + - - - - + + + + -
+
-
+

HTML Formatting

+ Home
- - -
-

Read-only list

+
+

Inset Readonly Lists

+ Home
-
    +
    +

    Here is a variety of full-width lists that are read-only. If a list has the data-role="listview" attribute, but the contents aren't linked, it will display as read-only. These look like normal lists, but don't have a right arrow. + +

    Simple list

    + +
    • Acura
    • Audi
    • BMW
    • Cadillac
    • -
    • Chrysler
    • -
    • Dodge
    • Ferrari
    • -
    • Ford
    • -
    • GMC
    • -
    • Honda
    • -
    • Hyundai
    • -
    • Infiniti
    • -
    • Jeep
    • -
    • Kia
    • -
    • Lexus
    • -
    • Mini
    • -
    • Nissan
    • -
    • Porsche
    • -
    • Subaru
    • -
    • Toyota
    • -
    • Volkswagon
    • -
    • Volvo
    -
    -
- - \ No newline at end of file +

Count bubbles

+
    +
  • Inbox 12
  • +
  • Outbox 0
  • +
  • Drafts 4
  • +
  • Sent 328
  • +
  • Trash 62
  • +
+ +

Numbered list

+
    +
  1. The Godfather
  2. +
  3. Inception
  4. +
  5. The Good, the Bad and the Ugly
  6. +
  7. Pulp Fiction
  8. +
  9. Schindler's List
  10. +
+ +

Divided, formatted content

+
    +
  • +

    Stephen Weber

    +

    You've been invited to a meeting at Filament Group in Boston, MA

    +

    Hey Stephen, if you're available at 10am tomorrow, we've got a meeting with the jQuery team.

    +

    6:24PM

    +
  • +
  • +

    jQuery Team

    +

    Boston Conference Planning

    +

    In preparation for the upcoming conference in Boston, we need to start gathering a list of sponsors and speakers.

    +

    9:18AM

    +
  • +
+ + + + +

Icon list

+
    +
  • FranceFrance 4
  • +
  • GermanyGermany 4
  • +
  • Great BritainGreat Britain 0
  • +
  • FinlandFinland 12
  • +
  • NorwayNorway 328
  • +
  • United StatesUnited States 62
  • +
+ +

Thumbnail list

+ +
    +
  • + +

    Broken Bells

    +

    Broken Bells

    +
  • +
  • + +

    Warning

    +

    Hot Chip

    +
  • +
  • + +

    Wolfgang Amadeus Phoenix

    +

    Phoenix

    +
  • +
+ +

Divided, filterable list

+
    +
  • A
  • +
  • Adam Kinkaid
  • +
  • Alex Wickerham
  • +
  • Avery Johnson
  • +
  • B
  • +
  • Bob Cabot
  • +
  • C
  • +
  • Caleb Booth
  • +
  • Christopher Adams
  • +
+ + +
+ + + +
+ + + +
+ + + diff --git a/docs/lists/lists-search-inset.html b/docs/lists/lists-search-inset.html new file mode 100755 index 00000000..8fc03815 --- /dev/null +++ b/docs/lists/lists-search-inset.html @@ -0,0 +1,97 @@ + + + + + + jQuery Mobile Docs - Filtered Inset Lists + + + + + + + + + + + + + diff --git a/docs/lists/lists-search-with-dividers.html b/docs/lists/lists-search-with-dividers.html new file mode 100644 index 00000000..44400959 --- /dev/null +++ b/docs/lists/lists-search-with-dividers.html @@ -0,0 +1,110 @@ + + + + + jQuery Mobile Docs - Filtered Lists with Dividers + + + + + + + + + + + + + diff --git a/docs/lists/lists-search.html b/docs/lists/lists-search.html index c059ba7a..c134f041 100755 --- a/docs/lists/lists-search.html +++ b/docs/lists/lists-search.html @@ -1,22 +1,27 @@ - - jQuery Mobile Docs - Lists - + + + jQuery Mobile Docs - Filtered Lists + - - + + + + -
+ + + + +
+ + + diff --git a/docs/lists/lists-split-purchase.html b/docs/lists/lists-split-purchase.html index fdf70f4b..ab46e839 100755 --- a/docs/lists/lists-split-purchase.html +++ b/docs/lists/lists-split-purchase.html @@ -1,31 +1,34 @@ - - jQuery Mobile Docs - Lists - + + + jQuery Mobile Docs - Sample Dialog + - - + + + + -
+
-
-

Purchase album?

+
+

Purchase?

This album costs $10.99 and includes 9 tracks.

Your download will begin immediately on your mobile device and all tracks will by added your your library next time you sync.

- Purchase album - No thanks -
-
+ Purchase album + No thanks +
+
- - \ No newline at end of file + + diff --git a/docs/lists/lists-split.html b/docs/lists/lists-split.html index a690d511..6993d41a 100755 --- a/docs/lists/lists-split.html +++ b/docs/lists/lists-split.html @@ -1,95 +1,144 @@ - - jQuery Mobile Docs - Lists - + + + jQuery Mobile Docs - Split Button Lists + - - + + + + -
+
-
+

List formatting

+ Home
- +
+
+ + +
-
+ - - \ No newline at end of file +
+ + + diff --git a/docs/lists/lists-themes.html b/docs/lists/lists-themes.html index d604610b..0880fee6 100755 --- a/docs/lists/lists-themes.html +++ b/docs/lists/lists-themes.html @@ -1,38 +1,42 @@ - - jQuery Mobile Docs - Lists - + + + jQuery Mobile Docs - Theming Lists + - - + + + + -
+
-
+

Theming lists

+ Home
- +
-

All the standard button swatches can be applied to lists. The framework assigns a default list theme swatch of "c" (silver in the default theme) and swatch "b" (blue in default theme) for dividers. Below is a default themed list.

+

All the standard button swatches can be applied to lists. The framework assigns a default list theme swatch of "c" (silver in the default theme) and swatch "b" (blue in default theme) for dividers. Below is a default themed list.

<ul data-role="listview" data-inset="true"> - -

Theming list items

-

The list item color scheme can be changed to any button color theme swatch by adding the data-theme attribute to the list, and setting the letter theme swatch. Here is the same list above with the "a" swatch applied.

+ +

Theming list items

+

The list item color scheme can be changed to any button color theme swatch by adding the data-theme attribute to the list, and setting the letter theme swatch. Here is the same list above with the "a" swatch applied.

<ul data-role="listview" data-inset="true" data-theme="d"> @@ -40,143 +44,218 @@ + +

data-theme attributes also work at the LI-level, for styling a single item.

+ +

Theming dividers

-

The theme for list dividers can be set by adding the data-dividertheme to the list and specifying a swatch letter. Here is an example of the same list above with swatch "d" set on the dividers.

+

The theme for list dividers can be set by adding the data-divider-theme to the list and specifying a swatch letter. Here is an example of the same list above with swatch "d" set on the dividers.

-<ul data-role="listview" data-inset="true" data-theme="d" data-dividertheme="c"> +<ul data-role="listview" data-inset="true" data-theme="d" data-divider-theme="e"> -
    +

    Theming count bubbles

    -

    The theme for count bubbles can be set by adding the data-counttheme to the list and specifying a swatch letter. Here is an example with swatch "e" set on the dividers.

    +

    The theme for count bubbles can be set by adding the data-count-theme to the list and specifying a swatch letter. Here is an example with swatch "e" set on the dividers.

    -<ul data-role="listview" data-inset="true" data-theme="a" data-dividertheme="d" data-counttheme="e"> +<ul data-role="listview" data-inset="true" data-theme="d" data-divider-theme="e" data-count-theme="b"> -
      + +

      Theming icons

      + +

      The default icon for each list item is arrow-r. To override this, set the data-icon attribute on the desired list item to the name of a standard icon. To prevent icons from appearing altogether, set the data-icon attribute to "false".

      +
      +
      +<li data-icon="info"><a href="#">Notices</a></li>
      +<li data-icon="alert"><a href="#">Alerts</a></li>
      +<li data-icon="false"><a href="#">No icon</a></li>
      +
      +
      + + +

      Theming split buttons

      -

      For split lists which a second button, the framework default to "b" for the theme swatch (blue in the default theme) Here is a default split list:

      +

      For split lists which a second button, the framework default to "b" for the theme swatch (blue in the default theme) Here is a default split list:

      -<ul data-role="listview" data-inset="true" data-splittheme="a"> +<ul data-role="listview" data-inset="true" data-split-theme="a"> -

      To specify the color swatch for the icon button on the right, add the data-splittheme to the list and specify a swatch letter. This attribute can also be added to individual split inside list items by adding a data-theme attribute to specific links (see second list item).

      -
-
+
- - \ No newline at end of file + + +
+ + + +
+ + + diff --git a/docs/lists/lists-thumbnails.html b/docs/lists/lists-thumbnails.html index 595b1b7a..d4d748e6 100755 --- a/docs/lists/lists-thumbnails.html +++ b/docs/lists/lists-thumbnails.html @@ -1,86 +1,132 @@ - - jQuery Mobile Docs - Lists - + + + jQuery Mobile Docs - Lists with Thumbnails + - - + + + + -
+
-
+

Thumbnails

+ Home
- +
+
+
-
- - \ No newline at end of file + + +
+ + + diff --git a/docs/lists/lists-ul.html b/docs/lists/lists-ul.html index 80b473f8..f55703e0 100755 --- a/docs/lists/lists-ul.html +++ b/docs/lists/lists-ul.html @@ -1,23 +1,28 @@ - + +Home - - jQuery Mobile Docs - Lists - + + + jQuery Mobile Docs - Basic Lists + - - + + + + -
+ + + + +
+ + + diff --git a/docs/pages/api-pages.html b/docs/pages/api-pages.html index f02884cc..233c16d5 100755 --- a/docs/pages/api-pages.html +++ b/docs/pages/api-pages.html @@ -1,18 +1,23 @@ - - jQuery Mobile Docs - Pages - + + + jQuery Mobile Docs - Pages API + - + + + + -
+
-
+

Pages API

+ Home
diff --git a/docs/pages/dialog-alt.html b/docs/pages/dialog-alt.html index 3920c818..3773ba92 100644 --- a/docs/pages/dialog-alt.html +++ b/docs/pages/dialog-alt.html @@ -1,16 +1,20 @@ - + + jQuery Mobile Framework - Dialog Example - + - + + + +
-
+

Dialog

@@ -18,8 +22,8 @@

I'm colorful

This is a regular page, styled as a dialog. To create a dialog, just link to a normal page and include a transition and data-rel="dialog" attribute.

- Good for you - Don't care, really + Good for you + Don't care, really
diff --git a/docs/pages/dialog-buttons.html b/docs/pages/dialog-buttons.html index dd7d9ecd..0358b6b3 100644 --- a/docs/pages/dialog-buttons.html +++ b/docs/pages/dialog-buttons.html @@ -1,11 +1,15 @@ - + + jQuery Mobile Framework - Dialog Example - + - + + + + @@ -14,12 +18,12 @@
diff --git a/docs/pages/dialog-success.html b/docs/pages/dialog-success.html index 63ae2bc0..bf66de90 100644 --- a/docs/pages/dialog-success.html +++ b/docs/pages/dialog-success.html @@ -1,26 +1,30 @@ - - + + - - jQuery Mobile Framework - Dialog Example - + + + jQuery Mobile Framework - Dialog Example + - - - + + + + + +
- +

Flickr upload:

Photos posted successfully

- - View photo page - Done + + View photo page + Done
- \ No newline at end of file + diff --git a/docs/pages/dialog-with-select.html b/docs/pages/dialog-with-select.html new file mode 100644 index 00000000..eac9d54d --- /dev/null +++ b/docs/pages/dialog-with-select.html @@ -0,0 +1,117 @@ + + + + + + jQuery Mobile Framework - Dialog Example with Select + + + + + + + + + + +
+ +
+

Dialog select test

+
+ + +
+ + + + + + + +
+ +
+

Sample Dialogs

+
+ +
+ +
+
+ + +
+ +
+ + +
+ + Real Submit Would go here +
+ Cancel +
+
+ + + + \ No newline at end of file diff --git a/docs/pages/dialog.html b/docs/pages/dialog.html index 993974dd..a8dcfbf6 100644 --- a/docs/pages/dialog.html +++ b/docs/pages/dialog.html @@ -1,17 +1,21 @@ - + + jQuery Mobile Framework - Dialog Example - + - + + + +
-
+

Dialog

@@ -19,8 +23,8 @@

Delete page?

This is a regular page, styled as a dialog. To create a dialog, just link to a normal page and include a transition and data-rel="dialog" attribute.

- Sounds good - Cancel + Sounds good + Cancel
diff --git a/docs/pages/docs-dialogs.html b/docs/pages/docs-dialogs.html deleted file mode 100755 index f861c6f9..00000000 --- a/docs/pages/docs-dialogs.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - jQuery Mobile Docs - Pages - - - - - - -
- -
-

Dialogs

-
- -
-

Creating dialogs

-

Any page can be presented as a modal dialog by adding the data-rel="dialog" attribute to the page anchor link. When the "dialog" attribute is applied, the framework adds styles to add rounded corners, margins around the page and a dark background to make the "dialog" appear to be suspended above the page.

- -

- - <a href="foo.html" data-rel="dialog">Open dialog</a> - -

- - Open dialog - - - -

Transitions

-

Since the dialog is a standard "page", it will open with the standard slide transition that's applied to all pages. And like all pages, you can specify any page transition you want on the dialog by adding the data-transition attribute to the link. To make it feel more dialog-like, we recommend specifying a transition of "pop", "slideup" or "flip".

- - -<a href="foo.html" data-rel="dialog" data-transition="pop">Open dialog</a> - - - - - -

Closing dialogs

-

When any link is clicked within in a dialog, the framework will automatically close the dialog and transition to the requested page, just as if the dialog were a normal page. To create a "cancel" button in a dialog, just link to the page that triggered the dialog to open. This pattern of linking to the previous page is also usable in non-JS devices as well.

-

For JavaScript-generated links, set their href to location.href to take advantage of the auto-close behavior. The close() method that can be used to programmatically close dialogs, for example: $('.ui-dialog').dialog('close').

- -

History & Back button behavior

-

Since dialogs are typically used to support actions within a page, the framework does not include dialogs in the hash state history tracking. This means that dialogs will not appear in your browsing history chronology when the Back button is clicked. For example, if you are on a page, click a link to open a dialog, close the dialog, then navigate to another page, if you were to click the browser's Back button at that point you will navigate back to the first page, not the dialog.

-

Styling & theming

-

Dialogs can be styled with different themes, just like any page. Here is a different dialog design:

- An alternate color scheme - - -

And dialogs can be can used more like a control sheet to offer multiple buttons by removing the header:

- Share photos... - - -
-
- - - \ No newline at end of file diff --git a/docs/pages/docs-link-scenarios.html b/docs/pages/docs-link-scenarios.html deleted file mode 100755 index bb6a588d..00000000 --- a/docs/pages/docs-link-scenarios.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - jQuery Mobile Docs - Pages - - - - - - -
- -
-

Linking pages

-
- -
- -

jQuery Mobile is designed to work with simple page linking conventions. The following list demonstrates how different types of links will be handled, either remotely or through an Ajax Request.

- - - -
-
- - - diff --git a/docs/pages/docs-links-urltest/index.html b/docs/pages/docs-links-urltest/index.html new file mode 100644 index 00000000..ecf6d8d7 --- /dev/null +++ b/docs/pages/docs-links-urltest/index.html @@ -0,0 +1,27 @@ + + + + + + jQuery Mobile Framework - Test URL Example + + + + + + + + + +
+
+

URL Test Page

+
+
+

This is a regular page that updated the url with a different value than was requested.

+
+
+ + + + \ No newline at end of file diff --git a/docs/pages/docs-links.html b/docs/pages/docs-links.html deleted file mode 100755 index b2b6dcca..00000000 --- a/docs/pages/docs-links.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - jQuery Mobile Docs - Pages - - - - - - -
- -
-

Linking pages

-
- -
- -

jQuery Mobile is designed to work with simple page linking conventions. Essentially, you can link pages and assets as you normally would, and jQuery Mobile will automatically handle page requests in a single-page model, using Ajax when possible. When Ajax isn't possible (such as a non-same-domain url, or if specified using certain attributes on the link), a normal http request is used instead.

- -

The goal of this model is to allow developers to create websites using best practices — where ordinary links will "just work" without any special configuration — while creating a rich, native-like experience that can't be achieved with standard HTTP requests.

- -

External page linking

- -

jQuery Mobile automates the process of building Ajax powered sites and applications.

- -

By default, when you click on link that points to an external page (ex. products.html), the framework will parse the link's href to formulate an Ajax request (Hijax) and displays the loading spinner.

- -

If the Ajax request is successful, the new page content is added to the DOM, all mobile widgets are auto-initialized, then the new page is animated into view with a page transition.

- -

If the Ajax request fails, the framework will display a small error message overlay (styled in the "e" swatch) that disappears after a brief time so this doesn't break the navigation flow. View an example of the error message.

- -

Local, internal linked "pages"

- -

A single HTML document can contain either a single 'page' or multiple 'pages' can be assembled and loaded together by stacking multiple divs with a data-role of "page". This allows you to build a small site or application within a single HTML document; jQuery Mobile will simply display the first 'page' it finds in the source order when the page loads.

- -

If a link points to an anchor (#foo), the framework will looks for a page with that ID. If it finds a page in the HTML document, it will transition the new page into view.

- -

Here is an example of a 2 "page" site built with two jQuery Mobile divs navigated by linking to an ID placed on each page wrapper. Note that the IDs on the page wrappers are only needed to support the internal page linking, and are optional if each page is a separate HTML document.

- -
-
-<div data-role="page" id="foo"> - <div data-role="content"> - I'm the "foo" page. Since I'm the first page - in the source order, I will be displayed onLoad. - <a href="#bar">Visit the bar "page"</a> - </div><!-- /content --> -</div><!-- /foo page --> - -<div data-role="page" id="bar"> - <div data-role="content"> - I'm the "bar" page. I will be shown only if the - anchor link on the <a href="#foo">foo</a> - page is clicked. - </div><!-- /content --> -</div><!-- /bar page --> - -
-
- -

You can seamlessly navigate between local, internal "pages" and external pages in jQuery Mobile. Both will look the same to the end user except that external pages will display the Ajax spinner while loading. In either situation, jQuery Mobile updates the page's URL hash to enable Back button support, deep-linking and bookmarking.

- -

PLEASE NOTE: Since we are using the hash to track navigation history for all the Ajax 'pages', it's not currently possible to deep link to an anchor (index.html#foo) on a page in jQuery Mobile, because the framework will look for a 'page' with and ID of #foo instead of the native behavior of scrolling to the content with that ID.

- -

Learn more about the technical details of the Ajax, hashes and history in jQuery mobile.

- - - -
-
- - - diff --git a/docs/pages/docs-navmodel.html b/docs/pages/docs-navmodel.html deleted file mode 100644 index efbf1bbe..00000000 --- a/docs/pages/docs-navmodel.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - jQuery Mobile Docs - Pages - - - - - - -
- -
-

Ajax, hashes & history

-
- -
- -

jQuery Mobile's navigation model

- -

A "page" in jQuery Mobile consists of an element (usually a div) with a data-role attribute set to "page", which generally contains div elements with roles of "header", "content", and "footer", each containing common markup, forms, and custom jQuery Mobile widgets.

- -

The basic workflow with page loading is as follows: first, a page is requested with a normal HTTP request, and subsequent "pages" are then requested and injected into that page's DOM. Because of this, the DOM may have a number of "pages" in it at a time, each of which can be re-visited by linking to its ID attribute.

- -

When a url is initially requested, there may be one or more "pages" in the response, and only the first one will be shown. The advantage of storing more than one "page" is that it allows you to pre-fetch static pages that are likely to be visited.

- -

Ajax-driven page navigation

- -

All navigation within jQuery Mobile is based on changes and updates to location.hash. Whenever possible, page changes will use a smooth transition between the current "page" and the next, whether it is either already present in the DOM, or is automatically loaded via Ajax.

- -

Hash values created by jQuery Mobile are normalized as full paths relative to the URL of the first "real" page that was loaded. The hash is always maintained as a valid URL, so any "page" in jQuery mobile can be bookmarked or referenced in a link. To retrieve a non-hash-based URL, simply remove the # from the address and refresh the page.

- -

In general, hash changes are created whenever a link is clicked in jQuery mobile. When a link is clicked, jQuery mobile will make sure the link is referencing a local URL, and if so, it'll prevent the link's default click behavior from occurring and request the referenced url via Ajax instead. When the page returns successfully, it will set the location.hash to the new page's relative url.

- -

Within the framework, page changes - both for pages already in the DOM and for pages that need to be loaded via Ajax - use the $.mobile.changePage() function. $.mobile.changePage() contains all of the logic for finding pages to transition to and from, and how to handle various response conditions such as a page not found. $.mobile.changePage() can be called externally and accepts the following arguments (to, transition, back, changeHash). The to argument can accept either a string (such as a file url or local element's ID), an array (in which the first array item is any local page you'd like to transition from, and the second array item is the to page), or an object (with expected properties: url, type ("get" or "post"), and data (for serialized parameters)), the latter of which is useful for loading pages that expect form data. The transition argument accepts a string representing a named transition, such as "slide". The back argument accepts a boolean representing whether the transition should go forward or in reverse. Lastly, the changeHash argument accepts a boolean for whether you'd like the url to be updated upon a successful page change.

- -

The $.mobile.changePage() function is used in a number of places in jQuery Mobile. For example, when a link is clicked, its href attribute is normalized and then $.mobile.changePage() handles the rest. When forms are submitted, jQuery Mobile simply gathers a few of the form's attributes, serializes its data, and once again, $.mobile.changePage() is used to handle the submission and response. Also, links that create dialogs use $.mobile.changePage()to open a referenced page without updating the hash, which is useful for keeping dialogs out of history tracking.

- -

Another key ingredient to jQuery Mobile's page navigation model is the base element, which is injected into the head and modified on every page change to ensure that any assets (css,images,js,etc) referenced on that page will be requested from a proper path. In browsers that don't support dynamic updates to the base element (such as Firefox 3.6), jQuery Mobile loops through all of the referenced assets on the page and prefixes their href and src attributes with the base path.

- - - -

Hash changes that occur independently of a click, such as when a user clicks the back button, are handled through the hashchange event, which is bound to the window object using Ben Alman's hashchange special event plugin (included in jQuery Mobile). When a hash change occurs (and also when the first page loads), the hashchange event handler will send the location.hash to the $.mobile.changePage() function, which in turn either loads or reveals the referenced page.

- - - -

Once the referenced page is present in the DOM, the $.mobile.changePage() function applies a transition between the current active page and the new page. Page transitions happen through adding and removing classes that apply CSS animations. For example, in a slide-left transition, the exiting page is given the classes "slideleft" and "out", and the entering page is given the classes "slideleft" and "in", as well as a class of "ui-page-active" to mark it as the new "active" page being viewed. When the animation is complete, the "in" and "out" classes are removed, and the exited page loses its "ui-page-active" class.

- -

Developer explanation of base url management:

- -

jQuery Mobile manages http requests using a combination of generated absolute URL paths and manipulating a generated <base> element's href attribute. The combination of these two approaches allows us to create URLs that contain full path information for loading pages, and a base element to properly direct asset requests made by those loaded pages (such as images and stylesheets).

- -

jQuery Mobile core contains 4 internal functions for manipulating a base url to be used in normalizing relative http requests:

-

Note: These functions have changed - docs will be updated soon!

-
    -
  • getPathDir: function that returns a path with the last "/"-split segment removed (which is assumed to be a file url).

  • -
  • getBaseURL: function that returns either the location.hash, or a path specified via its argument, with the last segment removed.

  • -
  • setBaseURL: sets the <base> element's href attribute to the value of getBaseURL()

  • -
  • resetBaseURL: sets the <base> element's href attribute to the relative path of the initially-http-requested page.

  • -

These are called at certain times during page requests and transitions: - On DOM ready, during the initial page load, a <base> element is created and appended to the <head> of the page. Immediately after that, resetBaseURL() is called to set the <base> element's href to location.pathname.

- -

Whenever a link with a relative URL is clicked, the $.mobile.changePage() function will prefixed that link's href with the value of getBaseURL(), which creates a full path to that file, relative to the document.

- -

Changing the hash value triggers the hashchange event handler, which first calls resetBaseURL(), and makes an Ajax request to the value of the hash (which is already a full path, requiring no base url). After that request is sent, setBaseURL() is called, which resets the <base> element's href attribute to the value of getBaseURL() and allows any references to images, stylesheets, and scripts within that page to be requested with a proper base path.

- -

Auto-generated pages and sub-hash urls

- -

Some plugins may choose to dynamically break a page's content into separate navigable pages, which can then be reached via deep links. One example of this would be the Listview plugin, which will break a nested UL (or OL) into separate pages, which are each given an ID so they can be linked to like any normal "page" in jQuery Mobile. However, in order to link to these pages, the page that generates them must first be requested from the server. To make this work, pages that are auto-generated by plugins use the following special ID structure: - <div id="page.html&subpageidentifier">

- -

So, for example, a page generated by the listview plugin may have an ID like this: id="artists.html&ui-page=listview-1"

- -

When a page is requested, jQuery Mobile knows to split the URL at "&ui-page" and make an HTTP request to the portion of the URL before that key. In the case of the listview example mentioned above, the URL would look like this: http://example.com/artists.html&ui-page=listview-1 - ...and jQuery Mobile would request artists.html, which would then generate its sub-pages, creating the div with id="artists.html&ui-page=listview-1", which it will then display as the active page.

- -

Note that the ID of the element contains the full URL path, not just the portion after &ui-page=. This allows jQuery Mobile to use a single consistent mechanism that matches URLs to page IDs.

- -

Cases when Ajax navigation will not be used

- -

Under certain conditions, normal http requests will be used instead of Ajax requests. One case where this is true is when linking to pages on external websites. You can also specify that a normal http request be made through the following link attributes:

- -
    -
  • rel=external

  • -
  • target (with any value, such as "_blank")

  • - -

Form submissions

- -

Form submissions are handled automatically through the navigation model as well. Visit the forms section for more information.

- -

Known limitations

- -

The non-standard environment created by jQuery Mobile's page navigation model introduces some conditions for which you should be aware when building pages:

- -
    -
  • When linking to directories, without a filename url, (such as href="typesofcats/" instead of href="typesofcats/index.html"), you must provide a trailing slash. This is because jQuery Mobile assumes the section after the last "/" character in a url is a filename, and it will remove that section when creating base urls from which future pages will be referenced.

  • -
  • Any unique assets referenced by pages in a jQuery Mobile-driven site should be placed inside the "page" element (the element with a data-role attribute of "page"). For example, links to styles and scripts that are specific to a particular page can be referenced inside that div. However, a better approach is to use jQuery Mobile's page events to trigger specific scripting when certain pages load.

  • -
  • Conversely, any non-unique assets (those used site-wide) should be referenced in the <head> section of an HTML document, or at the very least, outside of the "page" element, to prevent running scripts more than once.

  • -
  • The "ui-page" key name used in sub-hash url references can be set to any value you'd like, so as to blend into your URL structure. This value is stored in jQuery.mobile.subPageUrlKey.

  • -
- - -
-
- - - \ No newline at end of file diff --git a/docs/pages/docs-pages.html b/docs/pages/docs-pages.html deleted file mode 100755 index 8a2fc415..00000000 --- a/docs/pages/docs-pages.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - jQuery Mobile Docs - Pages - - - - - - -
- -
-

Anatomy of a Page

-
- -
- -

The jQuery Mobile "page" structure is optimized to support either single pages, or local internal linked "pages" within a page.

- -

The goal of this model is to allow developers to create websites using best practices — where ordinary links will "just work" without any special configuration — while creating a rich, native-like experience that can't be achieved with standard HTTP requests.

- -

Mobile page structure

- -

A jQuery Mobile site must start with an HTML5 'doctype' to take full advantage of all of the framework's features. (Older devices with browsers that don't understand HTML5 will safely ignore the 'doctype' and various custom attributes.) In the 'head', references to jQuery, jQuery Mobile and the mobile theme CSS are all required to start things off:

- -

-<!DOCTYPE html> 
-<html> 
-	<head> 
-	<title>Page Title</title> 
-	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.css" />
-	<script src="http://code.jquery.com/jquery-1.4.3.min.js"></script>
-	<script src="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.js"></script>
-</head> 
-<body> 
-
-...
-
-</body>
-</html>
-
- -

Inside the <body> tag, each view or "page" on the mobile device is identified with an element (usually a div) with the data-role="page" attribute:

- -
-
<div data-role="page"> 
-	...
-</div> 
-
-
- -

Within the "page" container, any valid HTML markup can be used, but for typical pages in jQuery Mobile, the immediate children of a "page" are divs with data-roles of "header", "content", and "footer".

- -
-
<div data-role="page"> 
-	<div data-role="header">...</div> 
-	<div data-role="content">...</div> 
-	<div data-role="footer">...</div> 
-</div> 
-
-
- - -

Complete single page template

- -

Putting it all together, this is the standard boilerplate page template you should start with:

- -

-<!DOCTYPE html> 
-<html> 
-	<head> 
-	<title>Page Title</title> 
-	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.css" />
-	<script src="http://code.jquery.com/jquery-1.4.3.min.js"></script>
-	<script src="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.js"></script>
-</head> 
-<body> 
-
-<div data-role="page">
-
-	<div data-role="header">
-		<h1>Page Title</h1>
-	</div><!-- /header -->
-
-	<div data-role="content">	
-		<p>Page content goes here.</p>		
-	</div><!-- /content -->
-
-	<div data-role="footer">
-		<h4>Page Footer</h4>
-	</div><!-- /header -->
-</div><!-- /page -->
-
-</body>
-</html>
-
- - View boilerplate template - -

External page linking

- -

jQuery Mobile automates the process of building Ajax powered sites and applications.

- -

By default, when you click on a link that points to an external page (ex. products.html), the framework will parse the link's href to formulate an Ajax request (Hijax) and displays the loading spinner.

- -

If the Ajax request is successful, the new page content is added to the DOM, all mobile widgets are auto-initialized, then the new page is animated into view with a page transition.

- -

If the Ajax request fails, the framework will display a small error message overlay (styled in the "e" swatch) that disappears after a brief time so this doesn't break the navigation flow. View an example of the error message.

- -

Local, internal linked "pages"

- -

A single HTML document can contain multiple 'pages' that are loaded together by stacking multiple divs with a data-role of "page". Each 'page' block needs a unique ID (id="foo") that will be used to link internally between 'pages' (href="#foo"). When a link is clicked, the framework will look for an internal 'page' with the ID and transition it into view.

- -

It's important to note if you are linking from a mobile page that was loaded via Ajax to a page with multiple internal pages, you need to add a rel="external" to the link. This tells the framework to do a full page reload to clear out the Ajax hash in the URL. This is critical because Ajax pages use the hash (#) to track the Ajax history, while multiple internal pages use the hash to indicate internal pages so there will be a conflicts.

-

For example, a link to a page containing multiple internal pages would look like this:

- -<a href="multipage.html" rel="external">Multi-page link</a> - -

Here is an example of a 2 "page" site built with two jQuery Mobile divs navigated by linking to an ID placed on each page wrapper. Note that the IDs on the page wrappers are only needed to support the internal page linking, and are optional if each page is a separate HTML document. Here is what two pages look inside the body element.

- -

-<body> 
-
-<!-- Start of first page -->
-<div data-role="page" id="foo">
-
-	<div data-role="header">
-		<h1>Foo</h1>
-	</div><!-- /header -->
-
-	<div data-role="content">	
-		<p>I'm first in the source order so I'm shown as the page.</p>		
-		<p>View internal page called <a href="#bar">bar</a></p>	
-	</div><!-- /content -->
-
-	<div data-role="footer">
-		<h4>Page Footer</h4>
-	</div><!-- /header -->
-</div><!-- /page -->
-
-
-<!-- Start of second page -->
-<div data-role="page" id="bar">
-
-	<div data-role="header">
-		<h1>Bar</h1>
-	</div><!-- /header -->
-
-	<div data-role="content">	
-		<p>I'm first in the source order so I'm shown as the page.</p>		
-		<p><a href="#foo">Back to foo</a></p>	
-	</div><!-- /content -->
-
-	<div data-role="footer">
-		<h4>Page Footer</h4>
-	</div><!-- /header -->
-</div><!-- /page -->
-</body>
-
- - View multi-page template - -

- -

PLEASE NOTE: Since we are using the hash to track navigation history for all the Ajax 'pages', it's not currently possible to deep link to an anchor (index.html#foo) on a page in jQuery Mobile, because the framework will look for a 'page' with an ID of #foo instead of the native behavior of scrolling to the content with that ID.

- -

Learn more about the technical details of the navigation model and Ajax, hashes and history in jQuery mobile.

- - - - - -
-
- - - diff --git a/docs/pages/docs-transitions.html b/docs/pages/docs-transitions.html deleted file mode 100755 index d3ceb313..00000000 --- a/docs/pages/docs-transitions.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - jQuery Mobile Docs - Pages - - - - - - -
- -
-

Transitions

-
- -
- -

Page transitions

- -

The jQuery Mobile framework includes a set of six CSS-based transition effects that can be applied to any object or page change event, which apply the chosen transition when navigating to a new page and the reverse transition for the Back button. By default, the framework applies the right to left slide transition.

- -

To set a custom transition effect, add the data-transition attribute to the link. Possible values include:

- - -<a href="index.html" data-transition="pop">I'll pop</a> - - -

- slide - slideup - slidedown - pop - fade - flip -

- -

In addition, you can also force a "backwards" transition by specifying data-back="true" on your link.

- -
-

Transitions from jQtouch (with small modifications): Built by David Kaneda and maintained by Jonathan Stark.

-
- -
-
- - - \ No newline at end of file diff --git a/docs/pages/dynamic-samples/animals.html b/docs/pages/dynamic-samples/animals.html new file mode 100644 index 00000000..cc7ee82f --- /dev/null +++ b/docs/pages/dynamic-samples/animals.html @@ -0,0 +1,25 @@ + + + + + +Animals + + + + + + +
+

Animals

+
+

All your favorites from aardvarks to zebras.

+
    +
  • Pets
  • +
  • Farm Animals
  • +
  • Wild Animals
  • +
+
+
+ + diff --git a/docs/pages/dynamic-samples/category.php b/docs/pages/dynamic-samples/category.php new file mode 100644 index 00000000..e698057f --- /dev/null +++ b/docs/pages/dynamic-samples/category.php @@ -0,0 +1,150 @@ + array( + name => "Animals", + description => "All your favorites from aardvarks to zebras.", + items => array( + array( + name => "Pets", + ), + array( + name => "Farm Animals", + ), + array( + name => "Wild Animals", + ) + ) + ), + colors => array( + name => "Colors", + description => "Fresh colors from the magic rainbow.", + items => array( + array( + name => "Blue", + ), + array( + name => "Green", + ), + array( + name => "Orange", + ), + array( + name => "Purple", + ), + array( + name => "Red", + ), + array( + name => "Yellow", + ), + array( + name => "Violet", + ) + ) + ), + vehicles => array( + name => "Vehicles", + description => "Everything from cars to planes.", + items => array( + array( + name => "Cars", + ), + array( + name => "Planes", + ), + array( + name => "Construction", + ) + ) + ) +); + +// Get the name of the category to display from +// the query params for the script. + +$category_name = ''; +if ( $_GET[ 'id' ] ) { + $category_name = $_GET[ 'id' ]; +} + +// Now get the category data, by name, from our in-memory +// dictionary. This is the part where a script normally fetches +// the data from a database. + +$category_obj = $category_data[ $category_name ]; + +// Now figure out how the script is being called. If it's being +// called via XmlHttpRequest, then send the data back as JSON. +// If not, then send it back as a list in an HTML document. + +if( $_SERVER[ "HTTP_X_REQUESTED_WITH" ] && $_SERVER[ "HTTP_X_REQUESTED_WITH" ] ==="XMLHttpRequest" ) { + // Data should be written out as JSON. + header("Content-type: application/json"); + if ( !$category_obj ) { + echo 'null'; + } else { + echo '{"name":"' . $category_obj[ 'name' ] + . '","description":"' . $category_obj[ 'description' ] + . '","items":['; + + $arr = $category_obj[ 'items' ]; + $count = count($arr); + for ( $i = 0; $i < $count; $i++ ) { + if ( $i ) { + echo ","; + } + echo '{"name":"' . $arr[ $i ][ 'name' ] . '"}'; + } + echo "]}"; + } +} else { + // Data should be written out as HTML. + header("Content-type: text/html"); +?> + + + + + +Vehicles + + + + + +
+

+
+ +

No matches found.

+ +

+
    +" . $arr[ $i ][ 'name' ] . "\n"; + } +?> +
+ +
+
+ + + + + + + +Colors + + + + + + +
+

Colors

+
+

Fresh colors from the magic rainbow.

+
    +
  • Blue
  • +
  • Green
  • +
  • Orange
  • +
  • Purple
  • +
  • Red
  • +
  • Yellow
  • +
  • Violet
  • +
+
+
+ + diff --git a/docs/pages/dynamic-samples/index.html b/docs/pages/dynamic-samples/index.html new file mode 100644 index 00000000..46bc19c0 --- /dev/null +++ b/docs/pages/dynamic-samples/index.html @@ -0,0 +1,24 @@ + + + + + +Dynamic Page Samples + + + + + + +
+

Categories

+ +
+ + diff --git a/docs/pages/dynamic-samples/sample-reuse-page-external.html b/docs/pages/dynamic-samples/sample-reuse-page-external.html new file mode 100644 index 00000000..f511c6a5 --- /dev/null +++ b/docs/pages/dynamic-samples/sample-reuse-page-external.html @@ -0,0 +1,119 @@ + + + + + +changePage JSON Sample + + + + + + + +
+

Categories

+
+

Select a Category Below:

+ +
+
+
+

+
+
+ + diff --git a/docs/pages/dynamic-samples/sample-reuse-page.html b/docs/pages/dynamic-samples/sample-reuse-page.html new file mode 100644 index 00000000..74da9909 --- /dev/null +++ b/docs/pages/dynamic-samples/sample-reuse-page.html @@ -0,0 +1,196 @@ + + + + + +changePage JSON Sample + + + + + + + +
+

Categories

+
+

Select a Category Below:

+ +
+ +
+
+

+
+
+ + diff --git a/docs/pages/dynamic-samples/vehicles.html b/docs/pages/dynamic-samples/vehicles.html new file mode 100644 index 00000000..736a288e --- /dev/null +++ b/docs/pages/dynamic-samples/vehicles.html @@ -0,0 +1,25 @@ + + + + + +Vehicles + + + + + + +
+

Vehicles

+
+

Everything from cars to planes.

+
    +
  • Cars
  • +
  • Planes
  • +
  • Destruction
  • +
+
+
+ + diff --git a/docs/pages/index.html b/docs/pages/index.html index 25b3a785..2ab11247 100755 --- a/docs/pages/index.html +++ b/docs/pages/index.html @@ -1,34 +1,46 @@ - + + jQuery Mobile Docs - Pages - + - + + + + -
+
-
+

Pages

+ Home
-

jQuery Mobile includes automatic AJAX page loading of external pages with back button history support, a set of animated page transitions and simple tools for displaying pages as dialogs.

+ +

jQuery Mobile includes automatic AJAX page loading of external pages with back button history support, a set of animated page transitions and simple tools for displaying pages as dialogs.

+
diff --git a/docs/pages/link-formats.html b/docs/pages/link-formats.html deleted file mode 100755 index b5aea256..00000000 --- a/docs/pages/link-formats.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - jQuery Mobile Docs - Links - - - - - - - -
- -
-

Link formats

-
- -
- -

All standard HTML link types are supported in jQuery Mobile. To make the experience as polished as possible, any links to pages within the same domain will be automatically turned into Ajax requests and displayed with an animated page transition by the framework.

-

Links that point to other domains or that have rel="external" or target attributes will not be loaded with Ajax and will cause a full page refresh. If multiple "pages" are contained within a single HTML document, they can be linked by referencing the ID of the page as an anchor (#foo).

-

All other types of links like mailto: and tel: aren't impacted by the framework and will work as expected. Learn more about the linking and navigation model in jQuery Mobile

- - -
-
- - - \ No newline at end of file diff --git a/docs/pages/multipage-template.html b/docs/pages/multipage-template.html index 9674a425..1d6defd1 100755 --- a/docs/pages/multipage-template.html +++ b/docs/pages/multipage-template.html @@ -1,48 +1,83 @@ - - - Page Title - - + + + + + Multi-page template + + + + + + - +
-

Foo

+

Multi-page

-
-

Foo

-

I'm first in the source order so I'm shown as the page.

-

View internal page called bar

+
+

One

+ +

I have an id of "one" on my page container. I'm first in the source order so I'm shown when the page loads.

+ +

This is a multi-page boilerplate template that you can copy to build you first jQuery Mobile page. This template contains multiple "page" containers inside, unlike a single page template that has just one page within it.

+

Just view the source and copy the code to get started. All the CSS and JS is linked to the jQuery CDN versions so this is super easy to set up. Remember to include a meta viewport tag in the head to set the zoom level.

+

You link to internal pages by referring to the ID of the page you want to show. For example, to link to the page with an ID of "two", my link would have a href="#two" in the code.

+ +

Show internal pages:

+

Show page "two"

+

Show page "popup" (as a dialog)

-
+

Page Footer

-
+
- -
+ +
-

Bar

+

Two

-
-

Bar

-

I'm first in the source order so I'm shown as the page.

-

Back to foo

+
+

Two

+

I have an id of "two" on my page container. I'm the second page container in this multi-page template.

+

Notice that the theme is different for this page because we've added a few data-theme swatch assigments here to show off how flexible it is. You can add any content or widget to these pages, but we're keeping these simple.

+

Back to page "one"

+

Page Footer

-
+
+ + + + \ No newline at end of file diff --git a/docs/pages/page-anatomy.html b/docs/pages/page-anatomy.html new file mode 100644 index 00000000..b7772c97 --- /dev/null +++ b/docs/pages/page-anatomy.html @@ -0,0 +1,220 @@ + + + + + + jQuery Mobile Docs - Anatomy of a Page + + + + + + + + + +
+ +
+

Anatomy of a Page

+ Home +
+ +
+
+

The jQuery Mobile "page" structure is optimized to support either single pages, or local internal linked "pages" within a page.

+ +

The goal of this model is to allow developers to create websites using best practices — where ordinary links will "just work" without any special configuration — while creating a rich, native-like experience that can't be achieved with standard HTTP requests.

+ +

Mobile page structure

+ +

A jQuery Mobile site must start with an HTML5 'doctype' to take full advantage of all of the framework's features. (Older devices with browsers that don't understand HTML5 will safely ignore the 'doctype' and various custom attributes.) In the 'head', references to jQuery, jQuery Mobile and the mobile theme CSS are all required to start things off. We recommend linking to the files hosted on the jQuery CDN for best performance:

+ +

+<!DOCTYPE html> 
+<html> 
+	<head> 
+	<title>Page Title</title> 
+	
+	<meta name="viewport" content="width=device-width, initial-scale=1"> 
+
+	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.css" />
+	<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
+	<script type="text/javascript" src="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.js"></script>
+</head> 
+
+<body> 
+...content goes here...
+</body>
+</html>
+
+ +

Viewport meta tag

+

Note above that there is a meta viewport tag in the head to specify how the browser should display the page zoom level and dimensions. If this isn't set, many mobile browsers will use a "virtual" page width around 900 pixels to make it work well with exisitng desktop sites but the screens may look zoomed out and too wide. By setting the viewport attributes to content="width=device-width, initial-scale=1", the width will be set to the pixel width of the device screen.

+ +
<meta name="viewport" content="width=device-width, initial-scale=1"> 
+ +

These settings do not disable the user's ability to zoom the pages, which is nice from an accessibility perspective. There is a minor issue in iOS that doesn't properly set the width when changing orientations with these viewport settings, but this will hopefully be fixed a a future release. You can set other viewport values to disable zooming if required since this is part of your page content, not the library.

+ +

Inside the body: Pages

+

Inside the <body> tag, each view or "page" on the mobile device is identified with an element (usually a div) with the data-role="page" attribute:

+ +
+
<div  data-role="page"> 
+	...
+</div> 
+
+
+ +

Within the "page" container, any valid HTML markup can be used, but for typical pages in jQuery Mobile, the immediate children of a "page" are divs with data-roles of "header", "content", and "footer".

+ +
+
<div data-role="page"> 
+	<div data-role="header">...</div> 
+	<div data-role="content">...</div> 
+	<div data-role="footer">...</div> 
+</div> 
+
+
+ + +

Putting it together: Basic single page template

+ +

Putting it all together, this is the standard boilerplate page template you should start with on a project:

+ +

+<!DOCTYPE html> 
+<html> 
+	<head> 
+	<title>Page Title</title> 
+	
+	<meta name="viewport" content="width=device-width, initial-scale=1"> 
+
+	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.css" />
+	<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
+	<script type="text/javascript" src="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.js"></script>
+</head> 
+<body> 
+
+<div data-role="page">
+
+	<div data-role="header">
+		<h1>Page Title</h1>
+	</div><!-- /header -->
+
+	<div data-role="content">	
+		<p>Page content goes here.</p>		
+	</div><!-- /content -->
+
+	<div data-role="footer">
+		<h4>Page Footer</h4>
+	</div><!-- /footer -->
+</div><!-- /page -->
+
+</body>
+</html>
+
+ + View boilerplate template + + +

Multi-page template structure

+ +

A single HTML document can contain multiple 'pages' that are loaded together by stacking multiple divs with a data-role of "page". Each 'page' block needs a unique ID (id="foo") that will be used to link internally between 'pages' (href="#foo"). When a link is clicked, the framework will look for an internal 'page' with the ID and transition it into view.

+ +

Here is an example of a 2 "page" site built with two jQuery Mobile divs navigated by linking to an ID placed on each page wrapper. Note that the IDs on the page wrappers are only needed to support the internal page linking, and are optional if each page is a separate HTML document. Here is what two pages look inside the body element.

+ +

+<body> 
+
+<!-- Start of first page -->
+<div data-role="page" id="foo">
+
+	<div data-role="header">
+		<h1>Foo</h1>
+	</div><!-- /header -->
+
+	<div data-role="content">	
+		<p>I'm first in the source order so I'm shown as the page.</p>		
+		<p>View internal page called <a href="#bar">bar</a></p>	
+	</div><!-- /content -->
+
+	<div data-role="footer">
+		<h4>Page Footer</h4>
+	</div><!-- /footer -->
+</div><!-- /page -->
+
+
+<!-- Start of second page -->
+<div data-role="page" id="bar">
+
+	<div data-role="header">
+		<h1>Bar</h1>
+	</div><!-- /header -->
+
+	<div data-role="content">	
+		<p>I'm the second in the source order so I'm hidden when the page load. I'm just shown, if a link that reference my ID is beeing clicked.</p>		
+		<p><a href="#foo">Back to foo</a></p>	
+	</div><!-- /content -->
+
+	<div data-role="footer">
+		<h4>Page Footer</h4>
+	</div><!-- /footer -->
+</div><!-- /page -->
+</body>
+
+ + View multi-page template + +

+ +

PLEASE NOTE: Since we are using the hash to track navigation history for all the Ajax 'pages', it's not currently possible to deep link to an anchor (index.html#foo) on a page in jQuery Mobile, because the framework will look for a 'page' with an ID of #foo instead of the native behavior of scrolling to the content with that ID.

+ + +

Conventions, not requirements

+ +

Although the page structure outlined above is a recommended approach for a standard web app built with jQuery Mobile, the framework is very flexible with document structure. The page, header, content, and footer data-role elements are optional and are mostly helpful for providing some basic formatting and structure. The page wrapper used to be required for auto-initialization to work but this too is now optional for single page documents so there isn't any required markup at all. For a web page with a custom layout, all of these structural elements can be omitted but the Ajax navigation and all widgets will work just like they do in the boilerplate structure. Behind the scenes, the framework will inject the page wrapper if it's not included in the markup because it’s needed for managing pages, but the starting markup can now be extremely simple.

+ +

Note that in a multi-page setup, you are required to have page wrappers in your markup in order to group the content into multiple pages.

+ + + + +
+ + + +
+ + + +
+ + + diff --git a/docs/pages/page-cache.html b/docs/pages/page-cache.html new file mode 100644 index 00000000..85db2d33 --- /dev/null +++ b/docs/pages/page-cache.html @@ -0,0 +1,121 @@ + + + + + + jQuery Mobile Docs - Prefetching & caching pages + + + + + + + + + +
+ +
+

Prefetching & caching pages

+ Home +
+ +
+
+ + +

Prefetching pages

+ +

Usually, it's a good idea to store your app's pages in several single-page templates instead of one large multi-page template. This minimizes the size of the page's DOM.

+ +

When using single-page templates, you can prefetch pages into the DOM so that they're available instantly when the user visits them. To prefetch a page, add the data-prefetch attribute to a link that points to the page. jQuery Mobile then loads the target page in the background after the primary page has loaded and the pagecreate event has triggered. For example:

+ +

+<a href="prefetchThisPage.html" data-prefetch> ... </a>
+
+ +

You can prefetch as many linked pages as you like. Just add data-prefetch to all the links you want to prefetch.

+ +

Alternatively, you can prefetch a page programmatically using $.mobile.loadPage():

+ +

+$.mobile.loadPage( pageUrl, { showLoadMsg: false } );
+
+ +

Another advantage of prefetching a page is that the user doesn't see the Ajax loading message when visiting the prefetched page. The Ajax loading message only appears if the framework hasn't finished prefetching the page by the time the link is followed.

+ +

Prefetching pages naturally creates additional HTTP requests and uses bandwidth, so it's wise to use this feature only in situations where it's highly likely that the prefetched page will be visited. A common scenario is a photo gallery, where you can prefetch the "previous" and "next" photo pages so that the user can move quickly between photos.

+ + +

DOM size management

+ +

For animated page transitions to work, the pages you're transitioning from and to both need to be in the DOM. However, keeping old pages in the DOM quickly fills the browser's memory, and can cause some mobile browsers to slow down or even crash.

+ +

jQuery Mobile therefore has a simple mechanism to keep the DOM tidy. Whenever it loads a page via Ajax, jQuery Mobile flags the page to be removed from the DOM when you navigate away from it later (technically, on the pagehide event). If you revisit a removed page, the browser may be able to retrieve the page's HTML file from its cache. If not, it refetches the file from the server. (In the case of nested list views, jQuery Mobile removes all the pages that make up the nested list once you navigate to a page that's not part of the list.)

+ +

Pages inside a multi-page template aren't affected by this feature at all - jQuery Mobile only removes pages loaded via Ajax.

+ + +

Caching pages in the DOM

+ +

If you prefer, you can tell jQuery Mobile to keep previously-visited pages in the DOM instead of removing them. This lets you cache pages so that they're available instantly if the user returns to them.

+ +

To keep all previously-visited pages in the DOM, set the domCache option on the page plugin to true, like this:

+ +

+$.mobile.page.prototype.options.domCache = true;
+
+ +

Alternatively, to cache just a particular page, you can add the data-dom-cache="true" attribute to the page's container:

+ +

+<div data-role="page" id="cacheMe" data-dom-cache="true">
+
+ +

You can also cache a page programmatically like this:

+ +

+pageContainerElement.page({ domCache: true });
+
+ +

The drawback of DOM caching is that the DOM can get very large, resulting in slowdowns and memory issues on some devices. If you enable DOM caching, take care to manage the DOM yourself and test thoroughly on a range of devices.

+ + +
+ + + +
+ + + +
+ + + diff --git a/docs/pages/page-dialogs.html b/docs/pages/page-dialogs.html new file mode 100755 index 00000000..4a5118b1 --- /dev/null +++ b/docs/pages/page-dialogs.html @@ -0,0 +1,118 @@ + + + + + + jQuery Mobile Docs - Dialogs + + + + + + + + + +
+ +
+

Dialogs

+ Home +
+ +
+
+

Creating dialogs

+

Any page can be presented as a modal dialog by adding the data-rel="dialog" attribute to the page anchor link. When the "dialog" attribute is applied, the framework adds styles to add rounded corners, margins around the page and a dark background to make the "dialog" appear to be suspended above the page.

+ +

+ + <a href="foo.html" data-rel="dialog">Open dialog</a> + +

+ + Open dialog + + + +

Transitions

+

By default, the dialog will open with a 'pop' transition. Like all pages, you can specify any page transition you want on the dialog by adding the data-transition attribute to the link. To make it feel more dialog-like, we recommend specifying a transition of "pop", "slideup" or "flip".

+ + +<a href="foo.html" data-rel="dialog" data-transition="pop">Open dialog</a> + + + + + +

Closing dialogs

+

When any link is clicked within in a dialog, the framework will automatically close the dialog and transition to the requested page, just as if the dialog were a normal page. To create a "cancel" button in a dialog, just link to the page that triggered the dialog to open and add the data-rel="back" attribute to your link. This pattern of linking to the previous page is also usable in non-JS devices as well.

+

For JavaScript-generated links, you can simply set the href attribute to "#" and use the data-rel="back" attribute. You can also call the dialog's close() method to programmatically close dialogs, for example: $('.ui-dialog').dialog('close').

+ +

Setting the close button text

+

Just like the page plugin, you can set a dialog's close button text through option or data attribute. The option can be configured for all dialogs by binding to the mobileinit event and setting the $.mobile.dialog.prototype.options.closeBtnText property to a string of your choosing, or you can place the data attribute data-close-btn-text to configure the text from your markup.

+ +

History & Back button behavior

+

Since dialogs are typically used to support actions within a page, the framework does not include dialogs in the hash state history tracking. This means that dialogs will not appear in your browsing history chronology when the Back button is clicked. For example, if you are on a page, click a link to open a dialog, close the dialog, then navigate to another page, if you were to click the browser's Back button at that point you will navigate back to the first page, not the dialog.

+ +

Styling & theming

+

Dialogs can be styled with different themes, just like any page. Here is a different dialog design:

+ An alternate color scheme + + +

And dialogs can be can used more like a control sheet to offer multiple buttons by removing the header:

+ Share photos... + +

For the sake of readability, dialogs have a default max-width of 500 pixels (plus 15px padding on each side). To override this, use the following CSS in your theme:

+ + +.ui-dialog .ui-header, +.ui-dialog .ui-content, +.ui-dialog .ui-footer { max-width: 100%; } + + + + + +
+ + + +
+ + + +
+ + + diff --git a/docs/pages/page-dynamic.html b/docs/pages/page-dynamic.html new file mode 100644 index 00000000..8ce8dfb9 --- /dev/null +++ b/docs/pages/page-dynamic.html @@ -0,0 +1,297 @@ + + + + + + jQuery Mobile Docs - Dynamically Injecting Pages + + + + + + + + + +
+ +
+

Dynamically Injecting Pages

+ Home +
+ +
+
+

jQuery Mobile and Dynamic Page Generation

+

jQuery Mobile allows pages to be pulled into the DOM dynamically via its default click hijacking behavior, or through manual calls to $.mobile.changePage(). This is great for applications that generate HTML pages/fragments on the server-side, but there are sometimes cases where an application needs to dynamically generate page content on the client-side from JSON or some other format. This may be necessary for bandwidth/performance reasons, or because it is the data format of choice for the server they are interacting with.

+

For applications that need to generate page markup on the client-side, it's important to know about the notifications that are triggered during a $.mobile.changePage() call because they can be used as hooks into the navigation system that will allow you to generate your content at the appropriate time.

+

A call to changePage() will usually trigger the following event notifications:

+
    +
  • pagebeforechange +
      +
    • Fired off before any page loading or transition.
    • +
    • NOTE: This event was formerly known as "beforechangepage".
    • +
    +
  • +
  • pagechange +
      +
    • Fired off after all page loading and transitions.
    • +
    • NOTE: this event was formerly known as "changepage".
    • +
    +
  • +
  • pagechangefailed +
      +
    • Fired off if an error has occurred while attempting to dynamically load a new page.
    • +
    +
  • +
+

These notifications are triggered on the parent container element ($.mobile.pageContainer) of pages, and will bubble all the way up to the document element and window.

+

For applications wishing to inject pages, or radically modify the content of an existing page, based on some non-HTML data, such as JSON or in-memory JS object, the pagebeforechange event is very useful since it gives you a hook for analyzing the URL or page element the application is being asked to load or switch to, and short-circuit the default changePage() behavior by simply calling preventDefault() on the pagebeforechange event.

+

To illustrate this technique, take a look at this working sample. In this sample, the main page starts off with a list of categories that the user can navigate into. The actual items in each category are stored in a JavaScript object in memory, for illustrative purposes, but the data can really come from anywhere.

+

+var categoryData = {
+	animals: {
+		name: "Animals",
+		description: "All your favorites from aardvarks to zebras.",
+		items: [
+			{
+				name: "Pets",
+			},
+			{
+				name: "Farm Animals",
+			},
+			{
+				name: "Wild Animals",
+			}
+		]
+	},
+	colors: {
+		name: "Colors",
+		description: "Fresh colors from the magic rainbow.",
+		items: [
+			{
+				name: "Blue",
+			},
+			{
+				name: "Green",
+			},
+			{
+				name: "Orange",
+			},
+			{
+				name: "Purple",
+			},
+			{
+				name: "Red",
+			},
+			{
+				name: "Yellow",
+			},
+			{
+				name: "Violet",
+			}
+		]
+	},
+	vehicles: {
+		name: "Vehicles",
+		description: "Everything from cars to planes.",
+		items: [
+			{
+				name: "Cars",
+			},
+			{
+				name: "Planes",
+			},
+			{
+				name: "Construction",
+			}
+		]
+	}
+};
+
+

The application uses links with urls that contain a hash that tells the application what category items to display:

+
+
+  	<h2>Select a Category Below:</h2>
+  	<ul data-role="listview" data-inset="true">
+    	<li><a href="#category-items?category=animals">Animals</a></li>
+    	<li><a href="#category-items?category=colors">Colors</a></li>
+    	<li><a href="#category-items?category=vehicles">Vehicles</a></li>
+    </ul>
+
+
+

Internally, when the user clicks on one of these links, the application intercepts the internal $.mobile.changePage() call that is invoked by the frameworks' default link hijacking behavior. It then analyzes the URL for the page about to be loaded, and then decides whether or not it should handle the loading itself, or to let the normal changePage() code handle things.

+

The application was able to insert itself into the changePage() flow by binding to the "pagebeforechange" event at the document level:

+
+
+// Listen for any attempts to call changePage().
+$(document).bind( "pagebeforechange", function( e, data ) {
+
+	// We only want to handle changePage() calls where the caller is
+	// asking us to load a page by URL.
+	if ( typeof data.toPage === "string" ) {
+
+		// We are being asked to load a page by URL, but we only
+		// want to handle URLs that request the data for a specific
+		// category.
+		var u = $.mobile.path.parseUrl( data.toPage ),
+			re = /^#category-item/;
+
+		if ( u.hash.search(re) !== -1 ) {
+
+			// We're being asked to display the items for a specific category.
+			// Call our internal method that builds the content for the category
+			// on the fly based on our in-memory category data structure.
+			showCategory( u, data.options );
+
+			// Make sure to tell changePage() we've handled this call so it doesn't
+			// have to do anything.
+			e.preventDefault();
+		}
+	}
+});
+
+
+

So why listen at the document level? In short, because of deep-linking. We need our binding to be active before the jQuery Mobile framework initializes and decides how to process the initial URL that invoked the application.

+

When the callback for the "pagebeforechange" binding is invoked, the 2nd argument to the callback will be a data object that contains the arguments that were passed to the initial $.mobile.changePage() call. The properties of this object are as follows:

+
    +
  • toPage +
      +
    • Can be either a jQuery collection object containing the page to be transitioned to, *OR* a URL reference for a page to be loaded/transitioned to.
    • +
    +
  • +
  • options +
      +
    • Object containing the options that were passed in by the caller of the $.mobile.changePage() function.
    • +
    • A list of the options can be found here.
    • +
    +
  • +
+

For our sample application, we are only interested in changePage() calls where URLs are initially passed in, so the first thing our callback does is check the type for the toPage. Next, with the help of some URL parsing utilities, it checks to make sure if the URL contains a hash that we are interested in handling ourselves. If so, it then calls an application function called showCategory() which will dynamically create the content for the category specified by the URL hash, and then it calls preventDefault() on the event.

+

Calling preventDefault() on a pagebeforechange event causes the originating $.mobile.changePage() call to exit without performing any work. Calling the preventDefault() method on the event is the equivalent of telling jQuery Mobile that you have handled the changePage() request yourself.

+

If preventDefault() is not called, changePage() will continue on processing as it normally does. One thing to point out about the data object that is passed into our callback, is that any changes you make to the toPage property, or options properties, will affect changePage() processing if preventDefault() is not called. So for example, if I wanted to redirect or map a specific URL to another internal/external page, my callback could simply set the data.toPage property in the callback to the URL or DOM element of the page to redirect to. Likewise, I could set, or un-set any option from within my callback, and changePage() would use the new settings.

+

So now that we know how to intercept changePage() calls, let's take a closer look at how this sample actually generates the markup for a page. Our example actually uses, or I should say, re-uses the same page to display each of the categories. Each time one of our special links is clicked, the function showCategory() gets invoked:

+

+// Load the data for a specific category, based on
+// the URL passed in. Generate markup for the items in the
+// category, inject it into an embedded page, and then make
+// that page the current active page.
+function showCategory( urlObj, options )
+{
+	var categoryName = urlObj.hash.replace( /.*category=/, "" ),
+
+		// Get the object that represents the category we
+		// are interested in. Note, that at this point we could
+		// instead fire off an ajax request to fetch the data, but
+		// for the purposes of this sample, it's already in memory.
+		category = categoryData[ categoryName ],
+
+		// The pages we use to display our content are already in
+		// the DOM. The id of the page we are going to write our
+		// content into is specified in the hash before the '?'.
+		pageSelector = urlObj.hash.replace( /\?.*$/, "" );
+
+	if ( category ) {
+		// Get the page we are going to dump our content into.
+		var $page = $( pageSelector ),
+
+			// Get the header for the page.
+			$header = $page.children( ":jqmData(role=header)" ),
+
+			// Get the content area element for the page.
+			$content = $page.children( ":jqmData(role=content)" ),
+
+			// The markup we are going to inject into the content
+			// area of the page.
+			markup = "<p>" + category.description + "</p><ul data-role='listview' data-inset='true'>",
+
+			// The array of items for this category.
+			cItems = category.items,
+
+			// The number of items in the category.
+			numItems = cItems.length;
+
+		// Generate a list item for each item in the category
+		// and add it to our markup.
+		for ( var i = 0; i < numItems; i++ ) {
+			markup += "<li>" + cItems[i].name + "</li>";
+		}
+		markup += "</ul>";
+
+		// Find the h1 element in our header and inject the name of
+		// the category into it.
+		$header.find( "h1" ).html( category.name );
+
+		// Inject the category items markup into the content element.
+		$content.html( markup );
+
+		// Pages are lazily enhanced. We call page() on the page
+		// element to make sure it is always enhanced before we
+		// attempt to enhance the listview markup we just injected.
+		// Subsequent calls to page() are ignored since a page/widget
+		// can only be enhanced once.
+		$page.page();
+
+		// Enhance the listview we just injected.
+		$content.find( ":jqmData(role=listview)" ).listview();
+
+		// We don't want the data-url of the page we just modified
+		// to be the url that shows up in the browser's location field,
+		// so set the dataUrl option to the URL for the category
+		// we just loaded.
+		options.dataUrl = urlObj.href;
+
+		// Now call changePage() and tell it to switch to
+		// the page we just modified.
+		$.mobile.changePage( $page, options );
+	}
+}
+
+

In our sample app, the hash of the URL we handle contains 2 parts:

+

+#category-items?category=vehicles
+
+

The first part, before the '?' is actually the id of the page to write content into, the part after the '?' is info the app uses to figure out what data it should use when generating the markup for the page. The first thing showCategory() does is deconstruct this hash to extract out the id of the page to write content into, and the name of the category it should use to get the correct set of data from our in-memory JavaScript category object. After it figures out what category data to use, it then generates the markup for the category, and then injects it into the header and content area of the page, wiping out any other markup that previously existed in those elements.

+

After it injects the markup, it then calls the appropriate jQuery Mobile widget calls to enhance the list markup it just injected. This is what turns the normal list markup into a fully styled listview with all its behaviors.

+

Once that's done, it then calls $.mobile.changePage(), passing it the DOM element of the page we just modified, to tell the framework that it wants to show that page.

+

Now an interesting problem here is that jQuery Mobile typically updates the browser's location hash with the URL associated with the page it is showing. Because we are re-using the same page for each category, this wouldn't be ideal, because the URL for that page has no specific category info associated with it. To get around this problem, showCategory() simply sets the dataUrl property on the options object it passes into changePage() to tell it to display our original URL instead.

+

That's the sample in a nutshell. It should be noted that this particular sample and its usage is not a very good example of an app that degrades gracefully when JavaScript is turned off. That means it probably won't work very well on C-Grade browsers. We will be posting other examples that demonstrate how to degrade gracefully in the future. Check this page for updates.

+
+ + + + +
+ + + +
+ + + diff --git a/docs/pages/page-links.html b/docs/pages/page-links.html new file mode 100755 index 00000000..d84ce292 --- /dev/null +++ b/docs/pages/page-links.html @@ -0,0 +1,138 @@ + + + + + + jQuery Mobile Docs - Linking Pages + + + + + + + + + +
+ +
+

Linking pages

+ Home +
+ +
+
+

Linking pages

+ +

jQuery Mobile is designed to work with simple page linking conventions. Essentially, you can link pages and assets as you normally would, and jQuery Mobile will automatically handle page requests in a single-page model, using Ajax when possible. When Ajax isn't possible (such as a non-same-domain url, or if specified using certain attributes on the link), a normal http request is used instead.

+ +

The goal of this model is to allow developers to create websites using best practices — where ordinary links will "just work" without any special configuration — while creating a rich, native-like experience that can't be achieved with standard HTTP requests.

+ +

Default link behavior: Ajax

+ +

To enable animated page transitions, all links that point to an external page (ex. products.html) will be loaded via Ajax. To do this unobtrusively, the framework parses the link's href to formulate an Ajax request (Hijax) and displays the loading spinner. All this happens automatically by jQuery Mobile.

+ +

If the Ajax request is successful, the new page content is added to the DOM, all mobile widgets are auto-initialized, then the new page is animated into view with a page transition.

+ +

If the Ajax request fails, the framework will display a small error message overlay (styled in the "e" swatch) that disappears after a brief time so this doesn't break the navigation flow. View an example of the error message.

+ + +

Linking without Ajax

+ +

Links that point to other domains or that have rel="external", data-ajax="false" or target attributes will not be loaded with Ajax. Instead, these links and will cause a full page refresh with no animated transition. Both attributes have the same effect, but different semantic meaning: rel="external" should be used when linking to another site or domain, while data-ajax="false" is useful for simply opting a page within your domain from being loaded via Ajax. Because of security restrictions, the framework always opts links to external domains out of the Ajax behavior.

+ +

Note: When building a jQuery Mobile application where the Ajax navigation system is disabled globally or frequently disabled on individual links, we recommend disabling the $.mobile.pushStateEnabled global configuration option to avoid inconsistent navigation behavior in some browsers.

+ + + +

Linking within a multi-page document

+ +

A single HTML document can contain or multiple 'page' containers simply be stacking multiple divs with a data-role of "page". This allows you to build a small site or application within a single HTML document; jQuery Mobile will simply display the first 'page' it finds in the source order when the page loads.

+ +

If a link in a multi-page document points to an anchor (#foo), the framework will looks for a page wrapper with that ID (id="foo"). If it finds a page in the HTML document, it will transition the new page into view. You can seamlessly navigate between local, internal "pages" and external pages in jQuery Mobile. Both will look the same to the end user except that external pages will display the Ajax spinner while loading. In either situation, jQuery Mobile updates the page's URL hash to enable Back button support, deep-linking and bookmarking.

+ +

It's important to note if you are linking from a mobile page that was loaded via Ajax to a page that contains multiple internal pages, you need to add a rel="external" or data-ajax="false" to the link. This tells the framework to do a full page reload to clear out the Ajax hash in the URL. This is critical because Ajax pages use the hash (#) to track the Ajax history, while multiple internal pages use the hash to indicate internal pages so there will be conflicts in the hash between these two modes.

+ +

For example, a link to a page containing multiple internal pages would look like this:

+ + <a href="multipage.html" rel="external">Multi-page link</a> + + + +

"Back" button links

+

If you use the attribute data-rel="back" on an anchor, any clicks on that anchor will mimic the back button, going back one history entry and ignoring the anchor's default href. This is particularly useful when linking back to a named page, such as a link that says "home", or when generating "back" buttons with JavaScript, such as a button to close a dialog. When using this feature in your source markup, be sure to provide a meaningful href that actually points to the URL of the referring page (this will allow the feature to work for users in C-Grade browsers. Also, please keep in mind that if you just want a reverse transition without actually going back in history, you should use the data-direction="reverse" attribute instead.

+ + +

Redirects and linking to directories

+ +

When linking to directory indexes (such as href="typesofcats/" instead of href="typesofcats/index.html"), you must provide a trailing slash. This is because jQuery Mobile assumes the section after the last "/" character in a url is a filename, and it will remove that section when creating base urls from which future pages will be referenced.

+ +

However, you can work around this issue by returning your page div with a data-url attribute already specified. When you do this, jQuery Mobile will use that attribute's value for updating the URL, instead of the url used to request that page. This also allows you to return urls that change as the result of a redirect, for example, you might post a form to "/login.html" but return a page from the url "/account" after a successful submission. This tool allows you to take control of the jQuery Mobile history stack in these situations. Here's an example:

+ +

The following link points to "docs-links-urltest/index.html": Test Link which is a directory with an index page. The return page will update the hash as "/docs/pages/docs-links-urltest/" with a trailing slash. This is done via the data-url attribute in that page's source. Keep in mind that the value will replace the entire hash, and it is up to you to replace it with a URL that actually resolves to the correct page when requested via refresh or deep link.

+ +

Learn more about the technical details of the navigation model and Ajax, hashes and history in jQuery mobile.

+ + + +

Link examples

+

All standard HTML link types are supported in jQuery Mobile in addition to the types outlined above. Here is a sampler of many common link types:

+ + + + +
+ + + +
+ + + +
+ + + diff --git a/docs/pages/page-navmodel.html b/docs/pages/page-navmodel.html new file mode 100644 index 00000000..b8f81191 --- /dev/null +++ b/docs/pages/page-navmodel.html @@ -0,0 +1,155 @@ + + + + + + jQuery Mobile Docs - Ajax, hashes & history + + + + + + + + + +
+ +
+

Ajax, hashes & history

+ Home +
+ +
+
+

jQuery Mobile's navigation model

+ +

A "page" in jQuery Mobile consists of an element (usually a div) with a data-role attribute set to "page", which generally contains div elements with roles of "header", "content", and "footer", each containing common markup, forms, and custom jQuery Mobile widgets.

+ +

The basic workflow with page loading is as follows: first, a page is requested with a normal HTTP request, and subsequent "pages" are then requested and injected into that page's DOM. Because of this, the DOM may have a number of "pages" in it at a time, each of which can be re-visited by linking to its data-url attribute.

+ +

When a url is initially requested, there may be one or more "pages" in the response, and only the first one will be shown. The advantage of storing more than one "page" is that it allows you to pre-fetch static pages that are likely to be visited.

+ +

Hash and Ajax driven page navigation

+ +

By default all navigation within jQuery Mobile is based on changes and updates to location.hash. Whenever possible, page changes will use a smooth transition between the current "page" and the next, whether it is either already present in the DOM, or is automatically loaded via Ajax.

+ +

Hash values created by jQuery Mobile are normalized as full paths relative to the URL of the first "real" page that was loaded. The hash is always maintained as a valid URL, so any "page" in jQuery mobile can be bookmarked or referenced in a link. To retrieve a non-hash-based URL, simply remove the # from the address and refresh the page.

+ +

In general, hash changes are created whenever a link is clicked in jQuery mobile. When a link is clicked, jQuery mobile will make sure the link is referencing a local URL, and if so, it'll prevent the link's default click behavior from occurring and request the referenced url via Ajax instead. When the page returns successfully, it will set the location.hash to the new page's relative url.

+ +

Hash changes that occur independently of a click, such as when a user clicks the back button, are handled through the hashchange event, which is bound to the window object using Ben Alman's hashchange special event plugin (included in jQuery Mobile). When a hash change occurs (and also when the first page loads), the hashchange event handler will send the location.hash to the $.mobile.changePage() function, which in turn either loads or reveals the referenced page.

+ + +

Once the referenced page is present in the DOM, the $.mobile.changePage() function applies a transition between the current active page and the new page. Page transitions happen through adding and removing classes that apply CSS animations. For example, in a slide-left transition, the exiting page is given the classes "slideleft" and "out", and the entering page is given the classes "slideleft" and "in", as well as a class of "ui-page-active" to mark it as the new "active" page being viewed. When the animation is complete, the "in" and "out" classes are removed, and the exited page loses its "ui-page-active" class.

+ +

pushState plugin

+ +

There is an optional feature is converts the longer, hash-based URLs mentioned in the previous section into the full document path which is cleaner and makes the Ajax tracking transparent in the URL structure. This is built as an enhancement on top of the hash-based URL system for Ajax links. Note that despite the name, this feature technically converts hash-based urls by using history.replaceState (not history.pushState) in the current release because this works more reliably across our target platforms. For browsers that do not support history.replaceState, or if this feature is disabled, hash-based URLs will be used instead.

+ +

Since the plugin initializes when the DOM is fully loaded you can enable and disable it manually by setting $.mobile.pushStateEnabled global configuration option to false anytime before document ready.

+ +
+

Important: rel="external" and $.mobile.ajaxEnabled=false

+

Slightly different implementations of the replaceState API in various browsers can cause odd behavior in specific scenarios. For example, some browser implementations (including desktop browsers) implement the popstate event differently when linking externally and moving back to a page onto which state has already been pushed/replaced. When building a jQuery Mobile application where the ajax navigation is being explicitly disabled, either though the frequent use of rel="external" on links or by disabling Ajax navigation completely via the $.mobile.ajaxEnabled=false, we recommend disabling the pushState feature to fall back to the hash based navigation for more consistent behavior.

+
+ +

changePage

+ +

Within the framework, page changes - both for pages already in the DOM and for pages that need to be loaded via Ajax - use the $.mobile.changePage() function. $.mobile.changePage() contains all of the logic for finding pages to transition to and from, and how to handle various response conditions such as a page not found. $.mobile.changePage() can be called externally and accepts the following arguments (to, transition, back, changeHash). The to argument can accept either a string (such as a file url or local element's ID), an array (in which the first array item is any local page you'd like to transition from, and the second array item is the to page), or an object (with expected properties: url, type ("get" or "post"), and data (for serialized parameters)), the latter of which is useful for loading pages that expect form data. The transition argument accepts a string representing a named transition, such as "slide". The back argument accepts a boolean representing whether the transition should go forward or in reverse. Lastly, the changeHash argument accepts a boolean for whether you'd like the url to be updated upon a successful page change.

+ +

The $.mobile.changePage() function is used in a number of places in jQuery Mobile. For example, when a link is clicked, its href attribute is normalized and then $.mobile.changePage() handles the rest. When forms are submitted, jQuery Mobile simply gathers a few of the form's attributes, serializes its data, and once again, $.mobile.changePage() is used to handle the submission and response. Also, links that create dialogs use $.mobile.changePage()to open a referenced page without updating the hash, which is useful for keeping dialogs out of history tracking.

+ +

Base element

+ +

Another key ingredient to jQuery Mobile's page navigation model is the base element, which is injected into the head and modified on every page change to ensure that any assets (images, CSS, JS, etc.) referenced on that page will be requested from a proper path. In browsers that don't support dynamic updates to the base element (such as Firefox 3.6), jQuery Mobile loops through all of the referenced assets on the page and prefixes their href and src attributes with the base path.

+ + +

Developer explanation of base url management:

+ +

jQuery Mobile manages http requests using a combination of generated absolute URL paths and manipulating a generated <base> element's href attribute. The combination of these two approaches allows us to create URLs that contain full path information for loading pages, and a base element to properly direct asset requests made by those loaded pages (such as images and stylesheets).

+ +

TODO: update description of internal base and urlHistory objects

+ +

Data-url storage

+ +

The nav model maintains a data-url attribute on all data-role="page" elements. This data-url attribute is used to track the origin of the page element. Pages embedded within the main application document all have their data-url parameter set to the ID of the @data-role="page" element. The only exception to this is the first-page in the document. The first-page is special because it can be addressed by its @id if it has one, or by the document or base URL (with no hash fragment).

+ +

Pages that are external to the application document get pulled in dynamically via ajax, their data-url is set to the site relative path to the external page. If you are running in an environment where loading an external page from a different domain is allowed, then the data-url is set to the absolute URL.

+ +

Auto-generated pages and sub-hash urls

+ +

Some plugins may choose to dynamically break a page's content into separate navigable pages, which can then be reached via deep links. One example of this would be the Listview plugin, which will break a nested UL (or OL) into separate pages, which are each given a data-url attribute so they can be linked to like any normal "page" in jQuery Mobile. However, in order to link to these pages, the page that generates them must first be requested from the server. To make this work, pages that are auto-generated by plugins use the following special data-url structure: + <div data-url="page.html&subpageidentifier">

+ +

So, for example, a page generated by the listview plugin may have an data-url attribute like this: data-url="artists.html&ui-page=listview-1"

+ +

When a page is requested, jQuery Mobile knows to split the URL at "&ui-page" and make an HTTP request to the portion of the URL before that key. In the case of the listview example mentioned above, the URL would look like this: http://example.com/artists.html&ui-page=listview-1 + ...and jQuery Mobile would request artists.html, which would then generate its sub-pages, creating the div with data-url="artists.html&ui-page=listview-1", which it will then display as the active page.

+ +

Note that the data-url attribute of the element contains the full URL path, not just the portion after &ui-page=. This allows jQuery Mobile to use a single consistent mechanism that matches URLs to page data-url attributes.

+ +

Cases when Ajax navigation will not be used

+ +

Under certain conditions, normal http requests will be used instead of Ajax requests. One case where this is true is when linking to pages on external websites. You can also specify that a normal http request be made through the following link attributes:

+ +
    +
  • rel=external

  • +
  • target (with any value, such as "_blank")

  • + +

Form submissions

+ +

Form submissions are handled automatically through the navigation model as well. Visit the forms section for more information.

+ +

Known limitations

+ +

The non-standard environment created by jQuery Mobile's page navigation model introduces some conditions for which you should be aware when building pages:

+ +
    +
  • When linking to directories, without a filename url, (such as href="typesofcats/" instead of href="typesofcats/index.html"), you must provide a trailing slash. This is because jQuery Mobile assumes the section after the last "/" character in a url is a filename, and it will remove that section when creating base urls from which future pages will be referenced.

  • +
  • Documents loaded via Ajax will select the first page in the DOM of that document to be loaded as a JQM page element. As a result the developer must make sure to manage the ID attributes of the loaded page and child elements to prevent confusion when manipulating the DOM.

  • +
  • Any unique assets referenced by pages in a jQuery Mobile-driven site should be placed inside the "page" element (the element with a data-role attribute of "page"). For example, links to styles and scripts that are specific to a particular page can be referenced inside that div. However, a better approach is to use jQuery Mobile's page events to trigger specific scripting when certain pages load. Note: you can return a page from the server with a data-url already specified in the markup, and jQuery Mobile will use that for the hash update. This allows you to ensure directory paths resolve with a trailing slash and will therefore be used in the base url path for future requests.

  • +
  • Conversely, any non-unique assets (those used site-wide) should be referenced in the <head> section of an HTML document, or at the very least, outside of the "page" element, to prevent running scripts more than once.

  • +
  • The "ui-page" key name used in sub-hash url references can be set to any value you'd like, so as to blend into your URL structure. This value is stored in jQuery.mobile.subPageUrlKey.

  • +
  • When traveling back to a previously loaded jQuery Mobile document from an external or internal document with the push state plugin enabled, some browsers load and trigger the popstate event on the wrong document or for the wrong reasons (two edge cases recorded so far). If you are regularly linking to external documents and find the application behaving eratically try disabling pushstate support.

  • +
+ + +
+ + + +
+ + + +
+ + + diff --git a/docs/pages/page-scripting.html b/docs/pages/page-scripting.html new file mode 100644 index 00000000..6de26a8a --- /dev/null +++ b/docs/pages/page-scripting.html @@ -0,0 +1,139 @@ + + + + + + jQuery Mobile Docs - Scripting pages + + + + + + + + + +
+ +
+

Scripting pages

+ Home +
+ +
+
+

Since jQuery Mobile uses an Ajax-powered navigation system, there are a few helpful things to know when writing scripts that manipulate your content. You can explore the mobile API in more detail by reading up on global configuration options, events, and methods or dig into the technical details of the Ajax navigation model.

+ +

Scripts & styles in the head

+ +

When the user clicks a link in a jQuery Mobile-driven site, the default behavior of the navigation system is to use that link's href to formulate an Ajax request (instead of allowing the browser's default link behavior of requesting that href with full page load). When that Ajax request goes out, the framework will receive its entire text content, but it will only inject the contents of the response's body element (or more specifically the data-role="page" element, if it's provided), meaning nothing in the head of the page will be used (with the exception of the page title, which is fetched specifically).

+ +

This means that any scripts and styles referenced the head of a page won't have any effect when a page is loaded via Ajax, but they will execute if the page is requested normally via HTTP. When scripting jQuery Mobile sites, both scenarios need to be considered. The reason that the head of a page is ignored when requested via Ajax is that the potential of re-executing the same JavaScript is very high (it's common to reference the same scripts in every page of a site). Due to the complexity of attempting to work around that issue, we leave the task of executing page-specific scripts to the developer, and assume head scripts are only expected to execute once per browsing session.

+ +

The simplest approach when building a jQuery Mobile site is to reference the same set of stylesheets and scripts in the head of every page. If you need to load in specific scripts or styles for a particular page, we recommend binding logic to the pagecreate event (details below) to run necessary code when a specific page is created (which can be determined by its id attribute, or a number of other ways). Following this approach will ensure that the code executes if the page is loaded directly or is pulled in and shown via Ajax.

+ +

Another approach for page-specific scripting would be to include scripts at the end of the body element. If you include your custom scripting this way, be aware that these scripts will execute when that page is loaded via Ajax or regular HTTP, so if these scripts are the same on every page, you'll likely run into problems. If you're including scripts this way, we'd recommend enclosing your page content in a data-role="page" element, and placing scripts that are referenced on every page outside of that element. Scripts that are unique to that page can be placed in that element, to ensure that they execute when the page is fetched via Ajax.

+ +

pagecreate = DOM ready

+ +

One of the first things people learn in jQuery is to use the $(document).ready() function for executing DOM-specific code as soon as the DOM is ready (which often occurs long before the onload event). However, in jQuery Mobile site and apps, pages are requested and injected into the same DOM as the user navigates, so the DOM ready event is not as useful, as it only executes for the first page. To execute code whenever a new page is loaded and created in jQuery Mobile, you can bind to the pagecreate event.

+ +

The pagecreate event is triggered on a page when it is initialized, right after initialization occurs. Most of jQuery Mobile's official widgets auto-initialize themselves based on this event, and you can set up your code to do the same.

+

+$( document ).delegate("#aboutPage", "pagecreate", function() {
+  alert('A page with an ID of "aboutPage" was just created by jQuery Mobile!');
+});
+
+ +

If you'd like to manipulate a page's contents before the pagecreate event fires and widgets are auto-initialized, you can instead bind to the pagebeforecreate event:

+ +

+$( document ).delegate("#aboutPage", "pagebeforecreate", function() {
+  alert('A page with an ID of "aboutPage" is about to be created by jQuery Mobile!');
+});
+
+ +

Changing pages

+

If you want to change the current active page with JavaScript, you can use the changePage method. There are a lot of methods and properties that you can set when changing pages, but here are two simple examples:

+

+//transition to the "about us" page with a slideup transition 			
+$.mobile.changePage( "about/us.html", { transition: "slideup"} );	
+
+//transition to the "search results" page, using data from a form with an ID of "search"" 		
+$.mobile.changePage( "searchresults.php", {
+	type: "post", 
+	data: $("form#search").serialize()
+});		
+
+ +

Loading pages

+

To load an external page, enhance its content, and insert it into the DOM, use the loadPage method. There are a lot of methods and properties that you can set when loading pages, but here is a simple example:

+

+//load the "about us" page into the DOM			
+$.mobile.loadPage( "about/us.html" );	
+
+ +

Enhancing new markup

+

The page plugin dispatches a “pagecreate” event, which most widgets use to auto-initialize themselves. As long as a widget plugin script is referenced, it will automatically enhance any instances of the widgets it finds on the page.

+

However, if you generate new markup client-side or load in content via Ajax and inject it into a page, you can trigger the create event to handle the auto-initialization for all the plugins contained within the new markup. This can be triggered on any element (even the page div itself), saving you the task of manually initializing each plugin (listview button, select, etc.).

+

For example, if a block of HTML markup (say a login form) was loaded in through Ajax, trigger the create event to automatically transform all the widgets it contains (inputs and buttons in this case) into the enhanced versions. The code for this scenario would be:

+
$( ...new markup that contains widgets... ).appendTo( ".ui-page" ).trigger( "create" );
+
+ +

Create vs. refresh: An important distinction

+

Note that there is an important difference between the create event and refresh method that some widgets have. The create event is suited for enhancing raw markup that contains one or more widgets. The refresh method should be used on existing (already enhanced) widgets that have been manipulated programmatically and need the UI be updated to match.

+ +

For example, if you had a page where you dynamically appended a new unordered list with data-role=listview attribute after page creation, triggering create on a parent element of that list would transform it into a listview styled widget. If more list items were then programmatically added, calling the listview’s refresh method would update just those new list items to the enhanced state and leave the existing list items untouched.

+ + +

Scrolling to a position within a page

+

Since we use the URL hash to preserve Back button behavior, using page anchors to jump down to a position on the page isn't supported by using the traditional anchor link (#foo). Use the silentScroll method to scroll to a particular Y position without triggering scroll event listeners. You can pass in a yPos arguments to scroll to that Y location. For example:

+

+//scroll to Y 300px 			
+$.mobile.silentScroll(300);	
+
+ +

Binding to mouse and touch events

+

One inportant consideration in mobile is handling mouse and touch events. These events differ significantly across mobile platforms, but the common denominator is that click events will work everywhere, but usually after a significant delay of 500-700ms. This delay is necessary for the browser to wait for double tap, scroll and extended hold tap events to potentially occur. To avoid this delay, it's possible to bind to touch events (ex. touchstart) but the issue with this approach is that some mobile platforms (WP7, Blackberry) don't support touch. To compound this issue, some platforms will emit both touch and mouse events so if you bind to both types, duplicate events will be fired for a single interaction.

+

Our solution is to create a set of virtual events that normalize mouse and touch events. This allows the developer to register listeners for the basic mouse events, such as mousedown, mousemove, mouseup, and click, and the plugin will take care of registering the correct listeners behind the scenes to invoke the listener at the fastest possible time for that device. This still retains the order of event firing in the traditional mouse environment, should multiple handlers be registered on the same element for different events. The virtual mouse system exposes the following virtual events to jQuery bind methods: vmouseover, vmousedown, vmousemove, vmouseup, vclick, and vmousecancel

+ + + +
+ + + +
+ + + +
+ + + diff --git a/docs/pages/page-template.html b/docs/pages/page-template.html index f7fc8ed0..36e4b0f5 100755 --- a/docs/pages/page-template.html +++ b/docs/pages/page-template.html @@ -1,27 +1,33 @@ - - - Page Title - - - + + + + + Single page template + + + +
-

Page Title

+

Single page

-

Page content goes here.

+

This is a single page boilerplate template that you can copy to build you first jQuery Mobile page. Each link or form from here will pull a new page in via Ajax to support the animated page transitions.

+

Just view the source and copy the code to get started. All the CSS and JS is linked to the jQuery CDN versions so this is super easy to set up. Remember to include a meta viewport tag in the head to set the zoom level.

+

This template is standard HTML document with a single "page" container inside, unlike a multi-page template that has multiple pages within it. We strongly recommend building your site or app as a series of separate pages like this because it's cleaner, more lightweight and works better without JavaScript.

-

Page Footer

+

Footer content

+
diff --git a/docs/pages/page-titles.html b/docs/pages/page-titles.html new file mode 100644 index 00000000..4bc9bcce --- /dev/null +++ b/docs/pages/page-titles.html @@ -0,0 +1,79 @@ + + + + + + jQuery Mobile Docs - Page titles + + + + + + + + + +
+ +
+

Page titles

+ Home +
+ +
+
+ +

Titles in Ajax navigation

+ +

When you load the first page of a jQuery Mobile based site, then click a link or submit a form, the jQuery Mobile uses Ajax to pull in the content of the requested page. Having both pages in the DOM is essential to enable the animated page transitions, but one downside of this approach is that the page title is always that of the first page, not the subsequent page you’re viewing.

+

To remedy this, jQuery Mobile automatically parses the title of the page pulled via Ajax and changes the title attribute of the parent document to match.

+ +

Titles in multi-page templates

+ +

On multi-page documents, we follow a similiar convention, but since all the page share a common title, we have a data-title attribute that can be added to each page container within a multi-page template to manually define a title. The title of the HTML document will be automatically updated to match the data-title of the page currently in view.

+ +

+<div data-role="page" id="foo" data-title="Page Foo">
+
+</div><!-- /page -->
+
+ + +
+ + + +
+ + + +
+ + + diff --git a/docs/pages/page-transitions.html b/docs/pages/page-transitions.html new file mode 100755 index 00000000..ef34ce39 --- /dev/null +++ b/docs/pages/page-transitions.html @@ -0,0 +1,107 @@ + + + + + + jQuery Mobile Docs - Transitions + + + + + + + + + +
+ +
+

Transitions

+ Home +
+ +
+
+

Page transitions

+ +

The jQuery Mobile framework includes a set of six CSS-based transition effects that can be applied to any object or page change event, which apply the chosen transition when navigating to a new page and the reverse transition for the Back button. By default, the framework applies the right to left slide transition.

+ +

To set a custom transition effect, add the data-transition attribute to the link. Possible values include:

+ + +<a href="index.html" data-transition="pop">I'll pop</a> + + +

+ slide + slideup + slidedown + pop + fade + flip * +

+ +

NOTE: The flip transition isn't rendered correctly on most versions of Android because it lacks 3D CSS transform capabilities. Unfortunately, instead of ignoring the flip, Android makes the page "cartwheel" away by rotating instead of flipping. We recommend using this transition sparingly until support improves.

+

In addition, you can also force a "backwards" transition by specifying data-direction="reverse" on your link. Note: (this was formerly data-back="true", which will remain supported until 1.0)

+ +
+

Transitions from jQtouch (with small modifications): Built by David Kaneda and maintained by Jonathan Stark.

+
+ + +
+ + + +
+ + + +
+ + + + + +
+ +
+

Ta-da!

+
+ +
+

That was an animated page transition effect that we added with a data-transition attribute on the link.

+

Since it uses CSS transforms, this should be hardware accelerated on many mobile devices.

+

What do you think?

+ I like it +
+
+ + + diff --git a/docs/pages/pages-themes.html b/docs/pages/pages-themes.html index 2d627a47..5ef7b19c 100755 --- a/docs/pages/pages-themes.html +++ b/docs/pages/pages-themes.html @@ -1,28 +1,32 @@ - - jQuery Mobile Docs - Pages - + + + jQuery Mobile Docs - Theming Pages + - - + + + + -
+
-
+

Theming pages

+ Home
- +

Page Theming

-

jQuery Mobile has a rich theming system that gives you full control of how pages are styled. There is detailed theming documentation within each page widget, but let's look at a few high-level examples of how theming is applied.

+

jQuery Mobile has a rich theming system that gives you full control of how pages are styled. There is detailed theming documentation within each page widget, but let's look at a few high-level examples of how theming is applied.

-

The data-theme attribute can be applied to the header and footer containers to apply any of the lettered theme color swatches. While the data-theme attribute could be added to the content container, we recommend adding it instead to div or container that has been assigned the data-role="page" attribute to ensure that the background color is applied to the full page.

+

The data-theme attribute can be applied to the header and footer containers to apply any of the lettered theme color swatches. While the data-theme attribute could be added to the content container, we recommend adding it instead to div or container that has been assigned the data-role="page" attribute to ensure that the background color is applied to the full page.

The default Theme mixes styles from multiple swatches to create visual texture and present the various elements in optimal contrast to one another:

@@ -43,7 +47,7 @@
Cache settings: - + @@ -52,7 +56,7 @@
@@ -79,7 +83,7 @@
@@ -104,7 +108,7 @@
@@ -128,7 +132,7 @@
@@ -152,7 +156,7 @@
@@ -162,7 +166,7 @@

Header

-

This is content color swatch "D" and a preview of a link.

+

This is content color swatch "E" and a preview of a link.

@@ -176,13 +180,46 @@
-
-
+
- - \ No newline at end of file + + +
+ + + +
+ + + diff --git a/docs/pages/transition-success.html b/docs/pages/transition-success.html index de2b1e7e..e23f80b9 100644 --- a/docs/pages/transition-success.html +++ b/docs/pages/transition-success.html @@ -1,25 +1,29 @@ - + + jQuery Mobile Framework - Dialog Example - + - + + + + -
+

Ta-da!

-
+

That was an animated page transition effect that we added with a data-transition attribute on the link.

Since it uses CSS transforms, this should be hardware accelerated on many mobile devices.

What do you think?

- I like it + I like it
diff --git a/docs/toolbars/api-bars.html b/docs/toolbars/api-bars.html index 18459b34..4703ae55 100755 --- a/docs/toolbars/api-bars.html +++ b/docs/toolbars/api-bars.html @@ -1,17 +1,21 @@ - - jQuery Mobile Docs - Toolbars - + + + jQuery Mobile Docs - Toolbars API + - + + + + -
+
-
+

Toolbar API

diff --git a/docs/toolbars/bars-fixed.html b/docs/toolbars/bars-fixed.html index 84d440d5..4c8094fa 100755 --- a/docs/toolbars/bars-fixed.html +++ b/docs/toolbars/bars-fixed.html @@ -1,129 +1,167 @@ - - jQuery Mobile Framework - Toolbars demo - + + + jQuery Mobile Framework - Fixed Toolbars + - + + + + -
- -
+
+ +
+

Fixed toolbars

+ Home
-

Fixed toolbars will re-appear after you scroll

-

This is a demo of the "fixed" headers and footers used in the jQuery Mobile framework. The page content flows naturally, allowing us to take advantage of native scrolling instead of a scripting a faux-scrolling workaround. The header and footer divs are right in the flow of the document, but whenever they are out of view, you can tap the screen to make them appear. Tapping again or scrolling the page will cause them to appear back in the flow of the page (at the top and bottom).

-

To set this behavior on a header or footer, add the data-position="fixed" attribute to the toolbar container.

- -

-<div data-role="header" data-position="fixed">
-	<h1>Fixed toolbars</h1>
-</div>
-
- - +
+

Fixed toolbars

+

This is a demo of the "fixed" headers and footers used in the jQuery Mobile framework. The page content flows naturally, allowing us to take advantage of native scrolling instead of a scripting a faux-scrolling workaround. The header and footer divs are right in the flow of the document, but whenever they are out of view the framework will dynamically re-position them into view if the browser supports this feature, otherwise they will simply stay inline.

+

To enable this behavior on a header or footer, add the data-position="fixed" attribute to the toolbar container.

+

+<div data-role="header" data-position="fixed">
+   Header content goes here
+</div><!-- end header -->
+		
-

Here is some text to make the page very long

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

- -

And an inset list

- - - -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

- -
+

Tap to toggle visibility

+

To toggle the visibility of fixed toolbars, tap the screen. For example, if the fixed toolbars are visible, tap the screen to hide the toolbars and take full advantage of the screen real estate for content. Tapping again will bring the toolbars back into view.

+

It's possible to turn off the the tap to toggle visibility behavior like this: $.mobile.fixedToolbars.setTouchToggleEnabled(false). -

-

Fixed Footer

-
+

Updating toolbar positioning

+

If the height of the page changes, either through dynamic injection of markup, or by widgets that hide or collapse content, it can throw off the dynamic positioning of the toolbars. To manually tell the toolbars to re-position themselves then fade in, use $.mobile.fixedToolbars.show();. To have them appear immediately without the fade, use $.mobile.fixedToolbars.show(true);.

+ +

There is also a updatelayout event that can be used to trigger the toolbars to re-position. Developers who are building dynamic applications that inject content into the current page can also manually trigger this updatelayout event to ensure components on the page update in response to the new content that was just added. This event is used internally in the collapsible and listview filter plugins and is powerful because it's not toolbar-specific -- any widget can be built to listen for the updatelayout event to update the widget in response.

+ + +

Known limitations

+ +

jQuery Mobile uses dynamically re-positioned toolbars for the fixed header effect because very few mobile browsers support the position:fixed CSS property. Although our fixed toolbar feature works fairly well, there are a number of technical limitations that can cause the toolbars to appear to scroll with the page. Most of these rendering issues are due to the fact many mobile platforms (iOS, Android, etc.) essentially take a static screenshot of the page and display this image during scrolling instead of the actual rendered HTML. This improves scrolling performance, but when scrolling happens quickly, the toolbars will be "burned" into the page screenshot before our script can hide them so they appear to scroll with the page. We have optimized this as much as we possibly can, but there are going to be situations where fixed toolbars won't work perfectly due to browser limitations so this si important to note when considering whether to use this feature.

+ +

True fixed toolbars: touchOverflowEnabled

+ +

In order to achieve true fixed toolbars, a browser needs to either support position:fixed or overflow:auto. Fortunately, this support is coming to mobile platforms so we can achieve, this with web standards. In jQuery Mobile, we have added a global feature called touchOverflowEnabled that leverages the overflow:auto CSS property on supported platforms like iOS5. When enabled, the framework wraps each page in a container with it's own internal scrolling. This allows up to position the toolbars outside the scrolling body so they truly stay fixed in place at all times. Learn more about this feature on the global options page or demo this feature (currently iOS5 only, other browsers will fall back to dynamically re-positioned fixed toolbars).

+ +
+ + +

The rest of the page is just sample content to make the page very long

+ +

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

+ +

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

+ + +

And an inset list

+ + + +
+

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

+ +

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

+ + +

Embedded form

+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
-
+
+ + +
- - \ No newline at end of file + +
+
+
+
+
+
+ + +

A bit more text

+ +

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.Donec non enim in turpis pulvinar facilisis. Ut felis.

+ + + + +
+ + + +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/toolbars/bars-fullscreen.html b/docs/toolbars/bars-fullscreen.html index b02e6145..b032d947 100755 --- a/docs/toolbars/bars-fullscreen.html +++ b/docs/toolbars/bars-fullscreen.html @@ -1,39 +1,67 @@ - - jQuery Mobile Framework - Fixed toolbars variation - + + + jQuery Mobile Framework - Fullscreen Fixed toolbars + - + + + + -
+
-
+

Fullscreen fixed header

+ Home
- Photo Run +
+ Photo Run

This page demonstrates the "fullscreen" toolbar mode. This toolbar treatment is used in special cases where you want the content to fill the whole screen, and you want the header and footer toolbars to appear and disappear when the page is clicked responsively — a common scenario for photo, image or video viewers.

-

To enable this toolbar feature type, you apply a data-fullscreen="true" attribute to the div contain that has the attribute data-role="page", and the data-position="fixed" attribute to both the header and footer div elements.

+

To enable this toolbar feature type, you apply a data-fullscreen="true" attribute to the div contain that has the attribute data-role="page", and the data-position="fixed" attribute to both the header and footer div elements.

Keep in mind that the toolbars in this mode will sit over page content, so not all content will be accessible with the toolbars open, just as shown in this demo.

- - -
- -
-

Fullscreen fixed footer

-
+
-
+
- - \ No newline at end of file + +
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/toolbars/bars-themes.html b/docs/toolbars/bars-themes.html old mode 100755 new mode 100644 index fbdb19de..cdedf9db --- a/docs/toolbars/bars-themes.html +++ b/docs/toolbars/bars-themes.html @@ -1,32 +1,37 @@ - - jQuery Mobile Framework - Toolbars demo - + + + jQuery Mobile Framework - Theming Toolbars + - + + + + -
- -
+
+ +

Bar theming

+ Home
- +

Both the header and footer bars will be styled by default with the theme's "a" color swatch (black in the default theme) because these bars are typically primary in the visual hierarchy of a page.

Theming headers and footers

-

To set the header or footer bars to a different color in your theme, add the data-theme attribute and specify the letter of the theme swatch (a, b, c, etc.). For example, this will set the bar to swatch "b" (blue in the default theme):

+

To set the header or footer bars to a different color in your theme, add the data-theme attribute and specify the letter of the theme swatch (a, b, c, etc.). For example, this will set the bar to swatch "b" (blue in the default theme):

-<div data-role="header" data-theme="b"> 
+<div data-role="header" data-theme="b"> 
 	<h1>Page Title</h1> 
 </div> 
 
@@ -35,11 +40,11 @@

Theming buttons in toolbars

-

Any link added inside the header block will be automatically styled as a button that matches the color of the bar's theme swatch. To make a button stand out as a primary call to action, the data-theme attribute can be used to specify a contrasting button color from a different theme swatch. For example, if we set the header to theme "c" (light gray), both buttons would be styled as the "c" button by default. If we wanted the Save button to visually pop, we can override the color by setting the data-theme attribute to "b" (blue in our default theme) on the Save button's anchor.

+

Any link added inside the header block will be automatically styled as a button that matches the color of the bar's theme swatch. To make a button stand out as a primary call to action, the data-theme attribute can be used to specify a contrasting button color from a different theme swatch. For example, if we set the header to theme "c" (light gray), both buttons would be styled as the "c" button by default. If we wanted the Save button to visually pop, we can override the color by setting the data-theme attribute to "b" (blue in our default theme) on the Save button's anchor.

-<a href="add-user.php" data-theme="b">Save</a> 
+<a href="add-user.php" data-theme="b">Save</a> 
 
@@ -48,7 +53,7 @@

Theme variations

This is a demo of the variation that can be achieved by tweaking the theme swatches and buttons inside the headers and footers.

Headers

-
+

Bar theme "a"

@@ -94,26 +99,78 @@ Save
-
-

Footers

-
- +

Footers

+

These are examples of a footer with link buttons inside. Note that footers do not have the same prescriptive markup contentions as headers with button slots so use layout grids or custom styles to achieve the design you want.

+ -
-
- left - right - up - down - plus - minus - delete -
+
+ left + right + up + down
+ +
+ left + right + up + down +
+ +
+ left + right + up + down +
+ +
+ left + right + up + down +
+ +
+ left + right + up + down +
-

-
- - \ No newline at end of file +
+ + + +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/toolbars/docs-bars.html b/docs/toolbars/docs-bars.html index 41dbe07c..e318ac87 100755 --- a/docs/toolbars/docs-bars.html +++ b/docs/toolbars/docs-bars.html @@ -1,27 +1,32 @@ - - jQuery Mobile Docs - Toolbars - + + + jQuery Mobile Docs - Toolbar Basics + - + + + + -
+
-
+

Toolbar basics

+ Home
- +

Toolbar types

In jQuery Mobile, there are two standard types of toolbars: Headers and Footers.

-
  • The Header bar serves as the page title, is usually the first element inside each mobile page, and typically contains a page title and up to two buttons.
  • +
    • The Header bar serves as the page title, is usually the first element inside each mobile page, and typically contains a page title and up to two buttons.
    • The Footer bar is usually the last element inside each mobile page, and tends to be more freeform than the header in terms of content and functionality, but typically contains a combination of text and buttons.
    @@ -40,10 +45,38 @@

    A "fullscreen" position mode works just like the fixed mode except that the toolbars aren't shown at the top and bottom of the page and only appear when the page is clicked. This is useful for immersive apps like photo or video viewers where you want the content to full when whole screen and toolbars can be summoned to appear by tapping the screen. Keep in mind that the toolbars in this mode will sit over page content so this is best used for specific situations.

    - - -
-
- - \ No newline at end of file +
+ + + +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/toolbars/docs-footers.html b/docs/toolbars/docs-footers.html index b2d2b77d..6e413f4c 100755 --- a/docs/toolbars/docs-footers.html +++ b/docs/toolbars/docs-footers.html @@ -1,29 +1,34 @@ - - jQuery Mobile Docs - Toolbars - + + + jQuery Mobile Docs - Footer Configuration + - + + + + -
+
-
+

Footer configuration

+ Home
- +

Footer bar structure

-

The footer bar has the same basic structure as the header except it uses the data-role attribute value of footer.

+

The footer bar has the same basic structure as the header except it uses the data-role attribute value of footer.

-<div data-role="footer"> 
+<div data-role="footer"> 
 	<h4>Footer content</h4> 
 </div> 
 
@@ -36,7 +41,7 @@

Footer content

-

The page footer is very similar to the header in terms of options and configuration. The primary differences are that the footer is designed to be less structured than the header to allow for more flexibility, so the framework doesn't automatically place buttons to the left or right based on source order as it does in the header.

+

The page footer is very similar to the header in terms of options and configuration. The primary differences are that the footer is designed to be less structured than the header to allow for more flexibility, so the framework doesn't automatically place buttons to the left or right based on source order as it does in the header. Since footers do not have the same prescriptive markup contentions as headers, use layout grids or custom styles to achieve the design you want in a footer.

@@ -66,7 +71,7 @@ Down
-

To group buttons together into a button set, wrap the links in a wrapper with data-role="controlgroup" and data-type="horizontal" attributes.

+

To group buttons together into a button set, wrap the links in a wrapper with data-role="controlgroup" and data-type="horizontal" attributes.

<div data-role="controlgroup" data-type="horizontal"> @@ -111,11 +116,38 @@ - +
-
-
+
- - \ No newline at end of file + +
+ +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/toolbars/docs-headers.html b/docs/toolbars/docs-headers.html old mode 100755 new mode 100644 index bf3e66bf..a91a66a7 --- a/docs/toolbars/docs-headers.html +++ b/docs/toolbars/docs-headers.html @@ -1,30 +1,35 @@ - - jQuery Mobile Docs - Toolbars - + + + jQuery Mobile Docs - Header Bars + - + + + + -
+
-
+

Header bars

+ Home
- +

Header structure

-

The header is an toolbar at the top of the page that usually contains the page title text and optional buttons positioned to the the left and/or right of the title for navigation or actions.

+

The header is a toolbar at the top of the page that usually contains the page title text and optional buttons positioned to the the left and/or right of the title for navigation or actions.

The title text is normally an H1 heading element but it's possible to use any heading level (H1-H6) to allow for semantic flexibility. For example, a page containing multiple mobile 'pages' may use a H1 element on the home 'page' and a H2 element on the secondary pages. All heading levels are styled identically by default to maintain visual consistency.

-<div data-role="header"> 
+<div data-role="header"> 
 	<h1>Page Title</h1> 
 </div> 
 
@@ -39,12 +44,17 @@

Page title

-

See that "back" button? The framework automatically generates a "back" button on every page, to simplify the process of including this common navigation element. To prevent the back button from being added to a header, either add your own button to the left slot (see below) or, add this attribute: data-nobackbtn="true" to the header container.

Adding buttons

+ + + +

In the standard header configuration, there are slots for buttons on either side of the text heading. Each button is typically an anchor element, but any valid button markup will work. To save space, buttons in toolbars are set to inline styling so the button is only as wide as the text and icons it contains.

+ +

Default button positioning

@@ -65,7 +75,7 @@

Edit Contact

Save
-

Buttons automatically adopt the swatch color of the bar they sit in, so a link in a header bar with the "a" color will also be styled as "a" colored buttons. It's simple to make a button visually stand out — here, we add the data-theme attribute and set the color swatch for the button to "b" to make the "Save" button pop.

+

Buttons automatically adopt the swatch color of the bar they sit in, so a link in a header bar with the "a" color will also be styled as "a" colored buttons. It's simple to make a button visually stand out — here, we add the data-theme attribute and set the color swatch for the button to "b" to make the "Save" button pop.

			
 <div data-role="header" data-position="inline">
@@ -85,38 +95,94 @@
 			

Controlling button position with classes

The button position can also be controlled by adding classes to the button anchors, rather than relying on source order. This is especially useful if you only want a button in the right slot. To specify the button position, add the class of ui-btn-left or ui-btn-right to the anchor.

- -

In this example, we're adding only a single button to the right slot so the data-nobackbtn="true" needs to be added to the header container to suppress the automatic Back button behavior and the button needs the ui-btn-right class on the link.

+

-<div data-role="header" data-position="inline" data-nobackbtn="true">
+<div data-role="header" data-position="inline" 
 	<h1>Page Title</h1>
 	<a href="index.html" data-icon="gear" class="ui-btn-right">Options</a>
 </div>
 
-
+

Page Title

Options
+ +

Adding Back buttons

+ +

jQuery Mobile has a feature to automatically create and append "back" buttons to any header, though it is disabled by default. This is primarily useful in chromeless installed applications, such as those running in a native app web view. The framework automatically generates a "back" button on a header when the page plugin's addBackBtn option is true. This can also be set via markup if the page div has a data-add-back-btn="true" attribute.

+ + +

If you use the attribute data-rel="back" on an anchor, any clicks on that anchor will mimic the back button, going back one history entry and ignoring the anchor's default href. This is particularly useful when linking back to a named page, such as a link that says "home", or when generating "back" buttons with JavaScript, such as a button to close a dialog. When using this feature in your source markup, be sure to provide a meaningful href that actually points to the URL of the referring page (this will allow the feature to work for users in C-Grade browsers. Also, please keep in mind that if you just want a reverse transition without actually going back in history, you should use the data-direction="reverse" attribute instead.

+

Customizing the back button text

-

If you'd like to configure the back button text, you can either use the data-back-btn-text="previous" attribute on your page element, or set it programmatically via the page plugin's options: $.mobile.page.prototype.options.backBtnText = "previous";. If you're doing this programmatically, set this option inside the mobileinit event handler.

- - +

If you'd like to configure the back button text, you can either use the data-back-btn-text="previous" attribute on your page element, or set it programmatically via the page plugin's options: $.mobile.page.prototype.options.backBtnText = "previous";. +

Default back button style

+

If you'd like to configure the back button role-theme, you can use: $.mobile.page.prototype.options.backBtnTheme = "a";. + If you're doing this programmatically, set this option inside the mobileinit event handler.

Custom header configurations

If you need to to create a header that doesn't follow the default configuration, simply wrap your custom styled markup in a container div inside the header container and the plugin won't apply the automatic button logic so you can write custom styles for laying out the content in your header.

+

It's also possible to create custom bars without using the header data-role at all. For example, start with any container and add the ui-bar class to apply standard bar padding and add the ui-bar-b class to assign the bar swatch styles from your theme (the "b" can be any swatch letter).

+ +

+<div class="ui-bar ui-bar-b">
+	<h3>I'm just a div with bar classes and a <a href="#" data-role="button">Button</a></h3>
+</div>
+			
-
-
+

This will produce this bar:

+
+

I'm just a div with bar classes and a Button

+
+ +

By writing some simple styles, it's easy to build little message bars like this:

+ +
+

This is an alert message with dismiss button.

+ +

And here's some additional text in a paragraph.

+
+
- - \ No newline at end of file + + +
+ + + +
+ + + \ No newline at end of file diff --git a/docs/toolbars/docs-navbar.html b/docs/toolbars/docs-navbar.html index df89ac6f..77b2e1ef 100755 --- a/docs/toolbars/docs-navbar.html +++ b/docs/toolbars/docs-navbar.html @@ -1,53 +1,55 @@ - - jQuery Mobile Docs - Toolbars - + + + jQuery Mobile Docs - Navbar + - + + + + -
+
-
+

Navbar

+ Home
- +

Simple navbar

-

jQuery Mobile has a very basic navbar widget that is useful for providing up to 5 buttons with optional icons in a bar , typically within a header or footer.

- -

A navbar is coded as an unordered list of links wrapped in a container element that has the data-role="navbar" attribute. To set one of links to the active (selected) state, add class="ui-btn-active" to the anchor. In this example, we have a two-button navbar in the footer with the "One" item set to active:

+

jQuery Mobile has a very basic navbar widget that is useful for providing up to 5 buttons with optional icons in a bar, typically within a header or footer. There is also a persistent nav bar variation that works more like a tab bar that stays fixed as you navigate across pages.

+

A navbar is coded as an unordered list of links wrapped in a container element that has the data-role="navbar" attribute. To set one of links to the active (selected) state, add class="ui-btn-active" to the anchor. In this example, we have a two-button navbar in the footer with the "One" item set to active:


-<div data-role="footer">
-	<div data-role="navbar">
-		<ul>
-			<li><a href="a.html" class="ui-btn-active">One</a></li>
-			<li><a href="b.html">Two</a></li>
-		</ul>
-	</div><!-- /navbar -->
-</div><!-- /footer -->
+<div data-role="navbar">
+	<ul>
+		<li><a href="a.html" class="ui-btn-active">One</a></li>
+		<li><a href="b.html">Two</a></li>
+	</ul>
+</div><!-- /navbar -->
 

The navbar items are set to divide the space evenly so in this case, each button is 1/2 the width of the browser window:

-
+ -
+

Adding a third item will automatically make each button 1/3 the width of the browser window:

-
+
-
+

Adding a fourth more item will automatically make each button 1/4 the width of the browser window:

-
+
-
+

The navbar maxes out with 5 items, each 1/5 the width of the browser window:

-
+
-
+

If more than 5 items are added, the navbar will simply wrap to multiple lines:

-
  • One
  • @@ -101,7 +102,14 @@
  • Ten
-
+ +

As a fallback, navbars with 1 item will simply render as 100%.

+ +
+ +

Navbars in headers

@@ -120,9 +128,33 @@
+

Navbars in footers

+ +

If you want to add a navbar to the bottom of the page so it acts more like a tab bar, simply wrap the navbar in a container with a data-role="footer"

+

+<div data-role="footer">		
+	<div data-role="navbar">
+		<ul>
+			<li><a href="#">One</a></li>
+			<li><a href="#">Two</a></li>
+			<li><a href="#">Three</a></li>
+		</ul>
+	</div><!-- /navbar -->
+</div><!-- /footer -->
+
+
+
+ +
+
+

Icons in navbars

-

Icons can be added to navbar items by adding the data-icon attribute specifying a standard mobile icon to each anchor.

+

Icons can be added to navbar items by adding the data-icon attribute specifying a standard mobile icon to each anchor. By default icons are added above the text (data-iconpos="top"). The following examples add icons to a navbar in a footer.

@@ -134,14 +166,41 @@
-

Icons can be stacked above the labels by adding the data-iconpos="top" attribute to each anchor.

+

The icon position is set on the navbar container instead of for individual links within for visual consistency. For example, to place the icons below the labels, add the data-iconpos="bottom" attribute to navbar container.

+

+<div data-role="navbar" data-iconpos="bottom">
+
+

This will result in a bottom icon alignment:

+
+
+ +
+
+ +

The icon position can be set to data-iconpos="left":

-
+
+
+
+ +

Or the icon position can be set to data-iconpos="right":

+ +
+
+
@@ -151,9 +210,9 @@

You can add any of the popular icon libraries like Glyphish to achieve the iOS style tab tab that has large icons stacked on top of text labels. All that is required is a bit of custom styles to link to the icons and position them in the navbar. Here is an example using Glyphish icons and custom styles (view page source for styles) in our navbar:

- @@ -49,7 +51,7 @@
-
+

Edit conversions

Done
diff --git a/experiments/google-maps/index.html b/experiments/google-maps/index.html index 86cac950..7425b941 100644 --- a/experiments/google-maps/index.html +++ b/experiments/google-maps/index.html @@ -1,9 +1,11 @@ - + + Main Page - + + diff --git a/experiments/google-maps/map.html b/experiments/google-maps/map.html index c2ee622c..aca2141b 100644 --- a/experiments/google-maps/map.html +++ b/experiments/google-maps/map.html @@ -1,9 +1,11 @@ - + + Main Page - + + diff --git a/experiments/installedapp-assets/homeicon.png b/experiments/installedapp-assets/homeicon.png deleted file mode 100644 index bace30a4..00000000 Binary files a/experiments/installedapp-assets/homeicon.png and /dev/null differ diff --git a/experiments/installedapp-assets/startscreen.png b/experiments/installedapp-assets/startscreen.png deleted file mode 100644 index bb85deb8..00000000 Binary files a/experiments/installedapp-assets/startscreen.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/01-refresh.png b/experiments/navbar-glyphish/glyphish-icons/01-refresh.png deleted file mode 100644 index eeef8968..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/01-refresh.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/02-redo.png b/experiments/navbar-glyphish/glyphish-icons/02-redo.png deleted file mode 100644 index 8503fea1..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/02-redo.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/03-loopback.png b/experiments/navbar-glyphish/glyphish-icons/03-loopback.png deleted file mode 100644 index 6984fc92..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/03-loopback.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/04-squiggle.png b/experiments/navbar-glyphish/glyphish-icons/04-squiggle.png deleted file mode 100644 index 5f7b2060..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/04-squiggle.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/05-shuffle.png b/experiments/navbar-glyphish/glyphish-icons/05-shuffle.png deleted file mode 100644 index 37fc1083..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/05-shuffle.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/06-magnifying-glass.png b/experiments/navbar-glyphish/glyphish-icons/06-magnifying-glass.png deleted file mode 100644 index 6708a567..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/06-magnifying-glass.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/07-map-marker.png b/experiments/navbar-glyphish/glyphish-icons/07-map-marker.png deleted file mode 100644 index c2d89a74..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/07-map-marker.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/08-chat.png b/experiments/navbar-glyphish/glyphish-icons/08-chat.png deleted file mode 100644 index ea403867..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/08-chat.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/09-chat2.png b/experiments/navbar-glyphish/glyphish-icons/09-chat2.png deleted file mode 100644 index 1ccc85f2..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/09-chat2.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/10-medical.png b/experiments/navbar-glyphish/glyphish-icons/10-medical.png deleted file mode 100644 index 588a9663..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/10-medical.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/100-coffee.png b/experiments/navbar-glyphish/glyphish-icons/100-coffee.png deleted file mode 100644 index 355cede1..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/100-coffee.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/101-gameplan.png b/experiments/navbar-glyphish/glyphish-icons/101-gameplan.png deleted file mode 100644 index 7dab1897..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/101-gameplan.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/102-walk.png b/experiments/navbar-glyphish/glyphish-icons/102-walk.png deleted file mode 100644 index 1aae0945..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/102-walk.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/103-map.png b/experiments/navbar-glyphish/glyphish-icons/103-map.png deleted file mode 100644 index 41e650a8..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/103-map.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/104-index-cards.png b/experiments/navbar-glyphish/glyphish-icons/104-index-cards.png deleted file mode 100644 index b37e59ec..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/104-index-cards.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/105-piano.png b/experiments/navbar-glyphish/glyphish-icons/105-piano.png deleted file mode 100644 index 4e724d4b..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/105-piano.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/106-sliders.png b/experiments/navbar-glyphish/glyphish-icons/106-sliders.png deleted file mode 100644 index 186d5636..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/106-sliders.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/107-widescreen.png b/experiments/navbar-glyphish/glyphish-icons/107-widescreen.png deleted file mode 100644 index c166a890..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/107-widescreen.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/108-badge.png b/experiments/navbar-glyphish/glyphish-icons/108-badge.png deleted file mode 100644 index 04e2e9ee..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/108-badge.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/109-chicken.png b/experiments/navbar-glyphish/glyphish-icons/109-chicken.png deleted file mode 100644 index 81eef00d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/109-chicken.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/11-clock.png b/experiments/navbar-glyphish/glyphish-icons/11-clock.png deleted file mode 100644 index 02429642..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/11-clock.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/110-bug.png b/experiments/navbar-glyphish/glyphish-icons/110-bug.png deleted file mode 100644 index a445a149..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/110-bug.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/111-user.png b/experiments/navbar-glyphish/glyphish-icons/111-user.png deleted file mode 100644 index e6922ff4..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/111-user.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/112-group.png b/experiments/navbar-glyphish/glyphish-icons/112-group.png deleted file mode 100644 index c18c7bdd..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/112-group.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/113-navigation.png b/experiments/navbar-glyphish/glyphish-icons/113-navigation.png deleted file mode 100644 index 01e39986..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/113-navigation.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/114-balloon.png b/experiments/navbar-glyphish/glyphish-icons/114-balloon.png deleted file mode 100644 index 610586f8..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/114-balloon.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/115-bow-and-arrow.png b/experiments/navbar-glyphish/glyphish-icons/115-bow-and-arrow.png deleted file mode 100644 index d484af64..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/115-bow-and-arrow.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/116-controller.png b/experiments/navbar-glyphish/glyphish-icons/116-controller.png deleted file mode 100644 index 9c9fd2c9..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/116-controller.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/117-todo.png b/experiments/navbar-glyphish/glyphish-icons/117-todo.png deleted file mode 100644 index e21a0114..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/117-todo.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/118-coathanger.png b/experiments/navbar-glyphish/glyphish-icons/118-coathanger.png deleted file mode 100644 index 4c692553..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/118-coathanger.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/119-piggybank.png b/experiments/navbar-glyphish/glyphish-icons/119-piggybank.png deleted file mode 100644 index d99ad070..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/119-piggybank.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/12-eye.png b/experiments/navbar-glyphish/glyphish-icons/12-eye.png deleted file mode 100644 index 3feca351..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/12-eye.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/120-headphones.png b/experiments/navbar-glyphish/glyphish-icons/120-headphones.png deleted file mode 100644 index 17d420c2..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/120-headphones.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/121-lanscape.png b/experiments/navbar-glyphish/glyphish-icons/121-lanscape.png deleted file mode 100644 index 31cedaae..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/121-lanscape.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/122-stats.png b/experiments/navbar-glyphish/glyphish-icons/122-stats.png deleted file mode 100644 index a2838b7d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/122-stats.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/123-id-card.png b/experiments/navbar-glyphish/glyphish-icons/123-id-card.png deleted file mode 100644 index 0206c8dc..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/123-id-card.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/124-bullhorn.png b/experiments/navbar-glyphish/glyphish-icons/124-bullhorn.png deleted file mode 100644 index 8319514f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/124-bullhorn.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/125-food.png b/experiments/navbar-glyphish/glyphish-icons/125-food.png deleted file mode 100644 index 757bd27f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/125-food.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/126-moon.png b/experiments/navbar-glyphish/glyphish-icons/126-moon.png deleted file mode 100644 index 1b60e80e..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/126-moon.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/127-sock.png b/experiments/navbar-glyphish/glyphish-icons/127-sock.png deleted file mode 100644 index ffe298c6..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/127-sock.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/128-bone.png b/experiments/navbar-glyphish/glyphish-icons/128-bone.png deleted file mode 100644 index 47509d6b..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/128-bone.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/129-golf.png b/experiments/navbar-glyphish/glyphish-icons/129-golf.png deleted file mode 100644 index a73d76a0..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/129-golf.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/13-target.png b/experiments/navbar-glyphish/glyphish-icons/13-target.png deleted file mode 100644 index b2166e23..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/13-target.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/130-dice.png b/experiments/navbar-glyphish/glyphish-icons/130-dice.png deleted file mode 100644 index 87fb48c3..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/130-dice.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/14-tag.png b/experiments/navbar-glyphish/glyphish-icons/14-tag.png deleted file mode 100644 index 4f59d32a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/14-tag.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/15-tags.png b/experiments/navbar-glyphish/glyphish-icons/15-tags.png deleted file mode 100644 index 4389addb..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/15-tags.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/16-line-chart.png b/experiments/navbar-glyphish/glyphish-icons/16-line-chart.png deleted file mode 100644 index 37b48e95..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/16-line-chart.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/17-bar-chart.png b/experiments/navbar-glyphish/glyphish-icons/17-bar-chart.png deleted file mode 100644 index f3fbebbc..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/17-bar-chart.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/18-envelope.png b/experiments/navbar-glyphish/glyphish-icons/18-envelope.png deleted file mode 100644 index 11a8d1cf..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/18-envelope.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/19-gear.png b/experiments/navbar-glyphish/glyphish-icons/19-gear.png deleted file mode 100644 index d54828aa..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/19-gear.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/20-gear2.png b/experiments/navbar-glyphish/glyphish-icons/20-gear2.png deleted file mode 100644 index b8180ded..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/20-gear2.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/21-skull.png b/experiments/navbar-glyphish/glyphish-icons/21-skull.png deleted file mode 100644 index aeee6935..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/21-skull.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/22-skull-n-crossbones.png b/experiments/navbar-glyphish/glyphish-icons/22-skull-n-crossbones.png deleted file mode 100644 index d304b8d5..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/22-skull-n-crossbones.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/23-bird.png b/experiments/navbar-glyphish/glyphish-icons/23-bird.png deleted file mode 100644 index 94510c89..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/23-bird.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/24-gift.png b/experiments/navbar-glyphish/glyphish-icons/24-gift.png deleted file mode 100644 index 4eecba6a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/24-gift.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/25-weather.png b/experiments/navbar-glyphish/glyphish-icons/25-weather.png deleted file mode 100644 index e8307675..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/25-weather.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/26-bandaid.png b/experiments/navbar-glyphish/glyphish-icons/26-bandaid.png deleted file mode 100644 index 7afcf613..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/26-bandaid.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/27-planet.png b/experiments/navbar-glyphish/glyphish-icons/27-planet.png deleted file mode 100644 index d173e32f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/27-planet.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/28-star.png b/experiments/navbar-glyphish/glyphish-icons/28-star.png deleted file mode 100644 index cfeb4d63..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/28-star.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/29-heart.png b/experiments/navbar-glyphish/glyphish-icons/29-heart.png deleted file mode 100644 index 8dfc25db..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/29-heart.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/30-key.png b/experiments/navbar-glyphish/glyphish-icons/30-key.png deleted file mode 100644 index 99a1f6bd..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/30-key.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/31-ipod.png b/experiments/navbar-glyphish/glyphish-icons/31-ipod.png deleted file mode 100644 index 3b4735ef..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/31-ipod.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/32-iphone.png b/experiments/navbar-glyphish/glyphish-icons/32-iphone.png deleted file mode 100644 index b69e41cf..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/32-iphone.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/33-cabinet.png b/experiments/navbar-glyphish/glyphish-icons/33-cabinet.png deleted file mode 100644 index 38a6cf31..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/33-cabinet.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/34-coffee.png b/experiments/navbar-glyphish/glyphish-icons/34-coffee.png deleted file mode 100644 index f9cd35f9..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/34-coffee.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/35-shopping-bag.png b/experiments/navbar-glyphish/glyphish-icons/35-shopping-bag.png deleted file mode 100644 index 6395d39b..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/35-shopping-bag.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/36-toolbox.png b/experiments/navbar-glyphish/glyphish-icons/36-toolbox.png deleted file mode 100644 index cb9b3b17..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/36-toolbox.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/37-suitcase.png b/experiments/navbar-glyphish/glyphish-icons/37-suitcase.png deleted file mode 100644 index 94bada6c..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/37-suitcase.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/38-airplane.png b/experiments/navbar-glyphish/glyphish-icons/38-airplane.png deleted file mode 100644 index 3781362f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/38-airplane.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/39-spraycan.png b/experiments/navbar-glyphish/glyphish-icons/39-spraycan.png deleted file mode 100644 index ecadc994..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/39-spraycan.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/40-inbox.png b/experiments/navbar-glyphish/glyphish-icons/40-inbox.png deleted file mode 100644 index ce352ac0..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/40-inbox.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/41-picture-frame.png b/experiments/navbar-glyphish/glyphish-icons/41-picture-frame.png deleted file mode 100644 index 53bbf009..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/41-picture-frame.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/42-photos.png b/experiments/navbar-glyphish/glyphish-icons/42-photos.png deleted file mode 100644 index 1b5edef6..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/42-photos.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/43-film-roll.png b/experiments/navbar-glyphish/glyphish-icons/43-film-roll.png deleted file mode 100644 index 9a6a8699..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/43-film-roll.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/44-shoebox.png b/experiments/navbar-glyphish/glyphish-icons/44-shoebox.png deleted file mode 100644 index 744cb30e..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/44-shoebox.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/45-movie1.png b/experiments/navbar-glyphish/glyphish-icons/45-movie1.png deleted file mode 100644 index bfbdcd3a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/45-movie1.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/46-movie2.png b/experiments/navbar-glyphish/glyphish-icons/46-movie2.png deleted file mode 100644 index 3953c83b..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/46-movie2.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/47-fuel.png b/experiments/navbar-glyphish/glyphish-icons/47-fuel.png deleted file mode 100644 index 82f83115..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/47-fuel.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/48-fork-and-knife.png b/experiments/navbar-glyphish/glyphish-icons/48-fork-and-knife.png deleted file mode 100644 index 056a2fee..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/48-fork-and-knife.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/49-battery.png b/experiments/navbar-glyphish/glyphish-icons/49-battery.png deleted file mode 100644 index 8e002f7a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/49-battery.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/50-beaker.png b/experiments/navbar-glyphish/glyphish-icons/50-beaker.png deleted file mode 100644 index 233cc97a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/50-beaker.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/51-outlet.png b/experiments/navbar-glyphish/glyphish-icons/51-outlet.png deleted file mode 100644 index a70d0f81..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/51-outlet.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/52-pinetree.png b/experiments/navbar-glyphish/glyphish-icons/52-pinetree.png deleted file mode 100644 index 175ba430..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/52-pinetree.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/53-house.png b/experiments/navbar-glyphish/glyphish-icons/53-house.png deleted file mode 100644 index f2cf9358..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/53-house.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/54-lock.png b/experiments/navbar-glyphish/glyphish-icons/54-lock.png deleted file mode 100644 index 5be31e5d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/54-lock.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/55-network.png b/experiments/navbar-glyphish/glyphish-icons/55-network.png deleted file mode 100644 index 6e30e188..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/55-network.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/56-cloud.png b/experiments/navbar-glyphish/glyphish-icons/56-cloud.png deleted file mode 100644 index 262a71d8..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/56-cloud.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/57-download.png b/experiments/navbar-glyphish/glyphish-icons/57-download.png deleted file mode 100644 index a4e5884f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/57-download.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/58-bookmark.png b/experiments/navbar-glyphish/glyphish-icons/58-bookmark.png deleted file mode 100644 index c9de84ca..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/58-bookmark.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/59-flag.png b/experiments/navbar-glyphish/glyphish-icons/59-flag.png deleted file mode 100644 index b9e353d2..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/59-flag.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/60-signpost.png b/experiments/navbar-glyphish/glyphish-icons/60-signpost.png deleted file mode 100644 index a10f6179..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/60-signpost.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/61-brightness.png b/experiments/navbar-glyphish/glyphish-icons/61-brightness.png deleted file mode 100644 index 34ccde2f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/61-brightness.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/62-contrast.png b/experiments/navbar-glyphish/glyphish-icons/62-contrast.png deleted file mode 100644 index 98ec0ad2..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/62-contrast.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/63-runner.png b/experiments/navbar-glyphish/glyphish-icons/63-runner.png deleted file mode 100644 index 539d2467..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/63-runner.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/64-zap.png b/experiments/navbar-glyphish/glyphish-icons/64-zap.png deleted file mode 100644 index 3aa6dc53..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/64-zap.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/65-note.png b/experiments/navbar-glyphish/glyphish-icons/65-note.png deleted file mode 100644 index 76d28f6d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/65-note.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/66-microphone.png b/experiments/navbar-glyphish/glyphish-icons/66-microphone.png deleted file mode 100644 index 82e4a66a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/66-microphone.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/67-tshirt.png b/experiments/navbar-glyphish/glyphish-icons/67-tshirt.png deleted file mode 100644 index d6fdef12..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/67-tshirt.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/68-paperclip.png b/experiments/navbar-glyphish/glyphish-icons/68-paperclip.png deleted file mode 100644 index 743fefec..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/68-paperclip.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/69-display.png b/experiments/navbar-glyphish/glyphish-icons/69-display.png deleted file mode 100644 index de42c635..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/69-display.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/70-tv.png b/experiments/navbar-glyphish/glyphish-icons/70-tv.png deleted file mode 100644 index af8ae94f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/70-tv.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/71-compass.png b/experiments/navbar-glyphish/glyphish-icons/71-compass.png deleted file mode 100644 index 0e4e9c70..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/71-compass.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/72-pin.png b/experiments/navbar-glyphish/glyphish-icons/72-pin.png deleted file mode 100644 index ff760ca2..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/72-pin.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/73-radar.png b/experiments/navbar-glyphish/glyphish-icons/73-radar.png deleted file mode 100644 index 6cb8955d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/73-radar.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/74-location.png b/experiments/navbar-glyphish/glyphish-icons/74-location.png deleted file mode 100644 index b26cb65d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/74-location.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/75-phone.png b/experiments/navbar-glyphish/glyphish-icons/75-phone.png deleted file mode 100644 index 7df31596..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/75-phone.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/76-baby.png b/experiments/navbar-glyphish/glyphish-icons/76-baby.png deleted file mode 100644 index ee9e1c12..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/76-baby.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/77-ekg.png b/experiments/navbar-glyphish/glyphish-icons/77-ekg.png deleted file mode 100644 index 649da854..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/77-ekg.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/78-stopwatch.png b/experiments/navbar-glyphish/glyphish-icons/78-stopwatch.png deleted file mode 100644 index d20d9cb7..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/78-stopwatch.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/79-medical-bag.png b/experiments/navbar-glyphish/glyphish-icons/79-medical-bag.png deleted file mode 100644 index 903f624f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/79-medical-bag.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/80-shopping-cart.png b/experiments/navbar-glyphish/glyphish-icons/80-shopping-cart.png deleted file mode 100644 index 16ed6a1d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/80-shopping-cart.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/81-dashboard.png b/experiments/navbar-glyphish/glyphish-icons/81-dashboard.png deleted file mode 100644 index 4f21ec9d..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/81-dashboard.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/82-dogpaw.png b/experiments/navbar-glyphish/glyphish-icons/82-dogpaw.png deleted file mode 100644 index 1fe1803f..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/82-dogpaw.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/83-calendar.png b/experiments/navbar-glyphish/glyphish-icons/83-calendar.png deleted file mode 100644 index 30daaed5..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/83-calendar.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/84-lightbulb.png b/experiments/navbar-glyphish/glyphish-icons/84-lightbulb.png deleted file mode 100644 index 64848c64..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/84-lightbulb.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/85-trophy.png b/experiments/navbar-glyphish/glyphish-icons/85-trophy.png deleted file mode 100644 index 992148c2..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/85-trophy.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/86-camera.png b/experiments/navbar-glyphish/glyphish-icons/86-camera.png deleted file mode 100644 index bdace4d2..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/86-camera.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/87-wineglass.png b/experiments/navbar-glyphish/glyphish-icons/87-wineglass.png deleted file mode 100644 index d835c2bb..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/87-wineglass.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/88-beermug.png b/experiments/navbar-glyphish/glyphish-icons/88-beermug.png deleted file mode 100644 index b338946a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/88-beermug.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/89-dumbbell.png b/experiments/navbar-glyphish/glyphish-icons/89-dumbbell.png deleted file mode 100644 index f0bf94ab..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/89-dumbbell.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/90-lifebuoy.png b/experiments/navbar-glyphish/glyphish-icons/90-lifebuoy.png deleted file mode 100644 index f296ff13..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/90-lifebuoy.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/91-beaker2.png b/experiments/navbar-glyphish/glyphish-icons/91-beaker2.png deleted file mode 100644 index 6f3a59da..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/91-beaker2.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/92-testtube.png b/experiments/navbar-glyphish/glyphish-icons/92-testtube.png deleted file mode 100644 index 8e578b7a..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/92-testtube.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/93-thermometer.png b/experiments/navbar-glyphish/glyphish-icons/93-thermometer.png deleted file mode 100644 index e2fcb7b6..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/93-thermometer.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/94-pill.png b/experiments/navbar-glyphish/glyphish-icons/94-pill.png deleted file mode 100644 index 32af6d33..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/94-pill.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/95-equalizer.png b/experiments/navbar-glyphish/glyphish-icons/95-equalizer.png deleted file mode 100644 index 7683ea25..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/95-equalizer.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/96-book.png b/experiments/navbar-glyphish/glyphish-icons/96-book.png deleted file mode 100644 index 63b3d69e..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/96-book.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/97-puzzle.png b/experiments/navbar-glyphish/glyphish-icons/97-puzzle.png deleted file mode 100644 index 921729b5..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/97-puzzle.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/98-palette.png b/experiments/navbar-glyphish/glyphish-icons/98-palette.png deleted file mode 100644 index 31fc3bb8..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/98-palette.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/99-umbrella.png b/experiments/navbar-glyphish/glyphish-icons/99-umbrella.png deleted file mode 100644 index 3a8fcab1..00000000 Binary files a/experiments/navbar-glyphish/glyphish-icons/99-umbrella.png and /dev/null differ diff --git a/experiments/navbar-glyphish/glyphish-icons/Read me first - license.txt b/experiments/navbar-glyphish/glyphish-icons/Read me first - license.txt deleted file mode 100644 index b6be14a6..00000000 --- a/experiments/navbar-glyphish/glyphish-icons/Read me first - license.txt +++ /dev/null @@ -1,13 +0,0 @@ -Created by Joseph Wain (see http://penandthink.com) at and probably downloaded from http://glyphish.com - -This work is licensed under the Creative Commons Attribution 3.0 United States License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. - -You are free to share it and to remix it remix under the following conditions: - -* You must attribute the work in the manner specified by the author (SEE BELOW). -* For any reuse or distribution, you must make clear to others the license terms of this work. -* The above conditions can be waived if you get permission from the copyright holder (send me an email!). - -ATTRIBUTION -- a note reading "icons by Joseph Wain / glyphish.com" or similar, plus a link back to glyphish.com from your app's website, is the preferred form of attribution. Also acceptable would be, like, a link from within your iPhone application, or from the iTunes store page, but those aren't as useful to other people. If none of these work for you, please contact hello@glyphish.com and we can work something out. - -USE WITHOUT ATTRIBUTION -- If attribution is not possible, workable or desirable for your application, contact hello@glyphish.com for commercial non-attributed licensing terms. \ No newline at end of file diff --git a/experiments/navbar-glyphish/index.html b/experiments/navbar-glyphish/index.html deleted file mode 100644 index f8dfd205..00000000 --- a/experiments/navbar-glyphish/index.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - jQuery Mobile Framework - navbar Example - - - - - -
- - - -
-

Navbar Example

- -
- -
-

Demo description

- -

This page includes a navbar plugin, which will fix to the footer as a tabbed navigation, and persist as pages switch out behind it.

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

- -
    -
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. -
  3. Aliquam tincidunt mauris eu risus.
  4. -
- -

Header Level 2

- -
    -
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. -
  3. Aliquam tincidunt mauris eu risus.
  4. -
- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

- -

Header Level 3

- -
    -
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • -
  • Aliquam tincidunt mauris eu risus.
  • -
- -

-						#header h1 a { 
-							display: block; 
-							width: 300px; 
-							height: 80px; 
-						}
-						
- -

HTML Ipsum Presents

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

- -

Header Level 2

- -
    -
  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. -
  3. Aliquam tincidunt mauris eu risus.
  4. -
- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

- -

Header Level 3

- -
    -
  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • -
  • Aliquam tincidunt mauris eu risus.
  • -
- -

-						#header h1 a { 
-							display: block; 
-							width: 300px; 
-							height: 80px; 
-						}
-						
- -

HTML Ipsum Presents

- -

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

- - - - -
- - - -
- - - - - - - \ No newline at end of file diff --git a/experiments/photos/_photo2.html b/experiments/photos/_photo2.html deleted file mode 100644 index 71b4a581..00000000 --- a/experiments/photos/_photo2.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - jQuery Mobile Framework - Photo 1 - - - - - - - -
- -
-

A canoe

- -
- -
- - photo-canoe - -
- - - - -
- - - \ No newline at end of file diff --git a/experiments/photos/_photo3.html b/experiments/photos/_photo3.html deleted file mode 100644 index fe9eac0d..00000000 --- a/experiments/photos/_photo3.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - jQuery Mobile Framework - Photo 1 - - - - - - - -
- -
-

A dock

- -
- -
- - photo-dock - -
- - - - -
- - - \ No newline at end of file diff --git a/experiments/photos/_photo4.html b/experiments/photos/_photo4.html deleted file mode 100644 index e7957d9e..00000000 --- a/experiments/photos/_photo4.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - jQuery Mobile Framework - Photo 1 - - - - - - - -
- -
-

A kayak

- -
- -
- - photo-kayak - -
- - - - -
- - - \ No newline at end of file diff --git a/experiments/photos/_photo5.html b/experiments/photos/_photo5.html deleted file mode 100644 index 3aed7f19..00000000 --- a/experiments/photos/_photo5.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - jQuery Mobile Framework - Photo 1 - - - - - - - -
-
-

Nathan running

- -
- -
- - photo-run - -
- - - - -
- - - \ No newline at end of file diff --git a/experiments/photos/_photo6.html b/experiments/photos/_photo6.html deleted file mode 100644 index 18b7889d..00000000 --- a/experiments/photos/_photo6.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - jQuery Mobile Framework - Photo 1 - - - - - - - -
-
-

Sandy beach

- -
- -
- - photo-sand - -
- - - - -
- - - \ No newline at end of file diff --git a/experiments/photos/images/photo-bridge.jpeg b/experiments/photos/images/photo-bridge.jpeg deleted file mode 100644 index 38615ef8..00000000 Binary files a/experiments/photos/images/photo-bridge.jpeg and /dev/null differ diff --git a/experiments/photos/images/photo-canoe.jpeg b/experiments/photos/images/photo-canoe.jpeg deleted file mode 100644 index 99502240..00000000 Binary files a/experiments/photos/images/photo-canoe.jpeg and /dev/null differ diff --git a/experiments/photos/images/photo-dock.jpeg b/experiments/photos/images/photo-dock.jpeg deleted file mode 100644 index 983256e3..00000000 Binary files a/experiments/photos/images/photo-dock.jpeg and /dev/null differ diff --git a/experiments/photos/images/photo-kayak.jpeg b/experiments/photos/images/photo-kayak.jpeg deleted file mode 100644 index 7be6a9e0..00000000 Binary files a/experiments/photos/images/photo-kayak.jpeg and /dev/null differ diff --git a/experiments/photos/images/photo-run.jpeg b/experiments/photos/images/photo-run.jpeg deleted file mode 100644 index 32f08742..00000000 Binary files a/experiments/photos/images/photo-run.jpeg and /dev/null differ diff --git a/experiments/photos/images/photo-sand.jpeg b/experiments/photos/images/photo-sand.jpeg deleted file mode 100644 index 161b77a4..00000000 Binary files a/experiments/photos/images/photo-sand.jpeg and /dev/null differ diff --git a/experiments/photos/index.html b/experiments/photos/index.html deleted file mode 100644 index ab6a7dbf..00000000 --- a/experiments/photos/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - jQuery Mobile Framework - Photo 1 - - - - - - - -
- -
-

A bridge

- -
- -
- - photo-bridge - -
- -
- -
- - -
- - - \ No newline at end of file diff --git a/experiments/photos/photos.css b/experiments/photos/photos.css deleted file mode 100644 index 08047a41..00000000 --- a/experiments/photos/photos.css +++ /dev/null @@ -1,2 +0,0 @@ -.photoview .ui-content { padding: 0; } -.photoview img { width: 100%; } \ No newline at end of file diff --git a/experiments/photos/photos.js b/experiments/photos/photos.js deleted file mode 100644 index 0cfcf013..00000000 --- a/experiments/photos/photos.js +++ /dev/null @@ -1,21 +0,0 @@ - -$('.photoview') - .live('pagebeforehide',function(){ - $.fixedToolbars.hide(true); - }) - .live('pageshow',function(){ - $.fixedToolbars.show(); - }) - .live('swipeleft',function(){ - $(this).find('a.next').click(); - }) - .live('swiperight',function(){ - $(this).next().find('a.prev').click(); - }); - -$('.photoview img').live('mousedown touchstart',function(event){ - event.preventDefault(); -}) - - - diff --git a/experiments/progressbar/_progressbar-static.html b/experiments/progressbar/_progressbar-static.html deleted file mode 100644 index 883023db..00000000 --- a/experiments/progressbar/_progressbar-static.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - jQuery Mobile Framework - Static Progressbar Example - - - - - -
-
-

Static Progressbar

- -
- -
- -

Just a simple static progressbar, for future reference.

- - - -
-
-
- -
-
- - - - \ No newline at end of file diff --git a/experiments/scrollview/index.html b/experiments/scrollview/index.html index c9d56892..336badb0 100644 --- a/experiments/scrollview/index.html +++ b/experiments/scrollview/index.html @@ -1,12 +1,13 @@ - + + jQuery Mobile: Scrollview Demos and Tests - + - + diff --git a/experiments/scrollview/jquery.mobile.scrollview.css b/experiments/scrollview/jquery.mobile.scrollview.css index 5112e431..f7e05521 100644 --- a/experiments/scrollview/jquery.mobile.scrollview.css +++ b/experiments/scrollview/jquery.mobile.scrollview.css @@ -1,6 +1,7 @@ @charset "utf-8"; .ui-scrollview-clip { + position: relative; } .ui-scrollview-view { diff --git a/experiments/scrollview/jquery.mobile.scrollview.js b/experiments/scrollview/jquery.mobile.scrollview.js index ddece23a..b190c701 100644 --- a/experiments/scrollview/jquery.mobile.scrollview.js +++ b/experiments/scrollview/jquery.mobile.scrollview.js @@ -29,8 +29,8 @@ jQuery.widget( "mobile.scrollview", jQuery.mobile.widget, { showScrollBars: true, pagingEnabled: false, - delayedClickSelector: "a,input,textarea,select,.ui-btn", - delayedClickEnabled: true + delayedClickSelector: "a,input,textarea,select,button,.ui-btn", + delayedClickEnabled: false }, _makePositioned: function($ele) @@ -157,7 +157,7 @@ jQuery.widget( "mobile.scrollview", jQuery.mobile.widget, { } this._setScrollPosition(x, y); - this._$clip.trigger(this.options.updateEventName, { x: x, y: y }); + this._$clip.trigger(this.options.updateEventName, [ { x: x, y: y } ]); if (keepGoing) this._timerID = setTimeout(this._timerCB, this._timerInterval); @@ -254,7 +254,7 @@ jQuery.widget( "mobile.scrollview", jQuery.mobile.widget, { { var svh = []; this._$clip.parents(".ui-scrollview-clip").each(function(){ - var d = $(this).data("scrollview"); + var d = $(this).jqmData("scrollview"); if (d) svh.unshift(d); }); return svh; @@ -284,8 +284,9 @@ jQuery.widget( "mobile.scrollview", jQuery.mobile.widget, { var c = this._$clip; var v = this._$view; - if (this.options.delayedClickEnabled) + if (this.options.delayedClickEnabled) { this._$clickEle = $(e.target).closest(this.options.delayedClickSelector); + } this._lastX = ex; this._lastY = ey; this._doSnapBackX = false; @@ -330,7 +331,15 @@ jQuery.widget( "mobile.scrollview", jQuery.mobile.widget, { this._lastMove = 0; this._enableTracking(); - e.preventDefault(); + // If we're using mouse events, we need to prevent the default + // behavior to suppress accidental selection of text, etc. We + // can't do this on touch devices because it will disable the + // generation of "click" events. + // + // XXX: We should test if this has an effect on links! - kin + + if (this.options.eventType == "mouse" || this.options.delayedClickEnabled) + e.preventDefault(); e.stopPropagation(); }, @@ -504,8 +513,12 @@ jQuery.widget( "mobile.scrollview", jQuery.mobile.widget, { this._disableTracking(); - if (this.options.delayedClickEnabled && !this._didDrag) { - this._$clickEle.click(); + if (!this._didDrag && this.options.delayedClickEnabled && this._$clickEle.length) { + this._$clickEle + .trigger("mousedown") + //.trigger("focus") + .trigger("mouseup") + .trigger("click"); } // If a view scrolled, then we need to absorb @@ -728,7 +741,7 @@ jQuery.widget( "mobile.scrolllistview", jQuery.mobile.scrollview, { // XXX: Note that we need to update this cache if we ever support lists // that can dynamically update their content. - this._$dividers = this._$view.find("[data-role=list-divider]"); + this._$dividers = this._$view.find(":jqmData(role='list-divider')"); this._lastDivider = null; }, diff --git a/experiments/scrollview/lists-divider.html b/experiments/scrollview/lists-divider.html index 7d4ae783..08207ba6 100644 --- a/experiments/scrollview/lists-divider.html +++ b/experiments/scrollview/lists-divider.html @@ -1,12 +1,13 @@ - + + jQuery Mobile Docs - Lists - + - - + + - + diff --git a/experiments/scrollview/scrollview-direction.html b/experiments/scrollview/scrollview-direction.html index db0af59f..beb5113e 100644 --- a/experiments/scrollview/scrollview-direction.html +++ b/experiments/scrollview/scrollview-direction.html @@ -1,12 +1,13 @@ - + + jQuery Mobile Docs - Lists - + - - + + - + diff --git a/experiments/scrollview/scrollview-nested.html b/experiments/scrollview/scrollview-nested.html index 465c4177..bc0ecd72 100644 --- a/experiments/scrollview/scrollview-nested.html +++ b/experiments/scrollview/scrollview-nested.html @@ -1,12 +1,13 @@ - + + jQuery Mobile Docs - Lists - + - - + + - + diff --git a/experiments/scrollview/scrollview.js b/experiments/scrollview/scrollview.js index f14c03c1..eb6cb020 100644 --- a/experiments/scrollview/scrollview.js +++ b/experiments/scrollview/scrollview.js @@ -10,19 +10,19 @@ function ResizePageContentHeight(page) $content.height(wh - (hh + fh) - (pt + pb)); } -$("[data-role=page]").live("pageshow", function(event) { +$(":jqmData(role='page')").live("pageshow", function(event) { var $page = $(this); // For the demos that use this script, we want the content area of each // page to be scrollable in the 'y' direction. - $page.find(".ui-content").attr("data-scroll", "y"); + $page.find(".ui-content").attr("data-"+ $.mobile.ns +"scroll", "y"); // This code that looks for [data-scroll] will eventually be folded // into the jqm page processing code when scrollview support is "official" // instead of "experimental". - $page.find("[data-scroll]:not(.ui-scrollview-clip)").each(function(){ + $page.find(":jqmData(scroll):not(.ui-scrollview-clip)").each(function(){ var $this = $(this); // XXX: Remove this check for ui-scrolllistview once we've // integrated list divider support into the main scrollview class. @@ -30,7 +30,7 @@ $("[data-role=page]").live("pageshow", function(event) { $this.scrolllistview(); else { - var st = $this.data("scroll") + ""; + var st = $this.jqmData("scroll") + ""; var paging = st && st.search(/^[xy]p$/) != -1; var dir = st && st.search(/^[xy]/) != -1 ? st.charAt(0) : null; @@ -40,7 +40,7 @@ $("[data-role=page]").live("pageshow", function(event) { if (paging) opts.pagingEnabled = true; - var method = $this.data("scroll-method"); + var method = $this.jqmData("scroll-method"); if (method) opts.scrollMethod = method; @@ -55,6 +55,6 @@ $("[data-role=page]").live("pageshow", function(event) { ResizePageContentHeight(event.target); }); -$(document).live("orientationchange", function(event) { +$(window).bind("orientationchange", function(event) { ResizePageContentHeight($(".ui-page")); }); diff --git a/experiments/scrollview/sv-test-01.html b/experiments/scrollview/sv-test-01.html index 18400666..69f63754 100644 --- a/experiments/scrollview/sv-test-01.html +++ b/experiments/scrollview/sv-test-01.html @@ -1,12 +1,14 @@ - + + Scrollview Test 1 - Form Element Event Test - + - - - - + + + - - - - - -
- - - - -
-

Datepicker Styled for mobile

- -
- -
- - -
- -
- - -
- - -
- -
- -
- - - - - \ No newline at end of file diff --git a/experiments/ui-datepicker/jQuery.ui.datepicker.js b/experiments/ui-datepicker/jQuery.ui.datepicker.js deleted file mode 100644 index 2a2383c9..00000000 --- a/experiments/ui-datepicker/jQuery.ui.datepicker.js +++ /dev/null @@ -1,98 +0,0 @@ -/*! - * jQuery UI 1.8.5 - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI - */ -(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.5",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, -NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this, -"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position"); -if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"));if(!isNaN(b)&&b!=0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind("mousedown.ui-disableSelection selectstart.ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f, -"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c.style(this,h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c.style(this, -h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}}); -c(function(){var a=document.createElement("div"),b=document.body;c.extend(a.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.appendChild(a).offsetHeight===100;b.removeChild(a).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a
')}function E(a,b){d.extend(a, -b);for(var c in b)if(b[c]==null||b[c]==G)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.5"}});var y=(new Date).getTime();d.extend(L.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]= -f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('
')}}, -_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&& -b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f== -""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a, -c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b), -true);this._updateDatepicker(b);this._updateAlternate(b)}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{});b=b&&b.constructor== -Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]); -d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}}, -_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b= -d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false; -for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target|| -a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a); -d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&& -d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=d.datepicker._getBorders(b.dpDiv);b.dpDiv.find("iframe.ui-datepicker-cover").css({left:-i[0],top:-i[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f, -h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a)).find("iframe.ui-datepicker-cover").css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){d(this).removeClass("ui-state-hover"); -this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover"); -this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);var e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"); -a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input.focus()},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(), -k=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>k&&k>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[b?"previousSibling":"nextSibling"]; -a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val(): -"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&& -!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth; -b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b= -this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a= -d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a, -"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b== -"object"?b.toString():b+"";if(b=="")return null;for(var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff,f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,k=c=-1,l=-1,u=-1,j=false,o=function(p){(p=z+1 --1){k=1;l=u;do{e=this._getDaysInMonth(c,k-1);if(l<=e)break;k++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,k-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=k||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24* -60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=j+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e? -"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),k= -this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),j=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=j&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a, -"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-k,1)),this._getFormatConfig(a));n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+ -n+"";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m,g+k,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+r+"":f?"":''+r+"";k=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&&a.currentDay?u:b;k=!h?k:this.formatDate(k,r,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
'+(c?h:"")+(this._isInRange(a,r)?'":"")+(c?"":h)+"
":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;k=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),w=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var M=this._getDefaultDate(a),I="",C=0;C1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='
'+(/all|left/.test(t)&&C==0?c? -f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,j,o,C>0||D>0,z,v)+'
';var A=k?'":"";for(t=0;t<7;t++){var q=(t+h)%7;A+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+""}x+=A+"";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, -A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var O=0;O";var P=!k?"":'";for(t=0;t<7;t++){var F=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,K=B&&!H||!F[0]||j&&qo;P+='";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+=P+""}g++;if(g>11){g=0;m++}x+="
'+this._get(a,"weekHeader")+"
'+this._get(a,"calculateWeek")(q)+""+(B&&!w?" ":K?''+q.getDate()+ -"":''+q.getDate()+"")+"
"+(l?"
"+(i[0]>0&&D==i[1]-1?'
':""):"");N+=x}I+=N}I+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': -"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var k=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),j='
',o="";if(h||!k)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(j+=o+(h||!(k&&l)?" ":""));if(h||!l)j+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b, -i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(j+='"}j+=this._get(a,"yearSuffix");if(u)j+=(h||!(k&&l)?" ":"")+o;j+="
";return j},_adjustInstDate:function(a,b,c){var e= -a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a, -"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); -c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, -"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= -function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b)); -return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new L;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.5";window["DP_jQuery_"+y]=d})(jQuery); -; \ No newline at end of file diff --git a/experiments/ui-datepicker/jquery.ui.datepicker.css b/experiments/ui-datepicker/jquery.ui.datepicker.css deleted file mode 100755 index 91b2e9a9..00000000 --- a/experiments/ui-datepicker/jquery.ui.datepicker.css +++ /dev/null @@ -1,28 +0,0 @@ -/* - * jQuery UI Datepicker @VERSION - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Datepicker#theming - */ -div.hasDatepicker{ display: block; padding: 0; overflow: visible; margin: 8px 0; } -.ui-datepicker { overflow: visible; margin: 0; } -.ui-datepicker .ui-datepicker-header { position:relative; padding:.4em 0; border-bottom: 0; font-weight: bold; } -.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { padding: 1px 0 1px 2px; position:absolute; top: .5em; margin-top: 0; text-indent: -9999px; } - -.ui-datepicker .ui-datepicker-prev { left:2px; } -.ui-datepicker .ui-datepicker-next { right:2px; } -.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } -.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } -.ui-datepicker select.ui-datepicker-month-year {width: 100%;} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { width: 49%;} -.ui-datepicker table {width: 100%; border-collapse: collapse; margin:0; } -.ui-datepicker td { border-width: 1px; padding: 0; text-align: center; } -.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em 0; font-weight: bold; margin: 0; border-width: 0; text-align: center; text-decoration: none; } - -.min-width-480px { - div.hasDatepicker { width: 63%; display: inline-block; margin: 0; } -} \ No newline at end of file diff --git a/experiments/weather/index.php b/experiments/weather/index.php index 14066bb2..c61080a0 100644 --- a/experiments/weather/index.php +++ b/experiments/weather/index.php @@ -1,4 +1,4 @@ -xpath("/xml_api_reply/weather/forecast_conditions"); - - jQuery Mobile Framework - Weather for <?= $information[0]->city['data']; ?> - + + + jQuery Mobile Framework - Weather for <?php echo $information[0]->city['data']; ?> + " ); + var styleBlock = document.createElement( "style" ), + cssrule = "@media " + query + " { #jquery-mediatest { position:absolute; } }"; + + //must set type for IE! + styleBlock.type = "text/css"; + + if ( styleBlock.styleSheet ){ + styleBlock.styleSheet.cssText = cssrule; + } else { + styleBlock.appendChild( document.createTextNode(cssrule) ); + } + $html.prepend( fakeBody ).prepend( styleBlock ); cache[ query ] = testDiv.css( "position" ) === "absolute"; fakeBody.add( styleBlock ).remove(); @@ -39,71 +44,4 @@ $.mobile.media = (function() { }; })(); -/* - private function for adding/removing breakpoint classes to HTML element for faux media-query support - It does not require media query support, instead using JS to detect screen width > cross-browser support - This function is called on orientationchange, resize, and mobileinit, and is bound via the 'htmlclass' event namespace -*/ -function detectResolutionBreakpoints(){ - var currWidth = $window.width(), - minPrefix = "min-width-", - maxPrefix = "max-width-", - minBreakpoints = [], - maxBreakpoints = [], - unit = "px", - breakpointClasses; - - $html.removeClass( minPrefix + resolutionBreakpoints.join(unit + " " + minPrefix) + unit + " " + - maxPrefix + resolutionBreakpoints.join( unit + " " + maxPrefix) + unit ); - - $.each(resolutionBreakpoints,function( i, breakPoint ){ - if( currWidth >= breakPoint ){ - minBreakpoints.push( minPrefix + breakPoint + unit ); - } - if( currWidth <= breakPoint ){ - maxBreakpoints.push( maxPrefix + breakPoint + unit ); - } - }); - - if( minBreakpoints.length ){ breakpointClasses = minBreakpoints.join(" "); } - if( maxBreakpoints.length ){ breakpointClasses += " " + maxBreakpoints.join(" "); } - - $html.addClass( breakpointClasses ); -}; - -/* $.mobile.addResolutionBreakpoints method: - pass either a number or an array of numbers and they'll be added to the min/max breakpoint classes - Examples: - $.mobile.addResolutionBreakpoints( 500 ); - $.mobile.addResolutionBreakpoints( [500, 1200] ); -*/ -$.mobile.addResolutionBreakpoints = function( newbps ){ - if( $.type( newbps ) === "array" ){ - resolutionBreakpoints = resolutionBreakpoints.concat( newbps ); - } - else { - resolutionBreakpoints.push( newbps ); - } - resolutionBreakpoints.sort(function(a,b){ return a-b; }); - detectResolutionBreakpoints(); -}; - -/* on mobileinit, add classes to HTML element - and set handlers to update those on orientationchange and resize*/ -$(document).bind("mobileinit.htmlclass", function(){ - /* bind to orientationchange and resize - to add classes to HTML element for min/max breakpoints and orientation */ - $window.bind("orientationchange.htmlclass resize.htmlclass", function(event){ - //add orientation class to HTML element on flip/resize. - if(event.orientation){ - $html.removeClass( "portrait landscape" ).addClass( event.orientation ); - } - //add classes to HTML element for min/max breakpoints - detectResolutionBreakpoints(); - }); - - //trigger event manually - $window.trigger( "orientationchange.htmlclass" ); -}); - })(jQuery); \ No newline at end of file diff --git a/js/jquery.mobile.navbar.js b/js/jquery.mobile.navbar.js index a592bffb..4da6d903 100755 --- a/js/jquery.mobile.navbar.js +++ b/js/jquery.mobile.navbar.js @@ -4,37 +4,48 @@ * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license */ -(function($, undefined ) { + +(function( $, undefined ) { + $.widget( "mobile.navbar", $.mobile.widget, { options: { - iconpos: 'top', - grid: null + iconpos: "top", + grid: null, + initSelector: ":jqmData(role='navbar')" }, + _create: function(){ + var $navbar = this.element, - $navbtns = $navbar.find("a"), - iconpos = $navbtns.filter('[data-icon]').length ? this.options.iconpos : undefined; - - $navbar - .addClass('ui-navbar') - .attr("role","navigation") - .find("ul") - .grid({grid: this.options.grid }); - - if( !iconpos ){ - $navbar.addClass("ui-navbar-noicons"); + $navbtns = $navbar.find( "a" ), + iconpos = $navbtns.filter( ":jqmData(icon)" ).length ? + this.options.iconpos : undefined; + + $navbar.addClass( "ui-navbar" ) + .attr( "role","navigation" ) + .find( "ul" ) + .grid({ grid: this.options.grid }); + + if ( !iconpos ) { + $navbar.addClass( "ui-navbar-noicons" ); } - - $navbtns - .buttonMarkup({ - corners: false, - shadow: false, - iconpos: iconpos - }); - - $navbar.delegate("a", "click",function(event){ - $navbtns.removeClass("ui-btn-active"); - }); + + $navbtns.buttonMarkup({ + corners: false, + shadow: false, + iconpos: iconpos + }); + + $navbar.delegate( "a", "vclick", function( event ) { + $navbtns.not( ".ui-state-persist" ).removeClass( $.mobile.activeBtnClass ); + $( this ).addClass( $.mobile.activeBtnClass ); + }); } }); -})( jQuery ); \ No newline at end of file + +//auto self-init widgets +$( document ).bind( "pagecreate create", function( e ){ + $( $.mobile.navbar.prototype.options.initSelector, e.target ).navbar(); +}); + +})( jQuery ); diff --git a/js/jquery.mobile.navigation.js b/js/jquery.mobile.navigation.js old mode 100644 new mode 100755 index b051e21a..60f972d3 --- a/js/jquery.mobile.navigation.js +++ b/js/jquery.mobile.navigation.js @@ -4,92 +4,367 @@ * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license */ -(function($, undefined ) { +( function( $, undefined ) { //define vars for interal use - var $window = $(window), - $html = $('html'), - $head = $('head'), + var $window = $( window ), + $html = $( 'html' ), + $head = $( 'head' ), //url path helpers for use in relative url management path = { - //get path from current hash, or from a file path - get: function( newPath ){ - if( newPath == undefined ){ - newPath = location.hash; + // This scary looking regular expression parses an absolute URL or its relative + // variants (protocol, site, document, query, and hash), into the various + // components (protocol, host, path, query, fragment, etc that make up the + // URL as well as some other commonly used sub-parts. When used with RegExp.exec() + // or String.match, it parses the URL into a results array that looks like this: + // + // [0]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread#msg-content + // [1]: http://jblas:password@mycompany.com:8080/mail/inbox?msg=1234&type=unread + // [2]: http://jblas:password@mycompany.com:8080/mail/inbox + // [3]: http://jblas:password@mycompany.com:8080 + // [4]: http: + // [5]: // + // [6]: jblas:password@mycompany.com:8080 + // [7]: jblas:password + // [8]: jblas + // [9]: password + // [10]: mycompany.com:8080 + // [11]: mycompany.com + // [12]: 8080 + // [13]: /mail/inbox + // [14]: /mail/ + // [15]: inbox + // [16]: ?msg=1234&type=unread + // [17]: #msg-content + // + urlParseRE: /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/, + + //Parse a URL into a structure that allows easy access to + //all of the URL components by name. + parseUrl: function( url ) { + // If we're passed an object, we'll assume that it is + // a parsed url object and just return it back to the caller. + if ( $.type( url ) === "object" ) { + return url; } - newPath = newPath.replace(/#/,'').split('/'); - if(newPath.length){ - var lastSegment = newPath[newPath.length-1]; - if( lastSegment.indexOf('.') > -1 || lastSegment == ''){ - newPath.pop(); + + var matches = path.urlParseRE.exec( url || "" ) || []; + + // Create an object that allows the caller to access the sub-matches + // by name. Note that IE returns an empty string instead of undefined, + // like all other browsers do, so we normalize everything so its consistent + // no matter what browser we're running on. + return { + href: matches[ 0 ] || "", + hrefNoHash: matches[ 1 ] || "", + hrefNoSearch: matches[ 2 ] || "", + domain: matches[ 3 ] || "", + protocol: matches[ 4 ] || "", + doubleSlash: matches[ 5 ] || "", + authority: matches[ 6 ] || "", + username: matches[ 8 ] || "", + password: matches[ 9 ] || "", + host: matches[ 10 ] || "", + hostname: matches[ 11 ] || "", + port: matches[ 12 ] || "", + pathname: matches[ 13 ] || "", + directory: matches[ 14 ] || "", + filename: matches[ 15 ] || "", + search: matches[ 16 ] || "", + hash: matches[ 17 ] || "" + }; + }, + + //Turn relPath into an asbolute path. absPath is + //an optional absolute path which describes what + //relPath is relative to. + makePathAbsolute: function( relPath, absPath ) { + if ( relPath && relPath.charAt( 0 ) === "/" ) { + return relPath; + } + + relPath = relPath || ""; + absPath = absPath ? absPath.replace( /^\/|(\/[^\/]*|[^\/]+)$/g, "" ) : ""; + + var absStack = absPath ? absPath.split( "/" ) : [], + relStack = relPath.split( "/" ); + for ( var i = 0; i < relStack.length; i++ ) { + var d = relStack[ i ]; + switch ( d ) { + case ".": + break; + case "..": + if ( absStack.length ) { + absStack.pop(); + } + break; + default: + absStack.push( d ); + break; } } - return newPath.join('/') + (newPath.length ? '/' : ''); + return "/" + absStack.join( "/" ); + }, + + //Returns true if both urls have the same domain. + isSameDomain: function( absUrl1, absUrl2 ) { + return path.parseUrl( absUrl1 ).domain === path.parseUrl( absUrl2 ).domain; + }, + + //Returns true for any relative variant. + isRelativeUrl: function( url ) { + // All relative Url variants have one thing in common, no protocol. + return path.parseUrl( url ).protocol === ""; + }, + + //Returns true for an absolute url. + isAbsoluteUrl: function( url ) { + return path.parseUrl( url ).protocol !== ""; + }, + + //Turn the specified realtive URL into an absolute one. This function + //can handle all relative variants (protocol, site, document, query, fragment). + makeUrlAbsolute: function( relUrl, absUrl ) { + if ( !path.isRelativeUrl( relUrl ) ) { + return relUrl; + } + + var relObj = path.parseUrl( relUrl ), + absObj = path.parseUrl( absUrl ), + protocol = relObj.protocol || absObj.protocol, + doubleSlash = relObj.protocol ? relObj.doubleSlash : ( relObj.doubleSlash || absObj.doubleSlash ), + authority = relObj.authority || absObj.authority, + hasPath = relObj.pathname !== "", + pathname = path.makePathAbsolute( relObj.pathname || absObj.filename, absObj.pathname ), + search = relObj.search || ( !hasPath && absObj.search ) || "", + hash = relObj.hash; + + return protocol + doubleSlash + authority + pathname + search + hash; + }, + + //Add search (aka query) params to the specified url. + addSearchParams: function( url, params ) { + var u = path.parseUrl( url ), + p = ( typeof params === "object" ) ? $.param( params ) : params, + s = u.search || "?"; + return u.hrefNoSearch + s + ( s.charAt( s.length - 1 ) !== "?" ? "&" : "" ) + p + ( u.hash || "" ); + }, + + convertUrlToDataUrl: function( absUrl ) { + var u = path.parseUrl( absUrl ); + if ( path.isEmbeddedPage( u ) ) { + // For embedded pages, remove the dialog hash key as in getFilePath(), + // otherwise the Data Url won't match the id of the embedded Page. + return u.hash.split( dialogHashKey )[0].replace( /^#/, "" ); + } else if ( path.isSameDomain( u, documentBase ) ) { + return u.hrefNoHash.replace( documentBase.domain, "" ); + } + return absUrl; + }, + + //get path from current hash, or from a file path + get: function( newPath ) { + if( newPath === undefined ) { + newPath = location.hash; + } + return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' ); }, //return the substring of a filepath before the sub-page key, for making a server request - getFilePath: function( path ){ + getFilePath: function( path ) { var splitkey = '&' + $.mobile.subPageUrlKey; - return path && path.indexOf( splitkey ) > -1 ? path.split( splitkey )[0] : path; + return path && path.split( splitkey )[0].split( dialogHashKey )[0]; }, - set: function( path, disableListening){ - if(disableListening) { hashListener = false; } + //set location hash to path + set: function( path ) { location.hash = path; }, - //location pathname from intial directory request - origin: '', - - setOrigin: function(){ - path.origin = path.get( location.protocol + '//' + location.host + location.pathname ); - } - }, - - //base element management, defined depending on dynamic base tag support - base = $.support.dynamicBaseTag ? { - - //define base element, for use in routing asset urls that are referenced in Ajax-requested markup - element: $("", { href: path.origin }).prependTo( $head ), - - //set the generated BASE element's href attribute to a new page's base path - set: function( href ){ - base.element.attr('href', path.origin + path.get( href )); + //test if a given url (string) is a path + //NOTE might be exceptionally naive + isPath: function( url ) { + return ( /\// ).test( url ); }, - //set the generated BASE element's href attribute to a new page's base path - reset: function(){ - base.element.attr('href', path.origin ); + //return a url path with the window's location protocol/hostname/pathname removed + clean: function( url ) { + return url.replace( documentBase.domain, "" ); + }, + + //just return the url without an initial # + stripHash: function( url ) { + return url.replace( /^#/, "" ); + }, + + //remove the preceding hash, any query params, and dialog notations + cleanHash: function( hash ) { + return path.stripHash( hash.replace( /\?.*$/, "" ).replace( dialogHashKey, "" ) ); + }, + + //check whether a url is referencing the same domain, or an external domain or different protocol + //could be mailto, etc + isExternal: function( url ) { + var u = path.parseUrl( url ); + return u.protocol && u.domain !== documentUrl.domain ? true : false; + }, + + hasProtocol: function( url ) { + return ( /^(:?\w+:)/ ).test( url ); + }, + + //check if the specified url refers to the first page in the main application document. + isFirstPageUrl: function( url ) { + // We only deal with absolute paths. + var u = path.parseUrl( path.makeUrlAbsolute( url, documentBase ) ), + + // Does the url have the same path as the document? + samePath = u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ), + + // Get the first page element. + fp = $.mobile.firstPage, + + // Get the id of the first page element if it has one. + fpId = fp && fp[0] ? fp[0].id : undefined; + + // The url refers to the first page if the path matches the document and + // it either has no hash value, or the hash is exactly equal to the id of the + // first page element. + return samePath && ( !u.hash || u.hash === "#" || ( fpId && u.hash.replace( /^#/, "" ) === fpId ) ); + }, + + isEmbeddedPage: function( url ) { + var u = path.parseUrl( url ); + + //if the path is absolute, then we need to compare the url against + //both the documentUrl and the documentBase. The main reason for this + //is that links embedded within external documents will refer to the + //application document, whereas links embedded within the application + //document will be resolved against the document base. + if ( u.protocol !== "" ) { + return ( u.hash && ( u.hrefNoHash === documentUrl.hrefNoHash || ( documentBaseDiffers && u.hrefNoHash === documentBase.hrefNoHash ) ) ); + } + return (/^#/).test( u.href ); } - - } : undefined, - + }, //will be defined when a link is clicked and given an active class $activeClickedLink = null, - //array of pages that are visited during a single page load - //length will grow as pages are visited, and shrink as "back" link/button is clicked - //each item has a url (string matches ID), and transition (saved for reuse when "back" link/button is clicked) - urlStack = [ { - url: location.hash.replace( /^#/, "" ), - transition: undefined - } ], + //urlHistory is purely here to make guesses at whether the back or forward button was clicked + //and provide an appropriate transition + urlHistory = { + // Array of pages that are visited during a single page load. + // Each has a url and optional transition, title, and pageUrl (which represents the file path, in cases where URL is obscured, such as dialogs) + stack: [], + + //maintain an index number for the active page in the stack + activeIndex: 0, + + //get active + getActive: function() { + return urlHistory.stack[ urlHistory.activeIndex ]; + }, + + getPrev: function() { + return urlHistory.stack[ urlHistory.activeIndex - 1 ]; + }, + + getNext: function() { + return urlHistory.stack[ urlHistory.activeIndex + 1 ]; + }, + + // addNew is used whenever a new page is added + addNew: function( url, transition, title, pageUrl, role ) { + //if there's forward history, wipe it + if( urlHistory.getNext() ) { + urlHistory.clearForward(); + } + + urlHistory.stack.push( {url : url, transition: transition, title: title, pageUrl: pageUrl, role: role } ); + + urlHistory.activeIndex = urlHistory.stack.length - 1; + }, + + //wipe urls ahead of active index + clearForward: function() { + urlHistory.stack = urlHistory.stack.slice( 0, urlHistory.activeIndex + 1 ); + }, + + directHashChange: function( opts ) { + var back , forward, newActiveIndex, prev = this.getActive(); + + // check if url isp in history and if it's ahead or behind current page + $.each( urlHistory.stack, function( i, historyEntry ) { + + //if the url is in the stack, it's a forward or a back + if( opts.currentUrl === historyEntry.url ) { + //define back and forward by whether url is older or newer than current page + back = i < urlHistory.activeIndex; + forward = !back; + newActiveIndex = i; + } + }); + + // save new page index, null check to prevent falsey 0 result + this.activeIndex = newActiveIndex !== undefined ? newActiveIndex : this.activeIndex; + + if( back ) { + ( opts.either || opts.isBack )( true ); + } else if( forward ) { + ( opts.either || opts.isForward )( false ); + } + }, + + //disable hashchange event listener internally to ignore one change + //toggled internally when location.hash is updated to match the url of a successful page load + ignoreNextHashChange: false + }, //define first selector to receive focus when a page is shown focusable = "[tabindex],a,button:visible,select:visible,input", - //contains role for next page, if defined on clicked link via data-rel - nextPageRole = null, + //queue to hold simultanious page transitions + pageTransitionQueue = [], - //enable/disable hashchange event listener - //toggled internally when location.hash is updated to match the url of a successful page load - hashListener = true; + //indicates whether or not page is in process of transitioning + isPageTransitioning = false, - //set location pathname from intial directory request - path.setOrigin(); + //nonsense hash change key for dialogs, so they create a history entry + dialogHashKey = "&ui-state=dialog", + + //existing base tag? + $base = $head.children( "base" ), + + //tuck away the original document URL minus any fragment. + documentUrl = path.parseUrl( location.href ), + + //if the document has an embedded base tag, documentBase is set to its + //initial value. If a base tag does not exist, then we default to the documentUrl. + documentBase = $base.length ? path.parseUrl( path.makeUrlAbsolute( $base.attr( "href" ), documentUrl.href ) ) : documentUrl, + + //cache the comparison once. + documentBaseDiffers = ( documentUrl.hrefNoHash !== documentBase.hrefNoHash ); + + //base element management, defined depending on dynamic base tag support + var base = $.support.dynamicBaseTag ? { + + //define base element, for use in routing asset urls that are referenced in Ajax-requested markup + element: ( $base.length ? $base : $( "", { href: documentBase.hrefNoHash } ).prependTo( $head ) ), + + //set the generated BASE element's href attribute to a new page's base path + set: function( href ) { + base.element.attr( "href", path.makeUrlAbsolute( href, documentBase ) ); + }, + + //set the generated BASE element's href attribute to a new page's base path + reset: function() { + base.element.attr( "href", documentBase.hrefNoHash ); + } + + } : undefined; /* internal utility functions @@ -97,393 +372,1087 @@ //direct focus to the page title, or otherwise first focusable element - function reFocus( page ){ + function reFocus( page ) { var pageTitle = page.find( ".ui-title:eq(0)" ); - if( pageTitle.length ){ + + if( pageTitle.length ) { pageTitle.focus(); } else{ - page.find( focusable ).eq(0).focus(); + page.focus(); } - }; + } //remove active classes after page transition or error - function removeActiveLinkClass( forceRemoval ){ - if( !!$activeClickedLink && (!$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval )){ + function removeActiveLinkClass( forceRemoval ) { + if( !!$activeClickedLink && ( !$activeClickedLink.closest( '.ui-page-active' ).length || forceRemoval ) ) { $activeClickedLink.removeClass( $.mobile.activeBtnClass ); } $activeClickedLink = null; + } + + function releasePageTransitionLock() { + isPageTransitioning = false; + if( pageTransitionQueue.length > 0 ) { + $.mobile.changePage.apply( null, pageTransitionQueue.pop() ); + } + } + + // Save the last scroll distance per page, before it is hidden + var setLastScrollEnabled = true, + firstScrollElem, getScrollElem, setLastScroll, delayedSetLastScroll; + + getScrollElem = function() { + var scrollElem = $window, activePage, + touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled; + + if( touchOverflow ){ + activePage = $( ".ui-page-active" ); + scrollElem = activePage.is( ".ui-native-fixed" ) ? activePage.find( ".ui-content" ) : activePage; + } + + return scrollElem; }; - - //animation complete callback - $.fn.animationComplete = function( callback ){ - if($.support.cssTransitions){ - return $(this).one('webkitAnimationEnd', callback); - } - else{ - callback(); - } - }; - - - -/* exposed $.mobile methods */ - - //update location.hash, with or without triggering hashchange event - $.mobile.updateHash = path.set; - - //url stack, useful when plugins need to be aware of previous pages viewed - $.mobile.urlStack = urlStack; - - // changepage function - $.mobile.changePage = function( to, transition, back, changeHash){ - - //from is always the currently viewed page - var toIsArray = $.type(to) === "array", - from = toIsArray ? to[0] : $.mobile.activePage, - to = toIsArray ? to[1] : to, - url = fileUrl = $.type(to) === "string" ? to.replace( /^#/, "" ) : null, - data = undefined, - type = 'get', - isFormRequest = false, - duplicateCachedPage = null, - back = (back !== undefined) ? back : ( urlStack.length > 1 && urlStack[ urlStack.length - 2 ].url === url ), - transition = (transition !== undefined) ? transition : $.mobile.defaultTransition; - - - //If we are trying to transition to the same page that we are currently on ignore the request. - if(urlStack.length > 1 && url === urlStack[urlStack.length -1].url && !toIsArray ) { + setLastScroll = function( scrollElem ) { + // this barrier prevents setting the scroll value based on the browser + // scrolling the window based on a hashchange + if( !setLastScrollEnabled ) { return; } + var active = $.mobile.urlHistory.getActive(); - if( $.type(to) === "object" && to.url ){ - url = to.url, - data = to.data, - type = to.type, - isFormRequest = true; - //make get requests bookmarkable - if( data && type == 'get' ){ - url += "?" + data; - data = undefined; - } + if( active ) { + var lastScroll = scrollElem && scrollElem.scrollTop(); + + // Set active page's lastScroll prop. + // If the location we're scrolling to is less than minScrollBack, let it go. + active.lastScroll = lastScroll < $.mobile.minScrollBack ? $.mobile.defaultHomeScroll : lastScroll; } + }; + // bind to scrollstop to gather scroll position. The delay allows for the hashchange + // event to fire and disable scroll recording in the case where the browser scrolls + // to the hash targets location (sometimes the top of the page). once pagechange fires + // getLastScroll is again permitted to operate + delayedSetLastScroll = function() { + setTimeout( setLastScroll, 100, $(this) ); + }; + // disable an scroll setting when a hashchange has been fired, this only works + // because the recording of the scroll position is delayed for 100ms after + // the browser might have changed the position because of the hashchange + $window.bind( $.support.pushState ? "popstate" : "hashchange", function() { + setLastScrollEnabled = false; + }); + // handle initial hashchange from chrome :( + $window.one( $.support.pushState ? "popstate" : "hashchange", function() { + setLastScrollEnabled = true; + }); - //reset base to pathname for new request - if(base){ base.reset(); } + // wait until the mobile page container has been determined to bind to pagechange + $window.one( "pagecontainercreate", function(){ + // once the page has changed, re-enable the scroll recording + $.mobile.pageContainer.bind( "pagechange", function() { + var scrollElem = getScrollElem(); - //kill the keyboard - $( window.document.activeElement ).add(':focus').blur(); + setLastScrollEnabled = true; - // if the new href is the same as the previous one - if ( back ) { - var pop = urlStack.pop(); - if( pop ){ - transition = pop.transition; - } - } else { - urlStack.push({ url: url, transition: transition }); + // remove any binding that previously existed on the get scroll + // which may or may not be different than the scroll element determined for + // this page previously + scrollElem.unbind( "scrollstop", delayedSetLastScroll ); + + // determine and bind to the current scoll element which may be the window + // or in the case of touch overflow the element with touch overflow + scrollElem.bind( "scrollstop", delayedSetLastScroll ); + }); + }); + + // bind to scrollstop for the first page as "pagechange" won't be fired in that case + getScrollElem().bind( "scrollstop", delayedSetLastScroll ); + + // Make the iOS clock quick-scroll work again if we're using native overflow scrolling + /* + if( $.support.touchOverflow ){ + if( $.mobile.touchOverflowEnabled ){ + $( window ).bind( "scrollstop", function(){ + if( $( this ).scrollTop() === 0 ){ + $.mobile.activePage.scrollTop( 0 ); + } + }); } + } + */ - //function for transitioning between two existing pages - function transitionPages() { + //function for transitioning between two existing pages + function transitionPages( toPage, fromPage, transition, reverse ) { - //get current scroll distance - var currScroll = $window.scrollTop(), - perspectiveTransitions = ["flip"], - pageContainerClasses = []; + //get current scroll distance + var active = $.mobile.urlHistory.getActive(), + touchOverflow = $.support.touchOverflow && $.mobile.touchOverflowEnabled, + toScroll = active.lastScroll || ( touchOverflow ? 0 : $.mobile.defaultHomeScroll ), + screenHeight = getScreenHeight(); - //set as data for returning to that spot - from.data('lastScroll', currScroll); + // Scroll to top, hide addr bar + window.scrollTo( 0, $.mobile.defaultHomeScroll ); + if( fromPage ) { //trigger before show/hide events - from.data("page")._trigger("beforehide", {nextPage: to}); - to.data("page")._trigger("beforeshow", {prevPage: from}); + fromPage.data( "page" )._trigger( "beforehide", null, { nextPage: toPage } ); + } - function loadComplete(){ - $.mobile.pageLoading( true ); + if( !touchOverflow){ + toPage.height( screenHeight + toScroll ); + } - reFocus( to ); + toPage.data( "page" )._trigger( "beforeshow", null, { prevPage: fromPage || $( "" ) } ); - if( changeHash !== false && url ){ - path.set(url, (back !== true)); - } - removeActiveLinkClass(); + //clear page loader + $.mobile.hidePageLoadingMsg(); - //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden - if( duplicateCachedPage != null ){ - duplicateCachedPage.remove(); - } + if( touchOverflow && toScroll ){ - //jump to top or prev scroll, if set - $.mobile.silentScroll( to.data( 'lastScroll' ) ); + toPage.addClass( "ui-mobile-pre-transition" ); + // Send focus to page as it is now display: block + reFocus( toPage ); - //trigger show/hide events, allow preventing focus change through return false - from.data("page")._trigger("hide", null, {nextPage: to}); - if( to.data("page")._trigger("show", null, {prevPage: from}) !== false ){ - $.mobile.activePage = to; - } - }; - - function addContainerClass(className){ - $.mobile.pageContainer.addClass(className); - pageContainerClasses.push(className); - }; - - function removeContainerClasses(){ - $.mobile - .pageContainer - .removeClass(pageContainerClasses.join(" ")); - - pageContainerClasses = []; - }; - - if(transition && (transition !== 'none')){ - if( perspectiveTransitions.indexOf(transition) >= 0 ){ - addContainerClass('ui-mobile-viewport-perspective'); - } - - addContainerClass('ui-mobile-viewport-transitioning'); - - // animate in / out - from.addClass( transition + " out " + ( back ? "reverse" : "" ) ); - to.addClass( $.mobile.activePageClass + " " + transition + - " in " + ( back ? "reverse" : "" ) ); - - // callback - remove classes, etc - to.animationComplete(function() { - from.add( to ).removeClass("out in reverse " + transition ); - from.removeClass( $.mobile.activePageClass ); - loadComplete(); - removeContainerClasses(); - }); + //set page's scrollTop to remembered distance + if( toPage.is( ".ui-native-fixed" ) ){ + toPage.find( ".ui-content" ).scrollTop( toScroll ); } else{ - from.removeClass( $.mobile.activePageClass ); - to.addClass( $.mobile.activePageClass ); - loadComplete(); + toPage.scrollTop( toScroll ); } - }; + } - //shared page enhancements - function enhancePage(){ + //find the transition handler for the specified transition. If there + //isn't one in our transitionHandlers dictionary, use the default one. + //call the handler immediately to kick-off the transition. + var th = $.mobile.transitionHandlers[transition || "none"] || $.mobile.defaultTransitionHandler, + promise = th( transition, reverse, toPage, fromPage ); - //set next page role, if defined - if ( nextPageRole || to.data('role') == 'dialog' ) { - changeHash = false; - if(nextPageRole){ - to.attr( "data-role", nextPageRole ); - nextPageRole = null; + promise.done(function() { + //reset toPage height back + if( !touchOverflow ){ + toPage.height( "" ); + // Send focus to the newly shown page + reFocus( toPage ); + } + + // Jump to top or prev scroll, sometimes on iOS the page has not rendered yet. + if( !touchOverflow ){ + $.mobile.silentScroll( toScroll ); + } + + //trigger show/hide events + if( fromPage ) { + if( !touchOverflow ){ + fromPage.height( "" ); } + + fromPage.data( "page" )._trigger( "hide", null, { nextPage: toPage } ); } - //run page plugin - to.page(); - }; + //trigger pageshow, define prevPage as either fromPage or empty jQuery obj + toPage.data( "page" )._trigger( "show", null, { prevPage: fromPage || $( "" ) } ); + }); - //if url is a string - if( url ){ - to = $( "[data-url='" + url + "']" ); - fileUrl = path.getFilePath(url); + return promise; + } + + //simply set the active page's minimum height to screen height, depending on orientation + function getScreenHeight(){ + var orientation = $.event.special.orientationchange.orientation(), + port = orientation === "portrait", + winMin = port ? 480 : 320, + screenHeight = port ? screen.availHeight : screen.availWidth, + winHeight = Math.max( winMin, $( window ).height() ), + pageMin = Math.min( screenHeight, winHeight ); + + return pageMin; + } + + $.mobile.getScreenHeight = getScreenHeight; + + //simply set the active page's minimum height to screen height, depending on orientation + function resetActivePageHeight(){ + // Don't apply this height in touch overflow enabled mode + if( $.support.touchOverflow && $.mobile.touchOverflowEnabled ){ + return; } - else{ //find base url of element, if avail - var toID = to.attr('data-url'), - toIDfileurl = path.getFilePath(toID); + $( "." + $.mobile.activePageClass ).css( "min-height", getScreenHeight() ); + } - if(toID != toIDfileurl){ - fileUrl = toIDfileurl; - } + //shared page enhancements + function enhancePage( $page, role ) { + // If a role was specified, make sure the data-role attribute + // on the page element is in sync. + if( role ) { + $page.attr( "data-" + $.mobile.ns + "role", role ); } - // find the "to" page, either locally existing in the dom or by creating it through ajax - if ( to.length && !isFormRequest ) { - if( fileUrl && base ){ - base.set( fileUrl ); + //run page plugin + $page.page(); + } + +/* exposed $.mobile methods */ + + //animation complete callback + $.fn.animationComplete = function( callback ) { + if( $.support.cssTransitions ) { + return $( this ).one( 'webkitAnimationEnd', callback ); + } + else{ + // defer execution for consistency between webkit/non webkit + setTimeout( callback, 0 ); + return $( this ); + } + }; + + //expose path object on $.mobile + $.mobile.path = path; + + //expose base object on $.mobile + $.mobile.base = base; + + //history stack + $.mobile.urlHistory = urlHistory; + + $.mobile.dialogHashKey = dialogHashKey; + + //default non-animation transition handler + $.mobile.noneTransitionHandler = function( name, reverse, $toPage, $fromPage ) { + if ( $fromPage ) { + $fromPage.removeClass( $.mobile.activePageClass ); + } + $toPage.addClass( $.mobile.activePageClass ); + + return $.Deferred().resolve( name, reverse, $toPage, $fromPage ).promise(); + }; + + //default handler for unknown transitions + $.mobile.defaultTransitionHandler = $.mobile.noneTransitionHandler; + + //transition handler dictionary for 3rd party transitions + $.mobile.transitionHandlers = { + none: $.mobile.defaultTransitionHandler + }; + + //enable cross-domain page support + $.mobile.allowCrossDomainPages = false; + + //return the original document url + $.mobile.getDocumentUrl = function(asParsedObject) { + return asParsedObject ? $.extend( {}, documentUrl ) : documentUrl.href; + }; + + //return the original document base url + $.mobile.getDocumentBase = function(asParsedObject) { + return asParsedObject ? $.extend( {}, documentBase ) : documentBase.href; + }; + + $.mobile._bindPageRemove = function() { + var page = $(this); + + // when dom caching is not enabled or the page is embedded bind to remove the page on hide + if( !page.data("page").options.domCache + && page.is(":jqmData(external-page='true')") ) { + + page.bind( 'pagehide.remove', function() { + var $this = $( this ), + prEvent = new $.Event( "pageremove" ); + + $this.trigger( prEvent ); + + if( !prEvent.isDefaultPrevented() ){ + $this.removeWithDependents(); + } + }); + } + }; + + // Load a page into the DOM. + $.mobile.loadPage = function( url, options ) { + // This function uses deferred notifications to let callers + // know when the page is done loading, or if an error has occurred. + var deferred = $.Deferred(), + + // The default loadPage options with overrides specified by + // the caller. + settings = $.extend( {}, $.mobile.loadPage.defaults, options ), + + // The DOM element for the page after it has been loaded. + page = null, + + // If the reloadPage option is true, and the page is already + // in the DOM, dupCachedPage will be set to the page element + // so that it can be removed after the new version of the + // page is loaded off the network. + dupCachedPage = null, + + // determine the current base url + findBaseWithDefault = function(){ + var closestBase = ( $.mobile.activePage && getClosestBaseUrl( $.mobile.activePage ) ); + return closestBase || documentBase.hrefNoHash; + }, + + // The absolute version of the URL passed into the function. This + // version of the URL may contain dialog/subpage params in it. + absUrl = path.makeUrlAbsolute( url, findBaseWithDefault() ); + + + // If the caller provided data, and we're using "get" request, + // append the data to the URL. + if ( settings.data && settings.type === "get" ) { + absUrl = path.addSearchParams( absUrl, settings.data ); + settings.data = undefined; + } + + // If the caller is using a "post" request, reloadPage must be true + if( settings.data && settings.type === "post" ){ + settings.reloadPage = true; + } + + // The absolute version of the URL minus any dialog/subpage params. + // In otherwords the real URL of the page to be loaded. + var fileUrl = path.getFilePath( absUrl ), + + // The version of the Url actually stored in the data-url attribute of + // the page. For embedded pages, it is just the id of the page. For pages + // within the same domain as the document base, it is the site relative + // path. For cross-domain pages (Phone Gap only) the entire absolute Url + // used to load the page. + dataUrl = path.convertUrlToDataUrl( absUrl ); + + // Make sure we have a pageContainer to work with. + settings.pageContainer = settings.pageContainer || $.mobile.pageContainer; + + // Check to see if the page already exists in the DOM. + page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" ); + + // If we failed to find the page, check to see if the url is a + // reference to an embedded page. If so, it may have been dynamically + // injected by a developer, in which case it would be lacking a data-url + // attribute and in need of enhancement. + if ( page.length === 0 && dataUrl && !path.isPath( dataUrl ) ) { + page = settings.pageContainer.children( "#" + dataUrl ) + .attr( "data-" + $.mobile.ns + "url", dataUrl ) + } + + // If we failed to find a page in the DOM, check the URL to see if it + // refers to the first page in the application. + if ( page.length === 0 && $.mobile.firstPage && path.isFirstPageUrl( fileUrl ) ) { + page = $( $.mobile.firstPage ); + } + + // Reset base to the default document base. + if ( base ) { + base.reset(); + } + + // If the page we are interested in is already in the DOM, + // and the caller did not indicate that we should force a + // reload of the file, we are done. Otherwise, track the + // existing page as a duplicated. + if ( page.length ) { + if ( !settings.reloadPage ) { + enhancePage( page, settings.role ); + deferred.resolve( absUrl, options, page ); + return deferred.promise(); } - enhancePage(); - transitionPages(); + dupCachedPage = page; + } + + var mpc = settings.pageContainer, + pblEvent = new $.Event( "pagebeforeload" ), + triggerData = { url: url, absUrl: absUrl, dataUrl: dataUrl, deferred: deferred, options: settings }; + + // Let listeners know we're about to load a page. + mpc.trigger( pblEvent, triggerData ); + + // If the default behavior is prevented, stop here! + if( pblEvent.isDefaultPrevented() ){ + return deferred.promise(); + } + + if ( settings.showLoadMsg ) { + + // This configurable timeout allows cached pages a brief delay to load without showing a message + var loadMsgDelay = setTimeout(function(){ + $.mobile.showPageLoadingMsg(); + }, settings.loadMsgDelay ), + + // Shared logic for clearing timeout and removing message. + hideMsg = function(){ + + // Stop message show timer + clearTimeout( loadMsgDelay ); + + // Hide loading message + $.mobile.hidePageLoadingMsg(); + }; + } + + if ( !( $.mobile.allowCrossDomainPages || path.isSameDomain( documentUrl, absUrl ) ) ) { + deferred.reject( absUrl, options ); } else { - - //if to exists in DOM, save a reference to it in duplicateCachedPage for removal after page change - if( to.length ){ - duplicateCachedPage = to; - } - - $.mobile.pageLoading(); - + // Load the new page. $.ajax({ url: fileUrl, - type: type, - data: data, + type: settings.type, + data: settings.data, + dataType: "html", success: function( html ) { - if(base){ base.set(fileUrl); } + //pre-parse html to check for a data-url, + //use it as the new fileUrl, base path, etc + var all = $( "
" ), + + //page title regexp + newPageTitle = html.match( /]*>([^<]*)/ ) && RegExp.$1, + + // TODO handle dialogs again + pageElemRegex = new RegExp( "(<[^>]+\\bdata-" + $.mobile.ns + "role=[\"']?page[\"']?[^>]*>)" ), + dataUrlRegex = new RegExp( "\\bdata-" + $.mobile.ns + "url=[\"']?([^\"'>]*)[\"']?" ); + + + // data-url must be provided for the base tag so resource requests can be directed to the + // correct url. loading into a temprorary element makes these requests immediately + if( pageElemRegex.test( html ) + && RegExp.$1 + && dataUrlRegex.test( RegExp.$1 ) + && RegExp.$1 ) { + url = fileUrl = path.getFilePath( RegExp.$1 ); + } + + if ( base ) { + base.set( fileUrl ); + } - var all = $("
"); //workaround to allow scripts to execute when included in page divs - all.get(0).innerHTML = html; - to = all.find('[data-role="page"], [data-role="dialog"]').first(); + all.get( 0 ).innerHTML = html; + page = all.find( ":jqmData(role='page'), :jqmData(role='dialog')" ).first(); + + //if page elem couldn't be found, create one and insert the body element's contents + if( !page.length ){ + page = $( "
" + html.split( /<\/?body[^>]*>/gmi )[1] + "
" ); + } + + if ( newPageTitle && !page.jqmData( "title" ) ) { + page.jqmData( "title", newPageTitle ); + } //rewrite src and href attrs to use a base url - if( !$.support.dynamicBaseTag ){ + if( !$.support.dynamicBaseTag ) { var newPath = path.get( fileUrl ); - to.find('[src],link[href]').each(function(){ - var thisAttr = $(this).is('[href]') ? 'href' : 'src', - thisUrl = $(this).attr(thisAttr); + page.find( "[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]" ).each(function() { + var thisAttr = $( this ).is( '[href]' ) ? 'href' : + $(this).is('[src]') ? 'src' : 'action', + thisUrl = $( this ).attr( thisAttr ); + // XXX_jblas: We need to fix this so that it removes the document + // base URL, and then prepends with the new page URL. //if full path exists and is same, chop it - helps IE out - thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' ); + thisUrl = thisUrl.replace( location.protocol + '//' + location.host + location.pathname, '' ); - if( !/^(\w+:|#|\/)/.test(thisUrl) ){ - $(this).attr(thisAttr, newPath + thisUrl); + if( !/^(\w+:|#|\/)/.test( thisUrl ) ) { + $( this ).attr( thisAttr, newPath + thisUrl ); } }); } - to - .attr( "data-url", fileUrl ) - .appendTo( $.mobile.pageContainer ); + //append to page and enhance + // TODO taging a page with external to make sure that embedded pages aren't removed + // by the various page handling code is bad. Having page handling code in many + // places is bad. Solutions post 1.0 + page + .attr( "data-" + $.mobile.ns + "url", path.convertUrlToDataUrl( fileUrl ) ) + .attr( "data-" + $.mobile.ns + "external-page", true ) + .appendTo( settings.pageContainer ); - enhancePage(); - transitionPages(); + // wait for page creation to leverage options defined on widget + page.one( 'pagecreate', $.mobile._bindPageRemove ); + + enhancePage( page, settings.role ); + + // Enhancing the page may result in new dialogs/sub pages being inserted + // into the DOM. If the original absUrl refers to a sub-page, that is the + // real page we are interested in. + if ( absUrl.indexOf( "&" + $.mobile.subPageUrlKey ) > -1 ) { + page = settings.pageContainer.children( ":jqmData(url='" + dataUrl + "')" ); + } + + //bind pageHide to removePage after it's hidden, if the page options specify to do so + + // Remove loading message. + if ( settings.showLoadMsg ) { + hideMsg(); + } + + // Add the page reference to our triggerData. + triggerData.page = page; + + // Let listeners know the page loaded successfully. + settings.pageContainer.trigger( "pageload", triggerData ); + + deferred.resolve( absUrl, options, page, dupCachedPage ); }, error: function() { - $.mobile.pageLoading( true ); - removeActiveLinkClass(true); - base.set(path.get()); - $("

Error Loading Page

") - .css({ "display": "block", "opacity": 0.96, "top": $(window).scrollTop() + 100 }) - .appendTo( $.mobile.pageContainer ) - .delay( 800 ) - .fadeOut( 400, function(){ - $(this).remove(); - }); + //set base back to current path + if( base ) { + base.set( path.get() ); + } + + var plfEvent = new $.Event( "pageloadfailed" ); + + // Let listeners know the page load failed. + settings.pageContainer.trigger( plfEvent, triggerData ); + + // If the default behavior is prevented, stop here! + // Note that it is the responsibility of the listener/handler + // that called preventDefault(), to resolve/reject the + // deferred object within the triggerData. + if( plfEvent.isDefaultPrevented() ){ + return; + } + + // Remove loading message. + if ( settings.showLoadMsg ) { + + // Remove loading message. + hideMsg(); + + //show error message + $( "

"+ $.mobile.pageLoadErrorMessage +"

" ) + .css({ "display": "block", "opacity": 0.96, "top": $window.scrollTop() + 100 }) + .appendTo( settings.pageContainer ) + .delay( 800 ) + .fadeOut( 400, function() { + $( this ).remove(); + }); + } + + deferred.reject( absUrl, options ); } }); } + return deferred.promise(); }; + $.mobile.loadPage.defaults = { + type: "get", + data: undefined, + reloadPage: false, + role: undefined, // By default we rely on the role defined by the @data-role attribute. + showLoadMsg: false, + pageContainer: undefined, + loadMsgDelay: 50 // This delay allows loads that pull from browser cache to occur without showing the loading message. + }; + + // Show a specific page in the page container. + $.mobile.changePage = function( toPage, options ) { + // If we are in the midst of a transition, queue the current request. + // We'll call changePage() once we're done with the current transition to + // service the request. + if( isPageTransitioning ) { + pageTransitionQueue.unshift( arguments ); + return; + } + + var settings = $.extend( {}, $.mobile.changePage.defaults, options ); + + // Make sure we have a pageContainer to work with. + settings.pageContainer = settings.pageContainer || $.mobile.pageContainer; + + // Make sure we have a fromPage. + settings.fromPage = settings.fromPage || $.mobile.activePage; + + var mpc = settings.pageContainer, + pbcEvent = new $.Event( "pagebeforechange" ), + triggerData = { toPage: toPage, options: settings }; + + // Let listeners know we're about to change the current page. + mpc.trigger( pbcEvent, triggerData ); + + // If the default behavior is prevented, stop here! + if( pbcEvent.isDefaultPrevented() ){ + return; + } + + // We allow "pagebeforechange" observers to modify the toPage in the trigger + // data to allow for redirects. Make sure our toPage is updated. + + toPage = triggerData.toPage; + + // Set the isPageTransitioning flag to prevent any requests from + // entering this method while we are in the midst of loading a page + // or transitioning. + + isPageTransitioning = true; + + // If the caller passed us a url, call loadPage() + // to make sure it is loaded into the DOM. We'll listen + // to the promise object it returns so we know when + // it is done loading or if an error ocurred. + if ( typeof toPage == "string" ) { + $.mobile.loadPage( toPage, settings ) + .done(function( url, options, newPage, dupCachedPage ) { + isPageTransitioning = false; + options.duplicateCachedPage = dupCachedPage; + $.mobile.changePage( newPage, options ); + }) + .fail(function( url, options ) { + isPageTransitioning = false; + + //clear out the active button state + removeActiveLinkClass( true ); + + //release transition lock so navigation is free again + releasePageTransitionLock(); + settings.pageContainer.trigger( "pagechangefailed", triggerData ); + }); + return; + } + + // If we are going to the first-page of the application, we need to make + // sure settings.dataUrl is set to the application document url. This allows + // us to avoid generating a document url with an id hash in the case where the + // first-page of the document has an id attribute specified. + if ( toPage[ 0 ] === $.mobile.firstPage[ 0 ] && !settings.dataUrl ) { + settings.dataUrl = documentUrl.hrefNoHash; + } + + // The caller passed us a real page DOM element. Update our + // internal state and then trigger a transition to the page. + var fromPage = settings.fromPage, + url = ( settings.dataUrl && path.convertUrlToDataUrl( settings.dataUrl ) ) || toPage.jqmData( "url" ), + // The pageUrl var is usually the same as url, except when url is obscured as a dialog url. pageUrl always contains the file path + pageUrl = url, + fileUrl = path.getFilePath( url ), + active = urlHistory.getActive(), + activeIsInitialPage = urlHistory.activeIndex === 0, + historyDir = 0, + pageTitle = document.title, + isDialog = settings.role === "dialog" || toPage.jqmData( "role" ) === "dialog"; + + // By default, we prevent changePage requests when the fromPage and toPage + // are the same element, but folks that generate content manually/dynamically + // and reuse pages want to be able to transition to the same page. To allow + // this, they will need to change the default value of allowSamePageTransition + // to true, *OR*, pass it in as an option when they manually call changePage(). + // It should be noted that our default transition animations assume that the + // formPage and toPage are different elements, so they may behave unexpectedly. + // It is up to the developer that turns on the allowSamePageTransitiona option + // to either turn off transition animations, or make sure that an appropriate + // animation transition is used. + if( fromPage && fromPage[0] === toPage[0] && !settings.allowSamePageTransition ) { + isPageTransitioning = false; + mpc.trigger( "pagechange", triggerData ); + return; + } + + // We need to make sure the page we are given has already been enhanced. + enhancePage( toPage, settings.role ); + + // If the changePage request was sent from a hashChange event, check to see if the + // page is already within the urlHistory stack. If so, we'll assume the user hit + // the forward/back button and will try to match the transition accordingly. + if( settings.fromHashChange ) { + urlHistory.directHashChange({ + currentUrl: url, + isBack: function() { historyDir = -1; }, + isForward: function() { historyDir = 1; } + }); + } + + // Kill the keyboard. + // XXX_jblas: We need to stop crawling the entire document to kill focus. Instead, + // we should be tracking focus with a live() handler so we already have + // the element in hand at this point. + // Wrap this in a try/catch block since IE9 throw "Unspecified error" if document.activeElement + // is undefined when we are in an IFrame. + try { + $( document.activeElement || "" ).add( "input:focus, textarea:focus, select:focus" ).blur(); + } catch(e) {} + + // If we're displaying the page as a dialog, we don't want the url + // for the dialog content to be used in the hash. Instead, we want + // to append the dialogHashKey to the url of the current page. + if ( isDialog && active ) { + // on the initial page load active.url is undefined and in that case should + // be an empty string. Moving the undefined -> empty string back into + // urlHistory.addNew seemed imprudent given undefined better represents + // the url state + url = ( active.url || "" ) + dialogHashKey; + } + + // Set the location hash. + if( settings.changeHash !== false && url ) { + //disable hash listening temporarily + urlHistory.ignoreNextHashChange = true; + //update hash and history + path.set( url ); + } + + //if title element wasn't found, try the page div data attr too + var newPageTitle = toPage.jqmData( "title" ) || toPage.children(":jqmData(role='header')").find(".ui-title" ).getEncodedText(); + if( !!newPageTitle && pageTitle == document.title ) { + pageTitle = newPageTitle; + } + + // Make sure we have a transition defined. + settings.transition = settings.transition + || ( ( historyDir && !activeIsInitialPage ) ? active.transition : undefined ) + || ( isDialog ? $.mobile.defaultDialogTransition : $.mobile.defaultPageTransition ); + + //add page to history stack if it's not back or forward + if( !historyDir ) { + urlHistory.addNew( url, settings.transition, pageTitle, pageUrl, settings.role ); + } + + //set page title + document.title = urlHistory.getActive().title; + + //set "toPage" as activePage + $.mobile.activePage = toPage; + + // If we're navigating back in the URL history, set reverse accordingly. + settings.reverse = settings.reverse || historyDir < 0; + + transitionPages( toPage, fromPage, settings.transition, settings.reverse ) + .done(function() { + removeActiveLinkClass(); + + //if there's a duplicateCachedPage, remove it from the DOM now that it's hidden + if ( settings.duplicateCachedPage ) { + settings.duplicateCachedPage.remove(); + } + + //remove initial build class (only present on first pageshow) + $html.removeClass( "ui-mobile-rendering" ); + + releasePageTransitionLock(); + + // Let listeners know we're all done changing the current page. + mpc.trigger( "pagechange", triggerData ); + }); + }; + + $.mobile.changePage.defaults = { + transition: undefined, + reverse: false, + changeHash: true, + fromHashChange: false, + role: undefined, // By default we rely on the role defined by the @data-role attribute. + duplicateCachedPage: undefined, + pageContainer: undefined, + showLoadMsg: true, //loading message shows by default when pages are being fetched during changePage + dataUrl: undefined, + fromPage: undefined, + allowSamePageTransition: false + }; /* Event Bindings - hashchange, submit, and click */ - - //bind to form submit events, handle with Ajax - $('form').live('submit', function(event){ - if( !$.mobile.ajaxFormsEnabled ){ return; } - - var type = $(this).attr("method"), - url = $(this).attr( "action" ).replace( location.protocol + "//" + location.host, ""); - - //external submits use regular HTTP - if( /^(:?\w+:)/.test( url ) ){ - return; - } - - //if it's a relative href, prefix href with base url - if( url.indexOf('/') && url.indexOf('#') !== 0 ){ - url = path.get() + url; - } - - $.mobile.changePage({ - url: url, - type: type, - data: $(this).serialize() - }, - undefined, - undefined, - true - ); - event.preventDefault(); - }); - - - //click routing - direct to HTTP or Ajax, accordingly - $( "a" ).live( "click", function(event) { - - if( !$.mobile.ajaxLinksEnabled ){ return; } - var $this = $(this), - //get href, remove same-domain protocol and host - href = $this.attr( "href" ).replace( location.protocol + "//" + location.host, ""), - //if target attr is specified, it's external, and we mimic _blank... for now - target = $this.is( "[target]" ), - //if it still starts with a protocol, it's external, or could be :mailto, etc - external = target || /^(:?\w+:)/.test( href ) || $this.is( "[rel=external]" ); - - if( href === '#' ){ - //for links created purely for interaction - ignore - return false; - } - - $activeClickedLink = $this.closest( ".ui-btn" ).addClass( $.mobile.activeBtnClass ); - - if( external || !$.mobile.ajaxLinksEnabled ){ - //remove active link class if external - removeActiveLinkClass(true); - - //deliberately redirect, in case click was triggered - if( target ){ - window.open(href); - } - else{ - location.href = href; + function findClosestLink( ele ) + { + while ( ele ) { + if ( ele.nodeName.toLowerCase() == "a" ) { + break; } + ele = ele.parentNode; } - else { + return ele; + } + + // The base URL for any given element depends on the page it resides in. + function getClosestBaseUrl( ele ) + { + // Find the closest page and extract out its url. + var url = $( ele ).closest( ".ui-page" ).jqmData( "url" ), + base = documentBase.hrefNoHash; + + if ( !url || !path.isPath( url ) ) { + url = base; + } + + return path.makeUrlAbsolute( url, base); + } + + + //The following event bindings should be bound after mobileinit has been triggered + //the following function is called in the init file + $.mobile._registerInternalEvents = function(){ + + //bind to form submit events, handle with Ajax + $( "form" ).live('submit', function( event ) { + var $this = $( this ); + if( !$.mobile.ajaxEnabled || + $this.is( ":jqmData(ajax='false')" ) ) { + return; + } + + var type = $this.attr( "method" ), + target = $this.attr( "target" ), + url = $this.attr( "action" ); + + // If no action is specified, browsers default to using the + // URL of the document containing the form. Since we dynamically + // pull in pages from external documents, the form should submit + // to the URL for the source document of the page containing + // the form. + if ( !url ) { + // Get the @data-url for the page containing the form. + url = getClosestBaseUrl( $this ); + if ( url === documentBase.hrefNoHash ) { + // The url we got back matches the document base, + // which means the page must be an internal/embedded page, + // so default to using the actual document url as a browser + // would. + url = documentUrl.hrefNoSearch; + } + } + + url = path.makeUrlAbsolute( url, getClosestBaseUrl($this) ); + + //external submits use regular HTTP + if( path.isExternal( url ) || target ) { + return; + } + + $.mobile.changePage( + url, + { + type: type && type.length && type.toLowerCase() || "get", + data: $this.serialize(), + transition: $this.jqmData( "transition" ), + direction: $this.jqmData( "direction" ), + reloadPage: true + } + ); + event.preventDefault(); + }); + + //add active state on vclick + $( document ).bind( "vclick", function( event ) { + // if this isn't a left click we don't care. Its important to note + // that when the virtual event is generated it will create + if ( event.which > 1 || !$.mobile.linkBindingEnabled ){ + return; + } + + var link = findClosestLink( event.target ); + if ( link ) { + if ( path.parseUrl( link.getAttribute( "href" ) || "#" ).hash !== "#" ) { + removeActiveLinkClass( true ); + $activeClickedLink = $( link ).closest( ".ui-btn" ).not( ".ui-disabled" ); + $activeClickedLink.addClass( $.mobile.activeBtnClass ); + $( "." + $.mobile.activePageClass + " .ui-btn" ).not( link ).blur(); + } + } + }); + + // click routing - direct to HTTP or Ajax, accordingly + $( document ).bind( "click", function( event ) { + if( !$.mobile.linkBindingEnabled ){ + return; + } + + var link = findClosestLink( event.target ); + + // If there is no link associated with the click or its not a left + // click we want to ignore the click + if ( !link || event.which > 1) { + return; + } + + var $link = $( link ), + //remove active link class if external (then it won't be there if you come back) + httpCleanup = function(){ + window.setTimeout( function() { removeActiveLinkClass( true ); }, 200 ); + }; + + //if there's a data-rel=back attr, go back in history + if( $link.is( ":jqmData(rel='back')" ) ) { + window.history.back(); + return false; + } + + var baseUrl = getClosestBaseUrl( $link ), + + //get href, if defined, otherwise default to empty hash + href = path.makeUrlAbsolute( $link.attr( "href" ) || "#", baseUrl ); + + //if ajax is disabled, exit early + if( !$.mobile.ajaxEnabled && !path.isEmbeddedPage( href ) ){ + httpCleanup(); + //use default click handling + return; + } + + // XXX_jblas: Ideally links to application pages should be specified as + // an url to the application document with a hash that is either + // the site relative path or id to the page. But some of the + // internal code that dynamically generates sub-pages for nested + // lists and select dialogs, just write a hash in the link they + // create. This means the actual URL path is based on whatever + // the current value of the base tag is at the time this code + // is called. For now we are just assuming that any url with a + // hash in it is an application page reference. + if ( href.search( "#" ) != -1 ) { + href = href.replace( /[^#]*#/, "" ); + if ( !href ) { + //link was an empty hash meant purely + //for interaction, so we ignore it. + event.preventDefault(); + return; + } else if ( path.isPath( href ) ) { + //we have apath so make it the href we want to load. + href = path.makeUrlAbsolute( href, baseUrl ); + } else { + //we have a simple id so use the documentUrl as its base. + href = path.makeUrlAbsolute( "#" + href, documentUrl.hrefNoHash ); + } + } + + // Should we handle this link, or let the browser deal with it? + var useDefaultUrlHandling = $link.is( "[rel='external']" ) || $link.is( ":jqmData(ajax='false')" ) || $link.is( "[target]" ), + + // Some embedded browsers, like the web view in Phone Gap, allow cross-domain XHR + // requests if the document doing the request was loaded via the file:// protocol. + // This is usually to allow the application to "phone home" and fetch app specific + // data. We normally let the browser handle external/cross-domain urls, but if the + // allowCrossDomainPages option is true, we will allow cross-domain http/https + // requests to go through our page loading logic. + isCrossDomainPageLoad = ( $.mobile.allowCrossDomainPages && documentUrl.protocol === "file:" && href.search( /^https?:/ ) != -1 ), + + //check for protocol or rel and its not an embedded page + //TODO overlap in logic from isExternal, rel=external check should be + // moved into more comprehensive isExternalLink + isExternal = useDefaultUrlHandling || ( path.isExternal( href ) && !isCrossDomainPageLoad ); + + if( isExternal ) { + httpCleanup(); + //use default click handling + return; + } + //use ajax - var transition = $this.data( "transition" ), - back = $this.data( "back" ); + var transition = $link.jqmData( "transition" ), + direction = $link.jqmData( "direction" ), + reverse = ( direction && direction === "reverse" ) || + // deprecated - remove by 1.0 + $link.jqmData( "back" ), - nextPageRole = $this.attr( "data-rel" ); + //this may need to be more specific as we use data-rel more + role = $link.attr( "data-" + $.mobile.ns + "rel" ) || undefined; - //if it's a relative href, prefix href with base url - if( href.indexOf('/') && href.indexOf('#') !== 0 ){ - href = path.get() + href; + $.mobile.changePage( href, { transition: transition, reverse: reverse, role: role } ); + event.preventDefault(); + }); + + //prefetch pages when anchors with data-prefetch are encountered + $( ".ui-page" ).live( "pageshow.prefetch", function() { + var urls = []; + $( this ).find( "a:jqmData(prefetch)" ).each(function(){ + var $link = $(this), + url = $link.attr( "href" ); + + if ( url && $.inArray( url, urls ) === -1 ) { + urls.push( url ); + + $.mobile.loadPage( url, {role: $link.attr("data-" + $.mobile.ns + "rel")} ); + } + }); + }); + + $.mobile._handleHashChange = function( hash ) { + //find first page via hash + var to = path.stripHash( hash ), + //transition is false if it's the first page, undefined otherwise (and may be overridden by default) + transition = $.mobile.urlHistory.stack.length === 0 ? "none" : undefined, + + // default options for the changPage calls made after examining the current state + // of the page and the hash + changePageOptions = { + transition: transition, + changeHash: false, + fromHashChange: true + }; + + //if listening is disabled (either globally or temporarily), or it's a dialog hash + if( !$.mobile.hashListeningEnabled || urlHistory.ignoreNextHashChange ) { + urlHistory.ignoreNextHashChange = false; + return; } - href.replace(/^#/,''); + // special case for dialogs + if( urlHistory.stack.length > 1 && to.indexOf( dialogHashKey ) > -1 ) { - $.mobile.changePage(href, transition, back); - } - event.preventDefault(); - }); + // If current active page is not a dialog skip the dialog and continue + // in the same direction + if(!$.mobile.activePage.is( ".ui-dialog" )) { + //determine if we're heading forward or backward and continue accordingly past + //the current dialog + urlHistory.directHashChange({ + currentUrl: to, + isBack: function() { window.history.back(); }, + isForward: function() { window.history.forward(); } + }); + // prevent changePage() + return; + } else { + // if the current active page is a dialog and we're navigating + // to a dialog use the dialog objected saved in the stack + urlHistory.directHashChange({ + currentUrl: to, + // regardless of the direction of the history change + // do the following + either: function( isBack ) { + var active = $.mobile.urlHistory.getActive(); - //hashchange event handler - $window.bind( "hashchange", function(e, triggered) { - if( !hashListener ){ - hashListener = true; - return; - } + to = active.pageUrl; - if( $(".ui-page-active").is("[data-role=" + $.mobile.nonHistorySelectors + "]") ){ - return; - } - - var to = location.hash, - transition = triggered ? false : undefined; - - //if to is defined, use it - if ( to ){ - $.mobile.changePage( to, transition, undefined, false); - } - //there's no hash, the active page is not the start page, and it's not manually triggered hashchange - //we probably backed out to the first page visited - else if( $.mobile.activePage.length && $.mobile.startPage[0] !== $.mobile.activePage[0] && !triggered ) { - $.mobile.changePage( $.mobile.startPage, transition, true, false ); - } - //probably the first page - show it - else{ - $.mobile.startPage.trigger("pagebeforeshow", {prevPage: $('')}); - $.mobile.startPage.addClass( $.mobile.activePageClass ); - $.mobile.pageLoading( true ); - - if( $.mobile.startPage.trigger("pageshow", {prevPage: $('')}) !== false ){ - reFocus($.mobile.startPage); + // make sure to set the role, transition and reversal + // as most of this is lost by the domCache cleaning + $.extend( changePageOptions, { + role: active.role, + transition: active.transition, + reverse: isBack + }); + } + }); + } } - } - }); -})( jQuery ); \ No newline at end of file + + //if to is defined, load it + if ( to ) { + // At this point, 'to' can be one of 3 things, a cached page element from + // a history stack entry, an id, or site-relative/absolute URL. If 'to' is + // an id, we need to resolve it against the documentBase, not the location.href, + // since the hashchange could've been the result of a forward/backward navigation + // that crosses from an external page/dialog to an internal page/dialog. + to = ( typeof to === "string" && !path.isPath( to ) ) ? ( path.makeUrlAbsolute( '#' + to, documentBase ) ) : to; + $.mobile.changePage( to, changePageOptions ); + } else { + //there's no hash, go to the first page in the dom + $.mobile.changePage( $.mobile.firstPage, changePageOptions ); + } + }; + + //hashchange event handler + $window.bind( "hashchange", function( e, triggered ) { + $.mobile._handleHashChange( location.hash ); + }); + + //set page min-heights to be device specific + $( document ).bind( "pageshow", resetActivePageHeight ); + $( window ).bind( "throttledresize", resetActivePageHeight ); + + };//_registerInternalEvents callback + +})( jQuery ); diff --git a/js/jquery.mobile.navigation.pushstate.js b/js/jquery.mobile.navigation.pushstate.js new file mode 100644 index 00000000..69573732 --- /dev/null +++ b/js/jquery.mobile.navigation.pushstate.js @@ -0,0 +1,136 @@ +/* +* jQuery Mobile Framework : history.pushState support, layered on top of hashchange +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ +( function( $, window ) { + // For now, let's Monkeypatch this onto the end of $.mobile._registerInternalEvents + // Scope self to pushStateHandler so we can reference it sanely within the + // methods handed off as event handlers + var pushStateHandler = {}, + self = pushStateHandler, + $win = $( window ), + url = $.mobile.path.parseUrl( location.href ); + + $.extend( pushStateHandler, { + // TODO move to a path helper, this is rather common functionality + initialFilePath: (function() { + return url.pathname + url.search; + })(), + + initialHref: url.hrefNoHash, + + // Flag for tracking if a Hashchange naturally occurs after each popstate + replace + hashchangeFired: false, + + state: function() { + return { + hash: location.hash || "#" + self.initialFilePath, + title: document.title, + + // persist across refresh + initialHref: self.initialHref + }; + }, + + resetUIKeys: function( url ) { + var dialog = $.mobile.dialogHashKey, + subkey = "&" + $.mobile.subPageUrlKey, + dialogIndex = url.indexOf( dialog ); + + if( dialogIndex > -1 ) { + url = url.slice( 0, dialogIndex ) + "#" + url.slice( dialogIndex ); + } else if( url.indexOf( subkey ) > -1 ) { + url = url.split( subkey ).join( "#" + subkey ); + } + + return url; + }, + + // TODO sort out a single barrier to hashchange functionality + nextHashChangePrevented: function( value ) { + $.mobile.urlHistory.ignoreNextHashChange = value; + self.onHashChangeDisabled = value; + }, + + // on hash change we want to clean up the url + // NOTE this takes place *after* the vanilla navigation hash change + // handling has taken place and set the state of the DOM + onHashChange: function( e ) { + // disable this hash change + if( self.onHashChangeDisabled ){ + return; + } + + var href, state, + hash = location.hash, + isPath = $.mobile.path.isPath( hash ), + resolutionUrl = isPath ? location.href : $.mobile.getDocumentUrl(); + hash = isPath ? hash.replace( "#", "" ) : hash; + + // propulate the hash when its not available + state = self.state(); + + // make the hash abolute with the current href + href = $.mobile.path.makeUrlAbsolute( hash, resolutionUrl ); + + if ( isPath ) { + href = self.resetUIKeys( href ); + } + + // replace the current url with the new href and store the state + // Note that in some cases we might be replacing an url with the + // same url. We do this anyways because we need to make sure that + // all of our history entries have a state object associated with + // them. This allows us to work around the case where window.history.back() + // is called to transition from an external page to an embedded page. + // In that particular case, a hashchange event is *NOT* generated by the browser. + // Ensuring each history entry has a state object means that onPopState() + // will always trigger our hashchange callback even when a hashchange event + // is not fired. + history.replaceState( state, document.title, href ); + }, + + // on popstate (ie back or forward) we need to replace the hash that was there previously + // cleaned up by the additional hash handling + onPopState: function( e ) { + var poppedState = e.originalEvent.state, holdnexthashchange = false; + + // if there's no state its not a popstate we care about, ie chrome's initial popstate + // or forward popstate + if( poppedState ) { + // disable any hashchange triggered by the browser + self.nextHashChangePrevented( true ); + + // defer our manual hashchange until after the browser fired + // version has come and gone + setTimeout(function() { + // make sure that the manual hash handling takes place + self.nextHashChangePrevented( false ); + + // change the page based on the hash + $.mobile._handleHashChange( poppedState.hash ); + }, 100); + } + }, + + init: function() { + $win.bind( "hashchange", self.onHashChange ); + + // Handle popstate events the occur through history changes + $win.bind( "popstate", self.onPopState ); + + // if there's no hash, we need to replacestate for returning to home + if ( location.hash === "" ) { + history.replaceState( self.state(), document.title, location.href ); + } + } + }); + + $( function() { + if( $.mobile.pushStateEnabled && $.support.pushState ){ + pushStateHandler.init(); + } + }); +})( jQuery, this ); \ No newline at end of file diff --git a/js/jquery.mobile.nojs.js b/js/jquery.mobile.nojs.js new file mode 100644 index 00000000..7804f41a --- /dev/null +++ b/js/jquery.mobile.nojs.js @@ -0,0 +1,15 @@ +/* +* jQuery Mobile Framework : "nojs" plugin - class to make elements hidden to A grade browsers +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$( document ).bind( "pagecreate create", function( e ){ + $( ":jqmData(role='nojs')", e.target ).addClass( "ui-nojs" ); + +}); + +})( jQuery ); \ No newline at end of file diff --git a/js/jquery.mobile.page.js b/js/jquery.mobile.page.js index 80283117..5f492393 100644 --- a/js/jquery.mobile.page.js +++ b/js/jquery.mobile.page.js @@ -4,187 +4,34 @@ * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license */ -(function($, undefined ) { + +(function( $, undefined ) { $.widget( "mobile.page", $.mobile.widget, { options: { - backBtnText: "Back", - addBackBtn: true, - degradeInputs: { - color: false, - date: false, - datetime: false, - "datetime-local": false, - email: false, - month: false, - number: false, - range: "number", - search: true, - tel: false, - time: false, - url: false, - week: false - }, - keepNative: null + theme: "c", + domCache: false, + keepNativeDefault: ":jqmData(role='none'), :jqmData(role='nojs')" }, _create: function() { - var $elem = this.element, - o = this.options; - this.keepNative = "[data-role='none'], [data-role='nojs']" + (o.keepNative ? ", " + o.keepNative : ""); + this._trigger( "beforecreate" ); - if ( this._trigger( "beforeCreate" ) === false ) { - return; - } - - //some of the form elements currently rely on the presence of ui-page and ui-content - // classes so we'll handle page and content roles outside of the main role processing - // loop below. - $elem.find( "[data-role='page'], [data-role='content']" ).andSelf().each(function() { - $(this).addClass( "ui-" + $(this).data( "role" ) ); - }); - - $elem.find( "[data-role='nojs']" ).addClass( "ui-nojs" ); - - this._enchanceControls(); - - // pre-find data els - var $dataEls = $elem.find( "[data-role]" ).andSelf().each(function() { - var $this = $( this ), - role = $this.data( "role" ), - theme = $this.data( "theme" ); - - //apply theming and markup modifications to page,header,content,footer - if ( role === "header" || role === "footer" ) { - $this.addClass( "ui-bar-" + (theme || $this.parent('[data-role=page]').data( "theme" ) || "a") ); - - // add ARIA role - $this.attr( "role", role === "header" ? "banner" : "contentinfo" ); - - //right,left buttons - var $headeranchors = $this.children( "a" ), - leftbtn = $headeranchors.hasClass( "ui-btn-left" ), - rightbtn = $headeranchors.hasClass( "ui-btn-right" ); - - if ( !leftbtn ) { - leftbtn = $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length; - } - - if ( !rightbtn ) { - rightbtn = $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length; - } - - // auto-add back btn on pages beyond first view - if ( o.addBackBtn && role === "header" && - ($.mobile.urlStack.length > 1 || $(".ui-page").length > 1) && - !leftbtn && !$this.data( "noBackBtn" ) ) { - - $( ""+ o.backBtnText +"" ) - .click(function() { - history.back(); - return false; - }) - .prependTo( $this ); - } - - //page title - $this.children( "h1, h2, h3, h4, h5, h6" ) - .addClass( "ui-title" ) - //regardless of h element number in src, it becomes h1 for the enhanced page - .attr({ "tabindex": "0", "role": "heading", "aria-level": "1" }); - - } else if ( role === "content" ) { - if ( theme ) { - $this.addClass( "ui-body-" + theme ); - } - - // add ARIA role - $this.attr( "role", "main" ); - - } else if ( role === "page" ) { - $this.addClass( "ui-body-" + (theme || "c") ); - } - - switch(role) { - case "header": - case "footer": - case "page": - case "content": - $this.addClass( "ui-" + role ); - break; - case "collapsible": - case "fieldcontain": - case "navbar": - case "listview": - case "dialog": - $this[ role ](); - break; - } - }); - - //links in bars, or those with data-role become buttons - $elem.find( "[data-role='button'], .ui-bar > a, .ui-header > a, .ui-footer > a" ) - .not( ".ui-btn" ) - .not(this.keepNative) - .buttonMarkup(); - - $elem - .find("[data-role='controlgroup']") - .controlgroup(); - - //links within content areas - $elem.find( "a:not(.ui-btn):not(.ui-link-inherit)" ) - .not(this.keepNative) - .addClass( "ui-link" ); - - //fix toolbars - $elem.fixHeaderFooter(); + this.element + .attr( "tabindex", "0" ) + .addClass( "ui-page ui-body-" + this.options.theme ); }, - _enchanceControls: function() { - var o = this.options; + keepNativeSelector: function() { + var options = this.options, + keepNativeDefined = options.keepNative && $.trim(options.keepNative); - // degrade inputs to avoid poorly implemented native functionality - this.element.find( "input" ).not(this.keepNative).each(function() { - var type = this.getAttribute( "type" ), - optType = o.degradeInputs[ type ] || "text"; + if( keepNativeDefined && options.keepNative !== options.keepNativeDefault ){ + return [options.keepNative, options.keepNativeDefault].join(", "); + } - if ( o.degradeInputs[ type ] ) { - $( this ).replaceWith( - $( "
" ).html( $(this).clone() ).html() - .replace( /type="([a-zA-Z]+)"/, "type="+ optType +" data-type='$1'" ) ); - } - }); - - // enchance form controls - this.element - .find( "[type='radio'], [type='checkbox']" ) - .not(this.keepNative) - .checkboxradio(); - - this.element - .find( "button, [type='button'], [type='submit'], [type='reset'], [type='image']" ) - .not(this.keepNative) - .button(); - - this.element - .find( "input, textarea" ) - .not( "[type='radio'], [type='checkbox'], button, [type='button'], [type='submit'], [type='reset'], [type='image']" ) - .not(this.keepNative) - .textinput(); - - this.element - .find( "input, select" ) - .not(this.keepNative) - .filter( "[data-role='slider'], [data-type='range']" ) - .slider(); - - this.element - .find( "select:not([data-role='slider'])" ) - .not(this.keepNative) - .selectmenu(); + return options.keepNativeDefault; } }); - })( jQuery ); diff --git a/js/jquery.mobile.page.sections.js b/js/jquery.mobile.page.sections.js new file mode 100644 index 00000000..6e04bfbe --- /dev/null +++ b/js/jquery.mobile.page.sections.js @@ -0,0 +1,88 @@ +/* +* jQuery Mobile Framework : This plugin handles theming and layout of headers, footers, and content areas +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +(function( $, undefined ) { + +$.mobile.page.prototype.options.backBtnText = "Back"; +$.mobile.page.prototype.options.addBackBtn = false; +$.mobile.page.prototype.options.backBtnTheme = null; +$.mobile.page.prototype.options.headerTheme = "a"; +$.mobile.page.prototype.options.footerTheme = "a"; +$.mobile.page.prototype.options.contentTheme = null; + +$( ":jqmData(role='page'), :jqmData(role='dialog')" ).live( "pagecreate", function( e ) { + + var $page = $( this ), + o = $page.data( "page" ).options, + pageTheme = o.theme; + + $( ":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')", this ).each(function() { + var $this = $( this ), + role = $this.jqmData( "role" ), + theme = $this.jqmData( "theme" ), + $headeranchors, + leftbtn, + rightbtn, + backBtn; + + $this.addClass( "ui-" + role ); + + //apply theming and markup modifications to page,header,content,footer + if ( role === "header" || role === "footer" ) { + + var thisTheme = theme || ( role === "header" ? o.headerTheme : o.footerTheme ) || pageTheme; + + $this + //add theme class + .addClass( "ui-bar-" + thisTheme ) + // Add ARIA role + .attr( "role", role === "header" ? "banner" : "contentinfo" ); + + // Right,left buttons + $headeranchors = $this.children( "a" ); + leftbtn = $headeranchors.hasClass( "ui-btn-left" ); + rightbtn = $headeranchors.hasClass( "ui-btn-right" ); + + leftbtn = leftbtn || $headeranchors.eq( 0 ).not( ".ui-btn-right" ).addClass( "ui-btn-left" ).length; + + rightbtn = rightbtn || $headeranchors.eq( 1 ).addClass( "ui-btn-right" ).length; + + // Auto-add back btn on pages beyond first view + if ( o.addBackBtn && + role === "header" && + $( ".ui-page" ).length > 1 && + $this.jqmData( "url" ) !== $.mobile.path.stripHash( location.hash ) && + !leftbtn ) { + + backBtn = $( ""+ o.backBtnText +"" ) + // If theme is provided, override default inheritance + .attr( "data-"+ $.mobile.ns +"theme", o.backBtnTheme || thisTheme ) + .prependTo( $this ); + } + + // Page title + $this.children( "h1, h2, h3, h4, h5, h6" ) + .addClass( "ui-title" ) + // Regardless of h element number in src, it becomes h1 for the enhanced page + .attr({ + "tabindex": "0", + "role": "heading", + "aria-level": "1" + }); + + } else if ( role === "content" ) { + if (theme || o.contentTheme) { + $this.addClass( "ui-body-" + ( theme || o.contentTheme ) ); + } + + // Add ARIA role + $this.attr( "role", "main" ); + } + }); +}); + +})( jQuery ); \ No newline at end of file diff --git a/js/jquery.mobile.support.js b/js/jquery.mobile.support.js index ef9c0dd9..aa8dd967 100644 --- a/js/jquery.mobile.support.js +++ b/js/jquery.mobile.support.js @@ -2,55 +2,118 @@ * jQuery Mobile Framework : support tests * Copyright (c) jQuery Project * Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. -* Note: Code is in draft form and is subject to change */ -(function($, undefined ) { - +(function( $, undefined ) { var fakeBody = $( "" ).prependTo( "html" ), - fbCSS = fakeBody[0].style, - vendors = ['webkit','moz','o'], - webos = window.palmGetResource || window.PalmServiceBridge, //only used to rule out scrollTop + fbCSS = fakeBody[ 0 ].style, + vendors = [ "Webkit", "Moz", "O" ], + webos = "palmGetResource" in window, //only used to rule out scrollTop bb = window.blackberry; //only used to rule out box shadow, as it's filled opaque on BB -//thx Modernizr -function propExists( prop ){ - var uc_prop = prop.charAt(0).toUpperCase() + prop.substr(1), - props = (prop + ' ' + vendors.join(uc_prop + ' ') + uc_prop).split(' '); - for(var v in props){ - if( fbCSS[ v ] !== undefined ){ +// thx Modernizr +function propExists( prop ) { + var uc_prop = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ), + props = ( prop + " " + vendors.join( uc_prop + " " ) + uc_prop ).split( " " ); + + for ( var v in props ){ + if ( fbCSS[ props[ v ] ] !== undefined ) { return true; } } -}; +} + +// Test for dynamic-updating base tag support ( allows us to avoid href,src attr rewriting ) +function baseTagTest() { + var fauxBase = location.protocol + "//" + location.host + location.pathname + "ui-dir/", + base = $( "head base" ), + fauxEle = null, + href = "", + link, rebase; + + if ( !base.length ) { + base = fauxEle = $( "", { "href": fauxBase }).appendTo( "head" ); + } else { + href = base.attr( "href" ); + } + + link = $( "" ).prependTo( fakeBody ); + rebase = link[ 0 ].href; + base[ 0 ].href = href || location.pathname; + + if ( fauxEle ) { + fauxEle.remove(); + } + return rebase.indexOf( fauxBase ) === 0; +} + + +// non-UA-based IE version check by James Padolsey, modified by jdalton - from http://gist.github.com/527683 +// allows for inclusion of IE 6+, including Windows Mobile 7 +$.mobile.browser = {}; +$.mobile.browser.ie = (function() { + var v = 3, + div = document.createElement( "div" ), + a = div.all || []; + + while ( div.innerHTML = "", a[ 0 ] ); + + return v > 4 ? v : !v; +})(); -//test for dynamic-updating base tag support (allows us to avoid href,src attr rewriting) -function baseTagTest(){ - var fauxBase = location.protocol + '//' + location.host + location.pathname + "ui-dir/", - base = $("", {"href": fauxBase}).appendTo("head"), - link = $( "" ).prependTo( fakeBody ), - rebase = link[0].href; - base[0].href = location.pathname; - base.remove(); - return rebase.indexOf(fauxBase) === 0; -}; $.extend( $.support, { - orientation: "orientation" in window, + orientation: "orientation" in window && "onorientationchange" in window, touch: "ontouchend" in document, cssTransitions: "WebKitTransitionEvent" in window, - pushState: !!history.pushState, - mediaquery: $.mobile.media('only all'), - cssPseudoElement: !!propExists('content'), - boxShadow: !!propExists('boxShadow') && !bb, - scrollTop: ("pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[0]) && !webos, + pushState: "pushState" in history && "replaceState" in history, + mediaquery: $.mobile.media( "only all" ), + cssPseudoElement: !!propExists( "content" ), + touchOverflow: !!propExists( "overflowScrolling" ), + boxShadow: !!propExists( "boxShadow" ) && !bb, + scrollTop: ( "pageXOffset" in window || "scrollTop" in document.documentElement || "scrollTop" in fakeBody[ 0 ] ) && !webos, dynamicBaseTag: baseTagTest() }); fakeBody.remove(); -//for ruling out shadows via css -if( !$.support.boxShadow ){ $('html').addClass('ui-mobile-nosupport-boxshadow'); } -})( jQuery ); \ No newline at end of file +// $.mobile.ajaxBlacklist is used to override ajaxEnabled on platforms that have known conflicts with hash history updates (BB5, Symbian) +// or that generally work better browsing in regular http for full page refreshes (Opera Mini) +// Note: This detection below is used as a last resort. +// We recommend only using these detection methods when all other more reliable/forward-looking approaches are not possible +var nokiaLTE7_3 = (function(){ + + var ua = window.navigator.userAgent; + + //The following is an attempt to match Nokia browsers that are running Symbian/s60, with webkit, version 7.3 or older + return ua.indexOf( "Nokia" ) > -1 && + ( ua.indexOf( "Symbian/3" ) > -1 || ua.indexOf( "Series60/5" ) > -1 ) && + ua.indexOf( "AppleWebKit" ) > -1 && + ua.match( /(BrowserNG|NokiaBrowser)\/7\.[0-3]/ ); +})(); + +$.mobile.ajaxBlacklist = + // BlackBerry browsers, pre-webkit + window.blackberry && !window.WebKitPoint || + // Opera Mini + window.operamini && Object.prototype.toString.call( window.operamini ) === "[object OperaMini]" || + // Symbian webkits pre 7.3 + nokiaLTE7_3; + +// Lastly, this workaround is the only way we've found so far to get pre 7.3 Symbian webkit devices +// to render the stylesheets when they're referenced before this script, as we'd recommend doing. +// This simply reappends the CSS in place, which for some reason makes it apply +if ( nokiaLTE7_3 ) { + $(function() { + $( "head link[rel='stylesheet']" ).attr( "rel", "alternate stylesheet" ).attr( "rel", "stylesheet" ); + }); +} + +// For ruling out shadows via css +if ( !$.support.boxShadow ) { + $( "html" ).addClass( "ui-mobile-nosupport-boxshadow" ); +} + +})( jQuery ); diff --git a/js/jquery.mobile.transition.js b/js/jquery.mobile.transition.js new file mode 100644 index 00000000..03cf1735 --- /dev/null +++ b/js/jquery.mobile.transition.js @@ -0,0 +1,50 @@ +/*! + * jQuery Mobile v@VERSION + * http://jquerymobile.com/ + * + * Copyright 2010, jQuery Project + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ + +(function( $, window, undefined ) { + +function css3TransitionHandler( name, reverse, $to, $from ) { + + var deferred = new $.Deferred(), + reverseClass = reverse ? " reverse" : "", + viewportClass = "ui-mobile-viewport-transitioning viewport-" + name, + doneFunc = function() { + + $to.add( $from ).removeClass( "out in reverse " + name ); + + if ( $from && $from[ 0 ] !== $to[ 0 ] ) { + $from.removeClass( $.mobile.activePageClass ); + } + + $to.parent().removeClass( viewportClass ); + + deferred.resolve( name, reverse, $to, $from ); + }; + + $to.animationComplete( doneFunc ); + + $to.parent().addClass( viewportClass ); + + if ( $from ) { + $from.addClass( name + " out" + reverseClass ); + } + $to.addClass( $.mobile.activePageClass + " " + name + " in" + reverseClass ); + + return deferred.promise(); +} + +// Make our transition handler public. +$.mobile.css3TransitionHandler = css3TransitionHandler; + +// If the default transition handler is the 'none' handler, replace it with our handler. +if ( $.mobile.defaultTransitionHandler === $.mobile.noneTransitionHandler ) { + $.mobile.defaultTransitionHandler = css3TransitionHandler; +} + +})( jQuery, this ); diff --git a/js/jquery.mobile.vmouse.js b/js/jquery.mobile.vmouse.js new file mode 100644 index 00000000..ac90bca2 --- /dev/null +++ b/js/jquery.mobile.vmouse.js @@ -0,0 +1,498 @@ +/* +* jQuery Mobile Framework : "mouse" plugin +* Copyright (c) jQuery Project +* Dual licensed under the MIT or GPL Version 2 licenses. +* http://jquery.org/license +*/ + +// This plugin is an experiment for abstracting away the touch and mouse +// events so that developers don't have to worry about which method of input +// the device their document is loaded on supports. +// +// The idea here is to allow the developer to register listeners for the +// basic mouse events, such as mousedown, mousemove, mouseup, and click, +// and the plugin will take care of registering the correct listeners +// behind the scenes to invoke the listener at the fastest possible time +// for that device, while still retaining the order of event firing in +// the traditional mouse environment, should multiple handlers be registered +// on the same element for different events. +// +// The current version exposes the following virtual events to jQuery bind methods: +// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel" + +(function( $, window, document, undefined ) { + +var dataPropertyName = "virtualMouseBindings", + touchTargetPropertyName = "virtualTouchID", + virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ), + touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ), + activeDocHandlers = {}, + resetTimerID = 0, + startX = 0, + startY = 0, + didScroll = false, + clickBlockList = [], + blockMouseTriggers = false, + blockTouchTriggers = false, + eventCaptureSupported = "addEventListener" in document, + $document = $( document ), + nextTouchID = 1, + lastTouchID = 0; + +$.vmouse = { + moveDistanceThreshold: 10, + clickDistanceThreshold: 10, + resetTimerDuration: 1500 +}; + +function getNativeEvent( event ) { + + while ( event && typeof event.originalEvent !== "undefined" ) { + event = event.originalEvent; + } + return event; +} + +function createVirtualEvent( event, eventType ) { + + var t = event.type, + oe, props, ne, prop, ct, touch, i, j; + + event = $.Event(event); + event.type = eventType; + + oe = event.originalEvent; + props = $.event.props; + + // copy original event properties over to the new event + // this would happen if we could call $.event.fix instead of $.Event + // but we don't have a way to force an event to be fixed multiple times + if ( oe ) { + for ( i = props.length, prop; i; ) { + prop = props[ --i ]; + event[ prop ] = oe[ prop ]; + } + } + + // make sure that if the mouse and click virtual events are generated + // without a .which one is defined + if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ){ + event.which = 1; + } + + if ( t.search(/^touch/) !== -1 ) { + ne = getNativeEvent( oe ); + t = ne.touches; + ct = ne.changedTouches; + touch = ( t && t.length ) ? t[0] : ( (ct && ct.length) ? ct[ 0 ] : undefined ); + + if ( touch ) { + for ( j = 0, len = touchEventProps.length; j < len; j++){ + prop = touchEventProps[ j ]; + event[ prop ] = touch[ prop ]; + } + } + } + + return event; +} + +function getVirtualBindingFlags( element ) { + + var flags = {}, + b, k; + + while ( element ) { + + b = $.data( element, dataPropertyName ); + + for ( k in b ) { + if ( b[ k ] ) { + flags[ k ] = flags.hasVirtualBinding = true; + } + } + element = element.parentNode; + } + return flags; +} + +function getClosestElementWithVirtualBinding( element, eventType ) { + var b; + while ( element ) { + + b = $.data( element, dataPropertyName ); + + if ( b && ( !eventType || b[ eventType ] ) ) { + return element; + } + element = element.parentNode; + } + return null; +} + +function enableTouchBindings() { + blockTouchTriggers = false; +} + +function disableTouchBindings() { + blockTouchTriggers = true; +} + +function enableMouseBindings() { + lastTouchID = 0; + clickBlockList.length = 0; + blockMouseTriggers = false; + + // When mouse bindings are enabled, our + // touch bindings are disabled. + disableTouchBindings(); +} + +function disableMouseBindings() { + // When mouse bindings are disabled, our + // touch bindings are enabled. + enableTouchBindings(); +} + +function startResetTimer() { + clearResetTimer(); + resetTimerID = setTimeout(function(){ + resetTimerID = 0; + enableMouseBindings(); + }, $.vmouse.resetTimerDuration ); +} + +function clearResetTimer() { + if ( resetTimerID ){ + clearTimeout( resetTimerID ); + resetTimerID = 0; + } +} + +function triggerVirtualEvent( eventType, event, flags ) { + var ve; + + if ( ( flags && flags[ eventType ] ) || + ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) { + + ve = createVirtualEvent( event, eventType ); + + $( event.target).trigger( ve ); + } + + return ve; +} + +function mouseEventCallback( event ) { + var touchID = $.data(event.target, touchTargetPropertyName); + + if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ){ + var ve = triggerVirtualEvent( "v" + event.type, event ); + if ( ve ) { + if ( ve.isDefaultPrevented() ) { + event.preventDefault(); + } + if ( ve.isPropagationStopped() ) { + event.stopPropagation(); + } + if ( ve.isImmediatePropagationStopped() ) { + event.stopImmediatePropagation(); + } + } + } +} + +function handleTouchStart( event ) { + + var touches = getNativeEvent( event ).touches, + target, flags; + + if ( touches && touches.length === 1 ) { + + target = event.target; + flags = getVirtualBindingFlags( target ); + + if ( flags.hasVirtualBinding ) { + + lastTouchID = nextTouchID++; + $.data( target, touchTargetPropertyName, lastTouchID ); + + clearResetTimer(); + + disableMouseBindings(); + didScroll = false; + + var t = getNativeEvent( event ).touches[ 0 ]; + startX = t.pageX; + startY = t.pageY; + + triggerVirtualEvent( "vmouseover", event, flags ); + triggerVirtualEvent( "vmousedown", event, flags ); + } + } +} + +function handleScroll( event ) { + if ( blockTouchTriggers ) { + return; + } + + if ( !didScroll ) { + triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) ); + } + + didScroll = true; + startResetTimer(); +} + +function handleTouchMove( event ) { + if ( blockTouchTriggers ) { + return; + } + + var t = getNativeEvent( event ).touches[ 0 ], + didCancel = didScroll, + moveThreshold = $.vmouse.moveDistanceThreshold; + didScroll = didScroll || + ( Math.abs(t.pageX - startX) > moveThreshold || + Math.abs(t.pageY - startY) > moveThreshold ), + flags = getVirtualBindingFlags( event.target ); + + if ( didScroll && !didCancel ) { + triggerVirtualEvent( "vmousecancel", event, flags ); + } + + triggerVirtualEvent( "vmousemove", event, flags ); + startResetTimer(); +} + +function handleTouchEnd( event ) { + if ( blockTouchTriggers ) { + return; + } + + disableTouchBindings(); + + var flags = getVirtualBindingFlags( event.target ), + t; + triggerVirtualEvent( "vmouseup", event, flags ); + + if ( !didScroll ) { + var ve = triggerVirtualEvent( "vclick", event, flags ); + if ( ve && ve.isDefaultPrevented() ) { + // The target of the mouse events that follow the touchend + // event don't necessarily match the target used during the + // touch. This means we need to rely on coordinates for blocking + // any click that is generated. + t = getNativeEvent( event ).changedTouches[ 0 ]; + clickBlockList.push({ + touchID: lastTouchID, + x: t.clientX, + y: t.clientY + }); + + // Prevent any mouse events that follow from triggering + // virtual event notifications. + blockMouseTriggers = true; + } + } + triggerVirtualEvent( "vmouseout", event, flags); + didScroll = false; + + startResetTimer(); +} + +function hasVirtualBindings( ele ) { + var bindings = $.data( ele, dataPropertyName ), + k; + + if ( bindings ) { + for ( k in bindings ) { + if ( bindings[ k ] ) { + return true; + } + } + } + return false; +} + +function dummyMouseHandler(){} + +function getSpecialEventObject( eventType ) { + var realType = eventType.substr( 1 ); + + return { + setup: function( data, namespace ) { + // If this is the first virtual mouse binding for this element, + // add a bindings object to its data. + + if ( !hasVirtualBindings( this ) ) { + $.data( this, dataPropertyName, {}); + } + + // If setup is called, we know it is the first binding for this + // eventType, so initialize the count for the eventType to zero. + var bindings = $.data( this, dataPropertyName ); + bindings[ eventType ] = true; + + // If this is the first virtual mouse event for this type, + // register a global handler on the document. + + activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1; + + if ( activeDocHandlers[ eventType ] === 1 ) { + $document.bind( realType, mouseEventCallback ); + } + + // Some browsers, like Opera Mini, won't dispatch mouse/click events + // for elements unless they actually have handlers registered on them. + // To get around this, we register dummy handlers on the elements. + + $( this ).bind( realType, dummyMouseHandler ); + + // For now, if event capture is not supported, we rely on mouse handlers. + if ( eventCaptureSupported ) { + // If this is the first virtual mouse binding for the document, + // register our touchstart handler on the document. + + activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1; + + if (activeDocHandlers[ "touchstart" ] === 1) { + $document.bind( "touchstart", handleTouchStart ) + .bind( "touchend", handleTouchEnd ) + + // On touch platforms, touching the screen and then dragging your finger + // causes the window content to scroll after some distance threshold is + // exceeded. On these platforms, a scroll prevents a click event from being + // dispatched, and on some platforms, even the touchend is suppressed. To + // mimic the suppression of the click event, we need to watch for a scroll + // event. Unfortunately, some platforms like iOS don't dispatch scroll + // events until *AFTER* the user lifts their finger (touchend). This means + // we need to watch both scroll and touchmove events to figure out whether + // or not a scroll happenens before the touchend event is fired. + + .bind( "touchmove", handleTouchMove ) + .bind( "scroll", handleScroll ); + } + } + }, + + teardown: function( data, namespace ) { + // If this is the last virtual binding for this eventType, + // remove its global handler from the document. + + --activeDocHandlers[ eventType ]; + + if ( !activeDocHandlers[ eventType ] ) { + $document.unbind( realType, mouseEventCallback ); + } + + if ( eventCaptureSupported ) { + // If this is the last virtual mouse binding in existence, + // remove our document touchstart listener. + + --activeDocHandlers[ "touchstart" ]; + + if ( !activeDocHandlers[ "touchstart" ] ) { + $document.unbind( "touchstart", handleTouchStart ) + .unbind( "touchmove", handleTouchMove ) + .unbind( "touchend", handleTouchEnd ) + .unbind( "scroll", handleScroll ); + } + } + + var $this = $( this ), + bindings = $.data( this, dataPropertyName ); + + // teardown may be called when an element was + // removed from the DOM. If this is the case, + // jQuery core may have already stripped the element + // of any data bindings so we need to check it before + // using it. + if ( bindings ) { + bindings[ eventType ] = false; + } + + // Unregister the dummy event handler. + + $this.unbind( realType, dummyMouseHandler ); + + // If this is the last virtual mouse binding on the + // element, remove the binding data from the element. + + if ( !hasVirtualBindings( this ) ) { + $this.removeData( dataPropertyName ); + } + } + }; +} + +// Expose our custom events to the jQuery bind/unbind mechanism. + +for ( var i = 0; i < virtualEventNames.length; i++ ){ + $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] ); +} + +// Add a capture click handler to block clicks. +// Note that we require event capture support for this so if the device +// doesn't support it, we punt for now and rely solely on mouse events. +if ( eventCaptureSupported ) { + document.addEventListener( "click", function( e ){ + var cnt = clickBlockList.length, + target = e.target, + x, y, ele, i, o, touchID; + + if ( cnt ) { + x = e.clientX; + y = e.clientY; + threshold = $.vmouse.clickDistanceThreshold; + + // The idea here is to run through the clickBlockList to see if + // the current click event is in the proximity of one of our + // vclick events that had preventDefault() called on it. If we find + // one, then we block the click. + // + // Why do we have to rely on proximity? + // + // Because the target of the touch event that triggered the vclick + // can be different from the target of the click event synthesized + // by the browser. The target of a mouse/click event that is syntehsized + // from a touch event seems to be implementation specific. For example, + // some browsers will fire mouse/click events for a link that is near + // a touch event, even though the target of the touchstart/touchend event + // says the user touched outside the link. Also, it seems that with most + // browsers, the target of the mouse/click event is not calculated until the + // time it is dispatched, so if you replace an element that you touched + // with another element, the target of the mouse/click will be the new + // element underneath that point. + // + // Aside from proximity, we also check to see if the target and any + // of its ancestors were the ones that blocked a click. This is necessary + // because of the strange mouse/click target calculation done in the + // Android 2.1 browser, where if you click on an element, and there is a + // mouse/click handler on one of its ancestors, the target will be the + // innermost child of the touched element, even if that child is no where + // near the point of touch. + + ele = target; + + while ( ele ) { + for ( i = 0; i < cnt; i++ ) { + o = clickBlockList[ i ]; + touchID = 0; + + if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) || + $.data( ele, touchTargetPropertyName ) === o.touchID ) { + // XXX: We may want to consider removing matches from the block list + // instead of waiting for the reset timer to fire. + e.preventDefault(); + e.stopPropagation(); + return; + } + } + ele = ele.parentNode; + } + } + }, true); +} +})( jQuery, window, document ); diff --git a/js/jquery.mobile.widget.js b/js/jquery.mobile.widget.js index 9006d965..471f8920 100644 --- a/js/jquery.mobile.widget.js +++ b/js/jquery.mobile.widget.js @@ -4,21 +4,50 @@ * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license */ -(function($, undefined ) { + +(function( $, undefined ) { $.widget( "mobile.widget", { + // decorate the parent _createWidget to trigger `widgetinit` for users + // who wish to do post post `widgetcreate` alterations/additions + // + // TODO create a pull request for jquery ui to trigger this event + // in the original _createWidget + _createWidget: function() { + $.Widget.prototype._createWidget.apply( this, arguments ); + this._trigger( 'init' ); + }, + _getCreateOptions: function() { + var elem = this.element, options = {}; + $.each( this.options, function( option ) { - var value = elem.data( option.replace( /[A-Z]/g, function( c ) { - return "-" + c.toLowerCase(); - } ) ); + + var value = elem.jqmData( option.replace( /[A-Z]/g, function( c ) { + return "-" + c.toLowerCase(); + }) + ); + if ( value !== undefined ) { options[ option ] = value; } }); + return options; + }, + + enhanceWithin: function( target ) { + // TODO remove dependency on the page widget for the keepNative. + // Currently the keepNative value is defined on the page prototype so + // the method is as well + var page = $(target).closest(":jqmData(role='page')").data( "page" ), + keepNative = (page && page.keepNativeSelector()) || ""; + + + + $( this.options.initSelector, target ).not( keepNative )[ this.widgetName ](); } }); diff --git a/js/jquery.ui.position.js b/js/jquery.ui.position.js deleted file mode 100644 index 9981a06a..00000000 --- a/js/jquery.ui.position.js +++ /dev/null @@ -1,252 +0,0 @@ -/* - * jQuery UI Position @VERSION - * - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * http://docs.jquery.com/UI/Position - */ -(function( $, undefined ) { - -$.ui = $.ui || {}; - -var horizontalPositions = /left|center|right/, - verticalPositions = /top|center|bottom/, - center = "center", - _position = $.fn.position, - _offset = $.fn.offset; - -$.fn.position = function( options ) { - if ( !options || !options.of ) { - return _position.apply( this, arguments ); - } - - // make a copy, we don't want to modify arguments - options = $.extend( {}, options ); - - var target = $( options.of ), - targetElem = target[0], - collision = ( options.collision || "flip" ).split( " " ), - offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ], - targetWidth, - targetHeight, - basePosition; - - if ( targetElem.nodeType === 9 ) { - targetWidth = target.width(); - targetHeight = target.height(); - basePosition = { top: 0, left: 0 }; - // TODO: use $.isWindow() in 1.9 - } else if ( targetElem.setTimeout ) { - targetWidth = target.width(); - targetHeight = target.height(); - basePosition = { top: target.scrollTop(), left: target.scrollLeft() }; - } else if ( targetElem.preventDefault ) { - // force left top to allow flipping - options.at = "left top"; - targetWidth = targetHeight = 0; - basePosition = { top: options.of.pageY, left: options.of.pageX }; - } else { - targetWidth = target.outerWidth(); - targetHeight = target.outerHeight(); - basePosition = target.offset(); - } - - // force my and at to have valid horizontal and veritcal positions - // if a value is missing or invalid, it will be converted to center - $.each( [ "my", "at" ], function() { - var pos = ( options[this] || "" ).split( " " ); - if ( pos.length === 1) { - pos = horizontalPositions.test( pos[0] ) ? - pos.concat( [center] ) : - verticalPositions.test( pos[0] ) ? - [ center ].concat( pos ) : - [ center, center ]; - } - pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center; - pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center; - options[ this ] = pos; - }); - - // normalize collision option - if ( collision.length === 1 ) { - collision[ 1 ] = collision[ 0 ]; - } - - // normalize offset option - offset[ 0 ] = parseInt( offset[0], 10 ) || 0; - if ( offset.length === 1 ) { - offset[ 1 ] = offset[ 0 ]; - } - offset[ 1 ] = parseInt( offset[1], 10 ) || 0; - - if ( options.at[0] === "right" ) { - basePosition.left += targetWidth; - } else if (options.at[0] === center ) { - basePosition.left += targetWidth / 2; - } - - if ( options.at[1] === "bottom" ) { - basePosition.top += targetHeight; - } else if ( options.at[1] === center ) { - basePosition.top += targetHeight / 2; - } - - basePosition.left += offset[ 0 ]; - basePosition.top += offset[ 1 ]; - - return this.each(function() { - var elem = $( this ), - elemWidth = elem.outerWidth(), - elemHeight = elem.outerHeight(), - marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0, - marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0, - collisionWidth = elemWidth + marginLeft + - parseInt( $.curCSS( this, "marginRight", true ) ) || 0, - collisionHeight = elemHeight + marginTop + - parseInt( $.curCSS( this, "marginBottom", true ) ) || 0, - position = $.extend( {}, basePosition ), - collisionPosition; - - if ( options.my[0] === "right" ) { - position.left -= elemWidth; - } else if ( options.my[0] === center ) { - position.left -= elemWidth / 2; - } - - if ( options.my[1] === "bottom" ) { - position.top -= elemHeight; - } else if ( options.my[1] === center ) { - position.top -= elemHeight / 2; - } - - // prevent fractions (see #5280) - position.left = parseInt( position.left ); - position.top = parseInt( position.top ); - - collisionPosition = { - left: position.left - marginLeft, - top: position.top - marginTop - }; - - $.each( [ "left", "top" ], function( i, dir ) { - if ( $.ui.position[ collision[i] ] ) { - $.ui.position[ collision[i] ][ dir ]( position, { - targetWidth: targetWidth, - targetHeight: targetHeight, - elemWidth: elemWidth, - elemHeight: elemHeight, - collisionPosition: collisionPosition, - collisionWidth: collisionWidth, - collisionHeight: collisionHeight, - offset: offset, - my: options.my, - at: options.at - }); - } - }); - - if ( $.fn.bgiframe ) { - elem.bgiframe(); - } - elem.offset( $.extend( position, { using: options.using } ) ); - }); -}; - -$.ui.position = { - fit: { - left: function( position, data ) { - var win = $( window ), - over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(); - position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left ); - }, - top: function( position, data ) { - var win = $( window ), - over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(); - position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top ); - } - }, - - flip: { - left: function( position, data ) { - if ( data.at[0] === center ) { - return; - } - var win = $( window ), - over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(), - myOffset = data.my[ 0 ] === "left" ? - -data.elemWidth : - data.my[ 0 ] === "right" ? - data.elemWidth : - 0, - atOffset = data.at[ 0 ] === "left" ? - data.targetWidth : - -data.targetWidth, - offset = -2 * data.offset[ 0 ]; - position.left += data.collisionPosition.left < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; - }, - top: function( position, data ) { - if ( data.at[1] === center ) { - return; - } - var win = $( window ), - over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(), - myOffset = data.my[ 1 ] === "top" ? - -data.elemHeight : - data.my[ 1 ] === "bottom" ? - data.elemHeight : - 0, - atOffset = data.at[ 1 ] === "top" ? - data.targetHeight : - -data.targetHeight, - offset = -2 * data.offset[ 1 ]; - position.top += data.collisionPosition.top < 0 ? - myOffset + atOffset + offset : - over > 0 ? - myOffset + atOffset + offset : - 0; - } - } -}; - -// offset setter from jQuery 1.4 -if ( !$.offset.setOffset ) { - $.offset.setOffset = function( elem, options ) { - // set position first, in-case top/left are set even on static elem - if ( /static/.test( $.curCSS( elem, "position" ) ) ) { - elem.style.position = "relative"; - } - var curElem = $( elem ), - curOffset = curElem.offset(), - curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0, - curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0, - props = { - top: (options.top - curOffset.top) + curTop, - left: (options.left - curOffset.left) + curLeft - }; - - if ( 'using' in options ) { - options.using.call( elem, props ); - } else { - curElem.css( props ); - } - }; - - $.fn.offset = function( options ) { - var elem = this[ 0 ]; - if ( !elem || !elem.ownerDocument ) { return null; } - if ( options ) { - return this.each(function() { - $.offset.setOffset( this, options ); - }); - } - return _offset.call( this ); - }; -} - -}( jQuery )); \ No newline at end of file diff --git a/tests/functional/addrbar.html b/tests/functional/addrbar.html new file mode 100755 index 00000000..f68e6ca0 --- /dev/null +++ b/tests/functional/addrbar.html @@ -0,0 +1,48 @@ + + + + + + jQuery Mobile: Event Logger + + + + + + + + + + +
+
+

Event Logger

+
+ +
+

Touch events on this page will log out below, prepending to the top as they arrive.

+ + Click me + +
    + +
+ +
+
+ + diff --git a/tests/functional/eventlogger.html b/tests/functional/eventlogger.html new file mode 100755 index 00000000..e194aaaf --- /dev/null +++ b/tests/functional/eventlogger.html @@ -0,0 +1,38 @@ + + + + + + jQuery Mobile: Event Logger + + + + + + + + +
+
+

Event Logger

+
+ +
+

Touch events on this page will log out below, prepending to the top as they arrive.

+ +
    + +
+ +
+
+ + diff --git a/tests/functional/gridlayout.html b/tests/functional/gridlayout.html new file mode 100644 index 00000000..8a6e32d7 --- /dev/null +++ b/tests/functional/gridlayout.html @@ -0,0 +1,65 @@ + + + + + jQuery Mobile: Grid Layout + + + + + + + + +
+
+

Grid Layout

+
+ +
+

Touch events on this page will log out below, prepending to the top as they arrive.

+ +
+
+ Button 1 +
+
+ Button 2 +
+
+ Button 3 +
+
+ Button 4 +
+
+ Button 5 +
+
+ + Show all button + +
    + +
+ +
+
+ + diff --git a/tests/jquery.testHelper.js b/tests/jquery.testHelper.js index 75a5e465..d39ae983 100644 --- a/tests/jquery.testHelper.js +++ b/tests/jquery.testHelper.js @@ -16,24 +16,46 @@ } }, + // TODO prevent test suite loads when the browser doesn't support push state + // and push-state false is defined. + setPushStateFor: function( libs ) { + if( $.support.pushState && location.search.indexOf( "push-state" ) >= 0 ) { + $.support.pushState = false; + } + + $.each(libs, function(i, l) { + $( " + + + + + + -
+
-
+

400 item list

-
+
-
    +
    • Acura
    • Audi
    • BMW
    • diff --git a/tests/unit/button/button_core.js b/tests/unit/button/button_core.js new file mode 100644 index 00000000..7775e12e --- /dev/null +++ b/tests/unit/button/button_core.js @@ -0,0 +1,14 @@ +/* + * mobile button unit tests + */ +(function($){ + $.mobile.page.prototype.options.keepNative = "button.should-be-native"; + + test( "button elements in the keepNative set shouldn't be enhanced", function() { + same( $("button.should-be-native").siblings("div.ui-slider").length, 0 ); + }); + + test( "button elements should be enhanced", function() { + ok( $("#enhanced").hasClass( "ui-btn-hidden" ) ); + }); +})( jQuery ); \ No newline at end of file diff --git a/tests/unit/button/index.html b/tests/unit/button/index.html new file mode 100644 index 00000000..d3de8336 --- /dev/null +++ b/tests/unit/button/index.html @@ -0,0 +1,36 @@ + + + + + + jQuery Mobile Button Test Suite + + + + + + + + + + + + + + + + +

      jQuery Mobile Button Test Suite

      +

      +

      +
        +
      + +
      +
      + + +
      +
      + + diff --git a/tests/unit/buttonMarkup/buttonMarkup_core.js b/tests/unit/buttonMarkup/buttonMarkup_core.js new file mode 100644 index 00000000..ae135832 --- /dev/null +++ b/tests/unit/buttonMarkup/buttonMarkup_core.js @@ -0,0 +1,29 @@ +/* + * mobile buttonMarkup tests + */ +(function($){ + module("jquery.mobile.buttonMarkup.js"); + + test( "control group buttons should be enhanced inside a footer", function(){ + var group, linkCount; + + group = $("#control-group-footer"); + linkCount = group.find( "a" ).length; + + same( group.find("a.ui-btn").length, linkCount, "all 4 links should be buttons"); + same( group.find("a > span.ui-corner-left").length, 1, "only 1 left cornered button"); + same( group.find("a > span.ui-corner-right").length, 1, "only 1 right cornered button"); + same( group.find("a > span:not(.ui-corner-left):not(.ui-corner-right)").length, linkCount - 2, "only 2 buttons are cornered"); + }); + + test( "control group buttons should respect theme-related data attributes", function(){ + var group = $("#control-group-content"); + + ok(!group.find('[data-shadow=false]').hasClass("ui-shadow"), + "buttons with data-shadow=false should not have the ui-shadow class"); + ok(!group.find('[data-corners=false]').hasClass("ui-btn-corner-all"), + "buttons with data-corners=false should not have the ui-btn-corner-all class"); + ok(!group.find('[data-iconshadow=false] .ui-icon').hasClass("ui-icon-shadow"), + "buttons with data-iconshadow=false should not have the ui-icon-shadow class on their icons"); + }); +})(jQuery); diff --git a/tests/unit/buttonMarkup/index.html b/tests/unit/buttonMarkup/index.html new file mode 100644 index 00000000..32f714f7 --- /dev/null +++ b/tests/unit/buttonMarkup/index.html @@ -0,0 +1,44 @@ + + + + + jQuery Mobile Button Markup Test Suite + + + + + + + + + + + + + + +

      jQuery Mobile Button Markup Test Suite

      +

      +

      +
        +
      + +
      +
      + No shadow + No corners + No shadow or corners + No iconshadow + +
      + +
      + + diff --git a/tests/unit/checkboxradio/checkboxradio_core.js b/tests/unit/checkboxradio/checkboxradio_core.js new file mode 100644 index 00000000..61ae2fe5 --- /dev/null +++ b/tests/unit/checkboxradio/checkboxradio_core.js @@ -0,0 +1,124 @@ +/* + * mobile checkboxradio unit tests + */ +(function($){ + module( 'jquery.mobile.forms.checkboxradio.js' ); + + test( "widget can be disabled and enabled", function(){ + var input = $( "#checkbox-1" ), + button = input.parent().find( ".ui-btn" ); + + input.checkboxradio( "disable" ); + input.checkboxradio( "enable" ); + ok( !input.attr( "disabled" ), "start input as enabled" ); + ok( !input.parent().hasClass( "ui-disabled" ), "no disabled styles" ); + ok( !input.attr( "checked" ), "not checked before click" ); + button.trigger( "click" ); + ok( input.attr( "checked" ), "checked after click" ); + ok( button.hasClass( "ui-checkbox-on" ), "active styles after click" ); + button.trigger( "click" ); + + input.checkboxradio( "disable" ); + ok( input.attr( "disabled" ), "input disabled" ); + ok( input.parent().hasClass( "ui-disabled" ), "disabled styles" ); + ok( !input.attr( "checked" ), "not checked before click" ); + button.trigger( "click" ); + ok( !input.attr( "checked" ), "not checked after click" ); + ok( !button.hasClass( "ui-checkbox-on" ), "no active styles after click" ); + }); + + test( "clicking a checkbox within a controlgroup does not affect checkboxes with the same name in the same controlgroup", function(){ + var input1 = $("#checkbox-31"); + var button1 = input1.parent().find(".ui-btn"); + var input2 = $("#checkbox-32"); + var button2 = input2.parent().find(".ui-btn"); + + ok(!input1.attr("checked"), "input1 not checked before click"); + ok(!input2.attr("checked"), "input2 not checked before click"); + + button1.trigger("click"); + ok(input1.attr("checked"), "input1 checked after click on input1"); + ok(!input2.attr("checked"), "input2 not checked after click on input1"); + + button2.trigger("click"); + ok(input1.attr("checked"), "input1 not changed after click on input2"); + ok(input2.attr("checked"), "input2 checked after click on input2"); + }); + + asyncTest( "change events fired on checkbox for both check and uncheck", function(){ + var $checkbox = $( "#checkbox-2" ), + $checkboxLabel = $checkbox.parent().find( ".ui-btn" ); + + $checkbox.unbind( "change" ); + + expect( 1 ); + + $checkbox.one('change', function(){ + ok( true, "change fired on click to check the box" ); + }); + + $checkboxLabel.trigger( "click" ); + + //test above will be triggered twice, and the start here once + $checkbox.one('change', function(){ + start(); + }); + + $checkboxLabel.trigger( "click" ); + }); + + asyncTest( "radio button labels should update the active button class to last clicked and clear checked", function(){ + var $radioBtns = $( '#radio-active-btn-test input' ), + singleActiveAndChecked = function(){ + same( $( "#radio-active-btn-test .ui-radio-on" ).length, 1, "there should be only one active button" ); + same( $( "#radio-active-btn-test :checked" ).length, 1, "there should be only one checked" ); + }; + + $.testHelper.sequence([ + function(){ + $radioBtns.last().siblings( 'label' ).click(); + }, + + function(){ + ok( $radioBtns.last().prop( 'checked' ) ); + ok( $radioBtns.last().siblings( 'label' ).hasClass( 'ui-radio-on' ), + "last input label is an active button" ); + + ok( !$radioBtns.first().prop( 'checked' ) ); + ok( !$radioBtns.first().siblings( 'label' ).hasClass( 'ui-radio-on' ), + "first input label is not active" ); + + singleActiveAndChecked(); + + $radioBtns.first().siblings( 'label' ).click(); + }, + + function(){ + ok( $radioBtns.first().prop( 'checked' )); + ok( $radioBtns.first().siblings( 'label' ).hasClass( 'ui-radio-on' ), + "first input label is an active button" ); + + ok( !$radioBtns.last().prop( 'checked' )); + ok( !$radioBtns.last().siblings( 'label' ).hasClass( 'ui-radio-on' ), + "last input label is not active" ); + + singleActiveAndChecked(); + + start(); + } + ], 500); + + }); + + test( "checkboxradio controls will create when inside a container that receives a 'create' event", function(){ + ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-checkbox").length, "did not have enhancements applied" ); + ok( $("#enhancetest").trigger("create").find(".ui-checkbox").length, "enhancements applied" ); + }); + + $.mobile.page.prototype.options.keepNative = "input.should-be-native"; + + // not testing the positive case here since's it's obviously tested elsewhere + test( "checkboxradio elements in the keepNative set shouldn't be enhanced", function() { + ok( !$("input.should-be-native").parent().is("div.ui-checkbox") ); + }); +})(jQuery); diff --git a/tests/unit/checkboxradio/index.html b/tests/unit/checkboxradio/index.html new file mode 100644 index 00000000..63590121 --- /dev/null +++ b/tests/unit/checkboxradio/index.html @@ -0,0 +1,94 @@ + + + + + + jQuery Mobile Checkboxradio Test Suite + + + + + + + + + + + + + + + + +

      jQuery Mobile Checkbockradio Test Suite

      +

      +

      +
        +
      + +
      +
      +
      +
      + Agree to the terms: + + +
      +
      + +
      +
      + Agree to the terms: + + +
      +
      + +
      +
      + Agree to the terms 3.1: + + +
      +
      + Agree to the terms 3.2: + + +
      +
      +
      + +
      +
      + Choose a pet: + + + + + + + + + + + +
      +
      + +
      +
      + Agree to the terms: + + +
      +
      + +
      + +
      + + +
      + + + diff --git a/tests/unit/collapsible/collapsible_core.js b/tests/unit/collapsible/collapsible_core.js new file mode 100644 index 00000000..d353e8ac --- /dev/null +++ b/tests/unit/collapsible/collapsible_core.js @@ -0,0 +1,165 @@ +/* + * mobile listview unit tests + */ + +// TODO split out into seperate test files +(function( $ ){ + module( "Collapsible section", {}); + + asyncTest( "The page should enhanced correctly", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#basic-collapsible-test" ); + }, + + function() { + var $page = $( "#basic-collapsible-test" ); + ok($page.find( ".ui-content >:eq(0)" ).hasClass( "ui-collapsible" ), ".ui-collapsible class added to collapsible elements" ); + ok($page.find( ".ui-content >:eq(0) >:header" ).hasClass( "ui-collapsible-heading" ), ".ui-collapsible-heading class added to collapsible heading" ); + ok($page.find( ".ui-content >:eq(0) > div" ).hasClass( "ui-collapsible-content" ), ".ui-collapsible-content class added to collapsible content" ); + ok($page.find( ".ui-content >:eq(0)" ).hasClass( "ui-collapsible-collapsed" ), ".ui-collapsible-collapsed added to collapsed elements" ); + ok(!$page.find( ".ui-content >:eq(1)" ).hasClass( "ui-collapsible-collapsed" ), ".ui-collapsible-collapsed not added to expanded elements" ); + ok($page.find( ".ui-collapsible.ui-collapsible-collapsed" ).find( ".ui-collapsible-heading-toggle > .ui-btn-inner" ).hasClass( "ui-corner-top ui-corner-bottom" ), "Collapsible header button should have class ui-corner-all" ); + start(); + } + ]); + }); + + asyncTest( "Expand/Collapse", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#basic-collapsible-test" ); + }, + + function() { + ok($( "#basic-collapsible-test .ui-collapsible" ).eq(0).hasClass( "ui-collapsible-collapsed" ), "First collapsible should be collapsed"); + $( "#basic-collapsible-test .ui-collapsible-heading-toggle" ).eq(0).click(); + ok(!$( "#basic-collapsible-test .ui-collapsible" ).eq(0).hasClass( "ui-collapsible-collapsed" ), "First collapsible should be expanded after click"); + $( "#basic-collapsible-test .ui-collapsible-heading-toggle" ).eq(0).click(); + ok($( "#basic-collapsible-test .ui-collapsible" ).eq(0).hasClass( "ui-collapsible-collapsed" ), "First collapsible should be collapsed"); + start(); + } + ]); + }); + + module( "Collapsible set", {}); + + asyncTest( "The page should enhanced correctly", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#basic-collapsible-set-test" ); + }, + + function() { + var $page = $( "#basic-collapsible-set-test" ); + + ok($page.find( ".ui-content >:eq(0)" ).hasClass( "ui-collapsible-set" ), ".ui-collapsible-set class added to collapsible set" ); + ok($page.find( ".ui-content >:eq(0) > div" ).hasClass( "ui-collapsible" ), ".ui-collapsible class added to collapsible elements" ); + $page.find( ".ui-collapsible-set" ).each(function() { + var $this = $( this ); + ok($this.find( ".ui-collapsible" ).first().find( ".ui-collapsible-heading-toggle > .ui-btn-inner" ).hasClass( "ui-corner-top" ), "First collapsible header button should have class ui-corner-top" ); + ok($this.find( ".ui-collapsible" ).last().find( ".ui-collapsible-heading-toggle > .ui-btn-inner" ).hasClass( "ui-corner-bottom" ), "Last collapsible header button should have class ui-corner-bottom" ); + }); + + start(); + } + ]); + }); + + asyncTest( "Collapsible set with only one collapsible", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#collapsible-set-with-lonely-collapsible-test" ); + }, + + function() { + var $page = $( "#collapsible-set-with-lonely-collapsible-test" ); + $page.find( ".ui-collapsible-set" ).each(function() { + var $this = $( this ); + ok($this.find( ".ui-collapsible" ).first().find( ".ui-collapsible-heading-toggle > .ui-btn-inner" ).hasClass( "ui-corner-top" ), "First collapsible header button should have class ui-corner-top" ); + ok($this.find( ".ui-collapsible" ).last().find( ".ui-collapsible-heading-toggle > .ui-btn-inner" ).hasClass( "ui-corner-bottom" ), "Last collapsible header button should have class ui-corner-bottom" ); + }); + + start(); + } + ]); + }); + + asyncTest( "Section expanded by default", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#basic-collapsible-set-test" ); + }, + + function() { + equals($( "#basic-collapsible-set-test .ui-content >:eq(0) .ui-collapsible-collapsed" ).length, 2, "There should be 2 section collapsed" ); + ok(!$( "#basic-collapsible-set-test .ui-content >:eq(0) >:eq(1)" ).hasClass( "ui-collapsible-collapsed" ), "Section B should be expanded" ); + start(); + } + ]); + }); + + asyncTest( "Expand/Collapse", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#basic-collapsible-set-test" ); + }, + + function() { + ok($( "#basic-collapsible-set-test .ui-collapsible" ).eq(0).hasClass( "ui-collapsible-collapsed" ), "First collapsible should be collapsed"); + $( "#basic-collapsible-set-test .ui-collapsible-heading-toggle" ).eq(0).click(); + ok(!$( "#basic-collapsible-set-test .ui-collapsible" ).eq(0).hasClass( "ui-collapsible-collapsed" ), "First collapsible should be expanded after click"); + $( "#basic-collapsible-set-test .ui-collapsible-heading-toggle" ).eq(0).click(); + ok($( "#basic-collapsible-set-test .ui-collapsible" ).hasClass( "ui-collapsible-collapsed" ), "All collapsible should be collapsed"); + start(); + } + ]); + }); + + module( "Theming", {}); + + asyncTest( "Collapsible", 6, function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#collapsible-with-theming" ); + }, + + function() { + var collapsibles = $.mobile.activePage.find( ".ui-collapsible" ); + ok( collapsibles.eq(0).find( ".ui-collapsible-heading-toggle" ).hasClass( "ui-btn-up-a" ), "Heading of first collapsible should have class ui-btn-up-a"); + ok( !collapsibles.eq(0).find( ".ui-collapsible-content" ).hasClass( "ui-btn-up-a" ), "Content of first collapsible should NOT have class ui-btn-up-a"); + ok( collapsibles.eq(1).find( ".ui-collapsible-heading-toggle" ).hasClass( "ui-btn-up-b" ), "Heading of second collapsible should have class ui-btn-up-b"); + ok( collapsibles.eq(1).find( ".ui-collapsible-content" ).hasClass( "ui-body-b" ), "Content of second collapsible should have class ui-btn-up-b"); + ok( collapsibles.eq(2).find( ".ui-collapsible-heading-toggle" ).hasClass( "ui-btn-up-c" ), "Heading of third collapsible should have class ui-btn-up-c"); + ok( collapsibles.eq(2).find( ".ui-collapsible-content" ).hasClass( "ui-body-c" ), "Content of third collapsible should have class ui-btn-up-c"); + start(); + } + ]); + }); + + + asyncTest( "Collapsible Set", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage( "#collapsible-set-with-theming" ); + }, + + function() { + var collapsibles = $.mobile.activePage.find( ".ui-collapsible" ); + ok( collapsibles.eq(0).find( ".ui-collapsible-heading-toggle" ).hasClass( "ui-btn-up-a" ), "Heading of first collapsible should have class ui-btn-up-a"); + ok( !collapsibles.eq(0).find( ".ui-collapsible-content" ).hasClass( "ui-body-a" ), "Content of first collapsible should NOT have class ui-btn-up-a"); + ok( collapsibles.eq(0).find( ".ui-collapsible-content" ).hasClass( "ui-body-d" ), "Content of first collapsible should NOT have class ui-btn-up-d"); + ok( collapsibles.eq(1).find( ".ui-collapsible-heading-toggle" ).hasClass( "ui-btn-up-b" ), "Heading of second collapsible should have class ui-btn-up-b"); + ok( collapsibles.eq(1).find( ".ui-collapsible-content" ).hasClass( "ui-body-b" ), "Content of second collapsible should have class ui-btn-up-b"); + ok( collapsibles.eq(2).find( ".ui-collapsible-heading-toggle" ).hasClass( "ui-btn-up-d" ), "Heading of third collapsible should have class ui-btn-up-d"); + ok( collapsibles.eq(2).find( ".ui-collapsible-content" ).hasClass( "ui-body-d" ), "Content of third collapsible should have class ui-btn-up-d"); + ok( !collapsibles.eq(2).find( ".ui-collapsible-content" ).hasClass( "ui-collapsible-content-collapsed" ), "Content of third collapsible should NOT have class ui-collapsible-content-collapsed"); + ok( collapsibles.eq(3).find( ".ui-collapsible-heading-toggle" ).hasClass( "ui-btn-up-d" ), "Heading of fourth collapsible should have class ui-btn-up-d"); + ok( collapsibles.eq(3).find( ".ui-collapsible-content" ).hasClass( "ui-body-d" ), "Content of fourth collapsible should have class ui-btn-up-d"); + start(); + } + ]); + }); + + +})( jQuery ); diff --git a/tests/unit/collapsible/index.html b/tests/unit/collapsible/index.html new file mode 100644 index 00000000..6eaac1fc --- /dev/null +++ b/tests/unit/collapsible/index.html @@ -0,0 +1,167 @@ + + + + + + jQuery Mobile Collapsible Integration Test + + + + + + + + + + + + + + + +

      jQuery Mobile Collapsible Integration Test

      +

      +

      +
        +
      + +
      +
      +

      Basic collapsible

      +
      +
      +
      +

      Section A

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      + +
      +
      +

      Section B

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      + +
      +
      +
      + +
      +
      +

      Basic collapsible

      +
      +
      +
      +
      +

      Section A

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      + +
      +
      +

      Section B

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      + +
      +
      +

      Section C

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      + +
      +
      +
      +
      + +
      +
      +

      Basic collapsible

      +
      +
      +
      +
      +

      Section D

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      + +
      +

      Section E

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      +
      + +
      +
      +

      Themed collapsibles

      +
      +
      +
      +

      Section A

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      +

      Section B

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      +

      Section B

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      + +
      +
      + +
      +
      +

      Themed collapsibles

      +
      +
      +
      +
      +

      Section A

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      +

      Section B

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      +

      Section C

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      +

      Section D

      + +

      I'm the collapsible content in a set so this feels like an accordion. I'm hidden by default because I + have the "collapsed" state; you need to expand the header to see me.

      +
      +
      + +
      +
      + + + diff --git a/tests/unit/controlgroup/controlgroup_core.js b/tests/unit/controlgroup/controlgroup_core.js new file mode 100644 index 00000000..f16a0133 --- /dev/null +++ b/tests/unit/controlgroup/controlgroup_core.js @@ -0,0 +1,129 @@ +/* + * mobile checkboxradio unit tests + */ +(function($){ + module( 'vertical controlgroup, no refresh' , { + setup: function() { + this.vcontrolgroup = $( "#vertical-controlgroup" ); + } + }); + + test( "vertical controlgroup classes", function() { + var buttons = this.vcontrolgroup.find( ".ui-btn" ), + middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}), + length = buttons.length; + + ok( !buttons.hasClass( "ui-btn-corner-all" ), "no button should have class 'ui-btn-corner-all'"); + ok( buttons.first().hasClass( "ui-corner-top" ), "first button should have class 'ui-corner-top'" ); + ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" ); + ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" ); + ok( buttons.last().hasClass( "ui-corner-bottom"), "last button should have class 'ui-corner-bottom'" ); + }); + + module( 'vertical controlgroup, refresh', { + setup: function() { + this.vcontrolgroup = $( "#vertical-controlgroup" ); + this.vcontrolgroup.find( ".ui-btn" ).show(); + this.vcontrolgroup.controlgroup(); + } + }); + + test( "vertical controlgroup after first button was hidden", function() { + //https://github.com/jquery/jquery-mobile/issues/1929 + + //We hide the first button and refresh + this.vcontrolgroup.find( ".ui-btn" ).first().hide(); + this.vcontrolgroup.controlgroup(); + + var buttons = this.vcontrolgroup.find( ".ui-btn" ).filter( ":visible" ), + middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}), + length = buttons.length; + + ok( buttons.first().hasClass( "ui-corner-top" ), "first visible button should have class 'ui-corner-top'" ); + ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" ); + ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" ); + ok( buttons.last().hasClass( "ui-corner-bottom"), "last visible button should have class 'ui-corner-bottom'" ); + }); + + test( "vertical controlgroup after last button was hidden", function() { + //https://github.com/jquery/jquery-mobile/issues/1929 + + //We hide the last button and refresh + this.vcontrolgroup.find( ".ui-btn" ).last().hide(); + this.vcontrolgroup.controlgroup(); + + var buttons = this.vcontrolgroup.find( ".ui-btn" ).filter( ":visible" ), + middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}), + length = buttons.length; + + ok( buttons.first().hasClass( "ui-corner-top" ), "first visible button should have class 'ui-corner-top'" ); + ok( !middlebuttons.hasClass( "ui-corner-top" ), "middle buttons should not have class 'ui-corner-top'" ); + ok( !middlebuttons.hasClass( "ui-corner-bottom" ), "middle buttons should not have class 'ui-corner-bottom'" ); + ok( buttons.last().hasClass( "ui-corner-bottom"), "last visible button should have class 'ui-corner-bottom'" ); + }); + + module( 'horizontal controlgroup, no refresh', { + setup: function() { + this.hcontrolgroup = $( "#horizontal-controlgroup" ); + } + }); + + test( "horizontal controlgroup classes", function() { + var buttons = this.hcontrolgroup.find( ".ui-btn" ), + middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}), + length = buttons.length; + + ok( !buttons.hasClass( "ui-btn-corner-all" ), "no button should have class 'ui-btn-corner-all'"); + ok( buttons.first().hasClass( "ui-corner-left" ), "first button should have class 'ui-corner-left'" ); + ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" ); + ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" ); + ok( buttons.last().hasClass( "ui-corner-right"), "last button should have class 'ui-corner-right'" ); + }); + + module( 'horizontal controlgroup, refresh', { + setup: function() { + this.hcontrolgroup = $( "#horizontal-controlgroup" ); + this.hcontrolgroup.find( ".ui-btn" ).show(); + this.hcontrolgroup.controlgroup(); + } + }); + + test( "horizontal controlgroup after first button was hidden", function() { + //We hide the first button and refresh + this.hcontrolgroup.find( ".ui-btn" ).first().hide(); + this.hcontrolgroup.controlgroup(); + + var buttons = this.hcontrolgroup.find( ".ui-btn" ).filter( ":visible" ), + middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}), + length = buttons.length; + + ok( buttons.first().hasClass( "ui-corner-left" ), "first visible button should have class 'ui-corner-left'" ); + ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" ); + ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" ); + ok( buttons.last().hasClass( "ui-corner-right"), "last visible button should have class 'ui-corner-right'" ); + }); + + test( "horizontal controlgroup after last button was hidden", function() { + //We hide the last button and refresh + this.hcontrolgroup.find( ".ui-btn" ).last().hide(); + this.hcontrolgroup.controlgroup(); + + var buttons = this.hcontrolgroup.find( ".ui-btn" ).filter( ":visible" ), + middlebuttons = buttons.filter(function(index) { return index > 0 && index < (length-1)}), + length = buttons.length; + + ok( buttons.first().hasClass( "ui-corner-left" ), "first visible button should have class 'ui-corner-left'" ); + ok( !middlebuttons.hasClass( "ui-corner-left" ), "middle buttons should not have class 'ui-corner-left'" ); + ok( !middlebuttons.hasClass( "ui-corner-right" ), "middle buttons should not have class 'ui-corner-right'" ); + ok( buttons.last().hasClass( "ui-corner-right"), "last visible button should have class 'ui-corner-right'" ); + }); + + + test( "controlgroups will create when inside a container that receives a 'create' event", function(){ + ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-controlgroup").length, "did not have enhancements applied" ); + ok( $("#enhancetest").trigger("create").find(".ui-controlgroup").length, "enhancements applied" ); + }); + + + +})(jQuery); diff --git a/tests/unit/controlgroup/index.html b/tests/unit/controlgroup/index.html new file mode 100644 index 00000000..9c016a7c --- /dev/null +++ b/tests/unit/controlgroup/index.html @@ -0,0 +1,82 @@ + + + + + + jQuery Mobile Checkboxradio Test Suite + + + + + + + + + + + + + + + + +

      jQuery Mobile Controlgroup Test Suite

      +

      +

      +
        +
      + +
      +
      + +
      +
      + Choose a pet: + + + + + + + + + + + +
      +
      + +
      +
      + Font styling: + + + + + + + + + + + +
      +
      +
      + +
      + + + + + + +
      +
      + + +
      +
      + + + diff --git a/tests/unit/core/core.js b/tests/unit/core/core.js index ceadf207..06eb651b 100644 --- a/tests/unit/core/core.js +++ b/tests/unit/core/core.js @@ -2,9 +2,12 @@ * mobile core unit tests */ -(function( $ ) { +(function($){ var libName = "jquery.mobile.core.js", - setGradeA = function(value) { $.support.mediaquery = value; }, + setGradeA = function(value, version) { + $.support.mediaquery = value; + $.mobile.browser.ie = version; + }, extendFn = $.extend; module(libName, { @@ -21,131 +24,103 @@ }); $.testHelper.excludeFileProtocol(function(){ - test( "grade A browser support media queries", function(){ - setGradeA(false); + test( "grade A browser either supports media queries or is IE 7+", function(){ + setGradeA(false, 6); $.testHelper.reloadLib(libName); ok(!$.mobile.gradeA()); - setGradeA(true); + setGradeA(true, 8); $.testHelper.reloadLib(libName); ok($.mobile.gradeA()); }); - - test( "loading the core library triggers mobilinit on the document", function(){ - expect( 1 ); - - $(window.document).bind('mobileinit', function(event){ - ok(true); - }); - - $.testHelper.reloadLib(libName); - }); - - test( "enhancments are skipped when the browser is not grade A", function(){ - setGradeA(false); - $.testHelper.reloadLib(libName); - - //NOTE easiest way to check for enhancements, not the most obvious - ok(!$("html").hasClass("ui-mobile")); - }); - - test( "enhancments are added when the browser is grade A", function(){ - setGradeA(true); - $.testHelper.reloadLib(libName); - - ok($("html").hasClass("ui-mobile")); - }); - - - //TODO lots of duplication - test( "pageLoading doesn't add the dialog to the page when loading message is false", function(){ - $.testHelper.alterExtend({loadingMessage: false}); - $.testHelper.reloadLib(libName); - $.mobile.pageLoading(false); - ok(!$(".ui-loader").length); - }); - - test( "pageLoading doesn't add the dialog to the page when done is passed as true", function(){ - $.testHelper.alterExtend({loadingMessage: true}); - $.testHelper.reloadLib(libName); - - // TODO add post reload callback - $('.ui-loader').remove(); - - $.mobile.pageLoading(true); - ok(!$(".ui-loader").length); - }); - - test( "pageLoading adds the dialog to the page when done is true", function(){ - $.testHelper.alterExtend({loadingMessage: true}); - $.testHelper.reloadLib(libName); - $.mobile.pageLoading(false); - ok($(".ui-loader").length); - }); - - var metaViewportSelector = "head meta[name=viewport]", - setViewPortContent = function(value){ - $(metaViewportSelector).remove(); - $.testHelper.alterExtend({metaViewportContent: value}); - $.testHelper.reloadLib(libName); - }; - - test( "meta view port element is added to head when defined on mobile", function(){ - setViewPortContent("width=device-width"); - same($(metaViewportSelector).length, 1); - }); - - test( "meta view port element not added to head when not defined on mobile", function(){ - setViewPortContent(false); - same($(metaViewportSelector).length, 0); - }); - - var findFirstPage = function() { - return $("[data-role='page']").first(); - }; - - test( "active page and start page should be set to the fist page in the selected set", function(){ - var firstPage = findFirstPage(); - $.testHelper.reloadLib(libName); - - same($.mobile.startPage, firstPage); - same($.mobile.activePage, firstPage); - }); - - test( "mobile viewport class is defined on the first page's parent", function(){ - var firstPage = findFirstPage(); - $.testHelper.reloadLib(libName); - - ok(firstPage.parent().hasClass('ui-mobile-viewport')); - }); - - test( "mobile page container is the first page's parent", function(){ - var firstPage = findFirstPage(); - $.testHelper.reloadLib(libName); - - same($.mobile.pageContainer, firstPage.parent()); - }); - - test( "page loading is called on document ready", function(){ - expect( 2 ); - - $.testHelper.alterExtend({ pageLoading: function(){ - ok("called"); - }}); - - $.testHelper.reloadLib(libName); - }); - - test( "hashchange triggered on document ready with single argument: true", function(){ - expect( 2 ); - - $(window).bind("hashchange", function(ev, arg){ - same(arg, true); - }); - - $.testHelper.reloadLib(libName); - }); - - //TODO test that silentScroll is called on window load }); -})(jQuery); + + test( "$.mobile.nsNormalize works properly with namespace defined (test default)", function(){ + equal($.mobile.nsNormalize("foo"), "nstestFoo", "appends ns and initcaps"); + equal($.mobile.nsNormalize("fooBar"), "nstestFooBar", "leaves capped strings intact"); + equal($.mobile.nsNormalize("foo-bar"), "nstestFooBar", "changes dashed strings"); + equal($.mobile.nsNormalize("foo-bar-bak"), "nstestFooBarBak", "changes multiple dashed strings"); + }); + + test( "$.mobile.nsNormalize works properly with an empty namespace", function(){ + var realNs = $.mobile.ns; + + $.mobile.ns = ""; + + equal($.mobile.nsNormalize("foo"), "foo", "leaves uncapped and undashed"); + equal($.mobile.nsNormalize("fooBar"), "fooBar", "leaves capped strings intact"); + equal($.mobile.nsNormalize("foo-bar"), "fooBar", "changes dashed strings"); + equal($.mobile.nsNormalize("foo-bar-bak"), "fooBarBak", "changes multiple dashed strings"); + + $.mobile.ns = realNs; + }); + + //data tests + test( "$.fn.jqmData and $.fn.jqmRemoveData methods are working properly", function(){ + var data; + + same( $("body").jqmData("foo", true), $("body"), "setting data returns the element" ); + + same( $("body").jqmData("foo"), true, "getting data returns the right value" ); + + same( $("body").data($.mobile.nsNormalize("foo")), true, "data was set using namespace" ); + + same( $("body").jqmData("foo", undefined), true, "getting data still returns the value if there's an undefined second arg" ); + + data = $.extend( {}, $("body").data() ); + delete data[ $.expando ]; //discard the expando for that test + same( data , { "nstestFoo": true }, "passing .data() no arguments returns a hash with all set properties" ); + + same( $("body").jqmData(), undefined, "passing no arguments returns undefined" ); + + same( $("body").jqmData(undefined), undefined, "passing a single undefined argument returns undefined" ); + + same( $("body").jqmData(undefined, undefined), undefined, "passing 2 undefined arguments returns undefined" ); + + same( $("body").jqmRemoveData("foo"), $("body"), "jqmRemoveData returns the element" ); + + same( $("body").jqmData("foo"), undefined, "jqmRemoveData properly removes namespaced data" ); + + }); + + + test( "$.jqmData and $.jqmRemoveData methods are working properly", function(){ + same( $.jqmData(document.body, "foo", true), true, "setting data returns the value" ); + + same( $.jqmData(document.body, "foo"), true, "getting data returns the right value" ); + + same( $.data(document.body, $.mobile.nsNormalize("foo")), true, "data was set using namespace" ); + + same( $.jqmData(document.body, "foo", undefined), true, "getting data still returns the value if there's an undefined second arg" ); + + same( $.jqmData(document.body), undefined, "passing no arguments returns undefined" ); + + same( $.jqmData(document.body, undefined), undefined, "passing a single undefined argument returns undefined" ); + + same( $.jqmData(document.body, undefined, undefined), undefined, "passing 2 undefined arguments returns undefined" ); + + same( $.jqmRemoveData(document.body, "foo"), undefined, "jqmRemoveData returns the undefined value" ); + + same( $("body").jqmData("foo"), undefined, "jqmRemoveData properly removes namespaced data" ); + + }); + + test( "addDependents works properly", function() { + same( $("#parent").jqmData('dependents'), undefined ); + $( "#parent" ).addDependents( $("#dependent") ); + same( $("#parent").jqmData('dependents').length, 1 ); + }); + + test( "removeWithDependents removes the parent element and ", function(){ + $( "#parent" ).addDependents( $("#dependent") ); + same($( "#parent, #dependent" ).length, 2); + $( "#parent" ).removeWithDependents(); + same($( "#parent, #dependent" ).length, 0); + }); + + test( "$.fn.getEncodedText should return the encoded value where $.fn.text doesn't", function() { + same( $("#encoded").text(), "foo>"); + same( $("#encoded").getEncodedText(), "foo>"); + same( $("#unencoded").getEncodedText(), "foo"); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/core/core_scroll.js b/tests/unit/core/core_scroll.js index 6737cb82..561b37e8 100644 --- a/tests/unit/core/core_scroll.js +++ b/tests/unit/core/core_scroll.js @@ -2,7 +2,7 @@ * mobile core unit tests */ -(function( $ ) { +(function($){ var libName = "jquery.mobile.core.js", scrollTimeout = 20, // TODO expose timing as an attribute scrollStartEnabledTimeout = 150; @@ -19,56 +19,45 @@ var scrollUp = function( pos ){ $(window).scrollTop(1000); - ok($(window).scrollTop() > 0); - - if(pos) { - $.mobile.silentScroll(pos); - } else { - $.mobile.silentScroll(); - } + ok($(window).scrollTop() > 0, $(window).scrollTop()); + $.mobile.silentScroll(pos); }; - test( "silent scroll scrolls the page to the top by default", function(){ + asyncTest( "silent scroll scrolls the page to the top by default", function(){ scrollUp(); - stop(); setTimeout(function(){ same($(window).scrollTop(), 0); start(); }, scrollTimeout); }); - test( "silent scroll scrolls the page to the passed y position", function(){ + asyncTest( "silent scroll scrolls the page to the passed y position", function(){ var pos = 10; scrollUp(pos); - stop(); setTimeout(function(){ same($(window).scrollTop(), pos); start(); }, scrollTimeout); }); - // NOTE may be brittle depending on timing - test( "silent scroll takes at least 20 ms to scroll to the top", function(){ + test( "silent scroll is async", function(){ scrollUp(); - - stop(); - setTimeout(function(){ - ok($(window).scrollTop() != 0); - start(); - }, scrollTimeout - 1); + ok($(window).scrollTop() != 0, "scrolltop position should not be zero"); + start(); }); - test( "scrolling marks scrollstart as disabled for 150 ms", function(){ + asyncTest( "scrolling marks scrollstart as disabled for 150 ms", function(){ $.event.special.scrollstart.enabled = true; scrollUp(); ok(!$.event.special.scrollstart.enabled); - stop(); setTimeout(function(){ ok($.event.special.scrollstart.enabled); start(); }, scrollStartEnabledTimeout); }); + + //TODO test that silentScroll is called on window load })(jQuery); diff --git a/tests/unit/core/index.html b/tests/unit/core/index.html index 23d85838..8cafe48d 100644 --- a/tests/unit/core/index.html +++ b/tests/unit/core/index.html @@ -1,34 +1,23 @@ - + + jQuery Mobile Core Test Suite - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - + @@ -38,10 +27,14 @@
      -
      -
      -
      +
      +
      +
      +
      +
      foo>
      +
      +
      diff --git a/tests/unit/degradeInputs/degradeInputs.js b/tests/unit/degradeInputs/degradeInputs.js new file mode 100644 index 00000000..b45e411b --- /dev/null +++ b/tests/unit/degradeInputs/degradeInputs.js @@ -0,0 +1,29 @@ +/* + * degradeInputs unit tests + */ + +(function($){ + + module('jquery.mobile.slider.js'); + + test('keepNative elements should not be degraded', function() { + same($('input#not-to-be-degraded').attr("type"), "range"); + }); + + test('should degrade input type to a different type, as specified in page options', function(){ + var degradeInputs = $.mobile.page.prototype.options.degradeInputs; + + expect( degradeInputs.length ); + + $.each(degradeInputs, function( oldType, newType ) { + if (newType === false) { + newType = oldType; + } + + $('#test-container').html('').trigger("create"); + + same($('#test-container input').attr("type"), newType); + }); + }); + +})(jQuery); \ No newline at end of file diff --git a/tests/unit/degradeInputs/index.html b/tests/unit/degradeInputs/index.html new file mode 100644 index 00000000..4dfa1d6f --- /dev/null +++ b/tests/unit/degradeInputs/index.html @@ -0,0 +1,37 @@ + + + + + + jQuery Mobile Degrade Inputs Test Suite + + + + + + + + + + + + + + + +

      jQuery Mobile Degrade Inputs Test Suite

      +

      +

      +
        +
      + +
      + + + +
      +
      + +
      + + diff --git a/tests/unit/dialog/dialog_events.js b/tests/unit/dialog/dialog_events.js new file mode 100644 index 00000000..0e53179f --- /dev/null +++ b/tests/unit/dialog/dialog_events.js @@ -0,0 +1,38 @@ +/* + * mobile dialog unit tests + */ +(function($) { + module( "jquery.mobile.dialog.js" ); + + asyncTest( "dialog hash is added when the dialog is opened and removed when closed", function() { + expect( 6 ); + + $.testHelper.pageSequence([ + function() { + //bring up the dialog + $( "#foo-dialog-link" ).click(); + }, + + function() { + var fooDialog = $( "#foo-dialog" ); + + // make sure the dialog came up + ok( /&ui-state=dialog/.test(location.hash), "ui-state=dialog =~ location.hash", "dialog open" ); + + // Assert dialog theme inheritance (issue 1375): + ok( fooDialog.hasClass( "ui-body-b" ), "Expected explicit theme ui-body-b" ); + ok( fooDialog.find( ":jqmData(role=header)" ).hasClass( "ui-bar-a" ), "Expected header to inherit from $.mobile.page.prototype.options.headerTheme" ); + ok( fooDialog.find( ":jqmData(role=content)" ).hasClass( "ui-body-d" ), "Expect content to inherit from $.mobile.page.prototype.options.contentTheme" ); + ok( fooDialog.find( ":jqmData(role=footer)" ).hasClass( "ui-bar-a" ), "Expected footer to inherit from $.mobile.page.prototype.options.footerTheme" ); + + // close the dialog + $( ".ui-dialog" ).dialog( "close" ); + }, + + function() { + ok( !/&ui-state=dialog/.test(location.hash), "ui-state=dialog !~ location.hash" ); + start(); + } + ]); + }); +})( jQuery ); diff --git a/tests/unit/dialog/index.html b/tests/unit/dialog/index.html new file mode 100644 index 00000000..8318069e --- /dev/null +++ b/tests/unit/dialog/index.html @@ -0,0 +1,53 @@ + + + + + + jQuery Mobile Dialog Test Suite + + + + + + + + + + + + + + + + + + +

      jQuery Mobile Dialog Test Suite

      +

      +

      +
        +
      + +
      + +
      + +
      +
      +

      Dialog

      +
      +
      + foo +
      +
      + footer +
      +
      + + + diff --git a/tests/unit/event/event_core.js b/tests/unit/event/event_core.js index e02eb742..ac644f72 100644 --- a/tests/unit/event/event_core.js +++ b/tests/unit/event/event_core.js @@ -2,7 +2,7 @@ * mobile event unit tests */ -(function( $ ) { +(function($){ var libName = "jquery.mobile.event.js", absFn = Math.abs, originalEventFn = $.Event.prototype.originalEvent, @@ -11,20 +11,23 @@ "swipe swipeleft swiperight scrollstart scrollstop").split( " " ); module(libName, { - teardown: function(){ - $.each(events, function(i, name){ - $("#main").unbind(name); - }); + setup: function(){ - $($.event.special.scrollstart).unbind("scrollstart"); - $($.event.special.tap).unbind("tap"); - $($.event.special.tap).unbind("taphold"); - $($.event.special.swipe).unbind("swipe"); + // ensure bindings are removed + $.each(events + "vmouseup vmousedown".split(" "), function(i, name){ + $("#qunit-fixture").unbind(); + }); //NOTE unmock Math.abs = absFn; $.Event.prototype.originalEvent = originalEventFn; $.Event.prototype.preventDefault = preventDefaultFn; + + // make sure the event objects respond to touches to simulate + // the collections existence in non touch enabled test browsers + $.Event.prototype.touches = [{pageX: 1, pageY: 1 }]; + + $($.mobile.pageContainer).unbind( "throttledresize" ); } }); @@ -37,30 +40,32 @@ $.testHelper.reloadLib(libName); - $.each($.fn.clone(events), function( i, name ) { - ok($.fn[name] !== undefined, name + "is not undefined"); + $.each(events, function( i, name ) { + ok($.fn[name] !== undefined, name + " is not undefined"); }); }); }); - test( "defined event functions bind a closure when passed", function(){ + asyncTest( "defined event functions bind a closure when passed", function(){ expect( 1 ); - $('#main')[events[0]](function(){ + $('#qunit-fixture').bind(events[0], function(){ ok(true, "event fired"); + start(); }); - $('#main').trigger(events[0]); + $('#qunit-fixture').trigger(events[0]); }); - test( "defined event functions trigger the event with no arguments", function(){ + asyncTest( "defined event functions trigger the event with no arguments", function(){ expect( 1 ); - $('#main')[events[0]](function(){ + $('#qunit-fixture').bind('touchstart', function(){ ok(true, "event fired"); + start(); }); - $('#main')[events[0]](); + $('#qunit-fixture').touchstart(); }); test( "defining event functions sets the attrFn to true", function(){ @@ -75,44 +80,45 @@ ok($.event.special.scrollstart.enabled, "scrollstart enabled"); }); - test( "scrollstart setup binds a function that returns when its disabled", function(){ + asyncTest( "scrollstart setup binds a function that returns when its disabled", function(){ expect( 1 ); $.event.special.scrollstart.enabled = false; - $($.event.special.scrollstart).bind("scrollstart", function(){ + $( "#qunit-fixture" ).bind("scrollstart", function(){ ok(false, "scrollstart fired"); }); - $($.event.special.scrollstart).bind("touchmove", function(){ + $( "#qunit-fixture" ).bind("touchmove", function(){ ok(true, "touchmove fired"); + start(); }); - $($.event.special.scrollstart).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); }); - test( "scrollstart setup binds a function that triggers scroll start when enabled", function(){ + asyncTest( "scrollstart setup binds a function that triggers scroll start when enabled", function(){ $.event.special.scrollstart.enabled = true; - $($.event.special.scrollstart).bind("scrollstart", function(){ + $( "#qunit-fixture" ).bind("scrollstart", function(){ ok(true, "scrollstart fired"); + start(); }); - $($.event.special.scrollstart).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); }); - test( "scrollstart setup binds a function that triggers scroll stop after 50 ms", function(){ + asyncTest( "scrollstart setup binds a function that triggers scroll stop after 50 ms", function(){ var triggered = false; $.event.special.scrollstart.enabled = true; - $($.event.special.scrollstart).bind("scrollstop", function(){ + $( "#qunit-fixture" ).bind("scrollstop", function(){ triggered = true; }); ok(!triggered, "not triggered"); - $($.event.special.scrollstart).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); - stop(); setTimeout(function(){ ok(triggered, "triggered"); start(); @@ -122,20 +128,24 @@ var forceTouchSupport = function(){ $.support.touch = true; $.testHelper.reloadLib(libName); + + //mock originalEvent information + $.Event.prototype.originalEvent = { + touches: [{ 'pageX' : 0 }, { 'pageY' : 0 }] + }; }; - test( "long press fires tap hold after 750 ms", function(){ + asyncTest( "long press fires tap hold after 750 ms", function(){ var taphold = false; forceTouchSupport(); - $($.event.special.tap).bind("taphold", function(){ + $( "#qunit-fixture" ).bind("taphold", function(){ taphold = true; }); - $($.event.special.tap).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("vmousedown"); - stop(); setTimeout(function(){ ok(taphold); start(); @@ -150,24 +160,25 @@ }; }; - test( "touchmove prevents taphold", function(){ + asyncTest( "move prevents taphold", function(){ + expect( 1 ); var taphold = false; forceTouchSupport(); mockAbs(100); //NOTE record taphold event - stop(); - $($.event.special.tap).bind("taphold", function(){ + $( "#qunit-fixture" ).bind("taphold", function(){ + ok(false, "taphold fired"); taphold = true; }); //NOTE start the touch events - $($.event.special.tap).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("vmousedown"); //NOTE fire touchmove to push back taphold setTimeout(function(){ - $($.event.special.tap).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("vmousecancel"); }, 100); //NOTE verify that the taphold hasn't been fired @@ -178,30 +189,35 @@ }, 751); }); - test( "tap event fired without movement", function(){ - var tap = false; + asyncTest( "tap event fired without movement", function(){ + expect( 1 ); + var tap = false, + checkTap = function(){ + ok(true, "tap fired"); + }; forceTouchSupport(); //NOTE record the tap event - $($.event.special.tap).bind("tap", function(){ + $( "#qunit-fixture" ).bind("tap", checkTap); + + $( "#qunit-fixture" ).trigger("vmousedown"); + $( "#qunit-fixture" ).trigger("vmouseup"); + $( "#qunit-fixture" ).trigger("vclick"); + + setTimeout(function(){ start(); - tap = true; - }); - - stop(); - $($.event.special.tap).trigger("touchstart"); - $($.event.special.tap).trigger("touchend"); - - ok(tap, "tapped"); + }, 400); }); - test( "tap event not fired when there is movement", function(){ + asyncTest( "tap event not fired when there is movement", function(){ + expect( 1 ); var tap = false; forceTouchSupport(); //NOTE record tap event - $($.event.special.tap).bind("tap", function(){ + $( "#qunit-fixture" ).bind("tap", function(){ + ok(false, "tap fired"); tap = true; }); @@ -209,17 +225,107 @@ mockAbs(100); //NOTE start and move right away - $($.event.special.tap).trigger("touchstart"); - $($.event.special.tap).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchmove"); //NOTE end touch sequence after 20 ms - stop(); setTimeout(function(){ - $($.event.special.tap).trigger("touchend"); - start(); + $( "#qunit-fixture" ).trigger("touchend"); }, 20); - ok(!tap, "not tapped"); + setTimeout(function(){ + ok(!tap, "not tapped"); + start(); + }, 40); + }); + + asyncTest( "tap event propagates up DOM tree", function(){ + var tap = 0, + $qf = $( "#qunit-fixture" ), + $doc = $( document ), + docTapCB = function(){ + same(++tap, 2, "document tap callback called once after #qunit-fixture callback"); + }; + + $qf.bind( "tap", function() { + same(++tap, 1, "#qunit-fixture tap callback called once"); + }); + + $doc.bind( "tap", docTapCB ); + + $qf.trigger( "vmousedown" ) + .trigger( "vmouseup" ) + .trigger( "vclick" ); + + // tap binding should be triggered twice, once for + // #qunit-fixture, and a second time for document. + same( tap, 2, "final tap callback count is 2" ); + + $doc.unbind( "tap", docTapCB ); + + start(); + }); + + asyncTest( "stopPropagation() prevents tap from propagating up DOM tree", function(){ + var tap = 0, + $qf = $( "#qunit-fixture" ), + $doc = $( document ), + docTapCB = function(){ + ok(false, "tap should NOT be triggered on document"); + }; + + $qf.bind( "tap", function(e) { + same(++tap, 1, "tap callback 1 triggered once on #qunit-fixture"); + e.stopPropagation(); + }) + .bind( "tap", function(e) { + same(++tap, 2, "tap callback 2 triggered once on #qunit-fixture"); + }); + + $doc.bind( "tap", docTapCB); + + $qf.trigger( "vmousedown" ) + .trigger( "vmouseup" ) + .trigger( "vclick" ); + + // tap binding should be triggered twice. + same( tap, 2, "final tap count is 2" ); + + $doc.unbind( "tap", docTapCB ); + + start(); + }); + + asyncTest( "stopImmediatePropagation() prevents tap propagation and execution of 2nd handler", function(){ + var tap = 0, + $cf = $( "#qunit-fixture" ); + $doc = $( document ), + docTapCB = function(){ + ok(false, "tap should NOT be triggered on document"); + }; + + // Bind 2 tap callbacks on qunit-fixture. Only the first + // one should ever be called. + $cf.bind( "tap", function(e) { + same(++tap, 1, "tap callback 1 triggered once on #qunit-fixture"); + e.stopImmediatePropagation(); + }) + .bind( "tap", function(e) { + ok(false, "tap callback 2 should NOT be triggered on #qunit-fixture"); + }); + + $doc.bind( "tap", docTapCB); + + $cf.trigger( "vmousedown" ) + .trigger( "vmouseup" ) + .trigger( "vclick" ); + + // tap binding should be triggered once. + same( tap, 1, "final tap count is 1" ); + + $doc.unbind( "tap", docTapCB ); + + start(); }); var swipeTimedTest = function(opts){ @@ -227,9 +333,8 @@ forceTouchSupport(); - $($.event.special.swipe).bind('swipe', function(){ + $( "#qunit-fixture" ).bind('swipe', function(){ swipe = true; - start(); }); //NOTE bypass the trigger source check @@ -237,24 +342,23 @@ touches: false }; - $($.event.special.swipe).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchstart"); //NOTE make sure the coordinates are calculated within range // to be registered as a swipe mockAbs(opts.coordChange); setTimeout(function(){ - $($.event.special.swipe).trigger("touchmove"); - $($.event.special.swipe).trigger("touchend"); - }, opts.timeout); + $( "#qunit-fixture" ).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchend"); + }, opts.timeout + 100); - stop(); setTimeout(function(){ same(swipe, opts.expected, "swipe expected"); + start(); + }, opts.timeout + 200); - //NOTE the start in the event closure won't be fired, fire it here - if(!opts.expected) { start(); } - }, opts.timeout + 10); + stop(); }; test( "swipe fired when coordinate change in less than a second", function(){ @@ -273,9 +377,14 @@ swipeTimedTest({ timeout: 1000, coordChange: 75, expected: false }); }); - test( "scrolling prevented when coordinate change > 10", function(){ + asyncTest( "scrolling prevented when coordinate change > 10", function(){ expect( 1 ); + forceTouchSupport(); + + // ensure the swipe custome event is setup + $( "#qunit-fixture" ).bind('swipe', function(){}); + //NOTE bypass the trigger source check $.Event.prototype.originalEvent = { touches: false @@ -283,33 +392,41 @@ $.Event.prototype.preventDefault = function(){ ok(true, "prevent default called"); + start(); }; mockAbs(11); - $($.event.special.swipe).trigger("touchstart"); - $($.event.special.swipe).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchmove"); }); - test( "move handler returns when touchstart has been fired since touchstop", function(){ + asyncTest( "move handler returns when touchstart has been fired since touchstop", function(){ expect( 1 ); + // bypass triggered event check $.Event.prototype.originalEvent = { touches: false }; - $($.event.special.swipe).trigger("touchstart"); - $($.event.special.swipe).trigger("touchend"); + forceTouchSupport(); - $($.event.special.swipe).bind("touchmove", function(){ + // ensure the swipe custome event is setup + $( "#qunit-fixture" ).bind('swipe', function(){}); + + $( "#qunit-fixture" ).trigger("touchstart"); + $( "#qunit-fixture" ).trigger("touchend"); + + $( "#qunit-fixture" ).bind("touchmove", function(){ ok(true, "touchmove bound functions are fired"); + start(); }); Math.abs = function(){ ok(false, "shouldn't compare coordinates"); }; - $($.event.special.swipe).trigger("touchmove"); + $( "#qunit-fixture" ).trigger("touchmove"); }); var nativeSupportTest = function(opts){ @@ -348,4 +465,84 @@ returnValue: undefined //NOTE result of unbind function call }); }); -})(jQuery); \ No newline at end of file + + /* The following 4 tests are async so that the throttled event triggers don't interfere with subsequent tests */ + + asyncTest( "throttledresize event proxies resize events", function(){ + $( window ).one( "throttledresize", function(){ + ok( true, "throttledresize called"); + start(); + }); + + $( window ).trigger( "resize" ); + }); + + asyncTest( "throttledresize event prevents resize events from firing more frequently than 250ms", function(){ + var called = 0; + + $(window).bind( "throttledresize", function(){ + called++; + }); + + // NOTE 250 ms * 3 = 750ms which is plenty of time + // for the events to trigger before the next test, but + // not so much time that the second resize will be triggered + // before the call to same() is made + $.testHelper.sequence([ + function(){ + $(window).trigger( "resize" ).trigger( "resize" ); + }, + + // verify that only one throttled resize was called after 250ms + function(){ same( called, 1 ); }, + + function(){ + start(); + } + ], 250); + }); + + asyncTest( "throttledresize event promises that a held call will execute only once after throttled timeout", function(){ + var called = 0; + + expect( 2 ); + + $.testHelper.eventSequence( "throttledresize", [ + // ignore the first call + $.noop, + + function(){ + ok( true, "second throttled resize should run" ); + }, + + function(timedOut){ + ok( timedOut, "third throttled resize should not run"); + start(); + } + ]); + + $.mobile.pageContainer + .trigger( "resize" ) + .trigger( "resize" ) + .trigger( "resize" ); + }); + + asyncTest( "mousedown mouseup and click events should add a which when its not defined", function() { + var whichDefined = function( event ){ + same(event.which, 1); + }; + + $( document ).bind( "vclick", whichDefined); + $( document ).trigger( "click" ); + + $( document ).bind( "vmousedown", whichDefined); + $( document ).trigger( "mousedown" ); + + $( document ).bind( "vmouseup", function( event ){ + same(event.which, 1); + start(); + }); + + $( document ).trigger( "mouseup" ); + }); +})(jQuery); diff --git a/tests/unit/event/index.html b/tests/unit/event/index.html index b3be1ed5..bdcc89de 100644 --- a/tests/unit/event/index.html +++ b/tests/unit/event/index.html @@ -1,21 +1,23 @@ - + + jQuery Mobile Event Test Suite - - - - - - - + + + + + - - - + + + + + + @@ -25,7 +27,10 @@
      -
      +
      + +
      +
      diff --git a/tests/unit/fieldContain/fieldContain_events.js b/tests/unit/fieldContain/fieldContain_events.js new file mode 100644 index 00000000..7d20c1c2 --- /dev/null +++ b/tests/unit/fieldContain/fieldContain_events.js @@ -0,0 +1,16 @@ +/* + * mobile dialog unit tests + */ +(function($){ + module('jquery.mobile.fieldContain.js'); + + test( "Field container contains appropriate css styles", function(){ + ok($('#test-fieldcontain').hasClass('ui-field-contain ui-body ui-br'), 'A fieldcontain element must contain styles "ui-field-contain ui-body ui-br"'); + }); + + test( "Field container will create when inside a container that receives a 'create' event", function(){ + ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-field-contain").length, "did not have enhancements applied" ); + ok( $("#enhancetest").trigger("create").find(".ui-field-contain").length, "enhancements applied" ); + }); + +})(jQuery); diff --git a/tests/unit/fieldContain/index.html b/tests/unit/fieldContain/index.html new file mode 100644 index 00000000..263d350e --- /dev/null +++ b/tests/unit/fieldContain/index.html @@ -0,0 +1,47 @@ + + + + + jQuery Mobile FieldContain Integration Test + + + + + + + + + + + + + + + + + + +

      jQuery Mobile FieldContainer Test Suite

      +

      +

      +
        +
      + +
      + + +
      + + +
      + +
      + +
      +
      +
      +
      + + + + diff --git a/tests/unit/index.php b/tests/unit/index.php new file mode 100644 index 00000000..2ecb3ba5 --- /dev/null +++ b/tests/unit/index.php @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/tests/unit/init/index.html b/tests/unit/init/index.html new file mode 100644 index 00000000..6e0403b8 --- /dev/null +++ b/tests/unit/init/index.html @@ -0,0 +1,36 @@ + + + + + jQuery Mobile Init Test Suite + + + + + + + + + + + + + + + + + +

      jQuery Mobile Init Test Suite

      +

      +

      +
        +
      + +
      +
      + +
      +
      + + + diff --git a/tests/unit/init/init_core.js b/tests/unit/init/init_core.js new file mode 100644 index 00000000..171d9a99 --- /dev/null +++ b/tests/unit/init/init_core.js @@ -0,0 +1,268 @@ +/* + * mobile init tests + */ +(function($){ + var mobilePage = undefined, + libName = 'jquery.mobile.init.js', + coreLib = 'jquery.mobile.core.js', + extendFn = $.extend, + setGradeA = function(value) { $.mobile.gradeA = function(){ return value; }; }, + reloadCoreNSandInit = function(){ + $.testHelper.reloadLib(coreLib); + $.testHelper.reloadLib("jquery.setNamespace.js"); + $.testHelper.reloadLib(libName); + }; + + + module(libName, { + setup: function(){ + // NOTE reset for gradeA tests + $('html').removeClass('ui-mobile'); + + // TODO add post reload callback + $('.ui-loader').remove(); + }, + teardown: function(){ + $.extend = extendFn; + + // NOTE reset for showPageLoadingMsg/hidePageLoadingMsg tests + $('.ui-loader').remove(); + + // clear the classes added by reloading the init + $("html").attr('class', ''); + } + }); + + // NOTE important to use $.fn.one here to make sure library reloads don't fire + // the event before the test check below + $(document).one("mobileinit", function(){ + mobilePage = $.mobile.page; + }); + + // NOTE for the following two tests see index html for the binding + test( "mobile.page is available when mobile init is fired", function(){ + ok( mobilePage !== undefined, "$.mobile.page is defined" ); + }); + + $.testHelper.excludeFileProtocol(function(){ + asyncTest( "loading the init library triggers mobilinit on the document", function(){ + var initFired = false; + expect( 1 ); + + $(window.document).one('mobileinit', function(event){ + initFired = true; + }); + + $.testHelper.reloadLib(libName); + + setTimeout(function(){ + ok(initFired, "init fired"); + start(); + }, 1000); + }); + + test( "enhancments are skipped when the browser is not grade A", function(){ + setGradeA(false); + $.testHelper.reloadLib(libName); + + //NOTE easiest way to check for enhancements, not the most obvious + ok(!$("html").hasClass("ui-mobile"), "html elem doesn't have class ui-mobile"); + }); + + test( "enhancments are added when the browser is grade A", function(){ + setGradeA(true); + $.testHelper.reloadLib(libName); + + ok($("html").hasClass("ui-mobile"), "html elem has class mobile"); + }); + + asyncTest( "useFastClick is configurable via mobileinit", function(){ + $(document).one( "mobileinit", function(){ + $.mobile.useFastClick = false; + start(); + }); + + $.testHelper.reloadLib(libName); + + same( $.mobile.useFastClick, false , "fast click is set to false after init" ); + $.mobile.useFastClick = true; + }); + + + + var findFirstPage = function() { + return $(":jqmData(role='page')").first(); + }; + + test( "active page and start page should be set to the fist page in the selected set", function(){ + expect( 2 ); + $.testHelper.reloadLib(libName); + var firstPage = findFirstPage(); + + same($.mobile.firstPage[0], firstPage[0]); + same($.mobile.activePage[0], firstPage[0]); + }); + + test( "mobile viewport class is defined on the first page's parent", function(){ + expect( 1 ); + $.testHelper.reloadLib(libName); + var firstPage = findFirstPage(); + + ok(firstPage.parent().hasClass("ui-mobile-viewport"), "first page has viewport"); + }); + + test( "mobile page container is the first page's parent", function(){ + expect( 1 ); + $.testHelper.reloadLib(libName); + var firstPage = findFirstPage(); + + same($.mobile.pageContainer[0], firstPage.parent()[0]); + }); + + asyncTest( "hashchange triggered on document ready with single argument: true", function(){ + $.testHelper.sequence([ + function(){ + location.hash = "#foo"; + }, + + // delay the bind until the first hashchange + function(){ + $(window).one("hashchange", function(ev, arg){ + same(arg, true); + start(); + }); + }, + + function(){ + $.testHelper.reloadLib(libName); + } + ], 1000); + }); + + test( "pages without a data-url attribute have it set to their id", function(){ + same($("#foo").jqmData('url'), "foo"); + }); + + test( "pages with a data-url attribute are left with the original value", function(){ + same($("#bar").jqmData('url'), "bak"); + }); + + asyncTest( "showPageLoadingMsg doesn't add the dialog to the page when loading message is false", function(){ + expect( 1 ); + $.mobile.loadingMessage = false; + $.mobile.showPageLoadingMsg(); + + setTimeout(function(){ + ok(!$(".ui-loader").length, "no ui-loader element"); + start(); + }, 500); + }); + + asyncTest( "hidePageLoadingMsg doesn't add the dialog to the page when loading message is false", function(){ + expect( 1 ); + $.mobile.loadingMessage = true; + $.mobile.hidePageLoadingMsg(); + + setTimeout(function(){ + same($(".ui-loading").length, 0, "page should not be in the loading state"); + start(); + }, 500); + }); + + asyncTest( "showPageLoadingMsg adds the dialog to the page when loadingMessage is true", function(){ + expect( 1 ); + $.mobile.loadingMessage = true; + $.mobile.showPageLoadingMsg(); + + setTimeout(function(){ + same($(".ui-loading").length, 1, "page should be in the loading state"); + start(); + }, 500); + }); + + asyncTest( "page loading should contain default loading message", function(){ + expect( 1 ); + reloadCoreNSandInit(); + $.mobile.showPageLoadingMsg(); + + setTimeout(function(){ + same($(".ui-loader h1").text(), "loading"); + start(); + }, 500); + }); + + asyncTest( "page loading should contain custom loading message", function(){ + $.mobile.loadingMessage = "foo"; + $.testHelper.reloadLib(libName); + $.mobile.showPageLoadingMsg(); + + setTimeout(function(){ + same($(".ui-loader h1").text(), "foo"); + start(); + }, 500); + }); + + asyncTest( "page loading should contain custom loading message when set during runtime", function(){ + $.mobile.loadingMessage = "bar"; + $.mobile.showPageLoadingMsg(); + + setTimeout(function(){ + same($(".ui-loader h1").text(), "bar"); + start(); + }, 500); + }); + + + + // NOTE: the next two tests work on timeouts that assume a page will be created within 2 seconds + // it'd be great to get these using a more reliable callback or event + + asyncTest( "page does auto-initialize at domready when autoinitialize option is true (default) ", function(){ + + $( "
      ", { "data-nstest-role": "page", "id": "autoinit-on" } ).prependTo( "body" ) + + $(document).one("mobileinit", function(){ + $.mobile.autoInitializePage = true; + }); + + location.hash = ""; + + reloadCoreNSandInit(); + + setTimeout(function(){ + same( $( "#autoinit-on.ui-page" ).length, 1 ); + + start(); + }, 2000); + }); + + + asyncTest( "page does not initialize at domready when autoinitialize option is false ", function(){ + $(document).one("mobileinit", function(){ + $.mobile.autoInitializePage = false; + }); + + $( "
      ", { "data-nstest-role": "page", "id": "autoinit-off" } ).prependTo( "body" ) + + location.hash = ""; + + + reloadCoreNSandInit(); + + setTimeout(function(){ + same( $( "#autoinit-off.ui-page" ).length, 0 ); + + $(document).bind("mobileinit", function(){ + $.mobile.autoInitializePage = true; + }); + + reloadCoreNSandInit(); + + start(); + }, 2000); + }); + + + + }); +})(jQuery); diff --git a/tests/unit/init/init_core_nopage.js b/tests/unit/init/init_core_nopage.js new file mode 100644 index 00000000..193af255 --- /dev/null +++ b/tests/unit/init/init_core_nopage.js @@ -0,0 +1,12 @@ +/* + * mobile init tests + */ +(function($){ + + + test( "page element is generated when not present in initial markup", function(){ + ok( $( ".ui-page" ).length, 1 ); + }); + + +})(jQuery); diff --git a/tests/unit/init/nopage.html b/tests/unit/init/nopage.html new file mode 100644 index 00000000..3074b8b5 --- /dev/null +++ b/tests/unit/init/nopage.html @@ -0,0 +1,31 @@ + + + + + jQuery Mobile Init Test Suite + + + + + + + + + + + + + + +

      jQuery Mobile Init Test Suite

      +

      +

      +
        +
      + + + diff --git a/tests/unit/jquery.setNameSpace.js b/tests/unit/jquery.setNameSpace.js new file mode 100644 index 00000000..c4c9f223 --- /dev/null +++ b/tests/unit/jquery.setNameSpace.js @@ -0,0 +1,4 @@ +//set namespace for unit test markp +$( document ).bind( "mobileinit", function(){ + $.mobile.ns = "nstest-"; +}); \ No newline at end of file diff --git a/tests/unit/listview/cache-tests/cached-nested.html b/tests/unit/listview/cache-tests/cached-nested.html new file mode 100644 index 00000000..c6fbcdf1 --- /dev/null +++ b/tests/unit/listview/cache-tests/cached-nested.html @@ -0,0 +1,55 @@ + + + + + + +
      +
      +

      Basic multiple lists view

      +
      +
      +
        +
      • Item 1
      • +
      • Item 2
      • +
      • Item 3 +
          +
        • Item A-3-0
        • +
        • Item A-3-1
        • +
        • Item A-3-2
        • +
        +
      • +
      +
        +
      • Item 1
      • +
      • Item 2
      • +
      • Item 3 +
          +
        • Item B-3-0 +
            +
          • Item B-3-0-0
          • +
          • Item B-3-0-1 +
              +
            • Item B-3-0-1-0
            • +
            • Item B-3-0-1-1
            • +
            • Item B-3-0-1-2
            • +
            +
          • +
          • Item B-3-0-2
          • +
          +
        • +
        • Item B-3-1 +
            +
          • Item B-3-1-0
          • +
          • Item B-3-1-1
          • +
          • Item B-3-1-2
          • +
          +
        • +
        • Item B-3-2
        • +
        +
      • +
      +
      +
      + + diff --git a/tests/unit/listview/cache-tests/clear.html b/tests/unit/listview/cache-tests/clear.html new file mode 100644 index 00000000..c86bd968 --- /dev/null +++ b/tests/unit/listview/cache-tests/clear.html @@ -0,0 +1,13 @@ + + + + + + +
      +
      + cleared +
      +
      + + diff --git a/tests/unit/listview/cache-tests/uncached-nested.html b/tests/unit/listview/cache-tests/uncached-nested.html new file mode 100644 index 00000000..4a3e8d6d --- /dev/null +++ b/tests/unit/listview/cache-tests/uncached-nested.html @@ -0,0 +1,55 @@ + + + + + + +
      +
      +

      Basic multiple lists view

      +
      +
      +
        +
      • Item 1
      • +
      • Item 2
      • +
      • Item 3 +
          +
        • Item A-3-0
        • +
        • Item A-3-1
        • +
        • Item A-3-2
        • +
        +
      • +
      +
        +
      • Item 1
      • +
      • Item 2
      • +
      • Item 3 +
          +
        • Item B-3-0 +
            +
          • Item B-3-0-0
          • +
          • Item B-3-0-1 +
              +
            • Item B-3-0-1-0
            • +
            • Item B-3-0-1-1
            • +
            • Item B-3-0-1-2
            • +
            +
          • +
          • Item B-3-0-2
          • +
          +
        • +
        • Item B-3-1 +
            +
          • Item B-3-1-0
          • +
          • Item B-3-1-1
          • +
          • Item B-3-1-2
          • +
          +
        • +
        • Item B-3-2
        • +
        +
      • +
      +
      +
      + + diff --git a/tests/unit/listview/index.html b/tests/unit/listview/index.html new file mode 100644 index 00000000..9a6ed594 --- /dev/null +++ b/tests/unit/listview/index.html @@ -0,0 +1,318 @@ + + + + + + jQuery Mobile Listview Integration Test + + + + + + + + + + + + + + + +

      jQuery Mobile Listview Integration Test

      +

      +

      +
        +
      + + +
      +
      +

      Basic List View

      +
      + + +
      + + + + +
      +
      +

      Basic List View

      +
      +
      +
        +
      • Groups of animals +
          +
        • pod of whales
        • +
        • quiver of cobras
        • +
        • troop of baboons
        • +
        +
      • +
      • + + More animals + + +
          +
        • Shoal of Bass
        • +
        • Rhumba of rattlesnakes
        • +
        +
      • +
      +
      +
      + + +
      +
      +

      Basic multiple lists view

      +
      +
      +
        +
      • Item 1
      • +
      • Item 2
      • +
      • Item 3 +
          +
        • Item A-3-0
        • +
        • Item A-3-1
        • +
        • Item A-3-2
        • +
        +
      • +
      +
        +
      • Item 1
      • +
      • Item 2
      • +
      • Item 3 +
          +
        • Item B-3-0 +
            +
          • Item B-3-0-0
          • +
          • Item B-3-0-1 +
              +
            • Item B-3-0-1-0
            • +
            • Item B-3-0-1-1
            • +
            • Item B-3-0-1-2
            • +
            +
          • +
          • Item B-3-0-2
          • +
          +
        • +
        • Item B-3-1 +
            +
          • Item B-3-1-0
          • +
          • Item B-3-1-1
          • +
          • Item B-3-1-2
          • +
          +
        • +
        • Item B-3-2
        • +
        +
      • +
      +
      +
      + + +
      +
      +

      Basic List View

      +
      +
      +
        +
      1. Number 1
      2. +
      3. Number 2
      4. +
      5. Number 3
      6. +
      +
      +
      + +
      +
      +

      Numbered List

      +
      +
      + + +
      +
      +

      Basic List View

      +
      +
      +
        +
      • Read
      • +
      • Only
      • +
      • List
      • +
      • View
      • +
      +
      +
      + + +
      +
      +

      Split List View

      +
      + +
      + +
      +
      +

      Split List view 1

      +
      +
      + +
      +
      +

      Split List view 2

      +
      +
      + + +
      +
      +

      List Divider Test

      +
      +
      +
        +
      • a is for aquaman
      • +
      • b is for batman
      • +
      • This is a list divider
      • +
      • c is for catwoman
      • +
      • This is another list divider
      • +
      • d is for darkwing
      • +
      +
      +
      + + +
      +
      +

      Split List View

      +
      +
      +
        +
      • a is for aquaman
      • +
      • b is for batman
      • +
      • c is for catwoman
      • +
      • d is for darkwing
      • +
      +
      +
      + + +
      +
      +

      Split List View

      +
      +
      +
        +
      • a
      • +
      • a is for aquaman
      • +
      • b
      • +
      • b is for batman
      • +
      • c
      • +
      • c is for catwoman
      • +
      • d
      • +
      • d is for darkwing
      • +
      +
      +
      + + +
      +
      +

      Inset Filter List View

      +
      +
      +
        +
      • a is for aquaman
      • +
      • b is for batman
      • +
      • c is for catwoman
      • +
      • d is for darkwing
      • +
      +
      +
      + + +
      +
        +
        + + +
        +
        +

        Basic List View

        +
        +
        +
          +
        • Item 1
        • +
        • Item 2
        • +
        • Item 3
        • +
        • Item 4
        • +
        +
        +
        + + +
        +
        +

        Basic List View

        +
        +
        +
          +
        +
        +
        + +
        + +
        + +
        +
        +

        Right padding on item 1 is OK (75px).

        +

        Right padding on items 2 & 3 should probably be around 30 or 35 (not 25).

        +

        Right padding on item 4 should be 15px to match the left side.

        +
          +
        1. Link LI with counter --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------123
        2. +
        3. Link LI without counter -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        4. +
        5. Page1 Link LI without counter -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        6. +
        7. Static LI with counter ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------123
        8. +
        9. Static LI without counter ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        10. +
        +
        +
        + + + diff --git a/tests/unit/listview/listview_core.js b/tests/unit/listview/listview_core.js new file mode 100755 index 00000000..26ba63e3 --- /dev/null +++ b/tests/unit/listview/listview_core.js @@ -0,0 +1,767 @@ +/* + * mobile listview unit tests + */ + +// TODO split out into seperate test files +(function($){ + var home = $.mobile.path.parseUrl( location.href ).pathname; + + $.mobile.defaultTransition = "none"; + + module( "Basic Linked list", { + setup: function(){ + $.testHelper.openPage( "#basic-linked-test" ); + } + }); + + asyncTest( "The page should enhanced correctly", function(){ + setTimeout(function() { + ok($('#basic-linked-test .ui-li').length, ".ui-li classes added to li elements"); + start(); + }, 800); + }); + + asyncTest( "Slides to the listview page when the li a is clicked", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#basic-linked-test"); + }, + + function(){ + $('#basic-linked-test li a').first().click(); + }, + + function(){ + ok($('#basic-link-results').hasClass('ui-page-active')); + start(); + } + ]); + }); + + asyncTest( "Slides back to main page when back button is clicked", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#basic-link-results"); + }, + + function(){ + window.history.back(); + }, + + function(){ + ok($('#basic-linked-test').hasClass('ui-page-active')); + start(); + } + ]); + }); + + asyncTest( "Presence of ui-li-has- classes", function(){ + $.testHelper.pageSequence( [ + function() { + $.testHelper.openPage( "#ui-li-has-test" ); + }, + + function() { + var page = $( ".ui-page-active" ), + items = page.find( "li" ); + + ok( items.eq( 0 ).hasClass( "ui-li-has-count"), "First LI should have ui-li-has-count class" ); + ok( items.eq( 0 ).hasClass( "ui-li-has-arrow"), "First LI should have ui-li-has-arrow class" ); + ok( !items.eq( 1 ).hasClass( "ui-li-has-count"), "Second LI should NOT have ui-li-has-count class" ); + ok( items.eq( 1 ).hasClass( "ui-li-has-arrow"), "Second LI should have ui-li-has-arrow class" ); + ok( !items.eq( 2 ).hasClass( "ui-li-has-count"), "Third LI should NOT have ui-li-has-count class" ); + ok( !items.eq( 2 ).hasClass( "ui-li-has-arrow"), "Third LI should NOT have ui-li-has-arrow class" ); + ok( items.eq( 3 ).hasClass( "ui-li-has-count"), "Fourth LI should have ui-li-has-count class" ); + ok( !items.eq( 3 ).hasClass( "ui-li-has-arrow"), "Fourth LI should NOT have ui-li-has-arrow class" ); + ok( !items.eq( 4 ).hasClass( "ui-li-has-count"), "Fifth LI should NOT have ui-li-has-count class" ); + ok( !items.eq( 4 ).hasClass( "ui-li-has-arrow"), "Fifth LI should NOT have ui-li-has-arrow class" ); + start(); + } + ]); + }); + + module('Nested List Test'); + + asyncTest( "Changes page to nested list test and enhances", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#nested-list-test"); + }, + + function(){ + ok($('#nested-list-test').hasClass('ui-page-active'), "makes nested list test page active"); + ok($(':jqmData(url="nested-list-test&ui-page=0-0")').length == 1, "Adds first UL to the page"); + ok($(':jqmData(url="nested-list-test&ui-page=0-1")').length == 1, "Adds second nested UL to the page"); + start(); + } + ]); + }); + + asyncTest( "change to nested page when the li a is clicked", function() { + + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#nested-list-test"); + }, + + function(){ + $('.ui-page-active li:eq(1) a:eq(0)').click(); + }, + + function(){ + var $new_page = $(':jqmData(url="nested-list-test&ui-page=0-0")'); + + ok($new_page.hasClass('ui-page-active'), 'Makes the nested page the active page.'); + ok($('.ui-listview', $new_page).find(":contains('Rhumba of rattlesnakes')").length == 1, "The current page should have the proper text in the list."); + ok($('.ui-listview', $new_page).find(":contains('Shoal of Bass')").length == 1, "The current page should have the proper text in the list."); + start(); + } + ]); + }); + + asyncTest( "should go back to top level when the back button is clicked", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#nested-list-test&ui-page=0-0"); + }, + + function(){ + window.history.back(); + }, + + function(){ + ok($('#nested-list-test').hasClass('ui-page-active'), 'Transitions back to the parent nested page'); + start(); + } + ]); + }); + + test( "nested list title should use first text node, regardless of line breaks", function(){ + ok($('#nested-list-test .linebreaknode').text() === "More animals", 'Text should be "More animals"'); + }); + + asyncTest( "Multiple nested lists on a page with same labels", function() { + $.testHelper.pageSequence([ + function(){ + // https://github.com/jquery/jquery-mobile/issues/1617 + $.testHelper.openPage("#nested-lists-test"); + }, + + function(){ + // Click on the link of the third li element + $('.ui-page-active li:eq(2) a:eq(0)').click(); + }, + + function(){ + equal($('.ui-page-active .ui-content .ui-listview li').text(), "Item A-3-0Item A-3-1Item A-3-2", 'Text should be "Item A-3-0Item A-3-1Item A-3-2"'); + start(); + } + ]); + }); + + module('Ordered Lists'); + + asyncTest( "changes to the numbered list page and enhances it", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#numbered-list-test"); + }, + + function(){ + var $new_page = $('#numbered-list-test'); + ok($new_page.hasClass('ui-page-active'), "Makes the new page active when the hash is changed."); + ok($('.ui-link-inherit', $new_page).first().text() == "Number 1", "The text of the first LI should be Number 1"); + start(); + } + ]); + }); + + asyncTest( "changes to number 1 page when the li a is clicked", function() { + $.testHelper.pageSequence([ + function(){ + $('#numbered-list-test li a').first().click(); + }, + + function(){ + ok($('#numbered-list-results').hasClass('ui-page-active'), "The new numbered page was transitioned correctly."); + start(); + } + ]); + }); + + asyncTest( "takes us back to the numbered list when the back button is clicked", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage('#numbered-list-test'); + }, + + function(){ + $.testHelper.openPage('#numbered-list-results'); + }, + + function(){ + window.history.back(); + }, + + function(){ + ok($('#numbered-list-test').hasClass('ui-page-active')); + start(); + } + ]); + }); + + module('Read only list'); + + asyncTest( "changes to the read only page when hash is changed", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#read-only-list-test"); + }, + + function(){ + var $new_page = $('#read-only-list-test'); + ok($new_page.hasClass('ui-page-active'), "makes the read only page the active page"); + ok($('li', $new_page).first().text() === "Read", "The first LI has the proper text."); + start(); + } + ]); + }); + + module('Split view list'); + + asyncTest( "changes the page to the split view list and enhances it correctly.", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#split-list-test"); + }, + + function(){ + var $new_page = $('#split-list-test'); + ok($('.ui-li-link-alt', $new_page).length == 3); + ok($('.ui-link-inherit', $new_page).length == 3); + start(); + } + ]); + }); + + asyncTest( "change the page to the split view page 1 when the first link is clicked", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#split-list-test"); + }, + + function(){ + $('.ui-page-active .ui-li a:eq(0)').click(); + }, + + function(){ + ok($('#split-list-link1').hasClass('ui-page-active')); + start(); + } + ]); + }); + + asyncTest( "Slide back to the parent list view when the back button is clicked", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#split-list-test"); + }, + + function(){ + $('.ui-page-active .ui-listview a:eq(0)').click(); + }, + + function(){ + history.back(); + }, + + function(){ + ok($('#split-list-test').hasClass('ui-page-active')); + start(); + } + ]); + }); + + asyncTest( "Clicking on the icon (the second link) should take the user to other a href of this LI", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#split-list-test"); + }, + + function(){ + $('.ui-page-active .ui-li-link-alt:eq(0)').click(); + }, + + function(){ + ok($('#split-list-link2').hasClass('ui-page-active')); + start(); + } + ]); + }); + + module( "List Dividers" ); + + asyncTest( "Makes the list divider page the active page and enhances it correctly.", function() { + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#list-divider-test"); + }, + + function(){ + var $new_page = $('#list-divider-test'); + ok($new_page.find('.ui-li-divider').length == 2); + ok($new_page.hasClass('ui-page-active')); + start(); + } + ]); + }); + + module( "Search Filter"); + + var searchFilterId = "#search-filter-test"; + + + asyncTest( "Filter downs results when the user enters information", function() { + var $searchPage = $(searchFilterId); + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage(searchFilterId); + }, + + function() { + $searchPage.find('input').val('at'); + $searchPage.find('input').trigger('change'); + + same($searchPage.find('li.ui-screen-hidden').length, 2); + start(); + } + ]); + }); + + asyncTest( "Redisplay results when user removes values", function() { + var $searchPage = $(searchFilterId); + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage(searchFilterId); + }, + + function() { + $searchPage.find('input').val('a'); + $searchPage.find('input').trigger('change'); + + same($searchPage.find("li[style^='display: none;']").length, 0); + start(); + } + ]); + }); + + asyncTest( "Filter works fine with \\W- or regexp-special-characters", function() { + var $searchPage = $(searchFilterId); + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage(searchFilterId); + }, + + function() { + $searchPage.find('input').val('*'); + $searchPage.find('input').trigger('change'); + + same($searchPage.find('li.ui-screen-hidden').length, 4); + start(); + } + ]); + }); + + test( "Refresh applies thumb styling", function(){ + var ul = $('.ui-page-active ul'); + + ul.append("
      • "); + ok(!ul.find("#fiz img").hasClass("ui-li-thumb")); + ul.listview('refresh'); + ok(ul.find("#fiz img").hasClass("ui-li-thumb")); + }); + + asyncTest( "Filter downs results and dividers when the user enters information", function() { + var $searchPage = $("#search-filter-with-dividers-test"); + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage("#search-filter-with-dividers-test"); + }, + + // wait for the page to become active/enhanced + function(){ + $searchPage.find('input').val('at'); + $searchPage.find('input').trigger('change'); + setTimeout(function() { + //there should be four hidden list entries + same($searchPage.find('li.ui-screen-hidden').length, 4); + + //there should be two list entries that are list dividers and hidden + same($searchPage.find('li.ui-screen-hidden:jqmData(role=list-divider)').length, 2); + + //there should be two list entries that are not list dividers and hidden + same($searchPage.find('li.ui-screen-hidden:not(:jqmData(role=list-divider))').length, 2); + start(); + }, 1000); + } + ]); + }); + + asyncTest( "Redisplay results when user removes values", function() { + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage("#search-filter-with-dividers-test"); + }, + + function() { + $('.ui-page-active input').val('a'); + $('.ui-page-active input').trigger('change'); + + setTimeout(function() { + same($('.ui-page-active input').val(), 'a'); + same($('.ui-page-active li[style^="display: none;"]').length, 0); + start(); + }, 1000); + } + ]); + }); + + asyncTest( "Dividers are hidden when preceding hidden rows and shown when preceding shown rows", function () { + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage("#search-filter-with-dividers-test"); + }, + + function() { + var $page = $('.ui-page-active'); + + $page.find('input').val('at'); + $page.find('input').trigger('change'); + + setTimeout(function() { + same($page.find('li:jqmData(role=list-divider):hidden').length, 2); + same($page.find('li:jqmData(role=list-divider):hidden + li:not(:jqmData(role=list-divider)):hidden').length, 2); + same($page.find('li:jqmData(role=list-divider):not(:hidden) + li:not(:jqmData(role=list-divider)):not([:hidden)').length, 2); + start(); + }, 1000); + } + ]); + }); + + asyncTest( "Inset List View should refresh corner classes after filtering", 4 * 2, function () { + var checkClasses = function() { + var $page = $( ".ui-page-active" ), + $li = $page.find( "li:visible" ); + ok($li.first().hasClass( "ui-corner-top" ), $li.length+" li elements: First visible element should have class ui-corner-top"); + ok($li.last().hasClass( "ui-corner-bottom" ), $li.length+" li elements: Last visible element should have class ui-corner-bottom"); + }; + + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage("#search-filter-inset-test"); + }, + + function() { + var $page = $('.ui-page-active'); + $.testHelper.sequence([ + function() { + checkClasses(); + + $page.find('input').val('man'); + $page.find('input').trigger('change'); + }, + + function() { + checkClasses(); + + $page.find('input').val('at'); + $page.find('input').trigger('change'); + }, + + function() { + checkClasses(); + + $page.find('input').val('catwoman'); + $page.find('input').trigger('change'); + }, + + function() { + checkClasses(); + start(); + } + ], 50); + } + ]); + }); + + module( "Programmatically generated list items", { + setup: function(){ + var item, + data = [ + { + id: 1, + label: "Item 1" + }, + { + id: 2, + label: "Item 2" + }, + { + id: 3, + label: "Item 3" + }, + { + id: 4, + label: "Item 4" + } + ]; + + $( "#programmatically-generated-list-items" ).html(""); + + for ( var i = 0, len = data.length; i < len; i++ ) { + item = $( '
      • ' ); + label = $( "" + data[i].label + "").appendTo( item ); + $( "#programmatically-generated-list-items" ).append( item ); + } + } + }); + + asyncTest( "Corner styling on programmatically created list items", function() { + // https://github.com/jquery/jquery-mobile/issues/1470 + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage( "#programmatically-generated-list" ); + }, + function() { + ok(!$( "#programmatically-generated-list-items li:first-child" ).hasClass( "ui-corner-bottom" ), "First list item should not have class ui-corner-bottom" ); + start(); + } + ]); + }); + + module("Programmatic list items manipulation"); + + asyncTest("Removing list items", 4, function() { + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage("#removing-items-from-list-test"); + }, + + function() { + var ul = $('#removing-items-from-list-test ul'); + ul.find("li").first().remove(); + equal(ul.find("li").length, 3, "There should be only 3 list items left"); + + ul.listview('refresh'); + ok(ul.find("li").first().hasClass("ui-corner-top"), "First list item should have class ui-corner-top"); + + ul.find("li").last().remove(); + equal(ul.find("li").length, 2, "There should be only 2 list items left"); + + ul.listview('refresh'); + ok(ul.find("li").last().hasClass("ui-corner-bottom"), "Last list item should have class ui-corner-bottom"); + start(); + } + ]); + }); + + module("Rounded corners"); + + asyncTest("Top and bottom corners rounded in inset list", 14, function() { + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage("#corner-rounded-test"); + }, + + function() { + var ul = $('#corner-rounded-test ul'); + + for( var t = 0; t<3; t++){ + ul.append("
      • Item " + t + "
      • "); + ul.listview('refresh'); + equals(ul.find(".ui-corner-top").length, 1, "There should be only one element with class ui-corner-top"); + equals(ul.find("li:visible").first()[0], ul.find(".ui-corner-top")[0], "First list item should have class ui-corner-top in list with " + ul.find("li").length + " item(s)"); + equals(ul.find(".ui-corner-bottom").length, 1, "There should be only one element with class ui-corner-bottom"); + equals(ul.find("li:visible").last()[0], ul.find(".ui-corner-bottom")[0], "Last list item should have class ui-corner-bottom in list with " + ul.find("li").length + " item(s)"); + } + + ul.find( "li" ).first().hide(); + ul.listview( "refresh" ); + equals(ul.find("li:visible").first()[0], ul.find(".ui-corner-top")[0], "First visible list item should have class ui-corner-top"); + + ul.find( "li" ).last().hide(); + ul.listview( "refresh" ); + equals(ul.find("li:visible").last()[0], ul.find(".ui-corner-bottom")[0], "Last visible list item should have class ui-corner-bottom"); + + start(); + } + ]); + }); + + test( "Listview will create when inside a container that receives a 'create' event", function(){ + ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-listview").length, "did not have enhancements applied" ); + ok( $("#enhancetest").trigger("create").find(".ui-listview").length, "enhancements applied" ); + }); + + module( "Cached Linked List" ); + + var findNestedPages = function(selector){ + return $( selector + " #topmost" ).listview( 'childPages' ); + }; + + asyncTest( "nested pages are removed from the dom by default", function(){ + $.testHelper.pageSequence([ + function(){ + //reset for relative url refs + $.testHelper.openPage( "#" + home ); + }, + + function(){ + $.testHelper.openPage( "#cache-tests/uncached-nested.html" ); + }, + + function(){ + ok( findNestedPages( "#uncached-nested-list" ).length > 0, "verify that there are nested pages" ); + $.testHelper.openPage( "#" + home ); + }, + + function() { + $.testHelper.openPage( "#cache-tests/clear.html" ); + }, + + function(){ + same( findNestedPages( "#uncached-nested-list" ).length, 0 ); + start(); + } + ]); + }); + + asyncTest( "nested pages preserved when parent page is cached", function(){ + + $.testHelper.pageSequence([ + function(){ + //reset for relative url refs + $.testHelper.openPage( "#" + home ); + }, + + function(){ + $.testHelper.openPage( "#cache-tests/cached-nested.html" ); + }, + + function(){ + ok( findNestedPages( "#cached-nested-list" ).length > 0, "verify that there are nested pages" ); + $.testHelper.openPage( "#" + home ); + }, + + function() { + $.testHelper.openPage( "#cache-tests/clear.html" ); + }, + + function(){ + ok( findNestedPages( "#cached-nested-list" ).length > 0, "nested pages remain" ); + start(); + } + ]); + }); + + asyncTest( "parent page is not removed when visiting a sub page", function(){ + $.testHelper.pageSequence([ + function(){ + //reset for relative url refs + $.testHelper.openPage( "#" + home ); + }, + + function(){ + $.testHelper.openPage( "#cache-tests/cached-nested.html" ); + }, + + function(){ + same( $("#cached-nested-list").length, 1 ); + $.testHelper.openPage( "#" + home ); + }, + + function() { + $.testHelper.openPage( "#cache-tests/clear.html" ); + }, + + function(){ + same( $("#cached-nested-list").length, 1 ); + start(); + } + ]); + }); + + asyncTest( "filterCallback can be altered after widget creation", function(){ + var listPage = $( "#search-filter-test" ); + expect( listPage.find("li").length ); + + $.testHelper.pageSequence( [ + function(){ + //reset for relative url refs + $.testHelper.openPage( "#" + home ); + }, + + function() { + $.testHelper.openPage( "#search-filter-test" ); + }, + + function() { + // set the listview instance callback + listPage.find( "ul" ).listview( "option", "filterCallback", function() { + ok(true, "custom callback invoked"); + }); + + // trigger a change in the search filter + listPage.find( "input" ).val( "foo" ).trigger( "change" ); + + //NOTE beware a poossible issue with timing here + start(); + } + ]); + }); + + asyncTest( "nested pages hash key is always in the hash (replaceState)", function(){ + $.testHelper.pageSequence([ + function(){ + //reset for relative url refs + $.testHelper.openPage( "#" + home ); + }, + + function(){ + // https://github.com/jquery/jquery-mobile/issues/1617 + $.testHelper.openPage("#nested-lists-test"); + }, + + function(){ + // Click on the link of the third li element + $('.ui-page-active li:eq(2) a:eq(0)').click(); + }, + + function(){ + ok( location.hash.search($.mobile.subPageUrlKey) >= 0 ); + start(); + } + ]); + }); + + asyncTest( "embedded listview page with nested pages is not removed from the dom", function() { + $.testHelper.pageSequence([ + function() { + // open the nested list page + same( $("div#nested-list-test").length, 1 ); + $( "a#nested-list-test-anchor" ).click(); + }, + + function() { + // go back to the origin page + window.history.back(); + }, + + function() { + // make sure the page is still in place + same( $("div#nested-list-test").length, 1 ); + start(); + } + ]); + }); +})(jQuery); diff --git a/tests/unit/listview/listview_pushstate.js b/tests/unit/listview/listview_pushstate.js new file mode 100644 index 00000000..6af09e7c --- /dev/null +++ b/tests/unit/listview/listview_pushstate.js @@ -0,0 +1,15 @@ +(function($) { + asyncTest( "nested pages hash key is always in the hash on default page with no id (replaceState) ", function(){ + $.testHelper.pageSequence([ + function(){ + // Click on the link of the third li element + $('.ui-page-active li:eq(2) a:eq(0)').click(); + }, + + function(){ + ok( location.hash.search($.mobile.subPageUrlKey) >= 0 ); + start(); + } + ]); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/listview/pushstate-tests.html b/tests/unit/listview/pushstate-tests.html new file mode 100644 index 00000000..77126325 --- /dev/null +++ b/tests/unit/listview/pushstate-tests.html @@ -0,0 +1,77 @@ + + + + + + jQuery Mobile Listview Integration Test + + + + + + + + + + + + + + + +

        jQuery Mobile Listview Integration Test

        +

        +

        +
          +
        + +
        +
        +

        Basic multiple lists view

        +
        +
        +
          +
        • Item 1
        • +
        • Item 2
        • +
        • Item 3 +
            +
          • Item A-3-0
          • +
          • Item A-3-1
          • +
          • Item A-3-2
          • +
          +
        • +
        +
          +
        • Item 1
        • +
        • Item 2
        • +
        • Item 3 +
            +
          • Item B-3-0 +
              +
            • Item B-3-0-0
            • +
            • Item B-3-0-1 +
                +
              • Item B-3-0-1-0
              • +
              • Item B-3-0-1-1
              • +
              • Item B-3-0-1-2
              • +
              +
            • +
            • Item B-3-0-2
            • +
            +
          • +
          • Item B-3-1 +
              +
            • Item B-3-1-0
            • +
            • Item B-3-1-1
            • +
            • Item B-3-1-2
            • +
            +
          • +
          • Item B-3-2
          • +
          +
        • +
        +
        +
        + + + diff --git a/tests/unit/ls.php b/tests/unit/ls.php new file mode 100644 index 00000000..577bfecc --- /dev/null +++ b/tests/unit/ls.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/tests/unit/media/index.html b/tests/unit/media/index.html index 920d80a2..fc5cb809 100644 --- a/tests/unit/media/index.html +++ b/tests/unit/media/index.html @@ -1,18 +1,22 @@ - + + jQuery Mobile Media Test Suite - - - - + + + + + - - + + - + + + @@ -22,8 +26,7 @@
        -
        -
        +
        diff --git a/tests/unit/media/media_core.js b/tests/unit/media/media_core.js index cf63d11b..68eca0ca 100644 --- a/tests/unit/media/media_core.js +++ b/tests/unit/media/media_core.js @@ -2,12 +2,15 @@ * mobile media unit tests */ -(function( $ ) { +(function($){ var cssFn = $.fn.css, - widthFn = $.fn.width; + widthFn = $.fn.width; // make sure original definitions are reset module('jquery.mobile.media.js', { + setup: function(){ + $(document).trigger('mobileinit.htmlclass'); + }, teardown: function(){ $.fn.css = cssFn; $.fn.width = widthFn; @@ -32,66 +35,5 @@ same($.mobile.media("screen 3"), true); }); - test( "window widths smaller than the break points set max-width classes", function(){ - $.fn.width = function(){ return 120; }; - $.mobile.addResolutionBreakpoints([125]); - ok($("html").hasClass("max-width-125px")); - }); - - test( "window widths larger than the break points set min-width classes", function(){ - $.fn.width = function(){ return 1900; }; - - $.mobile.addResolutionBreakpoints([125]); - ok($("html").hasClass("min-width-125px")); - }); - - test( "many break points result in many class additions", function(){ - $.fn.width = function(){ return 1900; }; - $.mobile.addResolutionBreakpoints([1, 2]); - - ok($("html").hasClass("min-width-1px")); - ok($("html").hasClass("min-width-2px")); - }); - - test( "adds all classes for default res breakpoints", function(){ - expect( 4 ); - $.fn.width = function(){ return 1900; }; - $.mobile.addResolutionBreakpoints([]); - - // TODO should expose the defaults to prevent brittle tests - $.each([320, 480, 768, 1024], function(i, element){ - ok($("html").hasClass("min-width-" + element + "px")); - }); - }); - - test( "triggering mobile init triggers orientationchange.htmlclass", function(){ - expect( 1 ); - - $(window).bind("orientationchange.htmlclass", function(event){ - ok(event); - }); - - $(document).trigger("mobileinit.htmlclass"); - }); - - test( "binds remove of portrait and landscape classes resize/orientation fired", function(){ - $.Event.prototype.orientation = true; - - $("html").addClass("portrait landscape"); - $(window).trigger("resize.htmlclass"); - ok(!$("html").hasClass("portrait landscape")); - - $("html").addClass("portrait landscape"); - $(window).trigger("resize.htmlclass"); - ok(!$("html").hasClass("portrait landscape")); - }); - - test( "sets break point class additions on resize/orientation change", function(){ - $.fn.width = function(){ return 1900; }; - - $("html").removeClass("min-width-320px"); - $(window).trigger("resize.htmlclass"); - ok($("html").hasClass("min-width-320px")); - }); })(jQuery); \ No newline at end of file diff --git a/tests/unit/navigation/base-tests.html b/tests/unit/navigation/base-tests.html new file mode 100644 index 00000000..a2beeb59 --- /dev/null +++ b/tests/unit/navigation/base-tests.html @@ -0,0 +1,73 @@ + + + + + + jQuery Mobile Navigation Test Suite + + + + + + + + + + + + + + + + + +

        jQuery Mobile Navigation Base Tag Test Suite

        +

        +

        +
          +
        + +
        + + + + + + +
        + +
        + + + + + + +
        + +
        +
        +
        + + +
        +
        +
        + + + diff --git a/tests/unit/navigation/base-tests/app-base/base-page-1.html b/tests/unit/navigation/base-tests/app-base/base-page-1.html new file mode 100644 index 00000000..b417713d --- /dev/null +++ b/tests/unit/navigation/base-tests/app-base/base-page-1.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/tests/unit/navigation/base-tests/app-base/base-page-2.html b/tests/unit/navigation/base-tests/app-base/base-page-2.html new file mode 100644 index 00000000..ac84a98c --- /dev/null +++ b/tests/unit/navigation/base-tests/app-base/base-page-2.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/tests/unit/navigation/base-tests/content/content-page-1.html b/tests/unit/navigation/base-tests/content/content-page-1.html new file mode 100644 index 00000000..68cef02c --- /dev/null +++ b/tests/unit/navigation/base-tests/content/content-page-1.html @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/tests/unit/navigation/base-tests/content/content-page-2.html b/tests/unit/navigation/base-tests/content/content-page-2.html new file mode 100644 index 00000000..76c9bbd5 --- /dev/null +++ b/tests/unit/navigation/base-tests/content/content-page-2.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/tests/unit/navigation/cached-external.html b/tests/unit/navigation/cached-external.html new file mode 100644 index 00000000..5ebcf062 --- /dev/null +++ b/tests/unit/navigation/cached-external.html @@ -0,0 +1,10 @@ + + + + + + +
        +
        + + diff --git a/tests/unit/navigation/data-url-tests/data-url.html b/tests/unit/navigation/data-url-tests/data-url.html new file mode 100644 index 00000000..bece3f86 --- /dev/null +++ b/tests/unit/navigation/data-url-tests/data-url.html @@ -0,0 +1,10 @@ + + + + + +
        + This text intentionally left blank +
        + + diff --git a/tests/unit/navigation/data-url-tests/nested.html b/tests/unit/navigation/data-url-tests/nested.html new file mode 100644 index 00000000..da75dbca --- /dev/null +++ b/tests/unit/navigation/data-url-tests/nested.html @@ -0,0 +1,8 @@ + + + + + +
        + + diff --git a/tests/unit/navigation/data-url-tests/non-data-url.html b/tests/unit/navigation/data-url-tests/non-data-url.html new file mode 100644 index 00000000..e0a299fe --- /dev/null +++ b/tests/unit/navigation/data-url-tests/non-data-url.html @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/tests/unit/navigation/data-url-tests/reverse-attr.html b/tests/unit/navigation/data-url-tests/reverse-attr.html new file mode 100644 index 00000000..379577f2 --- /dev/null +++ b/tests/unit/navigation/data-url-tests/reverse-attr.html @@ -0,0 +1,8 @@ + + + + + +
        + + diff --git a/tests/unit/navigation/data-url-tests/single-quotes.html b/tests/unit/navigation/data-url-tests/single-quotes.html new file mode 100644 index 00000000..74afd7f1 --- /dev/null +++ b/tests/unit/navigation/data-url-tests/single-quotes.html @@ -0,0 +1,8 @@ + + + + + +
        + + diff --git a/tests/unit/navigation/dialog-param-test/dialog-param.html b/tests/unit/navigation/dialog-param-test/dialog-param.html new file mode 100644 index 00000000..5c13d5fe --- /dev/null +++ b/tests/unit/navigation/dialog-param-test/dialog-param.html @@ -0,0 +1,18 @@ + + + + + + +

        jQuery Mobile Navigation Test Suite

        +

        +

        +
          +
        + + + + + diff --git a/tests/unit/navigation/external.html b/tests/unit/navigation/external.html new file mode 100644 index 00000000..c9a011dd --- /dev/null +++ b/tests/unit/navigation/external.html @@ -0,0 +1,9 @@ + + + + + + +
        + + diff --git a/tests/unit/navigation/file.html b/tests/unit/navigation/file.html new file mode 100644 index 00000000..5109dee7 --- /dev/null +++ b/tests/unit/navigation/file.html @@ -0,0 +1,11 @@ + + + + + + +
        +
        doc rel test one
        +
        + + diff --git a/tests/unit/navigation/form-tests/changepage-data.html b/tests/unit/navigation/form-tests/changepage-data.html new file mode 100644 index 00000000..2305c206 --- /dev/null +++ b/tests/unit/navigation/form-tests/changepage-data.html @@ -0,0 +1,8 @@ + + + + + +
        + + diff --git a/tests/unit/navigation/form-tests/form-no-action.html b/tests/unit/navigation/form-tests/form-no-action.html new file mode 100644 index 00000000..1b4ff7c5 --- /dev/null +++ b/tests/unit/navigation/form-tests/form-no-action.html @@ -0,0 +1,15 @@ + + + + + +
        +
        +
        + + +
        +
        +
        + + diff --git a/tests/unit/navigation/index.html b/tests/unit/navigation/index.html index 530062f4..ac05ed5d 100644 --- a/tests/unit/navigation/index.html +++ b/tests/unit/navigation/index.html @@ -1,43 +1,30 @@ - + + jQuery Mobile Navigation Test Suite - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - + + + - + + + @@ -47,26 +34,257 @@
        - - - - - - -
        - +
        -
        +
        + +
        + + + +
        +
        + +
        -
        - +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        +
        +
        + +
        +
        + +
        +
        +
        + +
        + +
        + +
        +
        + + + + + +
        +
        +

        Dialog

        +
        +
        + +
        +
        + Dialog +
        +
        + +
        +
        + Page 2 +
        +
        + +
        + Go Back +
        + + +
        +
        + Dialog +
        +
        + +
        +
        + Dialog 2 +
        +
        + +
        +
        + +
        + +
        + + + +
        + test + test + test +
        + +
        +

        Title Heading

        +
        + +
        +

        Title Heading

        +
        + + + + + +
        + + go + go + go + go + go + go + + + + go + go + go + go + go + go + + + + go + go + go + go + go + go + + + + go + go + go + go + go + go + +
        + +
        +
        page didn't change!
        +
        + + + +
        +
        + page2 +
        +
        + + + + + +
        + foo +
        + +
        + + + + diff --git a/tests/unit/navigation/navigation_base.js b/tests/unit/navigation/navigation_base.js new file mode 100644 index 00000000..3b7f2b9c --- /dev/null +++ b/tests/unit/navigation/navigation_base.js @@ -0,0 +1,205 @@ +/* + * mobile navigation base tag unit tests + */ +(function($){ + var baseDir = $.mobile.path.parseUrl($("base").attr("href")).directory, + contentDir = $.mobile.path.makePathAbsolute("../content/", baseDir); + + module('jquery.mobile.navigation.js - base tag', { + setup: function(){ + if ( location.hash ) { + stop(); + $(document).one("pagechange", function() { + start(); + } ); + location.hash = ""; + } + } + }); + + asyncTest( "can navigate between internal and external pages", function(){ + $.testHelper.pageSequence([ + function(){ + // Navigate from default internal page to another internal page. + $.testHelper.openPage( "#internal-page-2" ); + }, + + function(){ + // Verify that we are on the 2nd internal page. + $.testHelper.assertUrlLocation({ + push: location.pathname + "#internal-page-2", + hash: "internal-page-2", + report: "navigate to internal page" + }); + + // Navigate to a page that is in the base directory. Note that the application + // document and this new page are *NOT* in the same directory. + $("#internal-page-2 .bp1").click(); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hashOrPush: baseDir + "base-page-1.html", + report: "navigate from internal page to page in base directory" + }); + + // Navigate to another page in the same directory as the current page. + $("#base-page-1 .bp2").click(); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hashOrPush: baseDir + "base-page-2.html", + report: "navigate from base directory page to another base directory page" + }); + + // Navigate to another page in a directory that is the sibling of the base. + $("#base-page-2 .cp1").click(); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hashOrPush: contentDir + "content-page-1.html", + report: "navigate from base directory page to a page in a different directory hierarchy" + }); + + // Navigate to another page in a directory that is the sibling of the base. + $("#content-page-1 .cp2").click(); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hashOrPush: contentDir + "content-page-2.html", + report: "navigate to another page within the same non-base directory hierarchy" + }); + + // Navigate to an internal page. + $("#content-page-2 .ip1").click(); + }, + + function(){ + // Verify that we are on the expected page. + // the hash based nav result (hash:) is dictate by the fact that #internal-page-1 + // is the original root page element + $.testHelper.assertUrlLocation({ + hashOrPush: location.pathname + location.search, + report: "navigate from a page in a non-base directory to an internal page" + }); + + // Try calling changePage() directly with a relative path. + $.mobile.changePage("base-page-1.html"); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hashOrPush: baseDir + "base-page-1.html", + report: "call changePage() with a filename (no path)" + }); + + // Try calling changePage() directly with a relative path. + $.mobile.changePage("../content/content-page-1.html"); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hashOrPush: contentDir + "content-page-1.html", + report: "call changePage() with a relative path containing up-level references" + }); + + // Try calling changePage() with an id + $.mobile.changePage("content-page-2.html"); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hashOrPush: contentDir + "content-page-2.html", + report: "call changePage() with a relative path should resolve relative to current page" + }); + + // test that an internal page works + $("a.ip2").click(); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hash: "internal-page-2", + push: location.pathname + "#internal-page-2", + report: "call changePage() with a page id" + }); + + // Try calling changePage() with an id + $.mobile.changePage("internal-page-1"); + }, + + function(){ + // Verify that we are on the expected page. + $.testHelper.assertUrlLocation({ + hash: "internal-page-2", + push: location.pathname + "#internal-page-2", + report: "calling changePage() with a page id that is not prefixed with '#' should not change page" + }); + + // Previous load should have failed and left us on internal-page-2. + start(); + } + ]); + }); + + asyncTest( "internal form with no action submits to document URL", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage( "#internal-no-action-form-page" ); + }, + + function(){ + $( "#internal-no-action-form-page form" ).eq( 0 ).submit(); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: location.pathname + "?foo=1&bar=2", + report: "hash should match document url and not base url" + }); + + start(); + } + ]); + }); + + asyncTest( "external page form with no action submits to external page URL", function(){ + $.testHelper.pageSequence([ + function(){ + // Go to an external page that has a form. + $("#internal-page-1 .cp1").click(); + }, + + function(){ + // Make sure we actually navigated to the external page. + $.testHelper.assertUrlLocation({ + hashOrPush: contentDir + "content-page-1.html", + report: "should be on content-page-1.html" + }); + + // Now submit the form in the external page. + $("#content-page-1 form").eq(0).submit(); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: contentDir + "content-page-1.html?foo=1&bar=2", + report: "hash should match page url and not document url" + }); + + start(); + }]); + }); +})(jQuery); diff --git a/tests/unit/navigation/navigation_core.js b/tests/unit/navigation/navigation_core.js index a51a3124..291f688b 100644 --- a/tests/unit/navigation/navigation_core.js +++ b/tests/unit/navigation/navigation_core.js @@ -1,48 +1,1001 @@ /* * mobile navigation unit tests */ -(function( $ ) { - var perspective = "ui-mobile-viewport-perspective", - transitioning = "ui-mobile-viewport-transitioning", - animationCompleteFn = $.fn.animationComplete, - - removeClasses = function(){ - $("body").removeClass([perspective, transitioning].join(" ")); - }; +(function($){ + // TODO move siteDirectory over to the nav path helper + var changePageFn = $.mobile.changePage, + originalTitle = document.title, + originalLinkBinding = $.mobile.linkBindingEnabled, + siteDirectory = location.pathname.replace( /[^/]+$/, "" ), + home = $.mobile.path.parseUrl(location.pathname).directory, + navigateTestRoot = function(){ + $.testHelper.openPage( "#" + location.pathname + location.search ); + }; module('jquery.mobile.navigation.js', { - teardown: function(){ - // unmock animation complete - $.fn.animationComplete = animationCompleteFn; + setup: function(){ + $.mobile.changePage = changePageFn; + document.title = originalTitle; - removeClasses(); - }}); + var pageReset = function( hash ) { + hash = hash || ""; - test( "changePage applys perspective class to mobile viewport for flip", function(){ - //stub to prevent class removal - $.fn.animationComplete = function(){}; + stop(); - $("#foo > a").click(); + $(document).one( "pagechange", function() { + start(); + }); - ok($("body").hasClass(perspective), "has perspective class"); + location.hash = "#" + hash; + }; + + // force the page reset for hash based tests + if ( location.hash && !$.support.pushState ) { + pageReset(); + } + + // force the page reset for all pushstate tests + if ( $.support.pushState ) { + pageReset( home ); + } + + $.mobile.urlHistory.stack = []; + $.mobile.urlHistory.activeIndex = 0; + $.Event.prototype.which = undefined; + $.mobile.linkBindingEnabled = originalLinkBinding; + } + }); + + asyncTest( "window.history.back() from external to internal page", function(){ + + $.testHelper.pageSequence([ + + // open our test page + function(){ + $.testHelper.openPage("#active-state-page1"); + }, + + function(){ + ok( $.mobile.activePage[0] === $( "#active-state-page1" )[ 0 ], "successful navigation to internal page." ); + + //location.hash = siteDirectory + "external.html"; + $.mobile.changePage("external.html"); + }, + + function(){ + ok( $.mobile.activePage[0] !== $( "#active-state-page1" )[ 0 ], "successful navigation to external page." ); + + window.history.back(); + }, + + function(){ + ok( $.mobile.activePage[0] === $( "#active-state-page1" )[ 0 ], "successful navigation back to internal page." ); + + start(); + } + ]); + }); + + asyncTest( "external page is removed from the DOM after pagehide", function(){ + $.testHelper.pageSequence([ + navigateTestRoot, + + function(){ + $.mobile.changePage( "external.html" ); + }, + + // page is pulled and displayed in the dom + function(){ + same( $( "#external-test" ).length, 1 ); + window.history.back(); + }, + + // external-test is *NOT* cached in the dom after transitioning away + function(){ + same( $( "#external-test" ).length, 0 ); + start(); + } + ]); + }); + + asyncTest( "preventDefault on pageremove event can prevent external page from being removed from the DOM", function(){ + var preventRemoval = true, + removeCallback = function( e ) { + if ( preventRemoval ) { + e.preventDefault(); + } + }; + + $( document ).bind( "pageremove", removeCallback ); + + $.testHelper.pageSequence([ + navigateTestRoot, + + function(){ + $.mobile.changePage( "external.html" ); + }, + + // page is pulled and displayed in the dom + function(){ + same( $( "#external-test" ).length, 1 ); + window.history.back(); + }, + + // external-test *IS* cached in the dom after transitioning away + function(){ + same( $( "#external-test" ).length, 1 ); + + // Switch back to the page again! + $.mobile.changePage( "external.html" ); + }, + + // page is still present and displayed in the dom + function(){ + same( $( "#external-test" ).length, 1 ); + + // Now turn off our removal prevention. + preventRemoval = false; + + window.history.back(); + }, + + // external-test is *NOT* cached in the dom after transitioning away + function(){ + same( $( "#external-test" ).length, 0 ); + $( document ).unbind( "pageremove", removeCallback ); + start(); + } + ]); + }); + + asyncTest( "external page is cached in the DOM after pagehide", function(){ + $.testHelper.pageSequence([ + navigateTestRoot, + + function(){ + $.mobile.changePage( "cached-external.html" ); + }, + + // page is pulled and displayed in the dom + function(){ + same( $( "#external-test-cached" ).length, 1 ); + window.history.back(); + }, + + // external test page is cached in the dom after transitioning away + function(){ + same( $( "#external-test-cached" ).length, 1 ); + start(); + } + ]); + }); + + asyncTest( "external page is cached in the DOM after pagehide when option is set globally", function(){ + $.testHelper.pageSequence([ + navigateTestRoot, + + function(){ + $.mobile.page.prototype.options.domCache = true; + $.mobile.changePage( "external.html" ); + }, + + // page is pulled and displayed in the dom + function(){ + same( $( "#external-test" ).length, 1 ); + window.history.back(); + }, + + // external test page is cached in the dom after transitioning away + function(){ + same( $( "#external-test" ).length, 1 ); + $.mobile.page.prototype.options.domCache = false; + $( "#external-test" ).remove(); + start(); + }]); + }); + + asyncTest( "page last scroll distance is remembered while navigating to and from pages", function(){ + $.testHelper.pageSequence([ + function(){ + $( "body" ).height( $( window ).height() + 500 ); + $.mobile.changePage( "external.html" ); + }, + + function(){ + // wait for the initial scroll to 0 + setTimeout( function() { + window.scrollTo( 0, 300 ); + same( $(window).scrollTop(), 300, "scrollTop is 300 after setting it" ); + }, 300); + + // wait for the scrollstop to fire and for the scroll to be + // recorded 100 ms afterward (see changes made to handle hash + // scrolling in some browsers) + setTimeout( navigateTestRoot, 500 ); + }, + + function(){ + history.back(); + }, + + function(){ + // Give the silentScroll function some time to kick in. + setTimeout(function() { + same( $(window).scrollTop(), 300, "scrollTop is 300 after returning to the page" ); + $( "body" ).height( "" ); + start(); + }, 300 ); + } + ]); + }); + + asyncTest( "forms with data attribute ajax set to false will not call changePage", function(){ + var called = false; + var newChangePage = function(){ + called = true; + }; + + $.testHelper.sequence([ + // avoid initial page load triggering changePage early + function(){ + $.mobile.changePage = newChangePage; + + $('#non-ajax-form').one('submit', function(event){ + ok(true, 'submit callbacks are fired'); + event.preventDefault(); + }).submit(); + }, + + function(){ + ok(!called, "change page should not be called"); + start(); + }], 1000); + }); + + asyncTest( "forms with data attribute ajax not set or set to anything but false will call changePage", function(){ + var called = 0, + newChangePage = function(){ + called++; + }; + + $.testHelper.sequence([ + // avoid initial page load triggering changePage early + function(){ + $.mobile.changePage = newChangePage; + $('#ajax-form, #rand-ajax-form').submit(); + }, + + function(){ + ok(called >= 2, "change page should be called at least twice"); + start(); + }], 300); }); - test( "changePage applys does not apply perspective class to mobile viewport for transitions other than flip", function(){ - //stub to prevent class removal - $.fn.animationComplete = function(){}; + asyncTest( "anchors with no href attribute will do nothing when clicked", function(){ + var fired = false; - $("#bar > a").click(); + $(window).bind("hashchange.temp", function(){ + fired = true; + }); - ok(!$("body").hasClass(perspective), "doesn't have perspective class"); + $( "test" ).appendTo( $.mobile.firstPage ).click(); + + setTimeout(function(){ + same(fired, false, "hash shouldn't change after click"); + $(window).unbind("hashchange.temp"); + start(); + }, 500); }); - test( "changePage applys transition class to mobile viewport for default transition", function(){ - //stub to prevent class removal - $.fn.animationComplete = function(){}; + test( "urlHistory is working properly", function(){ - $("#baz > a").click(); + //urlHistory + same( $.type( $.mobile.urlHistory.stack ), "array", "urlHistory.stack is an array" ); - ok($("body").hasClass(transitioning), "has transitioning class"); + //preload the stack + $.mobile.urlHistory.stack[0] = { url: "foo", transition: "bar" }; + $.mobile.urlHistory.stack[1] = { url: "baz", transition: "shizam" }; + $.mobile.urlHistory.stack[2] = { url: "shizoo", transition: "shizaah" }; + + //active index + same( $.mobile.urlHistory.activeIndex , 0, "urlHistory.activeIndex is 0" ); + + //getActive + same( $.type( $.mobile.urlHistory.getActive() ) , "object", "active item is an object" ); + same( $.mobile.urlHistory.getActive().url , "foo", "active item has url foo" ); + same( $.mobile.urlHistory.getActive().transition , "bar", "active item has transition bar" ); + + //get prev / next + same( $.mobile.urlHistory.getPrev(), undefined, "urlHistory.getPrev() is undefined when active index is 0" ); + $.mobile.urlHistory.activeIndex = 1; + same( $.mobile.urlHistory.getPrev().url, "foo", "urlHistory.getPrev() has url foo when active index is 1" ); + $.mobile.urlHistory.activeIndex = 0; + same( $.mobile.urlHistory.getNext().url, "baz", "urlHistory.getNext() has url baz when active index is 0" ); + + //add new + $.mobile.urlHistory.activeIndex = 2; + $.mobile.urlHistory.addNew("test"); + same( $.mobile.urlHistory.stack.length, 4, "urlHistory.addNew() adds an item after the active index" ); + same( $.mobile.urlHistory.activeIndex, 3, "urlHistory.addNew() moves the activeIndex to the newly added item" ); + + //clearForward + $.mobile.urlHistory.activeIndex = 0; + $.mobile.urlHistory.clearForward(); + same( $.mobile.urlHistory.stack.length, 1, "urlHistory.clearForward() clears the url stack after the active index" ); }); -})(jQuery); \ No newline at end of file + + //url listening + function testListening( prop ){ + var stillListening = false; + $(document).bind("pagebeforehide", function(){ + stillListening = true; + }); + location.hash = "foozball"; + setTimeout(function(){ + ok( prop == stillListening, prop + " = false disables default hashchange event handler"); + location.hash = ""; + prop = true; + start(); + }, 1000); + } + + asyncTest( "ability to disable our hash change event listening internally", function(){ + testListening( ! $.mobile.urlHistory.ignoreNextHashChange ); + }); + + asyncTest( "ability to disable our hash change event listening globally", function(){ + testListening( $.mobile.hashListeningEnabled ); + }); + + var testDataUrlHash = function( linkSelector, matches ) { + $.testHelper.pageSequence([ + function(){ window.location.hash = ""; }, + function(){ $(linkSelector).click(); }, + function(){ + $.testHelper.assertUrlLocation( + $.extend(matches, { + report: "url or hash should match" + }) + ); + + start(); + } + ]); + + stop(); + }; + + test( "when loading a page where data-url is not defined on a sub element hash defaults to the url", function(){ + testDataUrlHash( "#non-data-url a", {hashOrPush: siteDirectory + "data-url-tests/non-data-url.html"} ); + }); + + test( "data url works for nested paths", function(){ + var url = "foo/bar.html"; + testDataUrlHash( "#nested-data-url a", {hash: url, push: home + url} ); + }); + + test( "data url works for single quoted paths and roles", function(){ + var url = "foo/bar/single.html"; + testDataUrlHash( "#single-quotes-data-url a", {hash: url, push: home + url} ); + }); + + test( "data url works when role and url are reversed on the page element", function(){ + var url = "foo/bar/reverse.html"; + testDataUrlHash( "#reverse-attr-data-url a", {hash: url, push: home + url} ); + }); + + asyncTest( "last entry choosen amongst multiple identical url history stack entries on hash change", function(){ + // make sure the stack is clear after initial page load an any other delayed page loads + // TODO better browser state management + $.mobile.urlHistory.stack = []; + $.mobile.urlHistory.activeIndex = 0; + + $.testHelper.pageSequence([ + function(){ $.testHelper.openPage("#dup-history-first"); }, + function(){ $("#dup-history-first a").click(); }, + function(){ $("#dup-history-second a:first").click(); }, + function(){ $("#dup-history-first a").click(); }, + function(){ $("#dup-history-second a:last").click(); }, + function(){ $("#dup-history-dialog a:contains('Close')").click(); }, + function(){ + + // fourth page (third index) in the stack to account for first page being hash manipulation, + // the third page is dup-history-second which has two entries in history + // the test is to make sure the index isn't 1 in this case, or the first entry for dup-history-second + same($.mobile.urlHistory.activeIndex, 3, "should be the fourth page in the stack"); + start(); + }]); + }); + + asyncTest( "going back from a page entered from a dialog skips the dialog and goes to the previous page", function(){ + $.testHelper.pageSequence([ + // setup + function(){ $.testHelper.openPage("#skip-dialog-first"); }, + + // transition to the dialog + function(){ $("#skip-dialog-first a").click(); }, + + // transition to the second page + function(){ $("#skip-dialog a").click(); }, + + // transition past the dialog via data-rel=back link on the second page + function(){ $("#skip-dialog-second a").click(); }, + + // make sure we're at the first page and not the dialog + function(){ + $.testHelper.assertUrlLocation({ + hash: "skip-dialog-first", + push: home + "#skip-dialog-first", + report: "should be the first page in the sequence" + }); + + start(); + }]); + }); + + asyncTest( "going forward from a page entered from a dialog skips the dialog and goes to the next page", function(){ + $.testHelper.pageSequence([ + // setup + function(){ $.testHelper.openPage("#skip-dialog-first"); }, + + // transition to the dialog + function(){ $("#skip-dialog-first a").click(); }, + + // transition to the second page + function(){ $("#skip-dialog a").click(); }, + + // transition to back past the dialog + function(){ window.history.back(); }, + + // transition to the second page past the dialog through history + function(){ window.history.forward(); }, + + // make sure we're on the second page and not the dialog + function(){ + $.testHelper.assertUrlLocation({ + hash: "skip-dialog-second", + push: home + "#skip-dialog-second", + report: "should be the second page after the dialog" + }); + + start(); + }]); + }); + + asyncTest( "going back from a dialog triggered from a dialog should result in the first dialog ", function(){ + $.testHelper.pageSequence([ + // setup + function(){ $.testHelper.openPage("#nested-dialog-page"); }, + + // transition to the dialog + function(){ $("#nested-dialog-page a").click(); }, + + // transition to the second dialog + function(){ $("#nested-dialog-first a").click(); }, + + // transition to back to the first dialog + function(){ window.history.back(); }, + + // make sure we're on first dialog + function(){ + same($(".ui-page-active")[0], $("#nested-dialog-first")[0], "should be the first dialog"); + start(); + }]); + }); + + asyncTest( "loading a relative file path after an embeded page works", function(){ + $.testHelper.pageSequence([ + // transition second page + function(){ $.testHelper.openPage("#relative-after-embeded-page-first"); }, + + // transition second page + function(){ $("#relative-after-embeded-page-first a").click(); }, + + // transition to the relative ajax loaded page + function(){ $("#relative-after-embeded-page-second a").click(); }, + + // make sure the page was loaded properly via ajax + function(){ + // data attribute intentionally left without namespace + same($(".ui-page-active").data("other"), "for testing", "should be relative ajax loaded page"); + start(); + }]); + }); + + asyncTest( "Page title updates properly when clicking back to previous page", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#relative-after-embeded-page-first"); + }, + + function(){ + window.history.back(); + }, + + function(){ + same(document.title, "jQuery Mobile Navigation Test Suite"); + start(); + } + ]); + }); + + asyncTest( "Page title updates properly from title tag when loading an external page", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#ajax-title-page"); + }, + + function(){ + $("#titletest1").click(); + }, + + function(){ + same(document.title, "Title Tag"); + start(); + } + ]); + }); + + asyncTest( "Page title updates properly from data-title attr when loading an external page", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#ajax-title-page"); + }, + + function(){ + $("#titletest2").click(); + }, + + function(){ + same(document.title, "Title Attr"); + start(); + } + ]); + }); + + asyncTest( "Page title updates properly from heading text in header when loading an external page", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#ajax-title-page"); + }, + + function(){ + $("#titletest3").click(); + }, + + function(){ + same(document.title, "Title Heading"); + start(); + } + ]); + }); + + asyncTest( "Page links to the current active page result in the same active page", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#self-link"); + }, + + function(){ + $("a[href='#self-link']").click(); + }, + + function(){ + same($.mobile.activePage[0], $("#self-link")[0], "self-link page is still the active page" ); + start(); + } + ]); + }); + + asyncTest( "links on subdirectory pages with query params append the params and load the page", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#data-url-tests/non-data-url.html"); + }, + + function(){ + $("#query-param-anchor").click(); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: home + "data-url-tests/non-data-url.html?foo=bar", + report: "the hash or url has query params" + }); + + ok($(".ui-page-active").jqmData("url").indexOf("?foo=bar") > -1, "the query params are in the data url"); + start(); + } + ]); + }); + + asyncTest( "identical query param link doesn't add additional set of query params", function(){ + $.testHelper.pageSequence([ + function(){ + $.testHelper.openPage("#data-url-tests/non-data-url.html"); + }, + + function(){ + $("#query-param-anchor").click(); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: home + "data-url-tests/non-data-url.html?foo=bar", + report: "the hash or url has query params" + }); + + $("#query-param-anchor").click(); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: home + "data-url-tests/non-data-url.html?foo=bar", + report: "the hash or url still has query params" + }); + + start(); + } + ]); + }); + + // Special handling inside navigation because query params must be applied to the hash + // or absolute reference and dialogs apply extra information int the hash that must be removed + asyncTest( "query param link from a dialog to itself should be a not add another dialog", function(){ + var firstDialogLoc; + + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage("#dialog-param-link"); + }, + + // navigate to the subdirectory page with the query link + function(){ + $("#dialog-param-link a").click(); + }, + + // navigate to the query param self reference link + function(){ + $("#dialog-param-link-page a").click(); + }, + + // attempt to navigate to the same link + function(){ + // store the current hash for comparison (with one dialog hash key) + firstDialogLoc = location.hash || location.href; + $("#dialog-param-link-page a").click(); + }, + + function(){ + same(location.hash || location.href, firstDialogLoc, "additional dialog hash key not added"); + start(); + } + ]); + }); + + asyncTest( "query data passed as string to changePage is appended to URL", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + $.mobile.changePage( "form-tests/changepage-data.html", { + data: "foo=1&bar=2" + } ); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: home + "form-tests/changepage-data.html?foo=1&bar=2", + report: "the hash or url still has query params" + }); + + start(); + } + ]); + }); + + asyncTest( "query data passed as object to changePage is appended to URL", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + $.mobile.changePage( "form-tests/changepage-data.html", { + data: { + foo: 3, + bar: 4 + } + } ); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: home + "form-tests/changepage-data.html?foo=3&bar=4", + report: "the hash or url still has query params" + }); + + start(); + } + ]); + }); + + asyncTest( "refresh of a dialog url should not duplicate page", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + same($(".foo-class").length, 1, "should only have one instance of foo-class in the document"); + location.hash = "#foo&ui-state=dialog"; + }, + + function(){ + $.testHelper.assertUrlLocation({ + hash: "foo&ui-state=dialog", + push: home + "#foo&ui-state=dialog", + report: "hash should match what was loaded" + }); + + same( $(".foo-class").length, 1, "should only have one instance of foo-class in the document" ); + start(); + } + ]); + }); + + asyncTest( "internal form with no action submits to document URL", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage("#internal-no-action-form-page"); + }, + + function(){ + $("#internal-no-action-form-page form").eq(0).submit(); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: home + "?foo=1&bar=2", + report: "hash should match what was loaded" + }); + + start(); + } + ]); + }); + + asyncTest( "external page containing form with no action submits to page URL", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage("#internal-no-action-form-page"); + }, + + function(){ + $("#internal-no-action-form-page a").eq(0).click(); + }, + + function(){ + $("#external-form-no-action-page form").eq(0).submit(); + }, + + function(){ + $.testHelper.assertUrlLocation({ + hashOrPush: home + "form-tests/form-no-action.html?foo=1&bar=2", + report: "hash should match page url and not document url" + }); + + start(); + } + ]); + }); + + asyncTest( "handling of active button state when navigating", 1, function(){ + + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage("#active-state-page1"); + }, + + function(){ + $("#active-state-page1 a").eq(0).click(); + }, + + function(){ + $("#active-state-page2 a").eq(0).click(); + }, + + function(){ + ok(!$("#active-state-page1 a").hasClass( $.mobile.activeBtnClass ), "No button should not have class " + $.mobile.activeBtnClass ); + start(); + } + ]); + }); + + // issue 2444 https://github.com/jquery/jquery-mobile/issues/2444 + // results from preventing spurious hash changes + asyncTest( "dialog should return to its parent page when open and closed multiple times", function() { + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage("#default-trans-dialog"); + }, + + function(){ + $.mobile.activePage.find( "a" ).click(); + }, + + function(){ + window.history.back(); + }, + + function(){ + same( $.mobile.activePage[0], $( "#default-trans-dialog" )[0] ); + $.mobile.activePage.find( "a" ).click(); + }, + + function(){ + window.history.back(); + }, + + function(){ + same( $.mobile.activePage[0], $( "#default-trans-dialog" )[0] ); + start(); + } + ]); + }); + + asyncTest( "clicks with middle mouse button are ignored", function() { + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage( "#odd-clicks-page" ); + }, + + function() { + $( "#right-or-middle-click" ).click(); + }, + + // make sure the page is opening first without the mocked button click value + // only necessary to prevent issues with test specific fixtures + function() { + same($.mobile.activePage[0], $("#odd-clicks-page-dest")[0]); + $.testHelper.openPage( "#odd-clicks-page" ); + + // mock the which value to simulate a middle click + $.Event.prototype.which = 2; + }, + + function() { + $( "#right-or-middle-click" ).click(); + }, + + function( timeout ) { + ok( timeout, "page event handler timed out due to ignored click" ); + ok($.mobile.activePage[0] !== $("#odd-clicks-page-dest")[0], "pages are not the same"); + start(); + } + ]); + }); + + asyncTest( "disabling link binding disables navigation via links and highlighting", function() { + $.mobile.linkBindingEnabled = false; + + $.testHelper.pageSequence([ + function() { + $.testHelper.openPage("#bar"); + }, + + function() { + $.mobile.activePage.find( "a" ).click(); + }, + + function( timeout ) { + ok( !$.mobile.activePage.find( "a" ).hasClass( $.mobile.activeBtnClass ), "vlick handler doesn't add the activebtn class" ); + ok( timeout, "no page change was fired" ); + start(); + } + ]); + }); + + asyncTest( "handling of button active state when navigating by clicking back button", 1, function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage("#active-state-page1"); + }, + + function(){ + $("#active-state-page1 a").eq(0).click(); + }, + + function(){ + $("#active-state-page2 a").eq(1).click(); + }, + + function(){ + $("#active-state-page1 a").eq(0).click(); + }, + + function(){ + ok(!$("#active-state-page2 a").hasClass( $.mobile.activeBtnClass ), "No button should not have class " + $.mobile.activeBtnClass ); + start(); + } + ]); + }); + + asyncTest( "can navigate to dynamically injected page with dynamically injected link", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + $.testHelper.openPage("#inject-links-page"); + }, + + function(){ + var $ilpage = $( "#inject-links-page" ), + $link = $( "injected-test-page link" ); + + // Make sure we actually navigated to the expected page. + ok( $.mobile.activePage[ 0 ] == $ilpage[ 0 ], "navigated successfully to #inject-links-page" ); + + // Now dynamically insert a page. + $ilpage.parent().append( "
        testing...
        " ); + + // Now inject a link to this page dynamically and attempt to navigate + // to the page we just inserted. + $link.appendTo( $ilpage ).click(); + }, + + function(){ + // Make sure we actually navigated to the expected page. + ok( $.mobile.activePage[ 0 ] == $( "#injected-test-page" )[ 0 ], "navigated successfully to #injected-test-page" ); + + start(); + } + ]); + }); + + asyncTest( "application url with dialogHashKey loads application's first page", function(){ + $.testHelper.pageSequence([ + // open our test page + function(){ + // Navigate to any page except the first page of the application. + $.testHelper.openPage("#foo"); + }, + + function(){ + ok( $.mobile.activePage[ 0 ] === $( "#foo" )[ 0 ], "navigated successfully to #foo" ); + + // Now navigate to an hash that contains just a dialogHashKey. + $.mobile.changePage("#" + $.mobile.dialogHashKey); + }, + + function(){ + // Make sure we actually navigated to the first page. + ok( $.mobile.activePage[ 0 ] === $.mobile.firstPage[ 0 ], "navigated successfully to first-page" ); + + // Now make sure opening the page didn't result in page duplication. + ok( $.mobile.firstPage.hasClass( "first-page" ), "first page has expected class" ); + same( $( ".first-page" ).length, 1, "first page was not duplicated" ); + + start(); + } + ]); + }); + + asyncTest( "prefetched links with data rel dialog result in a dialog", function() { + $.testHelper.pageSequence([ + // open our test page + function(){ + // Navigate to any page except the first page of the application. + $.testHelper.openPage("#prefetched-dialog-page"); + }, + + function() { + $("#prefetched-dialog-link").click(); + }, + + function() { + ok( $.mobile.activePage.is(".ui-dialog"), "prefetched page is rendered as a dialog" ); + start(); + } + ]); + }); +})(jQuery); diff --git a/tests/unit/navigation/navigation_dialog_pushstate.js b/tests/unit/navigation/navigation_dialog_pushstate.js new file mode 100644 index 00000000..a056f647 --- /dev/null +++ b/tests/unit/navigation/navigation_dialog_pushstate.js @@ -0,0 +1,16 @@ +(function($) { + asyncTest( "dialog ui-state should be part of the hash", function(){ + $.testHelper.sequence([ + function() { + // open the test page + $.mobile.activePage.find( "a" ).click(); + }, + + function() { + // verify that the hash contains the dialogHashKey + ok( location.hash.search($.mobile.dialogHashKey) >= 0 ); + start(); + } + ]); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/navigation/navigation_helpers.js b/tests/unit/navigation/navigation_helpers.js new file mode 100644 index 00000000..88533b76 --- /dev/null +++ b/tests/unit/navigation/navigation_helpers.js @@ -0,0 +1,218 @@ +/* + * mobile navigation unit tests + */ +(function($){ + var siteDirectory = location.pathname.replace(/[^/]+$/, ""); + + module('jquery.mobile.navigation.js', { + setup: function(){ + if ( location.hash ) { + stop(); + $(document).one("pagechange", function() { + start(); + } ); + location.hash = ""; + } + } + }); + + test( "path.get method is working properly", function(){ + window.location.hash = "foo"; + same($.mobile.path.get(), "foo", "get method returns location.hash minus hash character"); + same($.mobile.path.get( "#foo/bar/baz.html" ), "foo/bar/", "get method with hash arg returns path with no filename or hash prefix"); + same($.mobile.path.get( "#foo/bar/baz.html/" ), "foo/bar/baz.html/", "last segment of hash is retained if followed by a trailing slash"); + }); + + test( "path.isPath method is working properly", function(){ + ok(!$.mobile.path.isPath('bar'), "anything without a slash is not a path"); + ok($.mobile.path.isPath('bar/'), "anything with a slash is a path"); + ok($.mobile.path.isPath('/bar'), "anything with a slash is a path"); + ok($.mobile.path.isPath('a/r'), "anything with a slash is a path"); + ok($.mobile.path.isPath('/'), "anything with a slash is a path"); + }); + + test( "path.getFilePath method is working properly", function(){ + same($.mobile.path.getFilePath("foo.html" + "&" + $.mobile.subPageUrlKey ), "foo.html", "returns path without sub page key"); + }); + + test( "path.set method is working properly", function(){ + $.mobile.urlHistory.ignoreNextHashChange = false; + $.mobile.path.set("foo"); + same("foo", window.location.hash.replace(/^#/,""), "sets location.hash properly"); + }); + + test( "path.makeUrlAbsolute is working properly", function(){ + var mua = $.mobile.path.makeUrlAbsolute, + p1 = "http://jqm.com/", + p2 = "http://jqm.com/?foo=1&bar=2", + p3 = "http://jqm.com/#spaz", + p4 = "http://jqm.com/?foo=1&bar=2#spaz", + + p5 = "http://jqm.com/test.php", + p6 = "http://jqm.com/test.php?foo=1&bar=2", + p7 = "http://jqm.com/test.php#spaz", + p8 = "http://jqm.com/test.php?foo=1&bar=2#spaz", + + p9 = "http://jqm.com/dir1/dir2/", + p10 = "http://jqm.com/dir1/dir2/?foo=1&bar=2", + p11 = "http://jqm.com/dir1/dir2/#spaz", + p12 = "http://jqm.com/dir1/dir2/?foo=1&bar=2#spaz", + + p13 = "http://jqm.com/dir1/dir2/test.php", + p14 = "http://jqm.com/dir1/dir2/test.php?foo=1&bar=2", + p15 = "http://jqm.com/dir1/dir2/test.php#spaz", + p16 = "http://jqm.com/dir1/dir2/test.php?foo=1&bar=2#spaz"; + + // Test URL conversion against an absolute URL to the site root. + // directory tests + same( mua( "http://jqm.com/", p1 ), "http://jqm.com/", "absolute root - absolute root" ); + same( mua( "//jqm.com/", p1 ), "http://jqm.com/", "protocol relative root - absolute root" ); + same( mua( "/", p1 ), "http://jqm.com/", "site relative root - absolute root" ); + + same( mua( "http://jqm.com/?foo=1&bar=2", p1 ), "http://jqm.com/?foo=1&bar=2", "absolute root with query - absolute root" ); + same( mua( "//jqm.com/?foo=1&bar=2", p1 ), "http://jqm.com/?foo=1&bar=2", "protocol relative root with query - absolute root" ); + same( mua( "/?foo=1&bar=2", p1 ), "http://jqm.com/?foo=1&bar=2", "site relative root with query - absolute root" ); + same( mua( "?foo=1&bar=2", p1 ), "http://jqm.com/?foo=1&bar=2", "query relative - absolute root" ); + + same( mua( "http://jqm.com/#spaz", p1 ), "http://jqm.com/#spaz", "absolute root with fragment - absolute root" ); + same( mua( "//jqm.com/#spaz", p1 ), "http://jqm.com/#spaz", "protocol relative root with fragment - absolute root" ); + same( mua( "/#spaz", p1 ), "http://jqm.com/#spaz", "site relative root with fragment - absolute root" ); + same( mua( "#spaz", p1 ), "http://jqm.com/#spaz", "fragment relative - absolute root" ); + + same( mua( "http://jqm.com/?foo=1&bar=2#spaz", p1 ), "http://jqm.com/?foo=1&bar=2#spaz", "absolute root with query and fragment - absolute root" ); + same( mua( "//jqm.com/?foo=1&bar=2#spaz", p1 ), "http://jqm.com/?foo=1&bar=2#spaz", "protocol relative root with query and fragment - absolute root" ); + same( mua( "/?foo=1&bar=2#spaz", p1 ), "http://jqm.com/?foo=1&bar=2#spaz", "site relative root with query and fragment - absolute root" ); + same( mua( "?foo=1&bar=2#spaz", p1 ), "http://jqm.com/?foo=1&bar=2#spaz", "query relative and fragment - absolute root" ); + + // file tests + same( mua( "http://jqm.com/test.php", p1 ), "http://jqm.com/test.php", "absolute file at root - absolute root" ); + same( mua( "//jqm.com/test.php", p1 ), "http://jqm.com/test.php", "protocol relative file at root - absolute root" ); + same( mua( "/test.php", p1 ), "http://jqm.com/test.php", "site relative file at root - absolute root" ); + same( mua( "test.php", p1 ), "http://jqm.com/test.php", "document relative file at root - absolute root" ); + + same( mua( "http://jqm.com/test.php?foo=1&bar=2", p1 ), "http://jqm.com/test.php?foo=1&bar=2", "absolute file at root with query - absolute root" ); + same( mua( "//jqm.com/test.php?foo=1&bar=2", p1 ), "http://jqm.com/test.php?foo=1&bar=2", "protocol relative file at root with query - absolute root" ); + same( mua( "/test.php?foo=1&bar=2", p1 ), "http://jqm.com/test.php?foo=1&bar=2", "site relative file at root with query - absolute root" ); + same( mua( "test.php?foo=1&bar=2", p1 ), "http://jqm.com/test.php?foo=1&bar=2", "document relative file at root with query - absolute root" ); + + same( mua( "http://jqm.com/test.php#spaz", p1 ), "http://jqm.com/test.php#spaz", "absolute file at root with fragment - absolute root" ); + same( mua( "//jqm.com/test.php#spaz", p1 ), "http://jqm.com/test.php#spaz", "protocol relative file at root with fragment - absolute root" ); + same( mua( "/test.php#spaz", p1 ), "http://jqm.com/test.php#spaz", "site relative file at root with fragment - absolute root" ); + same( mua( "test.php#spaz", p1 ), "http://jqm.com/test.php#spaz", "file at root with fragment - absolute root" ); + + same( mua( "http://jqm.com/test.php?foo=1&bar=2#spaz", p1 ), "http://jqm.com/test.php?foo=1&bar=2#spaz", "absolute file at root with query and fragment - absolute root" ); + same( mua( "//jqm.com/test.php?foo=1&bar=2#spaz", p1 ), "http://jqm.com/test.php?foo=1&bar=2#spaz", "protocol relative file at root with query and fragment - absolute root" ); + same( mua( "/test.php?foo=1&bar=2#spaz", p1 ), "http://jqm.com/test.php?foo=1&bar=2#spaz", "site relative file at root with query and fragment - absolute root" ); + same( mua( "test.php?foo=1&bar=2#spaz", p1 ), "http://jqm.com/test.php?foo=1&bar=2#spaz", "query relative file at root fragment - absolute root" ); + + // Test URL conversion against an absolute URL to a file at the site root. + + same( mua( "http://jqm.com/", p5 ), "http://jqm.com/", "absolute root - absolute root" ); + same( mua( "//jqm.com/", p5 ), "http://jqm.com/", "protocol relative root - absolute root" ); + same( mua( "/", p5 ), "http://jqm.com/", "site relative root - absolute root" ); + + same( mua( "http://jqm.com/?foo=1&bar=2", p5 ), "http://jqm.com/?foo=1&bar=2", "absolute root with query - absolute root" ); + same( mua( "//jqm.com/?foo=1&bar=2", p5 ), "http://jqm.com/?foo=1&bar=2", "protocol relative root with query - absolute root" ); + same( mua( "/?foo=1&bar=2", p5 ), "http://jqm.com/?foo=1&bar=2", "site relative root with query - absolute root" ); + same( mua( "?foo=1&bar=2", p5 ), "http://jqm.com/test.php?foo=1&bar=2", "query relative - absolute root" ); + + same( mua( "http://jqm.com/#spaz", p5 ), "http://jqm.com/#spaz", "absolute root with fragment - absolute root" ); + same( mua( "//jqm.com/#spaz", p5 ), "http://jqm.com/#spaz", "protocol relative root with fragment - absolute root" ); + same( mua( "/#spaz", p5 ), "http://jqm.com/#spaz", "site relative root with fragment - absolute root" ); + same( mua( "#spaz", p5 ), "http://jqm.com/test.php#spaz", "fragment relative - absolute root" ); + + same( mua( "http://jqm.com/?foo=1&bar=2#spaz", p5 ), "http://jqm.com/?foo=1&bar=2#spaz", "absolute root with query and fragment - absolute root" ); + same( mua( "//jqm.com/?foo=1&bar=2#spaz", p5 ), "http://jqm.com/?foo=1&bar=2#spaz", "protocol relative root with query and fragment - absolute root" ); + same( mua( "/?foo=1&bar=2#spaz", p5 ), "http://jqm.com/?foo=1&bar=2#spaz", "site relative root with query and fragment - absolute root" ); + same( mua( "?foo=1&bar=2#spaz", p5 ), "http://jqm.com/test.php?foo=1&bar=2#spaz", "query relative and fragment - absolute root" ); + }); + + // https://github.com/jquery/jquery-mobile/issues/2362 + test( "ipv6 host support", function(){ + // http://www.ietf.org/rfc/rfc2732.txt ipv6 examples for tests + // most definitely not comprehensive + var ipv6_1 = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html", + ipv6_2 = "http://[1080:0:0:0:8:800:200C:417A]/index.html", + ipv6_3 = "http://[3ffe:2a00:100:7031::1]", + ipv6_4 = "http://[1080::8:800:200C:417A]/foo", + ipv6_5 = "http://[::192.9.5.5]/ipng", + ipv6_6 = "http://[::FFFF:129.144.52.38]:80/index.html", + ipv6_7 = "http://[2010:836B:4179::836B:4179]", + fromIssue = "http://[3fff:cafe:babe::]:443/foo"; + + same( $.mobile.path.parseUrl(ipv6_1).host, "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80"); + same( $.mobile.path.parseUrl(ipv6_1).hostname, "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]"); + same( $.mobile.path.parseUrl(ipv6_2).host, "[1080:0:0:0:8:800:200C:417A]"); + same( $.mobile.path.parseUrl(ipv6_3).host, "[3ffe:2a00:100:7031::1]"); + same( $.mobile.path.parseUrl(ipv6_4).host, "[1080::8:800:200C:417A]"); + same( $.mobile.path.parseUrl(ipv6_5).host, "[::192.9.5.5]"); + same( $.mobile.path.parseUrl(ipv6_6).host, "[::FFFF:129.144.52.38]:80"); + same( $.mobile.path.parseUrl(ipv6_6).hostname, "[::FFFF:129.144.52.38]"); + same( $.mobile.path.parseUrl(ipv6_7).host, "[2010:836B:4179::836B:4179]"); + same( $.mobile.path.parseUrl(fromIssue).host, "[3fff:cafe:babe::]:443"); + same( $.mobile.path.parseUrl(fromIssue).hostname, "[3fff:cafe:babe::]"); + }); + + test( "path.clean is working properly", function(){ + var localroot = location.protocol + "//" + location.host + location.pathname, + remoteroot = "http://google.com/", + fakepath = "#foo/bar/baz.html", + pathWithParam = localroot + "bar?baz=" + localroot, + localpath = localroot + fakepath, + remotepath = remoteroot + fakepath; + + same( $.mobile.path.clean( localpath ), location.pathname + fakepath, "removes location protocol, host, and portfrom same-domain path"); + same( $.mobile.path.clean( remotepath ), remotepath, "does nothing to an external domain path"); + same( $.mobile.path.clean( pathWithParam ), location.pathname + "bar?baz=" + localroot, "doesn't remove params with localroot value"); + }); + + test( "path.stripHash is working properly", function(){ + same( $.mobile.path.stripHash( "#bar" ), "bar", "returns a hash without the # prefix"); + }); + + test( "path.hasProtocol is working properly", function(){ + same( $.mobile.path.hasProtocol( "tel:5559999" ), true, "value in tel protocol format has protocol" ); + same( $.mobile.path.hasProtocol( location.href ), true, "location href has protocol" ); + same( $.mobile.path.hasProtocol( "foo/bar/baz.html" ), false, "simple directory path has no protocol" ); + same( $.mobile.path.hasProtocol( "file://foo/bar/baz.html" ), true, "simple directory path with file:// has protocol" ); + }); + + test( "path.isRelativeUrl is working properly", function(){ + same( $.mobile.path.isRelativeUrl("http://company.com/"), false, "absolute url is not relative" ); + same( $.mobile.path.isRelativeUrl("//company.com/"), true, "protocol relative url is relative" ); + same( $.mobile.path.isRelativeUrl("/"), true, "site relative url is relative" ); + + same( $.mobile.path.isRelativeUrl("http://company.com/test.php"), false, "absolute url is not relative" ); + same( $.mobile.path.isRelativeUrl("//company.com/test.php"), true, "protocol relative url is relative" ); + same( $.mobile.path.isRelativeUrl("/test.php"), true, "site relative url is relative" ); + same( $.mobile.path.isRelativeUrl("test.php"), true, "document relative url is relative" ); + + same( $.mobile.path.isRelativeUrl("http://company.com/dir1/dir2/test.php?foo=1&bar=2#frag"), false, "absolute url is not relative" ); + same( $.mobile.path.isRelativeUrl("//company.com/dir1/dir2/test.php?foo=1&bar=2#frag"), true, "protocol relative url is relative" ); + same( $.mobile.path.isRelativeUrl("/dir1/dir2/test.php?foo=1&bar=2#frag"), true, "site relative url is relative" ); + same( $.mobile.path.isRelativeUrl("dir1/dir2/test.php?foo=1&bar=2#frag"), true, "document relative path url is relative" ); + same( $.mobile.path.isRelativeUrl("test.php?foo=1&bar=2#frag"), true, "document relative file url is relative" ); + same( $.mobile.path.isRelativeUrl("?foo=1&bar=2#frag"), true, "query relative url is relative" ); + same( $.mobile.path.isRelativeUrl("#frag"), true, "fragments are relative" ); + }); + + test( "path.isExternal is working properly", function(){ + same( $.mobile.path.isExternal( location.href ), false, "same domain is not external" ); + same( $.mobile.path.isExternal( "http://example.com" ), true, "example.com is external" ); + same($.mobile.path.isExternal("mailto:"), true, "mailto protocol"); + same($.mobile.path.isExternal("http://foo.com"), true, "http protocol"); + same($.mobile.path.isExternal("http://www.foo.com"), true, "http protocol with www"); + same($.mobile.path.isExternal("tel:16178675309"), true, "tel protocol"); + same($.mobile.path.isExternal("foo.html"), false, "filename"); + same($.mobile.path.isExternal("foo/foo/foo.html"), false, "file path"); + same($.mobile.path.isExternal("../../index.html"), false, "relative parent path"); + same($.mobile.path.isExternal("/foo"), false, "root-relative path"); + same($.mobile.path.isExternal("foo"), false, "simple string"); + same($.mobile.path.isExternal("#foo"), false, "local id reference"); + }); + + test( "path.cleanHash", function(){ + same( $.mobile.path.cleanHash( "#anything/atall?akjfdjjf" ), "anything/atall", "removes query param"); + same( $.mobile.path.cleanHash( "#nothing/atall" ), "nothing/atall", "removes query param"); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/navigation/navigation_paths.js b/tests/unit/navigation/navigation_paths.js new file mode 100644 index 00000000..017a943a --- /dev/null +++ b/tests/unit/navigation/navigation_paths.js @@ -0,0 +1,178 @@ +/* + * mobile navigation path unit tests + */ +(function($){ + var url = $.mobile.path.parseUrl( location.href ), + home = location.href.replace( url.domain, "" ); + + var testPageLoad = function(testPageAnchorSelector, expectedTextValue){ + expect( 2 ); + + $.testHelper.pageSequence([ + function(){ + // reset before each test, all tests expect original page + // for relative urls + $.testHelper.openPage( "#" + home); + }, + + // open our test page + function(){ + $.testHelper.openPage("#pathing-tests"); + }, + + // navigate to the linked page + function(){ + var page = $.mobile.activePage; + + // check that the reset page isn't still open + equal("", page.find(".reset-value").text()); + + //click he test page link to execute the path + page.find("a" + testPageAnchorSelector).click(); + }, + + // verify that the page has changed and the expected text value is present + function(){ + same($.mobile.activePage.find(".test-value").text(), expectedTextValue); + start(); + } + ]); + }; + + // all of these alterations assume location.pathname will be a directory + // this is required to prevent the tests breaking in a subdirectory + // TODO could potentially be fragile since the tests could be running while + // the urls are being updated + $(function(){ + $("a.site-rel").each(function(i, elem){ + var $elem = $(elem); + $elem.attr("href", location.pathname + $(elem).attr("href")); + }); + + $('a.protocol-rel').each(function(i, elem){ + var $elem = $(elem); + $elem.attr("href", "//" + location.host + location.pathname + $(elem).attr("href")); + }); + + $('a.absolute').each(function(i, elem){ + var $elem = $(elem); + $elem.attr("href", + location.protocol + "//" + location.host + + location.pathname + $(elem).attr("href")); + }); + }); + + + //Doc relative tests + module("document relative paths"); + + asyncTest( "file reference no nesting", function(){ + testPageLoad("#doc-rel-test-one", "doc rel test one"); + }); + + asyncTest( "file reference with nesting", function(){ + testPageLoad("#doc-rel-test-two", "doc rel test two"); + }); + + asyncTest( "file reference with double nesting", function(){ + testPageLoad("#doc-rel-test-three", "doc rel test three"); + }); + + asyncTest( "dir refrence with nesting", function(){ + testPageLoad("#doc-rel-test-four", "doc rel test four"); + }); + + asyncTest( "file refrence with parent dir", function(){ + testPageLoad("#doc-rel-test-five", "doc rel test five"); + }); + + asyncTest( "dir refrence with parent dir", function(){ + testPageLoad("#doc-rel-test-six", "doc rel test six"); + }); + + + // Site relative tests + // NOTE does not test root path or non nested references + module("site relative paths"); + + asyncTest( "file reference no nesting", function(){ + testPageLoad("#site-rel-test-one", "doc rel test one"); + }); + + asyncTest( "file reference with nesting", function(){ + testPageLoad("#site-rel-test-two", "doc rel test two"); + }); + + asyncTest( "file reference with double nesting", function(){ + testPageLoad("#site-rel-test-three", "doc rel test three"); + }); + + asyncTest( "dir refrence with nesting", function(){ + testPageLoad("#site-rel-test-four", "doc rel test four"); + }); + + asyncTest( "file refrence with parent dir", function(){ + testPageLoad("#site-rel-test-five", "doc rel test five"); + }); + + asyncTest( "dir refrence with parent dir", function(){ + testPageLoad("#site-rel-test-six", "doc rel test six"); + }); + + + // Protocol relative tests + // NOTE does not test root path or non nested references + module("protocol relative paths"); + + asyncTest( "file reference no nesting", function(){ + testPageLoad("#protocol-rel-test-one", "doc rel test one"); + }); + + asyncTest( "file reference with nesting", function(){ + testPageLoad("#protocol-rel-test-two", "doc rel test two"); + }); + + asyncTest( "file reference with double nesting", function(){ + testPageLoad("#protocol-rel-test-three", "doc rel test three"); + }); + + asyncTest( "dir refrence with nesting", function(){ + testPageLoad("#protocol-rel-test-four", "doc rel test four"); + }); + + asyncTest( "file refrence with parent dir", function(){ + testPageLoad("#protocol-rel-test-five", "doc rel test five"); + }); + + asyncTest( "dir refrence with parent dir", function(){ + testPageLoad("#protocol-rel-test-six", "doc rel test six"); + }); + + // absolute tests + // NOTE does not test root path or non nested references + module("abolute paths"); + + asyncTest( "file reference no nesting", function(){ + testPageLoad("#absolute-test-one", "doc rel test one"); + }); + + asyncTest( "file reference with nesting", function(){ + testPageLoad("#absolute-test-two", "doc rel test two"); + }); + + asyncTest( "file reference with double nesting", function(){ + testPageLoad("#absolute-test-three", "doc rel test three"); + }); + + asyncTest( "dir refrence with nesting", function(){ + testPageLoad("#absolute-test-four", "doc rel test four"); + }); + + asyncTest( "file refrence with parent dir", function(){ + testPageLoad("#absolute-test-five", "doc rel test five"); + }); + + asyncTest( "dir refrence with parent dir", function(){ + testPageLoad("#absolute-test-six", "doc rel test six"); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/navigation/navigation_transitions.js b/tests/unit/navigation/navigation_transitions.js new file mode 100644 index 00000000..5a44806c --- /dev/null +++ b/tests/unit/navigation/navigation_transitions.js @@ -0,0 +1,151 @@ +/* + * mobile navigation unit tests + */ +(function($){ + var perspective = "viewport-flip", + transitioning = "ui-mobile-viewport-transitioning", + animationCompleteFn = $.fn.animationComplete, + + //TODO centralize class names? + transitionTypes = "in out fade slide flip reverse pop", + + isTransitioning = function(page){ + return $.grep(transitionTypes.split(" "), function(className, i){ + return page.hasClass(className); + }).length > 0; + }, + + isTransitioningIn = function(page){ + return page.hasClass("in") && isTransitioning(page); + }, + + //animationComplete callback queue + callbackQueue = [], + + finishPageTransition = function(){ + callbackQueue.pop()(); + }, + + clearPageTransitionStack = function(){ + stop(); + var checkTransitionStack = function(){ + if(callbackQueue.length>0) { + setTimeout(function(){ + finishPageTransition(); + checkTransitionStack(); + },0); + } + else { + start(); + } + }; + checkTransitionStack(); + }, + + //wipe all urls + clearUrlHistory = function(){ + $.mobile.urlHistory.stack = []; + $.mobile.urlHistory.activeIndex = 0; + }; + + + module('jquery.mobile.navigation.js', { + setup: function(){ + //stub to prevent class removal + $.fn.animationComplete = function(callback){ + callbackQueue.unshift(callback); + }; + + clearPageTransitionStack(); + clearUrlHistory(); + }, + + teardown: function(){ + // unmock animation complete + $.fn.animationComplete = animationCompleteFn; + } + }); + + test( "changePage applys perspective class to mobile viewport for flip", function(){ + $("#foo > a").click(); + + ok($("body").hasClass(perspective), "has perspective class"); + }); + + test( "changePage does not apply perspective class to mobile viewport for transitions other than flip", function(){ + $("#bar > a").click(); + + ok(!$("body").hasClass(perspective), "doesn't have perspective class"); + }); + + test( "changePage applys transition class to mobile viewport for default transition", function(){ + $("#baz > a").click(); + + ok($("body").hasClass(transitioning), "has transitioning class"); + }); + + test( "explicit transition preferred for page navigation reversal (ie back)", function(){ + $("#fade-trans > a").click(); + stop(); + setTimeout(function(){ + finishPageTransition(); + $("#flip-trans > a").click(); + setTimeout(function(){ + finishPageTransition(); + $("#fade-trans > a").click(); + setTimeout(function(){ + ok($("#flip-trans").hasClass("fade"), "has fade class"); + start(); + },0); + },0); + },0); + }); + + test( "default transition is slide", function(){ + $("#default-trans > a").click(); + stop(); + setTimeout(function(){ + ok($("#no-trans").hasClass("slide"), "has slide class"); + start(); + },0); + }); + + test( "changePage queues requests", function(){ + var firstPage = $("#foo"), + secondPage = $("#bar"); + + $.mobile.changePage(firstPage); + $.mobile.changePage(secondPage); + + stop(); + setTimeout(function(){ + ok(isTransitioningIn(firstPage), "first page begins transition"); + ok(!isTransitioningIn(secondPage), "second page doesn't transition yet"); + + finishPageTransition(); + + setTimeout(function(){ + ok(!isTransitioningIn(firstPage), "first page transition should be complete"); + ok(isTransitioningIn(secondPage), "second page should begin transitioning"); + start(); + },0); + },0); + }); + + test( "default transition is pop for a dialog", function(){ + expect( 1 ); + stop(); + setTimeout(function(){ + $("#default-trans-dialog > a").click(); + + ok($("#no-trans-dialog").hasClass("pop"), "expected the pop class to be present but instead was " + $("#no-trans-dialog").attr('class')); + + start(); + }, 900); + }); + + test( "animationComplete return value", function(){ + $.fn.animationComplete = animationCompleteFn; + equals($("#foo").animationComplete(function(){})[0], $("#foo")[0]); + }); +})(jQuery); diff --git a/tests/unit/navigation/path-tests/file.html b/tests/unit/navigation/path-tests/file.html new file mode 100644 index 00000000..98e20d59 --- /dev/null +++ b/tests/unit/navigation/path-tests/file.html @@ -0,0 +1,11 @@ + + + + + + +
        +
        doc rel test two
        +
        + + diff --git a/tests/unit/navigation/path-tests/parent-ref.html b/tests/unit/navigation/path-tests/parent-ref.html new file mode 100644 index 00000000..d4b62421 --- /dev/null +++ b/tests/unit/navigation/path-tests/parent-ref.html @@ -0,0 +1,11 @@ + + + + + + +
        +
        doc rel test five
        +
        + + diff --git a/tests/unit/navigation/path-tests/parent/index.html b/tests/unit/navigation/path-tests/parent/index.html new file mode 100644 index 00000000..3fc4f334 --- /dev/null +++ b/tests/unit/navigation/path-tests/parent/index.html @@ -0,0 +1,11 @@ + + + + + + +
        +
        doc rel test six
        +
        + + diff --git a/tests/unit/navigation/path-tests/sub-dir/file.html b/tests/unit/navigation/path-tests/sub-dir/file.html new file mode 100644 index 00000000..93aad526 --- /dev/null +++ b/tests/unit/navigation/path-tests/sub-dir/file.html @@ -0,0 +1,11 @@ + + + + + + +
        +
        doc rel test three
        +
        + + diff --git a/tests/unit/navigation/path-tests/sub-dir/index.html b/tests/unit/navigation/path-tests/sub-dir/index.html new file mode 100644 index 00000000..8ef666a4 --- /dev/null +++ b/tests/unit/navigation/path-tests/sub-dir/index.html @@ -0,0 +1,11 @@ + + + + + + +
        +
        doc rel test four
        +
        + + diff --git a/tests/unit/navigation/prefetched-dialog.html b/tests/unit/navigation/prefetched-dialog.html new file mode 100644 index 00000000..bea17990 --- /dev/null +++ b/tests/unit/navigation/prefetched-dialog.html @@ -0,0 +1,10 @@ + + + + + Title Tag + + +
        + + diff --git a/tests/unit/navigation/prefetched.html b/tests/unit/navigation/prefetched.html new file mode 100644 index 00000000..de66a407 --- /dev/null +++ b/tests/unit/navigation/prefetched.html @@ -0,0 +1,12 @@ + + + + + Title Tag + + + +
        + + + \ No newline at end of file diff --git a/tests/unit/navigation/push-state-dialog-tests.html b/tests/unit/navigation/push-state-dialog-tests.html new file mode 100644 index 00000000..8d9dd2eb --- /dev/null +++ b/tests/unit/navigation/push-state-dialog-tests.html @@ -0,0 +1,40 @@ + + + + + + jQuery Mobile Navigation Test Suite + + + + + + + + + + + + + + + + + +

        jQuery Mobile Navigation Test Suite

        +

        +

        +
          +
        + + + +
        +
        +

        Dialog

        +
        +
        + + diff --git a/tests/unit/navigation/push-state-disabled-base-tests.html b/tests/unit/navigation/push-state-disabled-base-tests.html new file mode 100644 index 00000000..b2b499e9 --- /dev/null +++ b/tests/unit/navigation/push-state-disabled-base-tests.html @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/tests/unit/navigation/push-state-disabled-tests.html b/tests/unit/navigation/push-state-disabled-tests.html new file mode 100644 index 00000000..27b6eb55 --- /dev/null +++ b/tests/unit/navigation/push-state-disabled-tests.html @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/tests/unit/navigation/title1.html b/tests/unit/navigation/title1.html new file mode 100644 index 00000000..e280211f --- /dev/null +++ b/tests/unit/navigation/title1.html @@ -0,0 +1,12 @@ + + + + + Title Tag + + + +
        + + + \ No newline at end of file diff --git a/tests/unit/navigation/title2.html b/tests/unit/navigation/title2.html new file mode 100644 index 00000000..9545c534 --- /dev/null +++ b/tests/unit/navigation/title2.html @@ -0,0 +1,12 @@ + + + + + + + + +
        + + + \ No newline at end of file diff --git a/tests/unit/navigation/title3.html b/tests/unit/navigation/title3.html new file mode 100644 index 00000000..714df826 --- /dev/null +++ b/tests/unit/navigation/title3.html @@ -0,0 +1,13 @@ + + + + + + + +
        +

        Title Heading

        +
        + + + \ No newline at end of file diff --git a/tests/unit/page-sections/index.html b/tests/unit/page-sections/index.html new file mode 100644 index 00000000..96fe88df --- /dev/null +++ b/tests/unit/page-sections/index.html @@ -0,0 +1,55 @@ + + + + + + jQuery Mobile Page Test Suite + + + + + + + + + + + + + + + + +

        jQuery Mobile Page Test Suite

        +

        +

        +
          +
        + +
        +
        +
        +
        + foo +
        + foo +
        + +
        +
        + foo +
        + + foo +
        + +
        +
        + foo +
        + + foo +
        +
        + + diff --git a/tests/unit/page-sections/page_core.js b/tests/unit/page-sections/page_core.js new file mode 100644 index 00000000..38bb6e88 --- /dev/null +++ b/tests/unit/page-sections/page_core.js @@ -0,0 +1,36 @@ +/* + * mobile page unit tests + */ +(function($){ + var libName = 'jquery.mobile.page.js'; + + module(libName); + + test( "nested header anchors aren't altered", function(){ + ok(!$('.ui-header > div > a').hasClass('ui-btn')); + }); + + test( "nested footer anchors aren't altered", function(){ + ok(!$('.ui-footer > div > a').hasClass('ui-btn')); + }); + + test( "nested bar anchors aren't styled", function(){ + ok(!$('.ui-bar > div > a').hasClass('ui-btn')); + }); + + test( "unnested footer anchors are styled", function(){ + ok($('.ui-footer > a').hasClass('ui-btn')); + }); + + test( "unnested footer anchors are styled", function(){ + ok($('.ui-footer > a').hasClass('ui-btn')); + }); + + test( "unnested bar anchors are styled", function(){ + ok($('.ui-bar > a').hasClass('ui-btn')); + }); + + test( "no auto-generated back button exists on first page", function(){ + ok( !$(".ui-header > :jqmData(rel='back')").length ); + }); +})(jQuery); diff --git a/tests/unit/page/index.html b/tests/unit/page/index.html index ced9b641..e50db0f5 100644 --- a/tests/unit/page/index.html +++ b/tests/unit/page/index.html @@ -1,42 +1,22 @@ - + + jQuery Mobile Page Test Suite - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + + - + + + @@ -46,30 +26,13 @@
        -
        -
        -
        -
        - foo -
        - foo -
        +
        +
        -
        -
        - foo -
        - foo -
        +
        -
        -
        - foo -
        - - foo -
        -
        +
        +
        diff --git a/tests/unit/page/page_core.js b/tests/unit/page/page_core.js index de967326..cc050b55 100644 --- a/tests/unit/page/page_core.js +++ b/tests/unit/page/page_core.js @@ -1,31 +1,106 @@ /* * mobile page unit tests */ -(function( $ ) { - module('jquery.mobile.page.js'); +(function($){ + var libName = 'jquery.mobile.page.sections.js', + themedefault = $.mobile.page.prototype.options.theme, + keepNative = $.mobile.page.prototype.options.keepNative; - test( "nested header anchors aren't altered", function(){ - ok(!$('.ui-header > div > a').hasClass('ui-btn')); + module(libName, { + setup: function() { + $.mobile.page.prototype.options.keepNative = keepNative; + } }); - test( "nested footer anchors aren't altered", function(){ - ok(!$('.ui-footer > div > a').hasClass('ui-btn')); + + var eventStack = [], + etargets = [], + cEvents=[], + cTargets=[]; + + + $( document ).bind( "pagebeforecreate pagecreate", function( e ){ + eventStack.push( e.type ); + etargets.push( e.target ); }); - test( "nested bar anchors aren't styled", function(){ - ok(!$('.ui-bar > div > a').hasClass('ui-btn')); + $("#c").live( "pagebeforecreate", function( e ){ + + cEvents.push( e.type ); + cTargets.push( e.target ); + return false; }); - test( "unnested footer anchors are styled", function(){ - ok($('.ui-footer > a').hasClass('ui-btn')); + test( "pagecreate event fires when page is created", function(){ + ok( eventStack[0] === "pagecreate" || eventStack[1] === "pagecreate" ); }); - test( "unnested footer anchors are styled", function(){ - ok($('.ui-footer > a').hasClass('ui-btn')); + test( "pagebeforecreate event fires when page is created", function(){ + ok( eventStack[0] === "pagebeforecreate" || eventStack[1] === "pagebeforecreate" ); }); - test( "unnested bar anchors are styled", function(){ - ok($('.ui-bar > a').hasClass('ui-btn')); + test( "pagebeforecreate fires before pagecreate", function(){ + ok( eventStack[0] === "pagebeforecreate" ); }); -})(jQuery); \ No newline at end of file + test( "target of pagebeforecreate event was div #a", function(){ + ok( $( etargets[0] ).is("#a") ); + }); + + test( "target of pagecreate event was div #a" , function(){ + ok( $( etargets[0] ).is("#a") ); + }); + + test( "page element has ui-page class" , function(){ + ok( $( "#a" ).hasClass( "ui-page" ) ); + }); + + test( "page element has default body theme when not overidden" , function(){ + ok( $( "#a" ).hasClass( "ui-body-" + themedefault ) ); + }); + + test( "B page has non-default theme matching its data-theme attr" , function(){ + $( "#b" ).page(); + var btheme = $( "#b" ).jqmData( "theme" ); + ok( $( "#b" ).hasClass( "ui-body-" + btheme ) ); + }); + + test( "Binding to pagebeforecreate and returning false prevents pagecreate event from firing" , function(){ + $("#c").page(); + + ok( cEvents[0] === "pagebeforecreate" ); + ok( !cTargets[1] ); + }); + + test( "Binding to pagebeforecreate and returning false prevents classes from being applied to page" , function(){ + ok( !$( "#b" ).hasClass( "ui-body-" + themedefault ) ); + ok( !$( "#b" ).hasClass( "ui-page" ) ); + }); + + test( "keepNativeSelector returns the default where keepNative is not different", function() { + var pageProto = $.mobile.page.prototype; + pageProto.options.keepNative = pageProto.options.keepNativeDefault; + + same(pageProto.keepNativeSelector(), pageProto.options.keepNativeDefault); + }); + + test( "keepNativeSelector returns the default where keepNative is empty, undefined, whitespace", function() { + var pageProto = $.mobile.page.prototype; + + pageProto.options.keepNative = ""; + same(pageProto.keepNativeSelector(), pageProto.options.keepNativeDefault); + + pageProto.options.keepNative = undefined; + same(pageProto.keepNativeSelector(), pageProto.options.keepNativeDefault); + + pageProto.options.keepNative = " "; + same(pageProto.keepNativeSelector(), pageProto.options.keepNativeDefault); + }); + + test( "keepNativeSelector returns a selector joined with the default", function() { + var pageProto = $.mobile.page.prototype; + + pageProto.options.keepNative = "foo, bar"; + same(pageProto.keepNativeSelector(), "foo, bar, " + pageProto.options.keepNativeDefault); + }); +})(jQuery); diff --git a/tests/unit/runner.js b/tests/unit/runner.js new file mode 100644 index 00000000..e8fd2209 --- /dev/null +++ b/tests/unit/runner.js @@ -0,0 +1,89 @@ +$(function() { + var Runner = function( ) { + var self = this; + + $.extend( self, { + frame: window.frames[ "testFrame" ], + + testTimeout: 3 * 60 * 1000, + + $frameElem: $( "#testFrame" ), + + assertionResultPrefix: "assertion result for test:", + + onTimeout: QUnit.start, + + onFrameLoad: function() { + // establish a timeout for a given suite in case of async tests hanging + self.testTimer = setTimeout( self.onTimeout, self.testTimeout ); + + // it might be a redirect with query params for push state + // tests skip this call and expect another + if( !self.frame.QUnit ) { + self.$frameElem.one( "load", self.onFrameLoad ); + return; + } + + // when the QUnit object reports done in the iframe + // run the onFrameDone method + self.frame.QUnit.done = self.onFrameDone; + self.frame.QUnit.testDone = self.onTestDone; + }, + + onTestDone: function( result ) { + QUnit.ok( !(result.failed > 0), result.name ); + self.recordAssertions( result.total - result.failed, result.name ); + }, + + onFrameDone: function( failed, passed, total, runtime ){ + // make sure we don't time out the tests + clearTimeout( self.testTimer ); + + // TODO decipher actual cause of multiple test results firing twice + // clear the done call to prevent early completion of other test cases + self.frame.QUnit.done = $.noop; + self.frame.QUnit.testDone = $.noop; + + // hide the extra assertions made to propogate the count + // to the suite level test + self.hideAssertionResults(); + + // continue on to the next suite + QUnit.start(); + }, + + recordAssertions: function( count, parentTest ) { + for( var i = 0; i < count; i++ ) { + ok( true, self.assertionResultPrefix + parentTest ); + } + }, + + hideAssertionResults: function() { + $( "li:not([id]):contains('" + self.assertionResultPrefix + "')" ).hide(); + }, + + exec: function( data ) { + var template = self.$frameElem.attr( "data-src" ); + + $.each( data.testPages, function(i, dir) { + QUnit.asyncTest( dir, function() { + self.dir = dir; + self.$frameElem.one( "load", self.onFrameLoad ); + self.$frameElem.attr( "src", template.replace("{{testdir}}", dir) ); + }); + }); + + // having defined all suite level tests let QUnit run + QUnit.start(); + } + }); + }; + + // prevent qunit from starting the test suite until all tests are defined + QUnit.begin = function( ) { + this.config.autostart = false; + }; + + // get the test directories + $.get( "ls.php", (new Runner()).exec ); +}); diff --git a/tests/unit/select/cached-dom-cache-true.html b/tests/unit/select/cached-dom-cache-true.html new file mode 100755 index 00000000..b5e719d3 --- /dev/null +++ b/tests/unit/select/cached-dom-cache-true.html @@ -0,0 +1,65 @@ + + + + + + +
        +
        + + +
        +
        + + diff --git a/tests/unit/select/cached-tests.html b/tests/unit/select/cached-tests.html new file mode 100644 index 00000000..3f8f74b9 --- /dev/null +++ b/tests/unit/select/cached-tests.html @@ -0,0 +1,29 @@ + + + + + + jQuery Mobile Select Events Test Suite + + + + + + + + + + + + + + + +

        jQuery Mobile Select Event Test Suite

        +

        +

        +
          +
        +
        + + diff --git a/tests/unit/select/cached.html b/tests/unit/select/cached.html new file mode 100644 index 00000000..0ca8691a --- /dev/null +++ b/tests/unit/select/cached.html @@ -0,0 +1,65 @@ + + + + + + +
        +
        + + +
        +
        + + diff --git a/tests/unit/select/index.html b/tests/unit/select/index.html new file mode 100644 index 00000000..047cce92 --- /dev/null +++ b/tests/unit/select/index.html @@ -0,0 +1,369 @@ + + + + + + jQuery Mobile Select Events Test Suite + + + + + + + + + + + + + + + + + + +

        jQuery Mobile Select Event Test Suite

        +

        +

        +
          +
        + +
        +
        + +
        + +
        + + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + + +
        + +
        + + +
        + +
        + + +
        + +
        + + +
        + +
        + + + +
        + +
        + +
        + + + + + + + + + + + + + + +
        + + +
        + +
        + + + diff --git a/tests/unit/select/select_cached.js b/tests/unit/select/select_cached.js new file mode 100644 index 00000000..2849cca8 --- /dev/null +++ b/tests/unit/select/select_cached.js @@ -0,0 +1,124 @@ +/* + * mobile select unit tests + */ + +(function($){ + var resetHash; + + resetHash = function(timeout){ + $.testHelper.openPage( location.hash.indexOf("#default") >= 0 ? "#" : "#default" ); + }; + + // https://github.com/jquery/jquery-mobile/issues/2181 + asyncTest( "dialog sized select should alter the value of its parent select", function(){ + var selectButton, value; + + $.testHelper.pageSequence([ + resetHash, + + function(){ + $.mobile.changePage( "cached.html" ); + }, + + function(){ + selectButton = $( "#cached-page-select" ).siblings( 'a' ); + selectButton.click(); + }, + + function(){ + ok( $.mobile.activePage.hasClass('ui-dialog'), "the dialog came up" ); + var option = $.mobile.activePage.find( "li a" ).not(":contains('" + selectButton.text() + "')").last(); + value = option.text(); + option.click(); + }, + + function(){ + same( value, selectButton.text(), "the selected value is propogated back to the button text" ); + start(); + } + ]); + }); + + // https://github.com/jquery/jquery-mobile/issues/2181 + asyncTest( "dialog sized select should prevent the removal of its parent page from the dom", function(){ + var selectButton, parentPageId; + + expect( 2 ); + + $.testHelper.pageSequence([ + resetHash, + + function(){ + $.mobile.changePage( "cached.html" ); + }, + + function(){ + selectButton = $.mobile.activePage.find( "#cached-page-select" ).siblings( 'a' ); + parentPageId = $.mobile.activePage.attr( 'id' ); + same( $("#" + parentPageId).length, 1, "establish the parent page exists" ); + selectButton.click(); + }, + + function(){ + same( $( "#" + parentPageId).length, 1, "make sure parent page is still there after opening the dialog" ); + $.mobile.activePage.find( "li a" ).last().click(); + }, + + start + ]); + }); + + asyncTest( "dialog sized select shouldn't rebind its parent page remove handler when closing, if the parent page domCache option is true", function(){ + expect( 3 ); + + $.testHelper.pageSequence([ + resetHash, + + function(){ + $.mobile.changePage( "cached-dom-cache-true.html" ); + }, + + function(){ + $.mobile.activePage.find( "#domcache-page-select" ).siblings( 'a' ).click(); + }, + + function(){ + ok( $.mobile.activePage.hasClass('ui-dialog'), "the dialog came up" ); + $.mobile.activePage.find( "li a" ).last().click(); + }, + + function(){ + ok( $.mobile.activePage.is( "#dialog-select-parent-domcache-test" ), "the dialog closed" ); + $.mobile.changePage( $( "#default" ) ); + }, + + function(){ + same( $("#dialog-select-parent-domcache-test").length, 1, "make sure the select parent page is still cached in the dom after changing page" ); + start(); + } + ]); + }); + + asyncTest( "menupage is removed when the parent page is removed", function(){ + var dialogCount = $(":jqmData(role='dialog')").length; + + $.testHelper.pageSequence([ + resetHash, + + function(){ + + $.mobile.changePage( "uncached-dom-cached-false.html" ); + }, + + function(){ + same( $(":jqmData(role='dialog')").length, dialogCount + 1 ); + window.history.back(); + }, + + function() { + same( $(":jqmData(role='dialog')").length, dialogCount ); + start(); + } + ]); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/select/select_core.js b/tests/unit/select/select_core.js new file mode 100644 index 00000000..3271000f --- /dev/null +++ b/tests/unit/select/select_core.js @@ -0,0 +1,384 @@ +/* + * mobile select unit tests + */ + +(function($){ + var libName = "jquery.mobile.forms.select.js", + originalDefaultDialogTrans = $.mobile.defaultDialogTransition, + originalDefTransitionHandler = $.mobile.defaultTransitionHandler, + originalGetEncodedText = $.fn.getEncodedText, + resetHash, closeDialog; + + resetHash = function(timeout){ + $.testHelper.openPage( location.hash.indexOf("#default") >= 0 ? "#" : "#default" ); + }; + + closeDialog = function(timeout){ + $.mobile.activePage.find("li a").first().click(); + }; + + module(libName, { + teardown: function(){ + $.mobile.defaultDialogTransition = originalDefaultDialogTrans; + $.mobile.defaultTransitionHandler = originalDefTransitionHandler; + + $.fn.getEncodedText = originalGetEncodedText; + window.encodedValueIsDefined = undefined; + } + }); + + asyncTest( "firing a click at least 400 ms later on the select screen overlay does close it", function(){ + $.testHelper.sequence([ + function(){ + // bring up the smaller choice menu + ok($("#select-choice-few-container a").length > 0, "there is in fact a button in the page"); + $("#select-choice-few-container a").trigger("click"); + }, + + function(){ + //select the first menu item + $("#select-choice-few-menu a:first").click(); + }, + + function(){ + same($("#select-choice-few-menu").parent(".ui-selectmenu-hidden").length, 1); + start(); + } + ], 1000); + }); + + asyncTest( "a large select menu should use the default dialog transition", function(){ + var select; + + $.testHelper.pageSequence([ + resetHash, + + function(timeout){ + select = $("#select-choice-many-container-1 a"); + + //set to something else + $.mobile.defaultTransitionHandler = $.testHelper.decorate({ + fn: $.mobile.defaultTransitionHandler, + + before: function(name){ + same(name, $.mobile.defaultDialogTransition); + } + }); + + // bring up the dialog + select.trigger("click"); + }, + + closeDialog, + + start + ]); + }); + + asyncTest( "custom select menu always renders screen from the left", function(){ + var select; + + expect( 1 ); + + $.testHelper.sequence([ + resetHash, + + function(){ + select = $("ul#select-offscreen-menu"); + $("#select-offscreen-container a").trigger("click"); + }, + + function(){ + ok(select.offset().left >= 30, "offset from the left is greater than or equal to 30px" ); + start(); + } + ], 1000); + }); + + asyncTest( "selecting an item from a dialog sized custom select menu leaves no dialog hash key", function(){ + var dialogHashKey = "ui-state=dialog"; + + $.testHelper.pageSequence([ + resetHash, + + function(timeout){ + $("#select-choice-many-container-hash-check a").click(); + }, + + function(){ + ok(location.hash.indexOf(dialogHashKey) > -1); + closeDialog(); + }, + + function(){ + same(location.hash.indexOf(dialogHashKey), -1); + start(); + } + ]); + }); + + asyncTest( "dialog sized select menu opened many times remains a dialog", function(){ + var dialogHashKey = "ui-state=dialog", + + openDialogSequence = [ + resetHash, + + function(){ + $("#select-choice-many-container-many-clicks a").click(); + }, + + function(){ + ok(location.hash.indexOf(dialogHashKey) > -1, "hash should have the dialog hash key"); + closeDialog(); + } + ], + + sequence = openDialogSequence.concat(openDialogSequence).concat([start]); + + $.testHelper.sequence(sequence, 1000); + }); + + test( "make sure the label for the select gets the ui-select class", function(){ + ok( $( "#native-select-choice-few-container label" ).hasClass( "ui-select" ), "created label has ui-select class" ); + }); + + module("Non native menus", { + setup: function() { + $.mobile.selectmenu.prototype.options.nativeMenu = false; + }, + teardown: function() { + $.mobile.selectmenu.prototype.options.nativeMenu = true; + } + }); + + asyncTest( "a large select option should not overflow", function(){ + // https://github.com/jquery/jquery-mobile/issues/1338 + var menu, select; + + $.testHelper.sequence([ + resetHash, + + function(){ + select = $("#select-long-option-label"); + // bring up the dialog + select.trigger("click"); + }, + + function() { + menu = $(".ui-selectmenu-list"); + + equal(menu.width(), menu.find("li:nth-child(2) .ui-btn-text").width(), "ui-btn-text element should not overflow"); + start(); + } + ], 500); + }); + + asyncTest( "using custom refocuses the button after close", function() { + var select, button, triggered = false; + + expect( 1 ); + + $.testHelper.sequence([ + resetHash, + + function() { + select = $("#select-choice-focus-test"); + button = select.find( "a" ); + button.trigger( "click" ); + }, + + function() { + // NOTE this is called twice per triggered click + button.focus(function() { + triggered = true; + }); + + $(".ui-selectmenu-screen:not(.ui-screen-hidden)").trigger("click"); + }, + + function(){ + ok(triggered, "focus is triggered"); + start(); + } + ], 5000); + }); + + asyncTest( "selected items are highlighted", function(){ + $.testHelper.sequence([ + resetHash, + + function(){ + // bring up the smaller choice menu + ok($("#select-choice-few-container a").length > 0, "there is in fact a button in the page"); + $("#select-choice-few-container a").trigger("click"); + }, + + function(){ + var firstMenuChoice = $("#select-choice-few-menu li:first"); + ok( firstMenuChoice.hasClass( $.mobile.activeBtnClass ), + "default menu choice has the active button class" ); + + $("#select-choice-few-menu a:last").click(); + }, + + function(){ + // bring up the menu again + $("#select-choice-few-container a").trigger("click"); + }, + + function(){ + var lastMenuChoice = $("#select-choice-few-menu li:last"); + ok( lastMenuChoice.hasClass( $.mobile.activeBtnClass ), + "previously slected item has the active button class" ); + + // close the dialog + lastMenuChoice.find( "a" ).click(); + }, + + start + ], 1000); + }); + + test( "enabling and disabling", function(){ + var select = $( "select" ).first(), button; + + button = select.siblings( "a" ).first(); + + select.selectmenu( 'disable' ); + same( select.attr('disabled'), "disabled", "select is disabled" ); + ok( button.hasClass("ui-disabled"), "disabled class added" ); + same( button.attr('aria-disabled'), "true", "select is disabled" ); + same( select.selectmenu( 'option', 'disabled' ), true, "disbaled option set" ); + + select.selectmenu( 'enable' ); + same( select.attr('disabled'), undefined, "select is disabled" ); + ok( !button.hasClass("ui-disabled"), "disabled class added" ); + same( button.attr('aria-disabled'), "false", "select is disabled" ); + same( select.selectmenu( 'option', 'disabled' ), false, "disbaled option set" ); + }); + + test( "adding options and refreshing a custom select defaults the text", function() { + var select = $( "#custom-refresh" ), + button = select.siblings( "a" ).find( ".ui-btn-inner" ), + text = "foo"; + + same(button.text(), "default"); + select.find( "option" ).remove(); //remove the loading message + select.append(''); + select.selectmenu( 'refresh' ); + same(button.text(), text); + }); + + asyncTest( "adding options and refreshing a custom select changes the options list", function(){ + var select = $( "#custom-refresh-opts-list" ), + button = select.siblings( "a" ).find( ".ui-btn-inner" ), + text = "foo"; + + $.testHelper.sequence([ + // bring up the dialog + function() { + button.click(); + }, + + function() { + same( $( ".ui-selectmenu.in ul" ).text(), "default" ); + $( ".ui-selectmenu-screen" ).click(); + }, + + function() { + select.find( "option" ).remove(); //remove the loading message + select.append(''); + select.selectmenu( 'refresh' ); + }, + + function() { + button.click(); + }, + + function() { + same( $( ".ui-selectmenu.in ul" ).text(), text ); + $( ".ui-selectmenu-screen" ).click(); + }, + + start + ], 500); + }); + + test( "theme defined on select is used", function(){ + var select = $("select#non-parent-themed"); + + ok( select.siblings( "a" ).hasClass("ui-btn-up-" + select.jqmData('theme'))); + }); + + test( "select without theme defined inherits theme from parent", function() { + var select = $("select#parent-themed"); + + ok( select + .siblings( "a" ) + .hasClass("ui-btn-up-" + select.parents(":jqmData(role='page')").jqmData('theme'))); + }); + + // issue #2547 + test( "custom select list item links have encoded option text values", function() { + $( "#encoded-option" ).data( 'selectmenu' )._buildList(); + same(window.encodedValueIsDefined, undefined); + }); + + // issue #2547 + test( "custom select list item links have unencoded option text values when using vanilla $.fn.text", function() { + // undo our changes, undone in teardown + $.fn.getEncodedText = $.fn.text; + + $( "#encoded-option" ).data( 'selectmenu' )._buildList(); + + same(window.encodedValueIsDefined, true); + }); + + $.mobile.page.prototype.options.keepNative = "select.should-be-native"; + + // not testing the positive case here since's it's obviously tested elsewhere + test( "select elements in the keepNative set shouldn't be enhanced", function() { + ok( !$("#keep-native").parent().is("div.ui-btn") ); + }); + + asyncTest( "dialog size select title should match the label", function() { + var $select = $( "#select-choice-many-1" ), + $label = $select.parent().siblings( "label" ), + $button = $select.siblings( "a" ); + + $.testHelper.pageSequence([ + function() { + $button.click(); + }, + + function() { + same($.mobile.activePage.find( ".ui-title" ).text(), $label.text()); + window.history.back(); + }, + + start + ]); + }); + + asyncTest( "dialog size select title should match the label when changed after the dialog markup is added to the DOM", function() { + var $select = $( "#select-choice-many-1" ), + $label = $select.parent().siblings( "label" ), + $button = $select.siblings( "a" ); + + $label.text( "foo" ); + + $.testHelper.pageSequence([ + function() { + $label.text( "foo" ); + $button.click(); + }, + + function() { + same($.mobile.activePage.find( ".ui-title" ).text(), $label.text()); + window.history.back(); + }, + + start + ]); + }); +})(jQuery); diff --git a/tests/unit/select/select_events.js b/tests/unit/select/select_events.js new file mode 100644 index 00000000..e65bf44f --- /dev/null +++ b/tests/unit/select/select_events.js @@ -0,0 +1,34 @@ +/* + * mobile select unit tests + */ + +(function($){ + var libName = "jquery.mobile.forms.select.js"; + + $(document).bind('mobileinit', function(){ + $.mobile.selectmenu.prototype.options.nativeMenu = false; + }); + + module(libName,{ + setup: function(){ + $.testHelper.openPage( location.hash.indexOf("#default") >= 0 ? "#" : "#default" ); + } + }); + + test( "selects marked with data-native-menu=true should use a div as their button", function(){ + same($("#select-choice-native-container div.ui-btn").length, 1); + }); + + test( "selects marked with data-native-menu=true should not have a custom menu", function(){ + same($("#select-choice-native-container ul").length, 0); + }); + + test( "selects marked with data-native-menu=true should sit inside the button", function(){ + same($("#select-choice-native-container div.ui-btn select").length, 1); + }); + + test( "select controls will create when inside a container that receives a 'create' event", function(){ + ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-select").length, "did not have enhancements applied" ); + ok( $("#enhancetest").trigger("create").find(".ui-select").length, "enhancements applied" ); + }); +})(jQuery); diff --git a/tests/unit/select/select_native.js b/tests/unit/select/select_native.js new file mode 100644 index 00000000..271dbf91 --- /dev/null +++ b/tests/unit/select/select_native.js @@ -0,0 +1,68 @@ +/* + * mobile select unit tests + */ + +(function($){ + module("jquery.mobile.forms.select native"); + + test( "native menu selections alter the button text", function(){ + var select = $( "#native-select-choice-few" ), setAndCheck; + + setAndCheck = function(key){ + var text; + + select.val( key ).selectmenu( 'refresh' ); + text = select.find( "option[value='" + key + "']" ).text(); + same( select.parent().find(".ui-btn-text").text(), text ); + }; + + setAndCheck( 'rush' ); + setAndCheck( 'standard' ); + }); + + asyncTest( "selecting a value removes the related buttons down state", function(){ + var select = $( "#native-select-choice-few" ); + + $.testHelper.sequence([ + function() { + // click the native menu parent button + select.parent().trigger( 'vmousedown' ); + }, + + function() { + ok( select.parent().hasClass("ui-btn-down-c"), "button down class added" ); + }, + + function() { + // trigger a change on the select + select.trigger( "change" ); + }, + + function() { + ok( !select.parent().hasClass("ui-btn-down-c"), "button down class removed" ); + start(); + } + ], 300); + }); + + // issue https://github.com/jquery/jquery-mobile/issues/2410 + test( "adding options and refreshing a native select defaults the text", function() { + var select = $( "#native-refresh" ), + button = select.siblings( '.ui-btn-inner' ), + text = "foo"; + + same(button.text(), "default"); + select.find( "option" ).remove(); //remove the loading message + select.append(''); + select.selectmenu('refresh'); + same(button.text(), text); + }); + + // issue 2424 + test( "native selects should provide open and close as a no-op", function() { + // exception will prevent test success if undef + $( "#native-refresh" ).selectmenu( 'open' ); + $( "#native-refresh" ).selectmenu( 'close' ); + ok( true ); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/select/suite.html b/tests/unit/select/suite.html new file mode 100644 index 00000000..9545af3c --- /dev/null +++ b/tests/unit/select/suite.html @@ -0,0 +1,297 @@ + + + + + + +
        +
        + +
        + +
        + +
        + +
        + +
        + +
        + + +
        + +
        + + +
        + +
        + + +
        + +
        + + +
        + +
        + + + +
        + +
        + +
        +
        + + + +
        + +
        + + diff --git a/tests/unit/select/uncached-dom-cached-false.html b/tests/unit/select/uncached-dom-cached-false.html new file mode 100644 index 00000000..2977c2ab --- /dev/null +++ b/tests/unit/select/uncached-dom-cached-false.html @@ -0,0 +1,65 @@ + + + + + + +
        +
        + + +
        +
        + + diff --git a/tests/unit/slider/index.html b/tests/unit/slider/index.html new file mode 100644 index 00000000..528d9efe --- /dev/null +++ b/tests/unit/slider/index.html @@ -0,0 +1,73 @@ + + + + + + jQuery Mobile Slider Test Suite + + + + + + + + + + + + + + + + + + +

        jQuery Mobile Slider Test Suite

        +

        +

        +
          +
        + +
        +
        + +
        + +
        + +
        + +
        + +
        + +
        + + +
        + +
        + +
        + +
        + +
        + +
        + +
        + +
        + + +
        +
        + +
        + +
        + diff --git a/tests/unit/slider/slider_core.js b/tests/unit/slider/slider_core.js new file mode 100644 index 00000000..087d95df --- /dev/null +++ b/tests/unit/slider/slider_core.js @@ -0,0 +1,11 @@ +/* + * mobile slider unit tests + */ +(function($){ + $.mobile.page.prototype.options.keepNative = "input.should-be-native"; + + // not testing the positive case here since's it's obviously tested elsewhere + test( "slider elements in the keepNative set shouldn't be enhanced", function() { + same( $("input.should-be-native").siblings("div.ui-slider").length, 0 ); + }); +})( jQuery ); \ No newline at end of file diff --git a/tests/unit/slider/slider_events.js b/tests/unit/slider/slider_events.js new file mode 100644 index 00000000..d68dfa76 --- /dev/null +++ b/tests/unit/slider/slider_events.js @@ -0,0 +1,202 @@ +/* + * mobile slider unit tests + */ + +(function($){ + var onChangeCnt = 0; + window.onChangeCounter = function() { + onChangeCnt++; + } + module('jquery.mobile.slider.js'); + + var keypressTest = function(opts){ + var slider = $(opts.selector), + val = window.parseFloat(slider.val()), + handle = slider.siblings('.ui-slider').find('.ui-slider-handle'); + + expect( opts.keyCodes.length ); + + $.each(opts.keyCodes, function(i, elem){ + + // stub the keycode value and trigger the keypress + $.Event.prototype.keyCode = $.mobile.keyCode[elem]; + handle.trigger('keydown'); + + val += opts.increment; + same(val, window.parseFloat(slider.val(), 10), "new value is " + opts.increment + " different"); + }); + }; + + test( "slider should move right with up, right, and page up keypress", function(){ + keypressTest({ + selector: '#range-slider-up', + keyCodes: ['UP', 'RIGHT', 'PAGE_UP'], + increment: 1 + }); + }); + + test( "slider should move left with down, left, and page down keypress", function(){ + keypressTest({ + selector: '#range-slider-down', + keyCodes: ['DOWN', 'LEFT', 'PAGE_DOWN'], + increment: -1 + }); + }); + + test( "slider should move to range minimum on end keypress", function(){ + var selector = "#range-slider-end", + initialVal = window.parseFloat($(selector).val(), 10), + max = window.parseFloat($(selector).attr('max'), 10); + + keypressTest({ + selector: selector, + keyCodes: ['END'], + increment: max - initialVal + }); + }); + + test( "slider should move to range minimum on end keypress", function(){ + var selector = "#range-slider-home", + initialVal = window.parseFloat($(selector).val(), 10); + + keypressTest({ + selector: selector, + keyCodes: ['HOME'], + increment: 0 - initialVal + }); + }); + + test( "slider should move positive by steps on keypress", function(){ + keypressTest({ + selector: "#stepped", + keyCodes: ['RIGHT'], + increment: 10 + }); + }); + + test( "slider should move negative by steps on keypress", function(){ + keypressTest({ + selector: "#stepped", + keyCodes: ['LEFT'], + increment: -10 + }); + }); + + test( "slider should validate input value on blur", function(){ + var slider = $("#range-slider-up"); + slider.focus(); + slider.val(200); + same(slider.val(), "200"); + slider.blur(); + same(slider.val(), slider.attr('max')); + }); + + test( "slider should not validate input on keyup", function(){ + var slider = $("#range-slider-up"); + slider.focus(); + slider.val(200); + same(slider.val(), "200"); + slider.keyup(); + same(slider.val(), "200"); + }); + + test( "input type should degrade to number when slider is created", function(){ + same($("#range-slider-up").attr( "type" ), "number"); + }); + + // generic switch test function + var sliderSwitchTest = function(opts){ + var slider = $("#slider-switch"), + handle = slider.siblings('.ui-slider').find('a'), + switchValues = { + 'off' : 0, + 'on' : 1 + }; + + // One for the select and one for the aria-valuenow + expect( opts.keyCodes.length * 2 ); + + $.each(opts.keyCodes, function(i, elem){ + // reset the values + slider[0].selectedIndex = switchValues[opts.start]; + handle.attr({'aria-valuenow' : opts.start }); + + // stub the keycode and trigger the event + $.Event.prototype.keyCode = $.mobile.keyCode[elem]; + handle.trigger('keydown'); + + same(handle.attr('aria-valuenow'), opts.finish, "handle value is " + opts.finish); + same(slider[0].selectedIndex, switchValues[opts.finish], "select input has correct index"); + }); + }; + + test( "switch should select on with up, right, page up and end", function(){ + sliderSwitchTest({ + start: 'off', + finish: 'on', + keyCodes: ['UP', 'RIGHT', 'PAGE_UP', 'END'] + }); + }); + + test( "switch should select off with down, left, page down and home", function(){ + sliderSwitchTest({ + start: 'on', + finish: 'off', + keyCodes: ['DOWN', 'LEFT', 'PAGE_DOWN', 'HOME'] + }); + }); + + test( "onchange should not be called on create", function(){ + equals(onChangeCnt, 0, "onChange should not have been called"); + }); + + test( "onchange should be called onchange", function(){ + onChangeCnt = 0; + $( "#onchange" ).slider( "refresh", 50 ); + equals(onChangeCnt, 1, "onChange should have been called once"); + }); + + test( "slider controls will create when inside a container that receives a 'create' event", function(){ + ok( !$("#enhancetest").appendTo(".ui-page-active").find(".ui-slider").length, "did not have enhancements applied" ); + ok( $("#enhancetest").trigger("create").find(".ui-slider").length, "enhancements applied" ); + }); + + test( "toggle switch should fire one change event when clicked", function(){ + var control = $( "#slider-switch" ), + widget = control.data( "slider" ), + slider = widget.slider, + handle = widget.handle, + changeCount = 0, + changeFunc = function( e ) { + ok( control[0].selectedIndex !== currentValue, "change event should only be triggered if the value changes"); + ++changeCount; + }, + event = null, + offset = handle.offset(), + currentValue = control[0].selectedIndex; + + function createEvent( name, target, x, y ) + { + var event = $.Event( name ); + event.target = target; + event.pageX = x; + event.pageY = y; + return event; + } + + control.bind( "change", changeFunc ); + + // The toggle switch actually updates on mousedown and mouseup events, so we go through + // the motions of generating all the events that happen during a click to make sure that + // during all of those events, the value only changes once. + + slider.trigger( createEvent( "mousedown", handle[ 0 ], offset.left + 10, offset.top + 10 ) ); + slider.trigger( createEvent( "mouseup", handle[ 0 ], offset.left + 10, offset.top + 10 ) ); + slider.trigger( createEvent( "click", handle[ 0 ], offset.left + 10, offset.top + 10 ) ); + + control.unbind( "change", changeFunc ); + + ok( control[0].selectedIndex !== currentValue, "value did change"); + same( changeCount, 1, "change event should be fired once during a click" ); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/support/index.html b/tests/unit/support/index.html index 157139d4..61e08793 100644 --- a/tests/unit/support/index.html +++ b/tests/unit/support/index.html @@ -1,20 +1,23 @@ - + + jQuery Mobile Support Test Suite - - - - - - + + + + + + - - + + - + + + @@ -24,8 +27,7 @@
        -
        -
        +
        diff --git a/tests/unit/support/support_core.js b/tests/unit/support/support_core.js index 54d0f7df..f7a390e1 100644 --- a/tests/unit/support/support_core.js +++ b/tests/unit/support/support_core.js @@ -2,76 +2,94 @@ * mobile support unit tests */ -(function( $ ) { - $.testHelper.excludeFileProtocol(function(){ - var prependToFn = $.fn.prependTo, - libName = "jquery.mobile.support.js"; +$.testHelper.excludeFileProtocol(function(){ + var prependToFn = $.fn.prependTo, + libName = "jquery.mobile.support.js"; - module(libName, { - teardown: function(){ - //NOTE undo any mocking - $.fn.prependTo = prependToFn; - } - }); - - // NOTE following two tests have debatable value as they only - // prevent property name changes and improper attribute checks - test( "detects functionality from basic affirmative properties and attributes", function(){ - // TODO expose properties for less brittle tests - $.extend(window, { - WebKitTransitionEvent: true, - orientation: true - }); - - document.ontouchend = true; - - history.pushState = function(){}; - $.mobile.media = function(){ return true; }; - - $.testHelper.reloadLib(libName); - - ok($.support.orientation); - ok($.support.touch); - ok($.support.cssTransitions); - ok($.support.pushState); - ok($.support.mediaquery); - }); - - test( "detects functionality from basic negative properties and attributes (where possible)", function(){ - delete window["orientation"]; - delete document["ontouchend"]; - - $.testHelper.reloadLib(libName); - - ok(!$.support.orientation); - ok(!$.support.touch); - }); - - // NOTE mocks prependTo to simulate base href updates or lack thereof - var mockBaseCheck = function( url ){ - var prependToFn = $.fn.prependTo; - - $.fn.prependTo = function( selector ){ - var result = prependToFn.call(this, selector); - if(this[0].href && this[0].href.indexOf("testurl") != -1) - result = [{href: url}]; - return result; - }; - }; - - test( "detects dynamic base tag when new base element added and base href updates", function(){ - mockBaseCheck(location.protocol + '//' + location.host + location.pathname + "ui-dir/"); - $.testHelper.reloadLib(libName); - ok($.support.dynamicBaseTag); - }); - - test( "detects no dynamic base tag when new base element added and base href unchanged", function(){ - mockBaseCheck('testurl'); - $.testHelper.reloadLib(libName); - ok(!$.support.dynamicBaseTag); - }); - - //TODO propExists testing, refactor propExists into mockable method - //TODO scrollTop testing, refactor scrollTop logic into mockable method + module(libName, { + teardown: function(){ + //NOTE undo any mocking + $.fn.prependTo = prependToFn; + } }); -})(jQuery); \ No newline at end of file + + // NOTE following two tests have debatable value as they only + // prevent property name changes and improper attribute checks + test( "detects functionality from basic affirmative properties and attributes", function(){ + // TODO expose properties for less brittle tests + $.extend(window, { + WebKitTransitionEvent: true, + orientation: true, + onorientationchange: true + }); + + document.ontouchend = true; + + window.history.pushState = function(){}; + window.history.replaceState = function(){}; + + $.mobile.media = function(){ return true; }; + + $.testHelper.reloadLib(libName); + + ok($.support.orientation); + ok($.support.touch); + ok($.support.cssTransitions); + ok($.support.pushState); + ok($.support.mediaquery); + }); + + test( "detects functionality from basic negative properties and attributes (where possible)", function(){ + delete window["orientation"]; + delete document["ontouchend"]; + + $.testHelper.reloadLib(libName); + + ok(!$.support.orientation); + ok(!$.support.touch); + }); + + // NOTE mocks prependTo to simulate base href updates or lack thereof + var mockBaseCheck = function( url ){ + var prependToFn = $.fn.prependTo; + + $.fn.prependTo = function( selector ){ + var result = prependToFn.call(this, selector); + if(this[0].href && this[0].href.indexOf("testurl") != -1) + result = [{href: url}]; + return result; + }; + }; + + test( "detects dynamic base tag when new base element added and base href updates", function(){ + mockBaseCheck(location.protocol + '//' + location.host + location.pathname + "ui-dir/"); + $.testHelper.reloadLib(libName); + ok($.support.dynamicBaseTag); + }); + + test( "detects no dynamic base tag when new base element added and base href unchanged", function(){ + mockBaseCheck('testurl'); + $.testHelper.reloadLib(libName); + ok(!$.support.dynamicBaseTag); + }); + + test( "jQM's IE browser check properly detects IE versions", function(){ + $.testHelper.reloadLib(libName); + + //here we're just comparing our version to what the conditional compilation finds + var ie = !!$.browser.msie, //get a boolean + version = parseInt( $.browser.version, 10), + jqmdetectedver = $.mobile.browser.ie; + + if( ie ){ + same(version, jqmdetectedver, "It's IE and the version is correct"); + } + else{ + same(ie, jqmdetectedver, "It's not IE"); + } + }); + + + //TODO propExists testing, refactor propExists into mockable method + //TODO scrollTop testing, refactor scrollTop logic into mockable method +}); diff --git a/tests/unit/swarminject.js b/tests/unit/swarminject.js new file mode 100755 index 00000000..db69326a --- /dev/null +++ b/tests/unit/swarminject.js @@ -0,0 +1,9 @@ +// load testswarm agent +(function() { + var url = window.location.search; + url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + 9 ) ); + if ( !url || url.indexOf("http") !== 0 ) { + return; + } + document.write(""); +})(); diff --git a/tests/unit/textinput/index.html b/tests/unit/textinput/index.html new file mode 100644 index 00000000..849b38c5 --- /dev/null +++ b/tests/unit/textinput/index.html @@ -0,0 +1,51 @@ + + + + + + jQuery Mobile Textinput Test Suite + + + + + + + + + + + + + + + +

        jQuery Mobile Textinput Test Suite

        +

        +

        +
          +
        + +
        + + + + + + + + +
        + + diff --git a/tests/unit/textinput/textinput_core.js b/tests/unit/textinput/textinput_core.js new file mode 100644 index 00000000..106e3975 --- /dev/null +++ b/tests/unit/textinput/textinput_core.js @@ -0,0 +1,42 @@ +/* + * mobile textinput unit tests + */ +(function($){ + module( "jquery.mobile.forms.textinput.js" ); + + test( "inputs without type specified are enhanced", function(){ + ok( $( "#typeless-input" ).hasClass( "ui-input-text" ) ); + }); + + $.mobile.page.prototype.options.keepNative = "textarea.should-be-native"; + + // not testing the positive case here since's it's obviously tested elsewhere + test( "textarea in the keepNative set shouldn't be enhanced", function() { + ok( !$("textarea.should-be-native").is("ui-input-text") ); + }); + + asyncTest( "textarea should autogrow on document ready", function() { + var test = $( "#init-autogrow" ); + + setTimeout(function() { + ok( $( "#reference-autogrow" )[0].clientHeight < test[0].clientHeight, "the height is greater than the reference text area with no content" ); + ok( test[0].clientHeight > 100, "autogrow text area's height is greater than any style padding"); + start(); + }, 400); + }); + + asyncTest( "textarea should autogrow when text is added via the keyboard", function() { + var test = $( "#keyup-autogrow" ), + originalHeight = test[0].clientHeight; + + test.keyup(function() { + setTimeout(function() { + ok( test[0].clientHeight > originalHeight, "the height is greater than original with no content" ); + ok( test[0].clientHeight > 100, "autogrow text area's height is greater any style/padding"); + start(); + }, 400); + }); + + test.val("foo\n\n\n\n\n\n\n\n\n\n\n\n\n\n").trigger("keyup"); + }); +})(jQuery); \ No newline at end of file diff --git a/tests/unit/widget/index.html b/tests/unit/widget/index.html index 64edaded..bd4a2459 100644 --- a/tests/unit/widget/index.html +++ b/tests/unit/widget/index.html @@ -1,17 +1,20 @@ - + + jQuery Mobile Widget Test Suite - - - + + + + - - + - + + + @@ -21,7 +24,7 @@
        -
        +
        @@ -31,5 +34,15 @@
        +
        + +
        +
        +
        +
        +
        + + + diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index a7cc8aa9..89d4c410 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -1,8 +1,7 @@ /* * mobile widget unit tests */ - -(function( $ ) { +(function($){ module('jquery.mobile.widget.js'); test( "getting data from creation options", function(){ @@ -15,7 +14,7 @@ }); test( "getting no data when the options are empty", function(){ - var expected = {}; + var expected = {}; $.mobile.widget.prototype.options = {}; $.mobile.widget.prototype.element = $("
        "); @@ -31,4 +30,23 @@ same($.mobile.widget.prototype._getCreateOptions(), expected); }); + + test( "elements embedded in sub page elements are excluded on create when they match the keep native selector", function() { + // uses default keep native of data-role=none + $("#enhance-prevented") + .append('') + .trigger("create"); + + ok( !$("#unenhanced").hasClass( "ui-input-text" ), "doesn't have the ui input text class (unenhanced)"); + }); + + test( "elements embedded in sub page elements are included on create when they don't match the keep native selector", function() { + + // uses default keep native of data-role=none + $("#enhance-allowed") + .append('') + .trigger("create"); + + ok( $("#enhanced").hasClass( "ui-input-text" ), "has the ui input text class (unenhanced)"); + }); })(jQuery); \ No newline at end of file diff --git a/tests/unit/widget/widget_init.js b/tests/unit/widget/widget_init.js new file mode 100644 index 00000000..515e54b0 --- /dev/null +++ b/tests/unit/widget/widget_init.js @@ -0,0 +1,20 @@ +/* + * mobile widget unit tests + */ +(function($){ + var widgetInitialized = false; + + module( 'jquery.mobile.widget.js' ); + + $( "#foo" ).live( 'pageinit', function(){ + // ordering sensitive here, the value has to be set after the call + // so that if the widget factory says that its not yet initialized, + // which is an exception, the value won't be set + $( "#foo-slider" ).slider( 'refresh' ); + widgetInitialized = true; + }); + + test( "page is enhanced before init is fired", function() { + ok( widgetInitialized ); + }); +})( jQuery ); \ No newline at end of file diff --git a/themes/default/images/form-check-off.png b/themes/default/images/form-check-off.png deleted file mode 100644 index 54e2fe0f..00000000 Binary files a/themes/default/images/form-check-off.png and /dev/null differ diff --git a/themes/default/images/form-check-on.png b/themes/default/images/form-check-on.png deleted file mode 100644 index e6daaaf8..00000000 Binary files a/themes/default/images/form-check-on.png and /dev/null differ diff --git a/themes/default/images/form-radio-off.png b/themes/default/images/form-radio-off.png deleted file mode 100644 index 32bd4339..00000000 Binary files a/themes/default/images/form-radio-off.png and /dev/null differ diff --git a/themes/default/images/form-radio-on.png b/themes/default/images/form-radio-on.png deleted file mode 100644 index ddc40497..00000000 Binary files a/themes/default/images/form-radio-on.png and /dev/null differ diff --git a/themes/default/images/icons-18-black.png b/themes/default/images/icons-18-black.png deleted file mode 100644 index 38f47267..00000000 Binary files a/themes/default/images/icons-18-black.png and /dev/null differ diff --git a/themes/default/images/icons-18-white.png b/themes/default/images/icons-18-white.png deleted file mode 100644 index ceb28345..00000000 Binary files a/themes/default/images/icons-18-white.png and /dev/null differ diff --git a/themes/default/images/icons-36-black.png b/themes/default/images/icons-36-black.png deleted file mode 100644 index b079c51f..00000000 Binary files a/themes/default/images/icons-36-black.png and /dev/null differ diff --git a/themes/default/images/icons-36-white.png b/themes/default/images/icons-36-white.png deleted file mode 100644 index 038cae40..00000000 Binary files a/themes/default/images/icons-36-white.png and /dev/null differ diff --git a/themes/default/index.php b/themes/default/index.php deleted file mode 100644 index ca6789e5..00000000 --- a/themes/default/index.php +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/themes/default/jquery.mobile.core.css b/themes/default/jquery.mobile.core.css deleted file mode 100644 index 14c4bac5..00000000 --- a/themes/default/jquery.mobile.core.css +++ /dev/null @@ -1,57 +0,0 @@ -/* -* jQuery Mobile Framework -* Copyright (c) jQuery Project -* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. -* Note: Code is in draft form and is subject to change -*/ - -/* some unsets - more probably needed */ -.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } -.ui-mobile a img, .ui-mobile fieldset { border: 0; } - -/* responsive page widths */ -.ui-mobile-viewport { margin: 0; overflow-x: hidden; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - -/*orientations from js are available */ -.portrait { } -.landscape { } - -/* "page" containers - full-screen views, one should always be in view post-pageload */ -.ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } -.ui-page-active { display: block; overflow: visible; min-height: 100%; } - -/* loading screen */ -.ui-loading .ui-mobile-viewport { overflow: hidden !important; } -.ui-loading .ui-loader { display: block; } -.ui-loading .ui-page { overflow: hidden; } -.ui-loader { display: none; position: absolute; opacity: .85; z-index: 10; top: 75px; left: 50%; width: 200px; margin-left: -130px; padding: 20px 30px; } -.ui-loader h1 { font-size: 15px; text-align: center; } -.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; } - -/*fouc*/ -.ui-mobile-rendering > * { visibility: hidden; } - -/*headers, content panels*/ -.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; } -.ui-bar { font-size: 16px; margin: 0; } -.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; } - -.ui-header, .ui-footer { display: block; } -.ui-page .ui-header, .ui-page .ui-footer { position: relative; } -.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; } -.ui-header .ui-title, .ui-footer .ui-title { text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } -.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; } - -/*content area*/ -.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; } -.ui-page-fullscreen .ui-content { padding:0; } - -/* icons sizing */ -.ui-icon { width: 18px; height: 18px; } - -/* fullscreen class on ui-content div */ -.ui-fullscreen { } -.ui-fullscreen img { max-width: 100%; } - -/* non-js content hiding */ -.ui-nojs { position: absolute; left: -9999px; } diff --git a/themes/default/jquery.mobile.forms.select.css b/themes/default/jquery.mobile.forms.select.css deleted file mode 100644 index d7f05d6f..00000000 --- a/themes/default/jquery.mobile.forms.select.css +++ /dev/null @@ -1,27 +0,0 @@ -/* -* jQuery Mobile Framework -* Copyright (c) jQuery Project -* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. -*/ -.ui-select { display: block; } -.ui-select select { position: absolute; left: -99999px; } -.ui-select .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; } -.ui-select .ui-btn-icon-right .ui-icon { right: 15px; } - -/* labels */ -label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; } - -/*listbox*/ -.ui-select .ui-btn-text, .ui-selectmenu .ui-btn-text { display: inline-block; min-height: 1em; } - -.ui-selectmenu { position: absolute; padding: 0; z-index: 100 !important; width: 80%; max-width: 350px; padding: 6px; } -.ui-selectmenu .ui-listview { margin: 0; } -.ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } -.ui-selectmenu-hidden { top: -999999px; left: -99999px; } -.ui-selectmenu-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; } -.ui-screen-hidden, .ui-selectmenu-list .ui-li .ui-icon { display: none; } -.ui-selectmenu-list .ui-btn-active .ui-icon { display: block; } -.ui-selectmenu .ui-selectmenu-placeholder { display: none; } - -.min-width-480px label.ui-select { display: inline-block; width: 20%; margin: 0 2% 0 0; } -.min-width-480px .ui-select { width: 60%; display: inline-block; } \ No newline at end of file diff --git a/themes/default/jquery.mobile.forms.textinput.css b/themes/default/jquery.mobile.forms.textinput.css deleted file mode 100644 index 29cc2810..00000000 --- a/themes/default/jquery.mobile.forms.textinput.css +++ /dev/null @@ -1,21 +0,0 @@ -/* -* jQuery Mobile Framework -* Copyright (c) jQuery Project -* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. -*/ -label.ui-input-text { font-size: 16px; line-height: 1.4; display: block; font-weight: normal; margin: 0 0 .3em; } -input.ui-input-text, textarea.ui-input-text { background-image: none; padding: .4em; line-height: 1.4; font-size: 16px; display: block; width: 95%; } -input.ui-input-text { -webkit-appearance: none; } -textarea.ui-input-text { height: 50px; -webkit-transition: height 200ms linear; -moz-transition: height 200ms linear; -o-transition: height 200ms linear; transition: height 200ms linear; } -.ui-input-search { padding: 0 30px; width: 77%; background-position: 8px 50%; background-repeat: no-repeat; position: relative; } -.ui-input-search input.ui-input-text { border: none; width: 98%; padding: .4em 0; margin: 0; display: block; background: transparent none; outline: 0 !important; } -.ui-input-search .ui-input-clear { position: absolute; right: 2px; top: 50%; margin-top: -12px; } -.ui-input-search .ui-input-clear-hidden { display: none; } - -/* orientation adjustments - incomplete!*/ -.min-width-480px label.ui-input-text { vertical-align: top; } -.min-width-480px label.ui-input-text { display: inline-block; width: 20%; margin: 0 2% 0 0; } -.min-width-480px input.ui-input-text, -.min-width-480px textarea.ui-input-text, -.min-width-480px .ui-input-search { width: 60%; display: inline-block; } -.min-width-480px .ui-input-search { width: 50%; } \ No newline at end of file diff --git a/themes/default/jquery.mobile.listview.css b/themes/default/jquery.mobile.listview.css deleted file mode 100644 index 44fcaba5..00000000 --- a/themes/default/jquery.mobile.listview.css +++ /dev/null @@ -1,43 +0,0 @@ -/* -* jQuery Mobile Framework -* Copyright (c) jQuery Project -* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. -*/ -.ui-listview { margin: 0; counter-reset: listnumbering; } -.ui-content .ui-listview { margin: -15px; } -.ui-content .ui-listview-inset { margin: 1em 0; } -.ui-listview, .ui-li { list-style:none; padding:0; zoom: 1; } -.ui-li { display: block; margin:0; position: relative; overflow: hidden; text-align: left; border-width: 0; border-top-width: 1px; } -.ui-li .ui-btn-text { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } -.ui-li-divider, .ui-li-static { padding: .5em 15px; font-size: 14px; font-weight: bold; counter-reset: listnumbering; } -ol.ui-listview .ui-link-inherit:before, .ui-li-dec { font-size: .8em; display: inline-block; padding-right: .3em; font-weight: normal;counter-increment: listnumbering; content: counter(listnumbering) ". "; } -ol.ui-listview .ui-li-jsnumbering:before { content: "" !important; } /* to avoid chance of duplication */ -.ui-listview-inset .ui-li { border-right-width: 1px; border-left-width: 1px; } -.ui-li:last-child { border-bottom-width: 1px; } -.ui-li .ui-btn-inner { display: block; position: relative; padding: .7em 75px .7em 15px; } -.ui-li-has-thumb .ui-btn-inner { min-height: 60px; padding-left: 100px; } -.ui-li-has-icon .ui-btn-inner { min-height: 20px; padding-left: 40px; } -.ui-li-heading { font-size: 16px; font-weight: bold; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } -.ui-li-desc { font-size: 12px; font-weight: normal; display: block; margin: -.5em 0 .6em; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } -.ui-li-thumb, .ui-li-icon { position: absolute; left: 1px; top: 0; max-height: 80px; max-width: 80px; } -.ui-li-icon { max-height: 40px; max-width: 40px; left: 10px; top: .9em; } -.ui-li-thumb, .ui-li-icon, .ui-li-content { float: left; margin-right: 10px; } - -.ui-li-aside { float: right; width: 50%; text-align: right; margin: .3em 0; } -.min-width-480px .ui-li-aside { width: 45%; } -.ui-li-has-alt .ui-btn-inner { padding-right: 95px; } -.ui-li-count { position: absolute; font-size: 11px; font-weight: bold; padding: .2em .5em; top: 50%; margin-top: -.9em; right: 38px; } -.ui-li-divider .ui-li-count { right: 10px; } -.ui-li-has-alt .ui-li-count { right: 55px; } -.ui-li-link-alt { position: absolute; width: 40px; height: 100%; border-width: 0; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; } -.ui-li-link-alt .ui-btn { overflow: hidden; position: absolute; right: 8px; top: 50%; margin: -11px 0 0 0; border-bottom-width: 1px; } -.ui-li-link-alt .ui-btn-inner { padding: 0; position: static; } -.ui-li-link-alt .ui-btn .ui-icon { right: 50%; margin-right: -9px; } - -.ui-listview-filter { border-width: 0; overflow: hidden; margin: -15px -15px 15px -15px } -.ui-listview-filter .ui-input-search { margin: 5px; width: auto; display: block; } - -/* Odd iPad positioning issue. */ -@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { - .ui-li .ui-btn-text { overflow: visible; } -} \ No newline at end of file diff --git a/themes/default/jquery.mobile.theme.css b/themes/default/jquery.mobile.theme.css deleted file mode 100755 index 8d2423be..00000000 --- a/themes/default/jquery.mobile.theme.css +++ /dev/null @@ -1,239 +0,0 @@ -/* -* jQuery Mobile Framework -* Copyright (c) jQuery Project -* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. -* Note: Code is in draft form and is subject to change -*/ - - - -/* theme bar,body,btn containers -----------------------------------*/ -.ui-bar-a { border: 1px solid #2A2A2A; background: #111111; color: #fff; font-weight: bold; text-shadow: 0 -1px 1px #000; background-image: -moz-linear-gradient(top, #3c3c3c, #111111); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #3c3c3c),color-stop(1, #111111)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#3c3c3c', EndColorStr='#111111')"; } -.ui-bar-a, .ui-bar-a input, .ui-bar-a select, .ui-bar-a textarea, .ui-bar-a button { font-family: Helvetica, Arial, sans-serif; } -.ui-bar-a .ui-link-inherit { color: #fff; } -.ui-bar-a .ui-link { color: #7cc4e7; font-weight: bold; } - -.ui-body-a { border: 1px solid #2A2A2A; background: #222222; color: #fff; text-shadow: 0 1px 0 #000; font-weight: normal; background-image: -moz-linear-gradient(top, #666666, #222222); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #666666),color-stop(1, #222222)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#666666', EndColorStr='#222222)')"; } -.ui-body-a, .ui-body-a input, .ui-body-a select, .ui-body-a textarea, .ui-body-a button { font-family: Helvetica, Arial, sans-serif; } -.ui-body-a .ui-link-inherit { color: #fff; } -.ui-body-a .ui-link { color: #2489CE; font-weight: bold; } -.ui-br { border-bottom: 1px solid rgba(130,130,130,.3); } - -.ui-btn-up-a { border: 1px solid #222; background: #333333; font-weight: bold; color: #fff; cursor: pointer; text-shadow: 0 -1px 1px #000; text-decoration: none; background-image: -moz-linear-gradient(top, #555555, #333333); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #555555),color-stop(1, #333333)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#555555', EndColorStr='#333333')"; } -.ui-btn-up-a a.ui-link-inherit { color: #fff; } -.ui-btn-hover-a { border: 1px solid #000; background: #444444; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #000; text-decoration: none; background-image: -moz-linear-gradient(top, #666666, #444444); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #666666),color-stop(1, #444444)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#666666', EndColorStr='#444444')"; } -.ui-btn-hover-a a.ui-link-inherit { color: #fff; } -.ui-btn-down-a { border: 1px solid #000; background: #3d3d3d; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #000; background-image: -moz-linear-gradient(top, #333333, #5a5a5a); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #333333),color-stop(1, #5a5a5a)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#333333', EndColorStr='#5a5a5a')"; } -.ui-btn-down-a a.ui-link-inherit { color: #fff; } -.ui-btn-up-a, .ui-btn-hover-a, .ui-btn-down-a { font-family: Helvetica, Arial, sans-serif; } - - - - -.ui-bar-b { border: 1px solid #456f9a; background: #5e87b0; color: #fff; font-weight: bold; text-shadow: 0 -1px 1px #254f7a; background-image: -moz-linear-gradient(top, #81a8ce, #5e87b0); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #81a8ce),color-stop(1, #5e87b0)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#81a8ce', EndColorStr='#5e87b0')"; } -.ui-bar-b, .ui-bar-b input, .ui-bar-b select, .ui-bar-b textarea, .ui-bar-b button { font-family: Helvetica, Arial, sans-serif; } -.ui-bar-b .ui-link-inherit { color: #fff; } -.ui-bar-b .ui-link { color: #7cc4e7; font-weight: bold; } - -.ui-body-b { border: 1px solid #C6C6C6; background: #cccccc; color: #333333; text-shadow: 0 1px 0 #fff; font-weight: normal; background-image: -moz-linear-gradient(top, #e6e6e6, #cccccc); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #e6e6e6),color-stop(1, #cccccc)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#e6e6e6', EndColorStr='#cccccc')"; } -.ui-body-b, .ui-body-b input, .ui-body-b select, .ui-body-b textarea, .ui-body-b button { font-family: Helvetica, Arial, sans-serif; } -.ui-body-b .ui-link-inherit { color: #333333; } -.ui-body-b .ui-link { color: #2489CE; font-weight: bold; } - -.ui-btn-up-b { border: 1px solid #145072; background: #2567ab; font-weight: bold; color: #fff; cursor: pointer; text-shadow: 0 -1px 1px #145072; text-decoration: none; background-image: -moz-linear-gradient(top, #4e89c5, #2567ab); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #5f9cc5),color-stop(1, #396b9e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#4e89c5', EndColorStr='#2567ab')"; } -.ui-btn-up-b a.ui-link-inherit { color: #fff; } -.ui-btn-hover-b { border: 1px solid #00516e; background: #4b88b6; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #014D68; background-image: -moz-linear-gradient(top, #72b0d4, #4b88b6); text-decoration: none; background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #72b0d4),color-stop(1, #4b88b6)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#72b0d4', EndColorStr='#4b88b6')"; } -.ui-btn-hover-b a.ui-link-inherit { color: #fff; } -.ui-btn-down-b { border: 1px solid #225377; background: #4e89c5; font-weight: bold; color: #fff; text-shadow: 0 -1px 1px #225377; background-image: -moz-linear-gradient(top, #396b9e, #4e89c5); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #396b9e),color-stop(1, #4e89c5)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#396b9e', EndColorStr='#4e89c5')"; } -.ui-btn-down-b a.ui-link-inherit { color: #fff; } -.ui-btn-up-b, .ui-btn-hover-b, .ui-btn-down-b { font-family: Helvetica, Arial, sans-serif; } - - - - -.ui-bar-c { border: 1px solid #B3B3B3; background: #e9eaeb; color: #3E3E3E; font-weight: bold; text-shadow: 0 1px 1px #fff; background-image: -moz-linear-gradient(top, #f0f0f0, #e9eaeb); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #f0f0f0),color-stop(1, #e9eaeb)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#f0f0f0', EndColorStr='#e9eaeb')"; } -.ui-bar-c, .ui-bar-c input, .ui-bar-c select, .ui-bar-c textarea, .ui-bar-c button { font-family: Helvetica, Arial, sans-serif; } - -.ui-body-c { border: 1px solid #B3B3B3; color: #333333; text-shadow: 0 1px 0 #fff; background: #f0f0f0; background-image: -moz-linear-gradient(top, #fff, #f0f0f0); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fff),color-stop(1, #f0f0f0)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#f0f0f0')"; } -.ui-body-c, .ui-body-c input, .ui-body-c select, .ui-body-c textarea, .ui-body-c button { font-family: Helvetica, Arial, sans-serif; } -.ui-body-c .ui-link-inherit { color: #333333; } -.ui-body-c .ui-link { color: #2489CE; font-weight: bold; } - -.ui-btn-up-c { border: 1px solid #ccc; background: #eee; font-weight: bold; color: #444; cursor: pointer; text-shadow: 0 1px 1px #f6f6f6; text-decoration: none; background-image: -moz-linear-gradient(top, #fdfdfd, #eeeeee); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fdfdfd),color-stop(1, #eeeeee)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fdfdfd', EndColorStr='#eeeeee')"; } -.ui-btn-up-c a.ui-link-inherit { color: #2F3E46; } - -.ui-btn-hover-c { border: 1px solid #aaa; background: #f5f5f5; font-weight: bold; color: #111111; text-decoration: none; text-shadow: 0 1px 1px #fff; background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #ffffff),color-stop(1, #f5f5f5)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#f5f5f5')"; } -.ui-btn-hover-c a.ui-link-inherit { color: #2F3E46; } - -.ui-btn-down-c { border: 1px solid #808080; background: #fdfdfd; font-weight: bold; color: #111111; text-shadow: 0 1px 1px #ffffff; background-image: -moz-linear-gradient(top, #eeeeee, #fdfdfd); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #eeeeee),color-stop(1, #fdfdfd)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#fdfdfd')"; } -.ui-btn-down-c a.ui-link-inherit { color: #2F3E46; } -.ui-btn-up-c, .ui-btn-hover-c, .ui-btn-down-c { font-family: Helvetica, Arial, sans-serif; } - - -.ui-bar-d { border: 1px solid #ccc; background: #bbb; color: #333; text-shadow: 0 1px 0 #eee; background-image: -moz-linear-gradient(top, #ddd, #bbb); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #ddd),color-stop(1, #bbb)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ddd', EndColorStr='#bbb')"; } -.ui-bar-d, .ui-bar-d input, .ui-bar-d select, .ui-bar-d textarea, .ui-bar-d button { font-family: Helvetica, Arial, sans-serif; } -.ui-bar-d .ui-link-inherit { color: #333; } -.ui-bar-d .ui-link { color: #2489CE; font-weight: bold; } - -.ui-body-d { border: 1px solid #ccc; color: #333333; text-shadow: 0 1px 0 #fff; background: #ffffff; } -.ui-body-d, .ui-body-d input, .ui-body-d select, .ui-body-d textarea, .ui-body-d button { font-family: Helvetica, Arial, sans-serif; } -.ui-body-d .ui-link-inherit { color: #333333; } -.ui-body-d .ui-link { color: #2489CE; font-weight: bold; } - -.ui-btn-up-d { border: 1px solid #ccc; background: #fff; font-weight: bold; color: #444; text-decoration: none; text-shadow: 0 1px 1px #fff; } -.ui-btn-up-d a.ui-link-inherit { color: #333; } -.ui-btn-hover-d { border: 1px solid #aaa; background: #eeeeee; font-weight: bold; color: #222; cursor: pointer; text-shadow: 0 1px 1px #fff; text-decoration: none; background-image: -moz-linear-gradient(top, #fdfdfd, #eeeeee); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fdfdfd),color-stop(1, #eeeeee)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fdfdfd', EndColorStr='#eeeeee')"; } -.ui-btn-hover-d a.ui-link-inherit { color: #222; } - -.ui-btn-down-d { border: 1px solid #aaaaaa; background: #ffffff; font-weight: bold; color: #111; text-shadow: 0 1px 1px #ffffff; background-image: -moz-linear-gradient(top, #eeeeee, #ffffff); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #eeeeee),color-stop(1, #ffffff)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#ffffff')"; } -.ui-btn-down-d a.ui-link-inherit { border: 1px solid #808080; background: #ced0d2; font-weight: bold; color: #111; text-shadow: none; background-image: -moz-linear-gradient(top, #cccccc, #eeeeee); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #cccccc),color-stop(1, #eeeeee)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#cccccc', EndColorStr='#eeeeee')"; } -.ui-btn-up-d, .ui-btn-hover-d, .ui-btn-down-d { font-family: Helvetica, Arial, sans-serif; } - - -.ui-bar-e { border: 1px solid #F7C942; background: #fadb4e; color: #333; text-shadow: 0 1px 0 #fff; background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fceda7),color-stop(1, #fadb4e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fceda7', EndColorStr='#fadb4e')"; } -.ui-bar-e, .ui-bar-e input, .ui-bar-e select, .ui-bar-e textarea, .ui-bar-d button { font-family: Helvetica, Arial, sans-serif; } -.ui-bar-e .ui-link-inherit { color: #333; } -.ui-bar-e .ui-link { color: #2489CE; font-weight: bold; } - -.ui-body-e { border: 1px solid #F7C942; color: #333333; text-shadow: 0 1px 0 #fff; background: #faeb9e; background-image: -moz-linear-gradient(top, #fff, #faeb9e); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fff),color-stop(1, #faeb9e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#faeb9e')"; } -.ui-body-e, .ui-body-e input, .ui-body-e select, .ui-body-e textarea, .ui-body-e button { font-family: Helvetica, Arial, sans-serif; } -.ui-body-e .ui-link-inherit { color: #333333; } -.ui-body-e .ui-link { color: #2489CE; font-weight: bold; } - - -.ui-btn-up-e { border: 1px solid #F7C942; background: #fadb4e; font-weight: bold; color: #333; cursor: pointer; text-shadow: 0 1px 1px #fe3; text-decoration: none; text-shadow: 0 1px 0 #fff; background-image: -moz-linear-gradient(top, #fceda7, #fadb4e); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fceda7),color-stop(1, #fadb4e)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fceda7', EndColorStr='#fadb4e')"; } -.ui-btn-up-e a.ui-link-inherit { color: #333; } - -.ui-btn-hover-e { border: 1px solid #e79952; background: #fbe26f; font-weight: bold; color: #111; text-decoration: none; text-shadow: 0 1px 1px #fff; background-image: -moz-linear-gradient(top, #fcf0b5, #fbe26f); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fcf0b5),color-stop(1, #fbe26f)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fcf0b5', EndColorStr='#fbe26f')"; } - -.ui-btn-hover-e a.ui-link-inherit { color: #333; } -.ui-btn-down-e { border: 1px solid #F7C942; background: #fceda7; font-weight: bold; color: #111; text-shadow: 0 1px 1px #ffffff; background-image: -moz-linear-gradient(top, #fadb4e, #fceda7); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #fadb4e),color-stop(1, #fceda7)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fadb4e', EndColorStr='#fceda7')"; } -.ui-btn-down-e a.ui-link-inherit { color: #333; } -.ui-btn-up-e, .ui-btn-hover-e, .ui-btn-down-e { font-family: Helvetica, Arial, sans-serif; } - - -/* links within "buttons" */ -a.ui-link-inherit { text-decoration: none !important; } - -/* Active class used as the "on" state across all themes */ -.ui-btn-active { border: 1px solid #155678; background: #4596ce; font-weight: bold; color: #fff; cursor: pointer; text-shadow: 0 -1px 1px #145072; text-decoration: none; background-image: -moz-linear-gradient(top, #85bae4, #5393c5); background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, #85bae4),color-stop(1, #5393c5)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#85bae4', EndColorStr='#5393c5')"; } -.ui-btn-active a.ui-link-inherit { color: #fff; } - -/* button inner top highlight */ -.ui-btn-inner { border-top: 1px solid #fff; border-color: rgba(255,255,255,.3); } - - -/* Container Corner radius */ -.ui-corner-tl { -moz-border-radius-topleft: .6em; -webkit-border-top-left-radius: .6em; border-top-left-radius: .6em; } -.ui-corner-tr { -moz-border-radius-topright: .6em; -webkit-border-top-right-radius: .6em; border-top-right-radius: .6em; } -.ui-corner-bl { -moz-border-radius-bottomleft: .6em; -webkit-border-bottom-left-radius: .6em; border-bottom-left-radius: .6em; } -.ui-corner-br { -moz-border-radius-bottomright: .6em; -webkit-border-bottom-right-radius: .6em; border-bottom-right-radius: .6em; } -.ui-corner-top { -moz-border-radius-topleft: .6em; -webkit-border-top-left-radius: .6em; border-top-left-radius: .6em; -moz-border-radius-topright: .6em; -webkit-border-top-right-radius: .6em; border-top-right-radius: .6em; } -.ui-corner-bottom { -moz-border-radius-bottomleft: .6em; -webkit-border-bottom-left-radius: .6em; border-bottom-left-radius: .6em; -moz-border-radius-bottomright: .6em; -webkit-border-bottom-right-radius: .6em; border-bottom-right-radius: .6em; } -.ui-corner-right { -moz-border-radius-topright: .6em; -webkit-border-top-right-radius: .6em; border-top-right-radius: .6em; -moz-border-radius-bottomright: .6em; -webkit-border-bottom-right-radius: .6em; border-bottom-right-radius: .6em; } -.ui-corner-left { -moz-border-radius-topleft: .6em; -webkit-border-top-left-radius: .6em; border-top-left-radius: .6em; -moz-border-radius-bottomleft: .6em; -webkit-border-bottom-left-radius: .6em; border-bottom-left-radius: .6em; } -.ui-corner-all { -moz-border-radius: .6em; -webkit-border-radius: .6em; border-radius: .6em; } - - - -/* Interaction Cues -----------------------------------*/ -.ui-disabled { opacity: .3; } -.ui-disabled, .ui-disabled a { cursor: default !important; } - -/* Icons -----------------------------------*/ -/* .ui-icon { background-position: 50% 50%; background-repeat: no-repeat; background-color: #fff; background-color: rgba(0,0,0,.4); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; } */ - -.ui-icon { background-image: url(images/icons-18-white.png); background-repeat: no-repeat; background-color: #666; background-color: rgba(0,0,0,.4); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; } -.ui-icon-disc { background-color: #666; background-color: rgba(0,0,0,.3); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; } - -/* alt color */ -.ui-icon-black { background-image: url(images/icons-18-black.png); } -.ui-icon-black-disc { background-color: #fff; background-color: rgba(255,255,255,.3); -moz-border-radius: 9px; -webkit-border-radius: 9px; border-radius: 9px; } - -/* retina */ -@media screen and (-webkit-min-device-pixel-ratio: 2), screen and (max--moz-device-pixel-ratio: 2) { - .ui-icon { background-image: url(images/icons-36-white.png); background-size: 558px 18px; } - .ui-icon-black { background-image: url(images/icons-36-black.png); } -} - -/*plus minus*/ -.ui-icon-plus { background-position: -0 0; } -.ui-icon-minus { background-position: -36px 0; } - -/* delete/close */ -.ui-icon-delete { background-position: -72px 0; } - -/*arrows*/ -.ui-icon-arrow-r { background-position: -108px 0; } -.ui-icon-arrow-l { background-position: -144px 0; } -.ui-icon-arrow-u { background-position: -180px 0; } -.ui-icon-arrow-d { background-position: -216px 0; } - -.ui-icon-check { background-position: -252px 0; } -.ui-icon-gear { background-position: -288px 0; } -.ui-icon-refresh { background-position: -324px 0; } -.ui-icon-forward { background-position: -360px 0; } -.ui-icon-back { background-position: -396px 0; } - -.ui-icon-grid { background-position: -432px 0; } -.ui-icon-star { background-position: -468px 0; } -.ui-icon-alert { background-position: -504px 0; } -.ui-icon-info { background-position: -540px 0; } - -/*checks,radios*/ -.ui-icon-checkbox-off, -.ui-icon-checkbox-on, -.ui-icon-radio-off, -.ui-icon-radio-on { background-color: transparent; -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; background-size: 20px 20px; } - -.ui-icon-checkbox-off { background-image: url(images/form-check-off.png); } -.ui-icon-checkbox-on { background-image: url(images/form-check-on.png); } -.ui-icon-radio-off { background-image: url(images/form-radio-off.png);} -.ui-icon-radio-on { background-image: url(images/form-radio-on.png); } - -.ui-icon-search { background-image: url(images/icon-search-black.png); background-size: 16px 16px; } - -/* loading icon */ -.ui-icon-loading { background-image: url(images/ajax-loader.png); width: 40px; height: 40px; -moz-border-radius: 20px; -webkit-border-radius: 20px; border-radius: 20px; background-size: 35px 35px; } - -/* btn Corner radius */ -.ui-btn-corner-tl { -moz-border-radius-topleft: 1em; -webkit-border-top-left-radius: 1em; border-top-left-radius: 1em; } -.ui-btn-corner-tr { -moz-border-radius-topright: 1em; -webkit-border-top-right-radius: 1em; border-top-right-radius: 1em; } -.ui-btn-corner-bl { -moz-border-radius-bottomleft: 1em; -webkit-border-bottom-left-radius: 1em; border-bottom-left-radius: 1em; } -.ui-btn-corner-br { -moz-border-radius-bottomright: 1em; -webkit-border-bottom-right-radius: 1em; border-bottom-right-radius: 1em; } -.ui-btn-corner-top { -moz-border-radius-topleft: 1em; -webkit-border-top-left-radius: 1em; border-top-left-radius: 1em; -moz-border-radius-topright: 1em; -webkit-border-top-right-radius: 1em; border-top-right-radius: 1em; } -.ui-btn-corner-bottom { -moz-border-radius-bottomleft: 1em; -webkit-border-bottom-left-radius: 1em; border-bottom-left-radius: 1em; -moz-border-radius-bottomright: 1em; -webkit-border-bottom-right-radius: 1em; border-bottom-right-radius: 1em; } -.ui-btn-corner-right { -moz-border-radius-topright: 1em; -webkit-border-top-right-radius: 1em; border-top-right-radius: 1em; -moz-border-radius-bottomright: 1em; -webkit-border-bottom-right-radius: 1em; border-bottom-right-radius: 1em; } -.ui-btn-corner-left { -moz-border-radius-topleft: 1em; -webkit-border-top-left-radius: 1em; border-top-left-radius: 1em; -moz-border-radius-bottomleft: 1em; -webkit-border-bottom-left-radius: 1em; border-bottom-left-radius: 1em; } -.ui-btn-corner-all { -moz-border-radius: 1em; -webkit-border-radius: 1em; border-radius: 1em;} - -/* radius clip */ -.ui-corner-tl, .ui-corner-tr, .ui-corner-bl, -.ui-corner-br, .ui-corner-top, .ui-corner-bottom, -.ui-corner-right, .ui-corner-left, .ui-corner-all, -.ui-btn-corner-tl, .ui-btn-corner-tr, .ui-btn-corner-bl, -.ui-btn-corner-br, .ui-btn-corner-top, .ui-btn-corner-bottom, -.ui-btn-corner-right, .ui-btn-corner-left, .ui-btn-corner-all { - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; -} - -/* Overlays */ -.ui-overlay { background: #666; opacity: .5; filter:Alpha(Opacity=50); position: absolute; width: 100%; height: 100%; } -.ui-overlay-shadow { -moz-box-shadow: 0px 0px 12px rgba(0,0,0,.6); -webkit-box-shadow: 0px 0px 12px rgba(0,0,0,.6); box-shadow: 0px 0px 12px rgba(0,0,0,.6); } - -.ui-shadow { -moz-box-shadow: 0px 1px 4px rgba(0,0,0,.3); -webkit-box-shadow: 0px 1px 4px rgba(0,0,0,.3); box-shadow: 0px 1px 4px rgba(0,0,0,.3); } -.ui-bar-a .ui-shadow, .ui-bar-b .ui-shadow , .ui-bar-c .ui-shadow { -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.3); -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.3); box-shadow: 0px 1px 0 rgba(255,255,255,.3); } -.ui-shadow-inset { -moz-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); -webkit-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); } -.ui-icon-shadow { -moz-box-shadow: 0px 1px 0 rgba(255,255,255,.4); -webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.4); box-shadow: 0px 1px 0 rgba(255,255,255,.4); } - -/* set focus state last */ -.ui-focus { outline-width: 0; -moz-box-shadow: 0px 0px 12px #387bbe; -webkit-box-shadow: 0px 0px 12px #387bbe; box-shadow: 0px 0px 12px #387bbe; } - -/* unset box shadow in browsers that don't do it right */ -.ui-mobile-nosupport-boxshadow * { -moz-box-shadow: none !important; -webkit-box-shadow: none !important; box-shadow: none !important; } -.ui-mobile-nosupport-boxshadow .ui-focus { outline-width: 2px; } diff --git a/themes/valencia/images/form-check-off.png b/themes/valencia/images/form-check-off.png deleted file mode 100644 index 54e2fe0f..00000000 Binary files a/themes/valencia/images/form-check-off.png and /dev/null differ diff --git a/themes/valencia/images/form-check-on.png b/themes/valencia/images/form-check-on.png deleted file mode 100644 index e6daaaf8..00000000 Binary files a/themes/valencia/images/form-check-on.png and /dev/null differ diff --git a/themes/valencia/images/form-radio-off.png b/themes/valencia/images/form-radio-off.png deleted file mode 100644 index 32bd4339..00000000 Binary files a/themes/valencia/images/form-radio-off.png and /dev/null differ diff --git a/themes/valencia/images/form-radio-on.png b/themes/valencia/images/form-radio-on.png deleted file mode 100644 index ddc40497..00000000 Binary files a/themes/valencia/images/form-radio-on.png and /dev/null differ diff --git a/themes/valencia/images/icon-search-black.png b/themes/valencia/images/icon-search-black.png deleted file mode 100644 index 5721120f..00000000 Binary files a/themes/valencia/images/icon-search-black.png and /dev/null differ diff --git a/themes/valencia/images/icons-18-black.png b/themes/valencia/images/icons-18-black.png deleted file mode 100644 index 38f47267..00000000 Binary files a/themes/valencia/images/icons-18-black.png and /dev/null differ diff --git a/themes/valencia/images/icons-18-white.png b/themes/valencia/images/icons-18-white.png deleted file mode 100644 index ceb28345..00000000 Binary files a/themes/valencia/images/icons-18-white.png and /dev/null differ diff --git a/themes/valencia/images/icons-36-black.png b/themes/valencia/images/icons-36-black.png deleted file mode 100644 index b079c51f..00000000 Binary files a/themes/valencia/images/icons-36-black.png and /dev/null differ diff --git a/themes/valencia/images/icons-36-white.png b/themes/valencia/images/icons-36-white.png deleted file mode 100644 index 038cae40..00000000 Binary files a/themes/valencia/images/icons-36-white.png and /dev/null differ diff --git a/themes/valencia/index.php b/themes/valencia/index.php deleted file mode 100644 index eae46f3b..00000000 --- a/themes/valencia/index.php +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/tools/log-page-events.html b/tools/log-page-events.html new file mode 100644 index 00000000..72825ba3 --- /dev/null +++ b/tools/log-page-events.html @@ -0,0 +1,24 @@ + + + + + +Page Event Logger Bookmarklet + + + +

        Page Event Logger Bookmarklet

        +

        A simple bookmarklet for logging jQuery Mobile page events. To use, bookmark the following link:

        + +

        For platforms that don't allow bookmarking of javascript: urls, you can copy/paste the following source for the bookmarklet directly into the browser's location bar then hit enter or hit the "go" button on your keypad:

        +

        + +

        +

        NOTE: Some browsers like Chrome will strip off the javascript: prefix from the string above when you paste it into the location bar. Make sure what you pasted is prefixed by javascript: before attempting to load the bookmarklet.

        + + + diff --git a/tools/log-page-events.js b/tools/log-page-events.js new file mode 100644 index 00000000..c5ed9f92 --- /dev/null +++ b/tools/log-page-events.js @@ -0,0 +1,108 @@ +/*! + * jQuery Mobile v@VERSION + * http://jquerymobile.com/ + * + * Copyright 2011, jQuery Project + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ + +// This is code that can be used as a simple bookmarklet for debugging +// page loading and navigation in pages that use the jQuery Mobile framework. +// All messages are sent to the browser's console.log so to see the messages, +// you need to make sure you enable the console/log in your browser. + +(function($, window, document) { + if ( typeof $ === "undefined" ) { + alert( "log-page-events.js requires jQuery core!" ); + return; + } + + var pageEvents = "mobileinit pagebeforechange pagechange pagechangefailed pagebeforeload pageload pageloadfailed pagebeforecreate pagecreate pageinit pagebeforeshow pageshow pagebeforehide pagehide pageremove"; + + function getElementDesc( ele ) + { + var result = []; + if ( ele ) { + result.push( ele.nodeName.toLowerCase() ); + var c = ele.className; + if ( c ) { + c = c.replace( /^\s+|\s+$/, "" ).replace( /\s+/, " " ); + if (c) { + result.push( "." + c.split( " " ).join( "." ) ); + } + } + if ( ele.id ){ + result.push( "#" + ele.id ) + } + } + return result.join( "" ); + } + + function debugLog( msg ) + { + console.log( msg ); + } + + function getNativeEvent( event ) { + + while ( event && typeof event.originalEvent !== "undefined" ) { + event = event.originalEvent; + } + return event; + } + + function logEvent( event, data ) + { + var result = event.type + " (" + (new Date).getTime() + ")\n"; + + switch( event.type ) + { + case "pagebeforechange": + case "pagechange": + case "pagechangefailed": + result += "\tpage: "; + if ( typeof data.toPage === "string" ) { + result += data.toPage; + } else { + result += getElementDesc( data.toPage[ 0 ] ) + "\n\tdata-url: " + data.toPage.jqmData( "url" ); + } + result += "\n\n" + break; + case "pagebeforeload": + case "pageloadfailed": + result += "\turl: " + data.url + "\n\tabsUrl: " + data.absUrl + "\n\n"; + break; + case "pageload": + result += "\turl: " + data.url + "\n\tabsUrl: " + data.absUrl + "\n\tpage: " + getElementDesc( data.page[ 0 ] ) + "\n\n"; + break; + case "pagebeforeshow": + case "pageshow": + case "pagebeforehide": + case "pagehide": + result += "\tpage: " + getElementDesc( event.target ) + "\n"; + result += "\tdata-url: " + $( event.target ).jqmData( "url" ) + "\n\n"; + break; + case "pagebeforecreate": + case "pagecreate": + case "pageinit": + result += "\telement: " + getElementDesc( event.target ) + "\n\n"; + break; + case "hashchange": + result += "\tlocation: " + location.href + "\n\n"; + break; + case "popstate": + var e = getNativeEvent( event ); + result += "\tlocation: " + location.href + "\n"; + result += "\tstate.hash: " + ( e.state && e.state.hash ? e.state.hash + "\n\n" : "" ); + break; + } + + debugLog( result ); + } + + // Now add our logger. + $( document ).bind( pageEvents, logEvent ); + $( window ).bind( "hashchange popstate", logEvent ); + +})( jQuery, window, document ); \ No newline at end of file diff --git a/version.txt b/version.txt index 96c8a8af..35b4b277 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0a3pre \ No newline at end of file +1.0rc2 \ No newline at end of file