diff --git a/.editorconfig b/.editorconfig index d06e31850a..b5bd7f60ec 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,41 +3,14 @@ root = true - [*] +indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -# Tabs in JS unless otherwise specified -[**.js] -indent_style = tab - -[Makefile] -indent_style = tab - - -[speed/**.html] -indent_style = tab - -[speed/**.css] -indent_style = tab - -[speed/benchmarker.js] +[package.json] indent_style = space indent_size = 2 - -[test/**.xml] -indent_style = tab - -[test/**.php] -indent_style = tab - -[test/**.html] -indent_style = tab - -[test/**.css] -indent_style = space -indent_size = 8 diff --git a/.gitattributes b/.gitattributes index d6dc47053c..b7ca95b5b7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,5 @@ -* eol=lf -*.jar binary +# Auto detect text files and perform LF normalization +* text=auto + +# JS files must always use LF for tools to work +*.js eol=lf diff --git a/.gitignore b/.gitignore index 9868a53695..e1e7dbfe4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -src/selector.js -dist .project .settings *~ @@ -7,7 +5,12 @@ dist *.patch /*.html .DS_Store -dist/.destination.json -dist/.sizecache.json -build/.sizecache.json -node_modules +.bower.json +.sizecache.json + +npm-debug.log + +/dist +/node_modules + +/test/node_smoke_tests/lib/ensure_iterability.js diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 19c60418e3..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "src/sizzle"] - path = src/sizzle - url = git://github.com/jquery/sizzle.git -[submodule "test/qunit"] - path = test/qunit - url = git://github.com/jquery/qunit.git diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000000..6a2c2fd990 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,13 @@ +{ + "preset": "jquery", + + // remove after https://github.com/jscs-dev/node-jscs/issues/1685 + // and https://github.com/jscs-dev/node-jscs/issues/1686 + "requireCapitalizedComments": null, + + "excludeFiles": [ + "external", "src/intro.js", "src/outro.js", + "node_modules", + "test/node_smoke_tests/lib/ensure_iterability.js" + ] +} diff --git a/.jshintignore b/.jshintignore new file mode 100644 index 0000000000..1ddafd635a --- /dev/null +++ b/.jshintignore @@ -0,0 +1,12 @@ +external +src/intro.js +src/outro.js +test/data/jquery-1.9.1.js +test/data/badcall.js +test/data/badjson.js +test/data/json_obj.js +test/data/readywaitasset.js +test/data/readywaitloader.js +test/data/support/csp.js +test/data/support/getComputedSupport.js +test/node_smoke_tests/lib/ensure_iterability.js diff --git a/.jshintrc b/.jshintrc index 4f0431ddc0..13a9e01b91 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,14 +1,17 @@ { + "boss": true, "curly": true, "eqeqeq": true, - "latedef": true, + "eqnull": true, + "expr": true, + "immed": true, "noarg": true, - "noempty": true, + "onevar": true, "quotmark": "double", + "smarttabs": true, + "trailing": true, "undef": true, "unused": true, - "strict": true, - "trailing": true, "node": true -} +} \ No newline at end of file diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000..6aa901636b --- /dev/null +++ b/.mailmap @@ -0,0 +1,100 @@ +Adam Coulombe +Adam J. Sontag +Alexander Farkas +Alexander Farkas +Alexis Abril +Andrew E Monat +Anton Matzneller +Anton Matzneller +Batiste Bieler +Benjamin Truyman +Brandon Aaron +Carl Danley +Carl Fürstenberg +Carl Fürstenberg +Charles McNulty +Christopher Jones +Colin Snover +Corey Frang +Dan Heberden +Daniel Chatfield +Daniel Gálvez +Danil Somsikov +Dave Methvin +Dave Reed +David Fox +David Hong +Devin Cooper +Dmitry Gusev +Earle Castledine +Erick Ruiz de Chávez +Gianni Alessandro Chiappetta +Heungsub Lee +Iraê Carvalho +Isaac Z. Schlueter +Ismail Khair +James Burke +James Padolsey +Jason Bedard +Jay Merrifield +Jay Merrifield +Jean Boussier +Jephte Clain +Jess Thrysoee +Joao Henrique de Andrade Bruni +Joe Presbrey +John Resig +John Resig +Jordan Boesch +Josh Varner +Julian Aubourg +Julian Aubourg +Julian Aubourg +Jörn Zaefferer +Jörn Zaefferer +Jörn Zaefferer +Karl Swedberg +Kris Borchers +Lee Carpenter +Li Xudong +Louis-Rémi Babé +Louis-Rémi Babé +Louis-Rémi Babé +Louis-Rémi Babé +Marcel Greter +Matthias Jäggli +Michael Murray +Michał Gołębiowski +Michał Gołębiowski +Mike Alsup +Nguyen Phuc Lam +Noah Hamann +Oleg Gaidarenko +Rafaël Blais Masson +Richard D. Worth +Rick Waldron +Rick Waldron +Robert Katić +Roman Reiß +Ron Otten +Sai Lung Wong +Scott González +Scott Jehl +Sebastian Burkhard +Senya Pugach +Thomas Tortorini Mr21 +Timmy Willison +Timmy Willison +Timo Tijhof +TJ Holowaychuk +Tom H Fuertes +Tom H Fuertes Tom H Fuertes +Tom Viner +Xavi Ramirez +Xavier Montillet +Yehuda Katz +Yehuda Katz +Yehuda Katz +Yehuda Katz +Yiming He +Terry Jones diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000000..d510949524 --- /dev/null +++ b/.npmignore @@ -0,0 +1,17 @@ +.jshintignore +.jshintrc + +/.editorconfig +/.gitattributes +/.jscs.json +/.mailmap +/.travis.yml + +/build +/speed +/test +/Gruntfile.js + +/external/qunit +/external/requirejs +/external/sinon diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..cffe8cdef1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..be62086021 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +os: linux +node_js: +- "4" +- "6" +- "8" +- "10" +- "12" +- "14" diff --git a/AUTHORS.txt b/AUTHORS.txt index 62df6f5a5f..16bc6c986b 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -14,7 +14,7 @@ Brandon Aaron Mike Alsup Dave Methvin Ed Engelhardt -Sean Catchpole +Sean Catchpole Paul Mclanahan David Serduke Richard D. Worth @@ -31,12 +31,12 @@ Rich Dougherty Kim Dalsgaard Andrea Giammarchi Mark Gibson -Karl Swedberg +Karl Swedberg Justin Meyer Ben Alman James Padolsey David Petersen -Batiste Bieler +Batiste Bieler Alexander Farkas Rick Waldron Filipe Fortes @@ -54,16 +54,19 @@ Ralph Whitbeck Carl Fürstenberg Jacob Wright J. Ryan Stinnett +unknown +temp01 Heungsub Lee Colin Snover Ryan W Tenney +Pinhook Ron Otten Jephte Clain Anton Matzneller Alex Sexton Dan Heberden Henri Wiechers -Russell Holbrook +Russell Holbrook Julian Aubourg Gianni Alessandro Chiappetta Scott Jehl @@ -76,6 +79,7 @@ Brandon Sterne Mathias Bynens Timmy Willison Corey Frang +Digitalxero Anton Kovalyov David Murdoch Josh Varner @@ -93,7 +97,8 @@ Brian Brennan Xavier Montillet Daniel Pihlstrom Sahab Yazdani -Scott Hughes +avaly +Scott Hughes Mike Sherov Greg Hazel Schalk Neethling @@ -105,17 +110,20 @@ Shi Chuan Berker Peksag Toby Brain Matt Mueller +Justin Daniel Herman Oleg Gaidarenko Richard Gibson Rafaël Blais Masson +cmc3cn <59194618@qq.com> Joe Presbrey Sindre Sorhus Arne de Bree Vladislav Zarakovsky Andrew E Monat +Oskari Joao Henrique de Andrade Bruni -Dominik D. Geyer +tsinha Matt Farmer Trey Hunner Jason Moon @@ -132,18 +140,19 @@ Elijah Manor Daniel Chatfield Nikita Govorov Wesley Walser -Michael Pennisi +Mike Pennisi Markus Staab +Dave Riddle +Callum Macrae Benjamin Truyman James Huston -Ismail Khair -Carl Danley -Michael Petrovich -Callum Macrae -David Bonner Erick Ruiz de Chávez +David Bonner Akintayo Akinwunmi -Dave Riddle +MORGAN +Ismail Khair +Carl Danley +Mike Petrovich Greg Lavallee Daniel Gálvez Sai Lung Wong @@ -151,9 +160,107 @@ Tom H Fuertes Roland Eckl Jay Merrifield Allen J Schmidt Jr +Jonathan Sampson Marcel Greter Matthias Jäggli +David Fox Yiming He Devin Cooper +Paul Ramos +Rod Vagg Bennett Sorbo Sebastian Burkhard +nanto +Danil Somsikov +Ryunosuke SATO +Jean Boussier +Adam Coulombe +Andrew Plummer +Mark Raddatz +Dmitry Gusev +Michał Gołębiowski +Nguyen Phuc Lam +Tom H Fuertes +Brandon Johnson +Jason Bedard +Kyle Robinson Young +Renato Oliveira dos Santos +Chris Talkington +Eddie Monge +Terry Jones +Jason Merino +Jeremy Dunck +Chris Price +Amey Sakhadeo +Anthony Ryan +Dominik D. Geyer +George Kats +Lihan Li +Ronny Springer +Marian Sollmann +Corey Frang +Chris Antaki +Noah Hamann +David Hong +Jakob Stoeck +Christopher Jones +Forbes Lindesay +John Paul +S. Andrew Sheppard +Leonardo Balter +Roman Reiß +Benjy Cui +Rodrigo Rosenfeld Rosas +John Hoven +Christian Kosmowski +Liang Peng +TJ VanToll +Senya Pugach +Aurelio De Rosa +Nazar Mokrynskyi +Arthur Verschaeve +Dan Hart +Scott González +Zheming Sun +Bin Xin +David Corbacho +Veaceslav Grimalschi +Daniel Husar +Jason Bedard +Ben Toews +Aditya Raghavan +Nicolas HENRY +Norman Xu +Anne-Gaelle Colom +Victor Homyakov +George Mauer +Leonardo Braga +Stephen Edgar +Thomas Tortorini +Winston Howes +Jon Hester +Alexander O'Mara +Bastian Buchholz +Arthur Stolyar +Calvin Metcalf +Mu Haibao +Richard McDaniel +Chris Rebert +Gilad Peleg +Martin Naumann +Bruno Pérel +Reed Loden +Daniel Nill +Yongwoo Jeon +Sean Henderson +Adrian Olek +Richard Kraaijenhagen +Gary Ye +Christian Grete +Liza Ramo +Joelle Fleurantin +Julian Alexander Murillo +Jun Sun +Devin Wilson +Todor Prikumov +Zack Hall diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 88a19420a3..e4373785f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,85 +1,65 @@ # Contributing to jQuery 1. [Getting Involved](#getting-involved) -2. [Discussion](#discussion) +2. [Questions and Discussion](#questions-and-discussion) 3. [How To Report Bugs](#how-to-report-bugs) -4. [Core Style Guide](#jquery-core-style-guide) -5. [Tips For Bug Patching](#tips-for-jquery-bug-patching) - +4. [Tips for Bug Patching](#tips-for-bug-patching) +Note: This is the code development repository for *jQuery Core* only. Before opening an issue or making a pull request, be sure you're in the right place. +* jQuery plugin issues should be reported to the author of the plugin. +* jQuery Core API documentation issues can be filed [at the API repo](http://github.com/jquery/api.jquery.com/issues). +* Bugs or suggestions for other jQuery Foundation projects should be filed in [their respective repos](http://github.com/jquery/). ## Getting Involved -There are a number of ways to get involved with the development of jQuery core. Even if you've never contributed code to an Open Source project before, we're always looking for help identifying bugs, writing and reducing test cases and documentation. +We've put together [a short guide with tips, tricks, and ideas on getting started](http://contribute.jquery.org/open-source/). We're always looking for help identifying bugs, writing and reducing test cases, and documentation. + +More information on how to contribute to this and other jQuery Foundation projects is at [contribute.jquery.org](http://contribute.jquery.org). Please review our [commit & pull request guide](http://contribute.jquery.org/commits-and-pull-requests/) and [style guides](http://contribute.jquery.org/style-guide/) for instructions on how to maintain a fork and submit patches. Before we can merge any pull request, we'll also need you to sign our [contributor license agreement](http://contribute.jquery.org/cla/). -This is the best way to contribute to jQuery core. Please read through the full guide detailing [How to Report Bugs](#How-to-Report-Bugs). -## Discussion +## Questions and Discussion ### Forum and IRC -The jQuery core development team frequently tracks posts on the [jQuery Development Forum](http://forum.jquery.com/developing-jquery-core). If you have longer posts or questions please feel free to post them there. If you think you've found a bug please [file it in the bug tracker](#How-to-Report-Bugs). +jQuery is so popular that many developers have knowledge of its capabilities and limitations. Most questions about using jQuery can be answered on popular forums such as [Stack Overflow](http://stackoverflow.com). Please start there when you have questions, even if you think you've found a bug. -Additionally most of the jQuery core development team can be found in the [#jquery-dev](http://webchat.freenode.net/?channels=jquery-dev) IRC channel on irc.freenode.net. +The jQuery Core team watches the [jQuery Development Forum](http://forum.jquery.com/developing-jquery-core). If you have longer posts or questions that can't be answered in places such as Stack Overflow, please feel free to post them there. If you think you've found a bug, please [file it in the bug tracker](#how-to-report-bugs). The Core team can be found in the [#jquery-dev](http://webchat.freenode.net/?channels=jquery-dev) IRC channel on irc.freenode.net. ### Weekly Status Meetings -Every week (unless otherwise noted) the jQuery core dev team has a meeting to discuss the progress of current work and to bring forward possible new blocker bugs for discussion. +The jQuery Core team has a weekly meeting to discuss the progress of current work. The meeting is held in the [#jquery-meeting](http://webchat.freenode.net/?channels=jquery-meeting) IRC channel on irc.freenode.net at [Noon EST](http://www.timeanddate.com/worldclock/fixedtime.html?month=1&day=17&year=2011&hour=12&min=0&sec=0&p1=43) on Mondays. -The meeting is held in the [#jquery-meeting](http://webchat.freenode.net/?channels=jquery-meeting) IRC channel on irc.freenode.net at [Noon EST](http://www.timeanddate.com/worldclock/fixedtime.html?month=1&day=17&year=2011&hour=12&min=0&sec=0&p1=43) on Mondays. - -[Past Meeting Notes](https://docs.google.com/document/d/1MrLFvoxW7GMlH9KK-bwypn77cC98jUnz7sMW1rg_TP4/edit?hl=en) +[jQuery Core Meeting Notes](http://meetings.jquery.org/category/core/) ## How to Report Bugs ### Make sure it is a jQuery bug -Many bugs reported to our bug tracker are actually bugs in user code, not in jQuery code. Keep in mind that just because your code throws an error and the console points to a line number inside of jQuery, this does *not* mean the bug is a jQuery bug; more often than not, these errors result from providing incorrect arguments when calling a jQuery function. - -If you are new to jQuery, it is usually a much better idea to ask for help first in the [Using jQuery Forum](http://forum.jquery.com/using-jquery) or the [jQuery IRC channel](http://webchat.freenode.net/?channels=%23jquery). You will get much quicker support, and you will help avoid tying up the jQuery team with invalid bug reports. These same resources can also be useful if you want to confirm that your bug is indeed a bug in jQuery before filing any tickets. +Most bugs reported to our bug tracker are actually bugs in user code, not in jQuery code. Keep in mind that just because your code throws an error inside of jQuery, this does *not* mean the bug is a jQuery bug. +Ask for help first in the [Using jQuery Forum](http://forum.jquery.com/using-jquery) or another discussion forum like [Stack Overflow](http://stackoverflow.com/). You will get much quicker support, and you will help avoid tying up the jQuery team with invalid bug reports. -### Disable any browser extensions +### Disable browser extensions Make sure you have reproduced the bug with all browser extensions and add-ons disabled, as these can sometimes cause things to break in interesting and unpredictable ways. Try using incognito, stealth or anonymous browsing modes. - ### Try the latest version of jQuery -Bugs in old versions of jQuery may have already been fixed. In order to avoid reporting known issues, make sure you are always testing against the [latest build](http://code.jquery.com/jquery.js). - -### Try an older version of jQuery +Bugs in old versions of jQuery may have already been fixed. In order to avoid reporting known issues, make sure you are always testing against the [latest build](http://code.jquery.com/jquery.js). We cannot fix bugs in older released files, if a bug has been fixed in a subsequent version of jQuery the site should upgrade. -Sometimes, bugs are introduced in newer versions of jQuery that do not exist in previous versions. When possible, it can be useful to try testing with an older release. +### Simplify the test case -### Reduce, reduce, reduce! +When experiencing a problem, [reduce your code](http://webkit.org/quality/reduction.html) to the bare minimum required to reproduce the issue. This makes it *much* easier to isolate and fix the offending code. Bugs reported without reduced test cases take on average 9001% longer to fix than bugs that are submitted with them, so you really should try to do this if at all possible. -When you are experiencing a problem, the most useful thing you can possibly do is to [reduce your code](http://webkit.org/quality/reduction.html) to the bare minimum required to reproduce the issue. This makes it *much* easier to isolate and fix the offending code. Bugs that are reported without reduced test cases take on average 9001% longer to fix than bugs that are submitted with them, so you really should try to do this if at all possible. +### Search for related or duplicate issues -## jQuery Core Style Guide +Go to the [jQuery Core issue tracker](https://github.com/jquery/jquery/issues) and make sure the problem hasn't already been reported. If not, create a new issue there and include your test case. -See: [jQuery Core Style Guide](http://docs.jquery.com/JQuery_Core_Style_Guidelines) ## Tips For Bug Patching - -### Environment: localhost w/ PHP, Node & Grunt - -Starting in jQuery 1.8, a newly overhauled development workflow has been introduced. In this new system, we rely on node & gruntjs to automate the building and validation of source code—while you write code. - -The Ajax tests still depend on PHP running locally*, so make sure you have the following installed: - -* Some kind of localhost server program that supports PHP (any will do) -* Node.js -* NPM (comes with the latest version of Node.js) -* Grunt (install with: `npm install grunt -g` - - -Maintaining a list of platform specific instructions is outside of the scope of this document and there is plenty of existing documentation for the above technologies. - -* The PHP dependency will soon be shed in favor of an all-node solution. - +We *love* when people contribute back to the project by patching the bugs they find. Since jQuery is used by so many people, we are cautious about the patches we accept and want to be sure they don't have a negative impact on the millions of people using jQuery each day. For that reason it can take a while for any suggested patch to work its way through the review and release process. The reward for you is knowing that the problem you fixed will improve things for millions of sites and billions of visits per day. ### Build a Local Copy of jQuery @@ -115,83 +95,21 @@ Get in the habit of pulling in the "upstream" master to stay up to date as jQuer $ git pull upstream master ``` -Run the Grunt tools: - -```bash -$ grunt && grunt watch -``` - -Now open the jQuery test suite in a browser at http://localhost/test. If there is a port, be sure to include it. - -Success! You just built and tested jQuery! - - -### Fix a bug from a ticket filed at bugs.jquery.com: - -**NEVER write your patches to the master branch** - it gets messy (I say this from experience!) - -**ALWAYS USE A "TOPIC" BRANCH!** Like so (#### = the ticket #)... - -Make sure you start with your up-to-date master: +Run the build script ```bash -$ git checkout master +$ npm run build ``` -Create and checkout a new branch that includes the ticket # - -```bash -$ git checkout -b bug_#### - -# ( Explanation: this useful command will: -# "checkout" a "-b" (branch) by the name of "bug_####" -# or create it if it doesn't exist ) -``` - -Now you're on branch: bug_#### - -Determine the module/file you'll be working in... - -Open up the corresponding /test/unit/?????.js and add the initial failing unit tests. This may seem awkward at first, but in the long run it will make sense. To truly and efficiently patch a bug, you need to be working against that bug. - -Next, open the module files and make your changes - -Run http://localhost/test --> **ALL TESTS MUST PASS** - -Once you're satisfied with your patch... - -Stage the files to be tracked: - -```bash -$ git add filename -# (you can use "git status" to list the files you've changed) -``` - - -( I recommend NEVER, EVER using "git add . " ) - -Once you've staged all of your changed files, go ahead and commit them - -```bash -$ git commit -m "Brief description of fix. Fixes #0000" -``` - -For a multiple line commit message, leave off the `-m "description"`. - -You will then be led into vi (or the text editor that you have set up) to complete your commit message. - -Then, push your branch with the bug fix commits to your github fork +Run the Grunt tools: ```bash -$ git push origin -u bug_#### +$ grunt && grunt watch ``` -Before you tackle your next bug patch, return to the master: - -```bash -$ git checkout master -``` +Now open the jQuery test suite in a browser at http://localhost/test. If there is a port, be sure to include it. +Success! You just built and tested jQuery! ### Test Suite Tips... @@ -207,10 +125,8 @@ This will only run the "css" module tests. This will significantly speed up your **ALWAYS RUN THE FULL SUITE BEFORE COMMITTING AND PUSHING A PATCH!** -### jQuery supports the following browsers: +### Browser support + +Remember that jQuery supports multiple browsers and their versions; any contributed code must work in all of them. You can refer to the [browser support page](http://jquery.com/browser-support/) for the current list of supported browsers. -* Chrome Current-1 -* Safari Current-1 -* Firefox Current-1 -* IE 6+ -* Opera Current-1 +Note that browser support differs depending on whether you are targeting the `master` or `1.x` branch. diff --git a/Gruntfile.js b/Gruntfile.js index ea51d180a8..41d7b616d7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,464 +1,208 @@ module.exports = function( grunt ) { - "use strict"; - var distpaths = [ - "dist/jquery.js", - "dist/jquery.min.map", - "dist/jquery.min.js" - ], - readOptionalJSON = function( filepath ) { - var data = {}; + function readOptionalJSON( filepath ) { + var data = {}; + try { + data = JSON.parse( stripJSONComments( + fs.readFileSync( filepath, { encoding: "utf8" } ) + ) ); + } catch ( e ) {} + return data; + } + + var fs = require( "fs" ), + stripJSONComments = require( "strip-json-comments" ), + gzip = require( "gzip-js" ), + srcHintOptions = readOptionalJSON( "src/.jshintrc" ), + newNode = !/^v0/.test( process.version ), + + // Allow to skip jsdom-related tests in Node.js < 1.0.0 + runJsdomTests = newNode || ( function() { try { - data = grunt.file.readJSON( filepath ); - } catch(e) {} - return data; - }; - - grunt.initConfig({ - pkg: grunt.file.readJSON("package.json"), - dst: readOptionalJSON("dist/.destination.json"), - compare_size: { - files: distpaths + require( "jsdom" ); + return true; + } catch ( e ) { + return false; + } + } )(); + + // The concatenated file won't pass onevar + // But our modules can + delete srcHintOptions.onevar; + + grunt.initConfig( { + pkg: grunt.file.readJSON( "package.json" ), + dst: readOptionalJSON( "dist/.destination.json" ), + "compare_size": { + files: [ "dist/jquery.js", "dist/jquery.min.js" ], + options: { + compress: { + gz: function( contents ) { + return gzip.zip( contents, {} ).length; + } + }, + cache: "build/.sizecache.json" + } }, - selector: { - "src/selector.js": [ - "src/sizzle-jquery.js", - "src/sizzle/sizzle.js" - ] + babel: { + options: { + sourceMap: "inline", + retainLines: true + }, + nodeSmokeTests: { + files: { + "test/node_smoke_tests/lib/ensure_iterability.js": + "test/node_smoke_tests/lib/ensure_iterability_es6.js" + } + } }, build: { - "dist/jquery.js": [ - "src/intro.js", - "src/core.js", - "src/callbacks.js", - "src/deferred.js", - "src/support.js", - "src/data.js", - "src/queue.js", - "src/attributes.js", - "src/event.js", - "src/selector.js", - "src/traversing.js", - "src/manipulation.js", + all: { + dest: "dist/jquery.js", + minimum: [ + "core", + "selector" + ], + removeWith: { + ajax: [ "manipulation/_evalUrl", "event/ajax" ], + callbacks: [ "deferred" ], + css: [ "effects", "dimensions", "offset" ] + } + } + }, + npmcopy: { + all: { + options: { + destPrefix: "external" + }, + files: { + "sizzle/dist": "sizzle/dist", + "sizzle/LICENSE.txt": "sizzle/LICENSE.txt", - { flag: "css", src: "src/css.js" }, - "src/serialize.js", - { flag: "ajax", src: "src/ajax.js" }, - { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"] }, - { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ] }, - { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"] }, - { flag: "effects", src: "src/effects.js", needs: ["css"] }, - { flag: "offset", src: "src/offset.js", needs: ["css"] }, - { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] }, - { flag: "deprecated", src: "src/deprecated.js" }, + "qunit/qunit.js": "qunitjs/qunit/qunit.js", + "qunit/qunit.css": "qunitjs/qunit/qunit.css", + "qunit/LICENSE.txt": "qunitjs/LICENSE.txt", - "src/exports.js", - "src/outro.js" - ] - }, + "qunit-assert-step/qunit-assert-step.js": + "qunit-assert-step/qunit-assert-step.js", + "qunit-assert-step/MIT-LICENSE.txt": + "qunit-assert-step/MIT-LICENSE.txt", - jshint: { - dist: { - src: [ "dist/jquery.js" ], - options: { - jshintrc: "src/.jshintrc" + "requirejs/require.js": "requirejs/require.js", + + "sinon/fake_timers.js": "sinon/lib/sinon/util/fake_timers.js", + "sinon/timers_ie.js": "sinon/lib/sinon/util/timers_ie.js", + "sinon/LICENSE.txt": "sinon/LICENSE" } - }, - grunt: { - src: [ "Gruntfile.js" ], + } + }, + jsonlint: { + pkg: { + src: [ "package.json" ] + } + }, + jshint: { + all: { + src: [ + "src/**/*.js", "Gruntfile.js", "test/**/*.js", "build/**/*.js" + ], options: { - jshintrc: ".jshintrc" + jshintrc: true } }, - tests: { - // TODO: Once .jshintignore is supported, use that instead. - // issue located here: https://github.com/gruntjs/grunt-contrib-jshint/issues/1 - src: [ "test/data/{test,testinit,testrunner}.js", "test/unit/**/*.js" ], - options: { - jshintrc: "test/.jshintrc" - } + dist: { + src: "dist/jquery.js", + options: srcHintOptions } }, - + jscs: { + src: "src", + gruntfile: "Gruntfile.js", + + // Check parts of tests that pass + test: [ + "test/data/testrunner.js", + "test/unit/basic.js", + "test/unit/wrap.js" + ], + build: "build" + }, testswarm: { - tests: "ajax attributes callbacks core css data deferred dimensions effects event manipulation offset queue selector serialize support traversing Sizzle".split(" ") + tests: [ + + // A special module with basic tests, meant for + // not fully supported environments like Android 2.3, + // jsdom or PhantomJS. We run it everywhere, though, + // to make sure tests are not broken. + "basic", + + "ajax", + "attributes", + "callbacks", + "core", + "css", + "data", + "deferred", + "deprecated", + "dimensions", + "effects", + "event", + "manipulation", + "offset", + "queue", + "selector", + "serialize", + "support", + "traversing" + ] }, - watch: { - files: [ "<%= jshint.grunt.src %>", "<%= jshint.tests.src %>", "src/**/*.js" ], - tasks: "dev" + files: [ "<%= jshint.all.src %>" ], + tasks: [ "dev" ] }, - uglify: { all: { files: { "dist/jquery.min.js": [ "dist/jquery.js" ] }, options: { - banner: "/*! jQuery v<%= pkg.version %> jquery.com | jquery.org/license */", - sourceMap: "dist/jquery.min.map", + preserveComments: false, + sourceMap: true, + sourceMapName: "dist/jquery.min.map", + report: "min", beautify: { - ascii_only: true + "ascii_only": true + }, + banner: "/*! jQuery v<%= pkg.version %> | " + + "(c) jQuery Foundation | jquery.org/license */", + compress: { + "hoist_funs": false, + loops: false, + unused: false } } } } - }); - - grunt.registerTask( "testswarm", function( commit, configFile ) { - var jobName, - testswarm = require( "testswarm" ), - testUrls = [], - pull = /PR-(\d+)/.exec( commit ), - config = grunt.file.readJSON( configFile ).jquery, - tests = grunt.config([ this.name, "tests" ]); - - if ( pull ) { - jobName = "jQuery pull #" + pull[ 1 ] + ""; - } else { - jobName = "jQuery commit #" + commit.substr( 0, 10 ) + ""; - } - - tests.forEach(function( test ) { - testUrls.push( config.testUrl + commit + "/test/index.html?module=" + test ); - }); - - testswarm({ - url: config.swarmUrl, - pollInterval: 10000, - timeout: 1000 * 60 * 30, - done: this.async() - }, { - authUsername: config.authUsername, - authToken: config.authToken, - jobName: jobName, - runMax: config.runMax, - "runNames[]": tests, - "runUrls[]": testUrls, - "browserSets[]": config.browserSets - }); - }); - - // Build src/selector.js - grunt.registerMultiTask( "selector", "Build src/selector.js", function() { - - var name = this.file.dest, - files = this.file.src, - sizzle = { - api: grunt.file.read( files[0] ), - src: grunt.file.read( files[1] ) - }, - compiled, parts; - - /** - - sizzle-jquery.js -> sizzle between "EXPOSE" blocks, - replace define & window.Sizzle assignment - - - // EXPOSE - if ( typeof define === "function" && define.amd ) { - define(function() { return Sizzle; }); - } else { - window.Sizzle = Sizzle; - } - // EXPOSE - - Becomes... - - Sizzle.attr = jQuery.attr; - jQuery.find = Sizzle; - jQuery.expr = Sizzle.selectors; - jQuery.expr[":"] = jQuery.expr.pseudos; - jQuery.unique = Sizzle.uniqueSort; - jQuery.text = Sizzle.getText; - jQuery.isXMLDoc = Sizzle.isXML; - jQuery.contains = Sizzle.contains; - - */ - - // Break into 3 pieces - parts = sizzle.src.split("// EXPOSE"); - // Replace the if/else block with api - parts[1] = sizzle.api; - // Rejoin the pieces - compiled = parts.join(""); - - grunt.verbose.write("Injected sizzle-jquery.js into sizzle.js"); - - // Write concatenated source to file - grunt.file.write( name, compiled ); - - // Fail task if errors were logged. - if ( this.errorCount ) { - return false; - } + } ); - // Otherwise, print a success message. - grunt.log.writeln( "File '" + name + "' created." ); - }); - - - // Special "alias" task to make custom build creation less grawlix-y - grunt.registerTask( "custom", function() { - var done = this.async(), - args = [].slice.call(arguments), - modules = args.length ? args[0].replace(/,/g, ":") : ""; - - - // Translation example - // - // grunt custom:+ajax,-dimensions,-effects,-offset - // - // Becomes: - // - // grunt build:*:*:+ajax:-dimensions:-effects:-offset - - grunt.log.writeln( "Creating custom build...\n" ); - - grunt.util.spawn({ - cmd: process.platform === "win32" ? "grunt.cmd" : "grunt", - args: [ "build:*:*:" + modules, "uglify", "dist" ] - }, function( err, result ) { - if ( err ) { - grunt.verbose.error(); - done( err ); - return; - } - - grunt.log.writeln( result.stdout.replace("Done, without errors.", "") ); - - done(); - }); - }); - - // Special concat/build task to handle various jQuery build requirements - // - grunt.registerMultiTask( - "build", - "Concatenate source (include/exclude modules with +/- flags), embed date/version", - function() { - // Concat specified files. - var compiled = "", - modules = this.flags, - optIn = !modules["*"], - explicit = optIn || Object.keys(modules).length > 1, - name = this.file.dest, - src = this.file.srcRaw, - deps = {}, - excluded = {}, - version = grunt.config( "pkg.version" ), - excluder = function( flag, needsFlag ) { - // optIn defaults implicit behavior to weak exclusion - if ( optIn && !modules[ flag ] && !modules[ "+" + flag ] ) { - excluded[ flag ] = false; - } - - // explicit or inherited strong exclusion - if ( excluded[ needsFlag ] || modules[ "-" + flag ] ) { - excluded[ flag ] = true; - - // explicit inclusion overrides weak exclusion - } else if ( excluded[ needsFlag ] === false && - ( modules[ flag ] || modules[ "+" + flag ] ) ) { - - delete excluded[ needsFlag ]; - - // ...all the way down - if ( deps[ needsFlag ] ) { - deps[ needsFlag ].forEach(function( subDep ) { - modules[ needsFlag ] = true; - excluder( needsFlag, subDep ); - }); - } - } - }; - - // append commit id to version - if ( process.env.COMMIT ) { - version += " " + process.env.COMMIT; - } - - // figure out which files to exclude based on these rules in this order: - // dependency explicit exclude - // > explicit exclude - // > explicit include - // > dependency implicit exclude - // > implicit exclude - // examples: - // * none (implicit exclude) - // *:* all (implicit include) - // *:*:-css all except css and dependents (explicit > implicit) - // *:*:-css:+effects same (excludes effects because explicit include is trumped by explicit exclude of dependency) - // *:+effects none except effects and its dependencies (explicit include trumps implicit exclude of dependency) - src.forEach(function( filepath ) { - var flag = filepath.flag; - - if ( flag ) { - - excluder(flag); - - // check for dependencies - if ( filepath.needs ) { - deps[ flag ] = filepath.needs; - filepath.needs.forEach(function( needsFlag ) { - excluder( flag, needsFlag ); - }); - } - } - }); - - // append excluded modules to version - if ( Object.keys( excluded ).length ) { - version += " -" + Object.keys( excluded ).join( ",-" ); - // set pkg.version to version with excludes, so minified file picks it up - grunt.config.set( "pkg.version", version ); - } - - - // conditionally concatenate source - src.forEach(function( filepath ) { - var flag = filepath.flag, - specified = false, - omit = false, - message = ""; - - if ( flag ) { - if ( excluded[ flag ] !== undefined ) { - message = ( "Excluding " + flag ).red; - specified = true; - omit = true; - } else { - message = ( "Including " + flag ).green; - - // If this module was actually specified by the - // builder, then st the flag to include it in the - // output list - if ( modules[ "+" + flag ] ) { - specified = true; - } - } - - // Only display the inclusion/exclusion list when handling - // an explicit list. - // - // Additionally, only display modules that have been specified - // by the user - if ( explicit && specified ) { - grunt.log.writetableln([ 27, 30 ], [ - message, - ( "(" + filepath.src + ")").grey - ]); - } - - filepath = filepath.src; - } - - if ( !omit ) { - compiled += grunt.file.read( filepath ); - } - }); - - // Embed Version - // Embed Date - compiled = compiled.replace( /@VERSION/g, version ) - .replace( "@DATE", function () { - var date = new Date(); - - // YYYY-MM-DD - return [ - date.getFullYear(), - date.getMonth() + 1, - date.getDate() - ].join( "-" ); - }); - - // Write concatenated source to file - grunt.file.write( name, compiled ); - - // Fail task if errors were logged. - if ( this.errorCount ) { - return false; - } - - // Otherwise, print a success message. - grunt.log.writeln( "File '" + name + "' created." ); - }); - - // Process files for distribution - grunt.registerTask( "dist", function() { - var flags, paths, stored; - - // Check for stored destination paths - // ( set in dist/.destination.json ) - stored = Object.keys( grunt.config("dst") ); - - // Allow command line input as well - flags = Object.keys( this.flags ); - - // Combine all output target paths - paths = [].concat( stored, flags ).filter(function( path ) { - return path !== "*"; - }); - - // Ensure the dist files are pure ASCII - var fs = require("fs"), - nonascii = false; - - distpaths.forEach(function( filename ) { - var text = fs.readFileSync( filename, "utf8" ), - i, c; - if ( text.length !== Buffer.byteLength( text, "utf8" ) ) { - grunt.log.writeln( filename + ": Non-ASCII characters detected:" ); - for ( i = 0; i < text.length; i++ ) { - c = text.charCodeAt( i ); - if ( c > 127 ) { - grunt.log.writeln( "- position " + i + ": " + c ); - grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) ); - nonascii = true; - break; - } - } - } - - // Modify map so that it points to files in the same folder; - // see https://github.com/mishoo/UglifyJS2/issues/47 - if ( /\.map$/.test( filename ) ) { - text = text.replace( /"dist\//g, "\"" ); - fs.writeFileSync( filename, text, "utf-8" ); - } - - // Optionally copy dist files to other locations - paths.forEach(function( path ) { - var created; - - if ( !/\/$/.test( path ) ) { - path += "/"; - } + // Load grunt tasks from NPM packages + require( "load-grunt-tasks" )( grunt ); - created = path + filename.replace( "dist/", "" ); - grunt.file.write( created, text ); - grunt.log.writeln( "File '" + created + "' created." ); - }); - }); + // Integrate jQuery specific tasks + grunt.loadTasks( "build/tasks" ); - return !nonascii; - }); + grunt.registerTask( "lint", [ "jsonlint", "jshint", "jscs" ] ); - // Load grunt tasks from NPM packages - grunt.loadNpmTasks("grunt-compare-size"); - grunt.loadNpmTasks("grunt-git-authors"); - grunt.loadNpmTasks("grunt-update-submodules"); - grunt.loadNpmTasks("grunt-contrib-watch"); - grunt.loadNpmTasks("grunt-contrib-jshint"); - grunt.loadNpmTasks("grunt-contrib-uglify"); + // Don't run Node-related tests in Node.js < 1.0.0 as they require an old + // jsdom version that needs compiling, making it harder for people to compile + // jQuery on Windows. (see gh-2519) + grunt.registerTask( "test_fast", runJsdomTests ? [ "node_smoke_tests" ] : [] ); - // Default grunt - grunt.registerTask( "default", [ "update_submodules", "selector", "build:*:*", "jshint", "uglify", "dist:*", "compare_size" ] ); + grunt.registerTask( "test", [ "test_fast" ] ); // Short list as a high frequency watch task - grunt.registerTask( "dev", [ "selector", "build:*:*", "jshint" ] ); + grunt.registerTask( "dev", [ "build:*:*", "lint", "uglify", "remove_map_comment", "dist:*" ] ); + + grunt.registerTask( "default", [ "dev", "test_fast", "compare_size" ] ); }; diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..5312a4c864 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,36 @@ +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. diff --git a/README.md b/README.md index 6eba3ba522..388574b745 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[jQuery](http://jquery.com/) - New Wave JavaScript +[jQuery](http://jquery.com/) — New Wave JavaScript ================================================== Contribution Guides @@ -6,153 +6,165 @@ Contribution Guides In the spirit of open source software development, jQuery always encourages community code contribution. To help you get started and before you jump into writing code, be sure to read these important contribution guidelines thoroughly: -1. [Getting Involved](http://docs.jquery.com/Getting_Involved) -2. [Core Style Guide](http://docs.jquery.com/JQuery_Core_Style_Guidelines) -3. [Tips For Bug Patching](http://docs.jquery.com/Tips_for_jQuery_Bug_Patching) +1. [Getting Involved](http://contribute.jquery.org/) +2. [Core Style Guide](http://contribute.jquery.org/style-guide/js/) +3. [Writing Code for jQuery Foundation Projects](http://contribute.jquery.org/code/) -What you need to build your own jQuery +Environments in which to use jQuery -------------------------------------- -In order to build jQuery, you need to have GNU make 3.8 or later, Node.js/npm latest, and git 1.7 or later. -(Earlier versions might work OK, but are not tested.) +- [Browser support](http://jquery.com/browser-support/) differs between the master branch and the 1.x branch. Specifically, the master branch does not support legacy browsers such as IE6-8. The jQuery team continues to provide support for legacy browsers on the 1.x branch. Use the latest 1.x release if support for those browsers is required. See [browser support](http://jquery.com/browser-support/) for more info. +- To use jQuery in Node, browser extensions, and other non-browser environments, use only master branch releases (2.x). The 1.x branch does not support these environments. + + +What you need to build your own jQuery +-------------------------------------- -Windows users have two options: +In order to build jQuery, you need to have the latest Node.js/npm and git 1.7 or later. Earlier versions might work, but are not supported. -1. Install [msysgit](https://code.google.com/p/msysgit/) (Full installer for official Git), - [GNU make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm), and a - [binary version of Node.js](http://node-js.prcn.co.cc/). Make sure all three packages are installed to the same - location (by default, this is C:\Program Files\Git). -2. Install [Cygwin](http://cygwin.com/) (make sure you install the git, make, and which packages), then either follow - the [Node.js build instructions](https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-%28Windows%29) or install - the [binary version of Node.js](http://node-js.prcn.co.cc/). +For Windows, you have to download and install [git](http://git-scm.com/downloads) and [Node.js](http://nodejs.org/download/). -Mac OS users should install Xcode (comes on your Mac OS install DVD, or downloadable from -[Apple's Xcode site](http://developer.apple.com/technologies/xcode.html)) and -[Homebrew](http://mxcl.github.com/homebrew/). Once Homebrew is installed, run `brew install git` to install git, +OS X users should install [Homebrew](http://brew.sh/). Once Homebrew is installed, run `brew install git` to install git, and `brew install node` to install Node.js. -Linux/BSD users should use their appropriate package managers to install make, git, and node, or build from source +Linux/BSD users should use their appropriate package managers to install git and Node.js, or build from source if you swing that way. Easy-peasy. How to build your own jQuery ---------------------------- -First, clone a copy of the main jQuery git repo by running: +Clone a copy of the main jQuery git repo by running: ```bash git clone git://github.com/jquery/jquery.git ``` -Enter the directory and install the Node dependencies: - +Enter the jquery directory and run the build script: ```bash -cd jquery && npm install +cd jquery && npm run build ``` +The built version of jQuery will be put in the `dist/` subdirectory, along with the minified copy and associated map file. +If you want to create custom build or help with jQuery development, it would be better to install [grunt command line interface](https://github.com/gruntjs/grunt-cli) as a global package: +``` +npm install -g grunt-cli +``` Make sure you have `grunt` installed by testing: - -```bash -grunt -version +``` +grunt -V ``` - - -Then, to get a complete, minified (w/ Uglify.js), linted (w/ JSHint) version of jQuery, type the following: - -```bash +Now by running the `grunt` command, in the jquery directory, you can build a full version of jQuery, just like with an `npm run build` command: +``` grunt ``` +There are many other tasks available for jQuery Core: +``` +grunt -help +``` -The built version of jQuery will be put in the `dist/` subdirectory. +### Modules +Special builds can be created that exclude subsets of jQuery functionality. +This allows for smaller custom builds when the builder is certain that those parts of jQuery are not being used. +For example, an app that only used JSONP for `$.ajax()` and did not need to calculate offsets or positions of elements could exclude the offset and ajax/xhr modules. -### Modules (new in 1.8) +Any module may be excluded except for `core`, and `selector`. To exclude a module, pass its path relative to the `src` folder (without the `.js` extension). -Starting in jQuery 1.8, special builds can now be created that optionally exclude or include any of the following modules: +Some example modules that can be excluded are: -- ajax -- css -- dimensions -- effects -- offset +- **ajax**: All AJAX functionality: `$.ajax()`, `$.get()`, `$.post()`, `$.ajaxSetup()`, `.load()`, transports, and ajax event shorthands such as `.ajaxStart()`. +- **ajax/xhr**: The XMLHTTPRequest AJAX transport only. +- **ajax/script**: The ` - - - - - - -
-

Hello

-
-
-

lorem ipsum

-

dolor sit amet

-
-
-
-
    - - - diff --git a/speed/css.html b/speed/css.html deleted file mode 100644 index 4d6700cf48..0000000000 --- a/speed/css.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - Test Event Handling Performance - - - - - - - - -

    Getting Values: Loading...

    -

    Setting Values: Loading...

    - - diff --git a/speed/event.html b/speed/event.html deleted file mode 100644 index 6d463b4d81..0000000000 --- a/speed/event.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - Test Event Handling Performance - - - - - - - -

    Move the mouse, please!

    -

    - - diff --git a/speed/filter.html b/speed/filter.html deleted file mode 100644 index e19e82c61a..0000000000 --- a/speed/filter.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - Test .filter() Performance - - - - - - - -
    -

    Hello

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
      - - - diff --git a/speed/find.html b/speed/find.html deleted file mode 100644 index 01ed1d3a1d..0000000000 --- a/speed/find.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - Test .find() Performance - - - - - - - -
      -

      Hello

      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      -
        - - - diff --git a/speed/index.html b/speed/index.html deleted file mode 100644 index d5810a2d18..0000000000 --- a/speed/index.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - Speed Test - - - - - - - - -

        Speed Test

        -
        -
        -

        Using the following selector expressions ( times each):

        -

        NOTE: Number shown is an average.

        -
        - - - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - -
        Run?Test
        - -
        - - - - - - -
        -
        -
        - - - - - diff --git a/speed/jquery-basis.js b/speed/jquery-basis.js deleted file mode 100644 index f5cb04ce36..0000000000 --- a/speed/jquery-basis.js +++ /dev/null @@ -1,6238 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Sat Feb 13 22:33:48 2010 -0500 - */ -(function( window, undefined ) { - -// Define a local copy of jQuery -var jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); - }, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - - // A central reference to the root jQuery(document) - rootjQuery, - - // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, - - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/, - - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, - - // Used for trimming whitespace - rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - - // Keep a UserAgent string for use with jQuery.browser - userAgent = navigator.userAgent, - - // For matching the engine and version of the browser - browserMatch, - - // Has the ready events already been bound? - readyBound = false, - - // The functions to execute on DOM ready - readyList = [], - - // The ready event handler - DOMContentLoaded, - - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwnProperty = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - indexOf = Array.prototype.indexOf; - -jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // The body element only exists once, optimize finding it - if ( selector === "body" && !context ) { - this.context = document; - this[0] = document.body; - this.selector = "body"; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - match = quickExpr.exec( selector ); - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - doc = (context ? context.ownerDocument || context : document); - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); - - if ( ret ) { - if ( jQuery.isPlainObject( context ) ) { - selector = [ document.createElement( ret[1] ) ]; - jQuery.fn.attr.call( selector, context, true ); - - } else { - selector = [ doc.createElement( ret[1] ) ]; - } - - } else { - ret = buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; - } - - return jQuery.merge( this, selector ); - - // HANDLE: $("#id") - } else { - elem = document.getElementById( match[2] ); - - if ( elem ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $("TAG") - } else if ( !context && /^\w+$/.test( selector ) ) { - this.selector = selector; - this.context = document; - selector = document.getElementsByTagName( selector ); - return jQuery.merge( this, selector ); - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return (context || rootjQuery).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return jQuery( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if (selector.selector !== undefined) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.4.2", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return slice.call( this, 0 ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = jQuery(); - - if ( jQuery.isArray( elems ) ) { - push.apply( ret, elems ); - - } else { - jQuery.merge( ret, elems ); - } - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + (this.selector ? " " : "") + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Attach the listeners - jQuery.bindReady(); - - // If the DOM is already ready - if ( jQuery.isReady ) { - // Execute the function immediately - fn.call( document, jQuery ); - - // Otherwise, remember the function for later - } else if ( readyList ) { - // Add the function to the wait list - readyList.push( fn ); - } - - return this; - }, - - eq: function( i ) { - return i === -1 ? - this.slice( i ) : - this.slice( i, +i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || jQuery(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging object literal values or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { - var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src - : jQuery.isArray(copy) ? [] : {}; - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - noConflict: function( deep ) { - window.$ = _$; - - if ( deep ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // Handle when the DOM is ready - ready: function() { - // Make sure that the DOM is not already loaded - if ( !jQuery.isReady ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 13 ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If there are functions bound, to execute - if ( readyList ) { - // Execute all of them - var fn, i = 0; - while ( (fn = readyList[ i++ ]) ) { - fn.call( document, jQuery ); - } - - // Reset the list of functions - readyList = null; - } - - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - } - } - }, - - bindReady: function() { - if ( readyBound ) { - return; - } - - readyBound = true; - - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - return jQuery.ready(); - } - - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); - - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent("onreadystatechange", DOMContentLoaded); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; - - try { - toplevel = window.frameElement == null; - } catch(e) {} - - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, - - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { - return false; - } - - // Not own constructor property must be Object - if ( obj.constructor - && !hasOwnProperty.call(obj, "constructor") - && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwnProperty.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw msg; - }, - - parseJSON: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") - .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) { - - // Try to use the native JSON parser first - return window.JSON && window.JSON.parse ? - window.JSON.parse( data ) : - (new Function("return " + data))(); - - } else { - jQuery.error( "Invalid JSON: " + data ); - } - }, - - noop: function() {}, - - // Evaluates a script in a global context - globalEval: function( data ) { - if ( data && rnotwhite.test(data) ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement, - script = document.createElement("script"); - - if ( jQuery.support.scriptEval ) { - script.appendChild( document.createTextNode( data ) ); - } else { - script.text = data; - } - - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709). - head.insertBefore( script, head.firstChild ); - head.removeChild( script ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || jQuery.isFunction(object); - - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} - } - } - - return object; - }, - - trim: function( text ) { - return (text || "").replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; - - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // The extra typeof function check is to prevent crashes - // in Safari 2 (See: #3039) - if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { - push.call( ret, array ); - } else { - jQuery.merge( ret, array ); - } - } - - return ret; - }, - - inArray: function( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; - }, - - merge: function( first, second ) { - var i = first.length, j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var ret = []; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - if ( !inv !== !callback( elems[ i ], i ) ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var ret = [], value; - - // Go through the array, translating each of the items to their - // new value (or values). - for ( var i = 0, length = elems.length; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - proxy: function( fn, proxy, thisObject ) { - if ( arguments.length === 2 ) { - if ( typeof proxy === "string" ) { - thisObject = fn; - fn = thisObject[ proxy ]; - proxy = undefined; - - } else if ( proxy && !jQuery.isFunction( proxy ) ) { - thisObject = proxy; - proxy = undefined; - } - } - - if ( !proxy && fn ) { - proxy = function() { - return fn.apply( thisObject || this, arguments ); - }; - } - - // Set the guid of unique handler to the same of original handler, so it can be removed - if ( fn ) { - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - } - - // So proxy can be declared as an argument - return proxy; - }, - - // Use of jQuery.browser is frowned upon. - // More details: http://docs.jquery.com/Utilities/jQuery.browser - uaMatch: function( ua ) { - ua = ua.toLowerCase(); - - var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) || - /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) || - /(msie) ([\w.]+)/.exec( ua ) || - !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) || - []; - - return { browser: match[1] || "", version: match[2] || "0" }; - }, - - browser: {} -}); - -browserMatch = jQuery.uaMatch( userAgent ); -if ( browserMatch.browser ) { - jQuery.browser[ browserMatch.browser ] = true; - jQuery.browser.version = browserMatch.version; -} - -// Deprecated, use jQuery.browser.webkit instead -if ( jQuery.browser.webkit ) { - jQuery.browser.safari = true; -} - -if ( indexOf ) { - jQuery.inArray = function( elem, array ) { - return indexOf.call( array, elem ); - }; -} - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); - -// Cleanup functions for the document ready method -if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - }; - -} else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }; -} - -// The DOM ready check for Internet Explorer -function doScrollCheck() { - if ( jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch( error ) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - jQuery.ready(); -} - -function evalScript( i, elem ) { - if ( elem.src ) { - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - } else { - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } -} - -// Mutifunctional method to get and set values to a collection -// The value/s can be optionally by executed if its a function -function access( elems, key, value, exec, fn, pass ) { - var length = elems.length; - - // Setting many attributes - if ( typeof key === "object" ) { - for ( var k in key ) { - access( elems, k, key[k], exec, fn, value ); - } - return elems; - } - - // Setting one attribute - if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = !pass && exec && jQuery.isFunction(value); - - for ( var i = 0; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - - return elems; - } - - // Getting an attribute - return length ? fn( elems[0], key ) : undefined; -} - -function now() { - return (new Date).getTime(); -} -(function() { - - jQuery.support = {}; - - var root = document.documentElement, - script = document.createElement("script"), - div = document.createElement("div"), - id = "script" + now(); - - div.style.display = "none"; - div.innerHTML = "
        a"; - - var all = div.getElementsByTagName("*"), - a = div.getElementsByTagName("a")[0]; - - // Can't get basic test support - if ( !all || !all.length || !a ) { - return; - } - - jQuery.support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: div.firstChild.nodeType === 3, - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText insted) - style: /red/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: a.getAttribute("href") === "/a", - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.55$/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: div.getElementsByTagName("input")[0].value === "on", - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected, - - parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null, - - // Will be defined later - deleteExpando: true, - checkClone: false, - scriptEval: false, - noCloneEvent: true, - boxModel: null - }; - - script.type = "text/javascript"; - try { - script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); - } catch(e) {} - - root.insertBefore( script, root.firstChild ); - - // Make sure that the execution of code works by injecting a script - // tag with appendChild/createTextNode - // (IE doesn't support this, fails, and uses .text instead) - if ( window[ id ] ) { - jQuery.support.scriptEval = true; - delete window[ id ]; - } - - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete script.test; - - } catch(e) { - jQuery.support.deleteExpando = false; - } - - root.removeChild( script ); - - if ( div.attachEvent && div.fireEvent ) { - div.attachEvent("onclick", function click() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - jQuery.support.noCloneEvent = false; - div.detachEvent("onclick", click); - }); - div.cloneNode(true).fireEvent("onclick"); - } - - div = document.createElement("div"); - div.innerHTML = ""; - - var fragment = document.createDocumentFragment(); - fragment.appendChild( div.firstChild ); - - // WebKit doesn't clone checked state correctly in fragments - jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; - - // Figure out if the W3C box model works as expected - // document.body must exist before we can do this - jQuery(function() { - var div = document.createElement("div"); - div.style.width = div.style.paddingLeft = "1px"; - - document.body.appendChild( div ); - jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; - document.body.removeChild( div ).style.display = 'none'; - - div = null; - }); - - // Technique from Juriy Zaytsev - // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ - var eventSupported = function( eventName ) { - var el = document.createElement("div"); - eventName = "on" + eventName; - - var isSupported = (eventName in el); - if ( !isSupported ) { - el.setAttribute(eventName, "return;"); - isSupported = typeof el[eventName] === "function"; - } - el = null; - - return isSupported; - }; - - jQuery.support.submitBubbles = eventSupported("submit"); - jQuery.support.changeBubbles = eventSupported("change"); - - // release memory in IE - root = script = div = all = a = null; -})(); - -jQuery.props = { - "for": "htmlFor", - "class": "className", - readonly: "readOnly", - maxlength: "maxLength", - cellspacing: "cellSpacing", - rowspan: "rowSpan", - colspan: "colSpan", - tabindex: "tabIndex", - usemap: "useMap", - frameborder: "frameBorder" -}; -var expando = "jQuery" + now(), uuid = 0, windowData = {}; - -jQuery.extend({ - cache: {}, - - expando:expando, - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - "object": true, - "applet": true - }, - - data: function( elem, name, data ) { - if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { - return; - } - - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ], cache = jQuery.cache, thisCache; - - if ( !id && typeof name === "string" && data === undefined ) { - return null; - } - - // Compute a unique ID for the element - if ( !id ) { - id = ++uuid; - } - - // Avoid generating a new cache unless none exists and we - // want to manipulate it. - if ( typeof name === "object" ) { - elem[ expando ] = id; - thisCache = cache[ id ] = jQuery.extend(true, {}, name); - - } else if ( !cache[ id ] ) { - elem[ expando ] = id; - cache[ id ] = {}; - } - - thisCache = cache[ id ]; - - // Prevent overriding the named cache with undefined values - if ( data !== undefined ) { - thisCache[ name ] = data; - } - - return typeof name === "string" ? thisCache[ name ] : thisCache; - }, - - removeData: function( elem, name ) { - if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { - return; - } - - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( thisCache ) { - // Remove the section of cache data - delete thisCache[ name ]; - - // If we've removed all the data, remove the element's cache - if ( jQuery.isEmptyObject(thisCache) ) { - jQuery.removeData( elem ); - } - } - - // Otherwise, we want to remove all of the element's data - } else { - if ( jQuery.support.deleteExpando ) { - delete elem[ jQuery.expando ]; - - } else if ( elem.removeAttribute ) { - elem.removeAttribute( jQuery.expando ); - } - - // Completely remove the data cache - delete cache[ id ]; - } - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - if ( typeof key === "undefined" && this.length ) { - return jQuery.data( this[0] ); - - } else if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - var parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value === undefined ) { - var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - if ( data === undefined && this.length ) { - data = jQuery.data( this[0], key ); - } - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } else { - return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() { - jQuery.data( this, key, value ); - }); - } - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); -jQuery.extend({ - queue: function( elem, type, data ) { - if ( !elem ) { - return; - } - - type = (type || "fx") + "queue"; - var q = jQuery.data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( !data ) { - return q || []; - } - - if ( !q || jQuery.isArray(data) ) { - q = jQuery.data( elem, type, jQuery.makeArray(data) ); - - } else { - q.push( data ); - } - - return q; - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), fn = queue.shift(); - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - } - - if ( fn ) { - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift("inprogress"); - } - - fn.call(elem, function() { - jQuery.dequeue(elem, type); - }); - } - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - } - - if ( data === undefined ) { - return jQuery.queue( this[0], type ); - } - return this.each(function( i, elem ) { - var queue = jQuery.queue( this, type, data ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; - type = type || "fx"; - - return this.queue( type, function() { - var elem = this; - setTimeout(function() { - jQuery.dequeue( elem, type ); - }, time ); - }); - }, - - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - } -}); -var rclass = /[\n\t]/g, - rspace = /\s+/, - rreturn = /\r/g, - rspecialurl = /href|src|style/, - rtype = /(button|input)/i, - rfocusable = /(button|input|object|select|textarea)/i, - rclickable = /^(a|area)$/i, - rradiocheck = /radio|checkbox/; - -jQuery.fn.extend({ - attr: function( name, value ) { - return access( this, name, value, true, jQuery.attr ); - }, - - removeAttr: function( name, fn ) { - return this.each(function(){ - jQuery.attr( this, name, "" ); - if ( this.nodeType === 1 ) { - this.removeAttribute( name ); - } - }); - }, - - addClass: function( value ) { - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - self.addClass( value.call(this, i, self.attr("class")) ); - }); - } - - if ( value && typeof value === "string" ) { - var classNames = (value || "").split( rspace ); - - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className ) { - elem.className = value; - - } else { - var className = " " + elem.className + " ", setClass = elem.className; - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { - setClass += " " + classNames[c]; - } - } - elem.className = jQuery.trim( setClass ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - self.removeClass( value.call(this, i, self.attr("class")) ); - }); - } - - if ( (value && typeof value === "string") || value === undefined ) { - var classNames = (value || "").split(rspace); - - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; - - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - var className = (" " + elem.className + " ").replace(rclass, " "); - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[c] + " ", " "); - } - elem.className = jQuery.trim( className ); - - } else { - elem.className = ""; - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function(i) { - var self = jQuery(this); - self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, i = 0, self = jQuery(this), - state = stateVal, - classNames = value.split( rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery.data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " "; - for ( var i = 0, l = this.length; i < l; i++ ) { - if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - if ( value === undefined ) { - var elem = this[0]; - - if ( elem ) { - if ( jQuery.nodeName( elem, "option" ) ) { - return (elem.attributes.value || {}).specified ? elem.value : elem.text; - } - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specific value for the option - value = jQuery(option).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - } - - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { - return elem.getAttribute("value") === null ? "on" : elem.value; - } - - - // Everything else, we just grab the value - return (elem.value || "").replace(rreturn, ""); - - } - - return undefined; - } - - var isFunction = jQuery.isFunction(value); - - return this.each(function(i) { - var self = jQuery(this), val = value; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call(this, i, self.val()); - } - - // Typecast each time if the value is a Function and the appended - // value is therefore different each time. - if ( typeof val === "number" ) { - val += ""; - } - - if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { - this.checked = jQuery.inArray( self.val(), val ) >= 0; - - } else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(val); - - jQuery( "option", this ).each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - this.selectedIndex = -1; - } - - } else { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - attrFn: { - val: true, - css: true, - html: true, - text: true, - data: true, - width: true, - height: true, - offset: true - }, - - attr: function( elem, name, value, pass ) { - // don't set attributes on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { - return undefined; - } - - if ( pass && name in jQuery.attrFn ) { - return jQuery(elem)[name](value); - } - - var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), - // Whether we are setting (or getting) - set = value !== undefined; - - // Try to normalize/fix the name - name = notxml && jQuery.props[ name ] || name; - - // Only do all the following if this is a node (faster for style) - if ( elem.nodeType === 1 ) { - // These attributes require special treatment - var special = rspecialurl.test( name ); - - // Safari mis-reports the default selected property of an option - // Accessing the parent's selectedIndex property fixes it - if ( name === "selected" && !jQuery.support.optSelected ) { - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - - // If applicable, access the attribute via the DOM 0 way - if ( name in elem && notxml && !special ) { - if ( set ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } - - elem[ name ] = value; - } - - // browsers index elements by id/name on forms, give priority to attributes. - if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { - return elem.getAttributeNode( name ).nodeValue; - } - - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - if ( name === "tabIndex" ) { - var attributeNode = elem.getAttributeNode( "tabIndex" ); - - return attributeNode && attributeNode.specified ? - attributeNode.value : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - - return elem[ name ]; - } - - if ( !jQuery.support.style && notxml && name === "style" ) { - if ( set ) { - elem.style.cssText = "" + value; - } - - return elem.style.cssText; - } - - if ( set ) { - // convert the value to a string (all browsers do this but IE) see #1070 - elem.setAttribute( name, "" + value ); - } - - var attr = !jQuery.support.hrefNormalized && notxml && special ? - // Some attributes require a special call on IE - elem.getAttribute( name, 2 ) : - elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return attr === null ? undefined : attr; - } - - // elem is actually elem.style ... set the style - // Using attr for specific style information is now deprecated. Use style instead. - return jQuery.style( elem, name, value ); - } -}); -var rnamespaces = /\.(.*)$/, - fcleanup = function( nm ) { - return nm.replace(/[^\w\s\.\|`]/g, function( ch ) { - return "\\" + ch; - }); - }; - -/* - * A number of helper functions used for managing events. - * Many of the ideas behind this code originated from - * Dean Edwards' addEvent library. - */ -jQuery.event = { - - // Bind an event to an element - // Original by Dean Edwards - add: function( elem, types, handler, data ) { - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // For whatever reason, IE has trouble passing the window object - // around, causing it to be cloned in the process - if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) { - elem = window; - } - - var handleObjIn, handleObj; - - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - } - - // Make sure that the function being executed has a unique ID - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure - var elemData = jQuery.data( elem ); - - // If no elemData is found then we must be trying to bind to one of the - // banned noData elements - if ( !elemData ) { - return; - } - - var events = elemData.events = elemData.events || {}, - eventHandle = elemData.handle, eventHandle; - - if ( !eventHandle ) { - elemData.handle = eventHandle = function() { - // Handle the second event of a trigger and when - // an event is called after a page has unloaded - return typeof jQuery !== "undefined" && !jQuery.event.triggered ? - jQuery.event.handle.apply( eventHandle.elem, arguments ) : - undefined; - }; - } - - // Add elem as a property of the handle function - // This is to prevent a memory leak with non-native events in IE. - eventHandle.elem = elem; - - // Handle multiple events separated by a space - // jQuery(...).bind("mouseover mouseout", fn); - types = types.split(" "); - - var type, i = 0, namespaces; - - while ( (type = types[ i++ ]) ) { - handleObj = handleObjIn ? - jQuery.extend({}, handleObjIn) : - { handler: handler, data: data }; - - // Namespaced event handlers - if ( type.indexOf(".") > -1 ) { - namespaces = type.split("."); - type = namespaces.shift(); - handleObj.namespace = namespaces.slice(0).sort().join("."); - - } else { - namespaces = []; - handleObj.namespace = ""; - } - - handleObj.type = type; - handleObj.guid = handler.guid; - - // Get the current list of functions bound to this event - var handlers = events[ type ], - special = jQuery.event.special[ type ] || {}; - - // Init the event handler queue - if ( !handlers ) { - handlers = events[ type ] = []; - - // Check for a special event handler - // Only use addEventListener/attachEvent if the special - // events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add the function to the element's handler list - handlers.push( handleObj ); - - // Keep track of which events have been used, for global triggering - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, pos ) { - // don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, - elemData = jQuery.data( elem ), - events = elemData && elemData.events; - - if ( !elemData || !events ) { - return; - } - - // types is actually an event object here - if ( types && types.type ) { - handler = types.handler; - types = types.type; - } - - // Unbind all events for the element - if ( !types || typeof types === "string" && types.charAt(0) === "." ) { - types = types || ""; - - for ( type in events ) { - jQuery.event.remove( elem, type + types ); - } - - return; - } - - // Handle multiple events separated by a space - // jQuery(...).unbind("mouseover mouseout", fn); - types = types.split(" "); - - while ( (type = types[ i++ ]) ) { - origType = type; - handleObj = null; - all = type.indexOf(".") < 0; - namespaces = []; - - if ( !all ) { - // Namespaced event handlers - namespaces = type.split("."); - type = namespaces.shift(); - - namespace = new RegExp("(^|\\.)" + - jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)") - } - - eventType = events[ type ]; - - if ( !eventType ) { - continue; - } - - if ( !handler ) { - for ( var j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( all || namespace.test( handleObj.namespace ) ) { - jQuery.event.remove( elem, origType, handleObj.handler, j ); - eventType.splice( j--, 1 ); - } - } - - continue; - } - - special = jQuery.event.special[ type ] || {}; - - for ( var j = pos || 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( handler.guid === handleObj.guid ) { - // remove the given handler for the given type - if ( all || namespace.test( handleObj.namespace ) ) { - if ( pos == null ) { - eventType.splice( j--, 1 ); - } - - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - - if ( pos != null ) { - break; - } - } - } - - // remove generic event handler if no more handlers exist - if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { - if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { - removeEvent( elem, type, elemData.handle ); - } - - ret = null; - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - var handle = elemData.handle; - if ( handle ) { - handle.elem = null; - } - - delete elemData.events; - delete elemData.handle; - - if ( jQuery.isEmptyObject( elemData ) ) { - jQuery.removeData( elem ); - } - } - }, - - // bubbling is internal - trigger: function( event, data, elem /*, bubbling */ ) { - // Event object or event type - var type = event.type || event, - bubbling = arguments[3]; - - if ( !bubbling ) { - event = typeof event === "object" ? - // jQuery.Event object - event[expando] ? event : - // Object literal - jQuery.extend( jQuery.Event(type), event ) : - // Just the event type (string) - jQuery.Event(type); - - if ( type.indexOf("!") >= 0 ) { - event.type = type = type.slice(0, -1); - event.exclusive = true; - } - - // Handle a global trigger - if ( !elem ) { - // Don't bubble custom events when global (to avoid too much overhead) - event.stopPropagation(); - - // Only trigger if we've ever bound an event for it - if ( jQuery.event.global[ type ] ) { - jQuery.each( jQuery.cache, function() { - if ( this.events && this.events[type] ) { - jQuery.event.trigger( event, data, this.handle.elem ); - } - }); - } - } - - // Handle triggering a single element - - // don't do events on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { - return undefined; - } - - // Clean up in case it is reused - event.result = undefined; - event.target = elem; - - // Clone the incoming data, if any - data = jQuery.makeArray( data ); - data.unshift( event ); - } - - event.currentTarget = elem; - - // Trigger the event, it is assumed that "handle" is a function - var handle = jQuery.data( elem, "handle" ); - if ( handle ) { - handle.apply( elem, data ); - } - - var parent = elem.parentNode || elem.ownerDocument; - - // Trigger an inline bound script - try { - if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) { - if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) { - event.result = false; - } - } - - // prevent IE from throwing an error for some elements with some event types, see #3533 - } catch (e) {} - - if ( !event.isPropagationStopped() && parent ) { - jQuery.event.trigger( event, data, parent, true ); - - } else if ( !event.isDefaultPrevented() ) { - var target = event.target, old, - isClick = jQuery.nodeName(target, "a") && type === "click", - special = jQuery.event.special[ type ] || {}; - - if ( (!special._default || special._default.call( elem, event ) === false) && - !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { - - try { - if ( target[ type ] ) { - // Make sure that we don't accidentally re-trigger the onFOO events - old = target[ "on" + type ]; - - if ( old ) { - target[ "on" + type ] = null; - } - - jQuery.event.triggered = true; - target[ type ](); - } - - // prevent IE from throwing an error for some elements with some event types, see #3533 - } catch (e) {} - - if ( old ) { - target[ "on" + type ] = old; - } - - jQuery.event.triggered = false; - } - } - }, - - handle: function( event ) { - var all, handlers, namespaces, namespace, events; - - event = arguments[0] = jQuery.event.fix( event || window.event ); - event.currentTarget = this; - - // Namespaced event handlers - all = event.type.indexOf(".") < 0 && !event.exclusive; - - if ( !all ) { - namespaces = event.type.split("."); - event.type = namespaces.shift(); - namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)"); - } - - var events = jQuery.data(this, "events"), handlers = events[ event.type ]; - - if ( events && handlers ) { - // Clone the handlers to prevent manipulation - handlers = handlers.slice(0); - - for ( var j = 0, l = handlers.length; j < l; j++ ) { - var handleObj = handlers[ j ]; - - // Filter the functions by class - if ( all || namespace.test( handleObj.namespace ) ) { - // Pass in a reference to the handler function itself - // So that we can later remove it - event.handler = handleObj.handler; - event.data = handleObj.data; - event.handleObj = handleObj; - - var ret = handleObj.handler.apply( this, arguments ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - - if ( event.isImmediatePropagationStopped() ) { - break; - } - } - } - } - - return event.result; - }, - - props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), - - fix: function( event ) { - if ( event[ expando ] ) { - return event; - } - - // store a copy of the original event object - // and "clone" to set read-only properties - var originalEvent = event; - event = jQuery.Event( originalEvent ); - - for ( var i = this.props.length, prop; i; ) { - prop = this.props[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary - if ( !event.target ) { - event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either - } - - // check if target is a textnode (safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && event.fromElement ) { - event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; - } - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && event.clientX != null ) { - var doc = document.documentElement, body = document.body; - event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); - event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); - } - - // Add which for key events - if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) { - event.which = event.charCode || event.keyCode; - } - - // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) - if ( !event.metaKey && event.ctrlKey ) { - event.metaKey = event.ctrlKey; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && event.button !== undefined ) { - event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); - } - - return event; - }, - - // Deprecated, use jQuery.guid instead - guid: 1E8, - - // Deprecated, use jQuery.proxy instead - proxy: jQuery.proxy, - - special: { - ready: { - // Make sure the ready event is setup - setup: jQuery.bindReady, - teardown: jQuery.noop - }, - - live: { - add: function( handleObj ) { - jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); - }, - - remove: function( handleObj ) { - var remove = true, - type = handleObj.origType.replace(rnamespaces, ""); - - jQuery.each( jQuery.data(this, "events").live || [], function() { - if ( type === this.origType.replace(rnamespaces, "") ) { - remove = false; - return false; - } - }); - - if ( remove ) { - jQuery.event.remove( this, handleObj.origType, liveHandler ); - } - } - - }, - - beforeunload: { - setup: function( data, namespaces, eventHandle ) { - // We only want to do this special case on windows - if ( this.setInterval ) { - this.onbeforeunload = eventHandle; - } - - return false; - }, - teardown: function( namespaces, eventHandle ) { - if ( this.onbeforeunload === eventHandle ) { - this.onbeforeunload = null; - } - } - } - } -}; - -var removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - elem.removeEventListener( type, handle, false ); - } : - function( elem, type, handle ) { - elem.detachEvent( "on" + type, handle ); - }; - -jQuery.Event = function( src ) { - // Allow instantiation without the 'new' keyword - if ( !this.preventDefault ) { - return new jQuery.Event( src ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - // Event type - } else { - this.type = src; - } - - // timeStamp is buggy for some events on Firefox(#3843) - // So we won't rely on the native value - this.timeStamp = now(); - - // Mark it as fixed - this[ expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - } - // otherwise set the returnValue property of the original event to false (IE) - e.returnValue = false; - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Checks if an event happened on an element within another element -// Used in jQuery.event.special.mouseenter and mouseleave handlers -var withinElement = function( event ) { - // Check if mouse(over|out) are still within the same parent element - var parent = event.relatedTarget; - - // Firefox sometimes assigns relatedTarget a XUL element - // which we cannot access the parentNode property of - try { - // Traverse up the tree - while ( parent && parent !== this ) { - parent = parent.parentNode; - } - - if ( parent !== this ) { - // set the correct event type - event.type = event.data; - - // handle event if we actually just moused on to a non sub-element - jQuery.event.handle.apply( this, arguments ); - } - - // assuming we've left the element since we most likely mousedover a xul element - } catch(e) { } -}, - -// In case of event delegation, we only need to rename the event.type, -// liveHandler will take care of the rest. -delegate = function( event ) { - event.type = event.data; - jQuery.event.handle.apply( this, arguments ); -}; - -// Create mouseenter and mouseleave events -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - setup: function( data ) { - jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); - }, - teardown: function( data ) { - jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); - } - }; -}); - -// submit delegation -if ( !jQuery.support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function( data, namespaces ) { - if ( this.nodeName.toLowerCase() !== "form" ) { - jQuery.event.add(this, "click.specialSubmit", function( e ) { - var elem = e.target, type = elem.type; - - if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { - return trigger( "submit", this, arguments ); - } - }); - - jQuery.event.add(this, "keypress.specialSubmit", function( e ) { - var elem = e.target, type = elem.type; - - if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { - return trigger( "submit", this, arguments ); - } - }); - - } else { - return false; - } - }, - - teardown: function( namespaces ) { - jQuery.event.remove( this, ".specialSubmit" ); - } - }; - -} - -// change delegation, happens here so we have bind. -if ( !jQuery.support.changeBubbles ) { - - var formElems = /textarea|input|select/i, - - changeFilters, - - getVal = function( elem ) { - var type = elem.type, val = elem.value; - - if ( type === "radio" || type === "checkbox" ) { - val = elem.checked; - - } else if ( type === "select-multiple" ) { - val = elem.selectedIndex > -1 ? - jQuery.map( elem.options, function( elem ) { - return elem.selected; - }).join("-") : - ""; - - } else if ( elem.nodeName.toLowerCase() === "select" ) { - val = elem.selectedIndex; - } - - return val; - }, - - testChange = function testChange( e ) { - var elem = e.target, data, val; - - if ( !formElems.test( elem.nodeName ) || elem.readOnly ) { - return; - } - - data = jQuery.data( elem, "_change_data" ); - val = getVal(elem); - - // the current data will be also retrieved by beforeactivate - if ( e.type !== "focusout" || elem.type !== "radio" ) { - jQuery.data( elem, "_change_data", val ); - } - - if ( data === undefined || val === data ) { - return; - } - - if ( data != null || val ) { - e.type = "change"; - return jQuery.event.trigger( e, arguments[1], elem ); - } - }; - - jQuery.event.special.change = { - filters: { - focusout: testChange, - - click: function( e ) { - var elem = e.target, type = elem.type; - - if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) { - return testChange.call( this, e ); - } - }, - - // Change has to be called before submit - // Keydown will be called before keypress, which is used in submit-event delegation - keydown: function( e ) { - var elem = e.target, type = elem.type; - - if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") || - (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || - type === "select-multiple" ) { - return testChange.call( this, e ); - } - }, - - // Beforeactivate happens also before the previous element is blurred - // with this event you can't trigger a change event, but you can store - // information/focus[in] is not needed anymore - beforeactivate: function( e ) { - var elem = e.target; - jQuery.data( elem, "_change_data", getVal(elem) ); - } - }, - - setup: function( data, namespaces ) { - if ( this.type === "file" ) { - return false; - } - - for ( var type in changeFilters ) { - jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); - } - - return formElems.test( this.nodeName ); - }, - - teardown: function( namespaces ) { - jQuery.event.remove( this, ".specialChange" ); - - return formElems.test( this.nodeName ); - } - }; - - changeFilters = jQuery.event.special.change.filters; -} - -function trigger( type, elem, args ) { - args[0].type = type; - return jQuery.event.handle.apply( elem, args ); -} - -// Create "bubbling" focus and blur events -if ( document.addEventListener ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - jQuery.event.special[ fix ] = { - setup: function() { - this.addEventListener( orig, handler, true ); - }, - teardown: function() { - this.removeEventListener( orig, handler, true ); - } - }; - - function handler( e ) { - e = jQuery.event.fix( e ); - e.type = fix; - return jQuery.event.handle.call( this, e ); - } - }); -} - -jQuery.each(["bind", "one"], function( i, name ) { - jQuery.fn[ name ] = function( type, data, fn ) { - // Handle object literals - if ( typeof type === "object" ) { - for ( var key in type ) { - this[ name ](key, data, type[key], fn); - } - return this; - } - - if ( jQuery.isFunction( data ) ) { - fn = data; - data = undefined; - } - - var handler = name === "one" ? jQuery.proxy( fn, function( event ) { - jQuery( this ).unbind( event, handler ); - return fn.apply( this, arguments ); - }) : fn; - - if ( type === "unload" && name !== "one" ) { - this.one( type, data, fn ); - - } else { - for ( var i = 0, l = this.length; i < l; i++ ) { - jQuery.event.add( this[i], type, handler, data ); - } - } - - return this; - }; -}); - -jQuery.fn.extend({ - unbind: function( type, fn ) { - // Handle object literals - if ( typeof type === "object" && !type.preventDefault ) { - for ( var key in type ) { - this.unbind(key, type[key]); - } - - } else { - for ( var i = 0, l = this.length; i < l; i++ ) { - jQuery.event.remove( this[i], type, fn ); - } - } - - return this; - }, - - delegate: function( selector, types, data, fn ) { - return this.live( types, data, fn, selector ); - }, - - undelegate: function( selector, types, fn ) { - if ( arguments.length === 0 ) { - return this.unbind( "live" ); - - } else { - return this.die( types, null, fn, selector ); - } - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - - triggerHandler: function( type, data ) { - if ( this[0] ) { - var event = jQuery.Event( type ); - event.preventDefault(); - event.stopPropagation(); - jQuery.event.trigger( event, data, this[0] ); - return event.result; - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, i = 1; - - // link all the functions, so any of them can unbind this click handler - while ( i < args.length ) { - jQuery.proxy( fn, args[ i++ ] ); - } - - return this.click( jQuery.proxy( fn, function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - })); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -var liveMap = { - focus: "focusin", - blur: "focusout", - mouseenter: "mouseover", - mouseleave: "mouseout" -}; - -jQuery.each(["live", "die"], function( i, name ) { - jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { - var type, i = 0, match, namespaces, preType, - selector = origSelector || this.selector, - context = origSelector ? this : jQuery( this.context ); - - if ( jQuery.isFunction( data ) ) { - fn = data; - data = undefined; - } - - types = (types || "").split(" "); - - while ( (type = types[ i++ ]) != null ) { - match = rnamespaces.exec( type ); - namespaces = ""; - - if ( match ) { - namespaces = match[0]; - type = type.replace( rnamespaces, "" ); - } - - if ( type === "hover" ) { - types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); - continue; - } - - preType = type; - - if ( type === "focus" || type === "blur" ) { - types.push( liveMap[ type ] + namespaces ); - type = type + namespaces; - - } else { - type = (liveMap[ type ] || type) + namespaces; - } - - if ( name === "live" ) { - // bind live handler - context.each(function(){ - jQuery.event.add( this, liveConvert( type, selector ), - { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); - }); - - } else { - // unbind live handler - context.unbind( liveConvert( type, selector ), fn ); - } - } - - return this; - } -}); - -function liveHandler( event ) { - var stop, elems = [], selectors = [], args = arguments, - related, match, handleObj, elem, j, i, l, data, - events = jQuery.data( this, "events" ); - - // Make sure we avoid non-left-click bubbling in Firefox (#3861) - if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) { - return; - } - - event.liveFired = this; - - var live = events.live.slice(0); - - for ( j = 0; j < live.length; j++ ) { - handleObj = live[j]; - - if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { - selectors.push( handleObj.selector ); - - } else { - live.splice( j--, 1 ); - } - } - - match = jQuery( event.target ).closest( selectors, event.currentTarget ); - - for ( i = 0, l = match.length; i < l; i++ ) { - for ( j = 0; j < live.length; j++ ) { - handleObj = live[j]; - - if ( match[i].selector === handleObj.selector ) { - elem = match[i].elem; - related = null; - - // Those two events require additional checking - if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { - related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; - } - - if ( !related || related !== elem ) { - elems.push({ elem: elem, handleObj: handleObj }); - } - } - } - } - - for ( i = 0, l = elems.length; i < l; i++ ) { - match = elems[i]; - event.currentTarget = match.elem; - event.data = match.handleObj.data; - event.handleObj = match.handleObj; - - if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) { - stop = false; - break; - } - } - - return stop; -} - -function liveConvert( type, selector ) { - return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&"); -} - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( fn ) { - return fn ? this.bind( name, fn ) : this.trigger( name ); - }; - - if ( jQuery.attrFn ) { - jQuery.attrFn[ name ] = true; - } -}); - -// Prevent memory leaks in IE -// Window isn't included so as not to unbind existing unload events -// More info: -// - http://isaacschlueter.com/2006/10/msie-memory-leaks/ -if ( window.attachEvent && !window.addEventListener ) { - window.attachEvent("onunload", function() { - for ( var id in jQuery.cache ) { - if ( jQuery.cache[ id ].handle ) { - // Try/Catch is to handle iframes being unloaded, see #4280 - try { - jQuery.event.remove( jQuery.cache[ id ].handle.elem ); - } catch(e) {} - } - } - }); -} -/*! - * Sizzle CSS Selector Engine - v1.0 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){ - -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, - done = 0, - toString = Object.prototype.toString, - hasDuplicate = false, - baseHasDuplicate = true; - -// Here we check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparison -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function(){ - baseHasDuplicate = false; - return 0; -}); - -var Sizzle = function(selector, context, results, seed) { - results = results || []; - var origContext = context = context || document; - - if ( context.nodeType !== 1 && context.nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context), - soFar = selector; - - // Reset the position of the chunker regexp (start from head) - while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { - soFar = m[3]; - - parts.push( m[1] ); - - if ( m[2] ) { - extra = m[3]; - break; - } - } - - if ( parts.length > 1 && origPOS.exec( selector ) ) { - if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { - set = posProcess( parts[0] + parts[1], context ); - } else { - set = Expr.relative[ parts[0] ] ? - [ context ] : - Sizzle( parts.shift(), context ); - - while ( parts.length ) { - selector = parts.shift(); - - if ( Expr.relative[ selector ] ) { - selector += parts.shift(); - } - - set = posProcess( selector, set ); - } - } - } else { - // Take a shortcut and set the context if the root selector is an ID - // (but not if it'll be faster if the inner selector is an ID) - if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && - Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { - var ret = Sizzle.find( parts.shift(), context, contextXML ); - context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; - } - - if ( context ) { - var ret = seed ? - { expr: parts.pop(), set: makeArray(seed) } : - Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); - set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; - - if ( parts.length > 0 ) { - checkSet = makeArray(set); - } else { - prune = false; - } - - while ( parts.length ) { - var cur = parts.pop(), pop = cur; - - if ( !Expr.relative[ cur ] ) { - cur = ""; - } else { - pop = parts.pop(); - } - - if ( pop == null ) { - pop = context; - } - - Expr.relative[ cur ]( checkSet, pop, contextXML ); - } - } else { - checkSet = parts = []; - } - } - - if ( !checkSet ) { - checkSet = set; - } - - if ( !checkSet ) { - Sizzle.error( cur || selector ); - } - - if ( toString.call(checkSet) === "[object Array]" ) { - if ( !prune ) { - results.push.apply( results, checkSet ); - } else if ( context && context.nodeType === 1 ) { - for ( var i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { - results.push( set[i] ); - } - } - } else { - for ( var i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && checkSet[i].nodeType === 1 ) { - results.push( set[i] ); - } - } - } - } else { - makeArray( checkSet, results ); - } - - if ( extra ) { - Sizzle( extra, origContext, results, seed ); - Sizzle.uniqueSort( results ); - } - - return results; -}; - -Sizzle.uniqueSort = function(results){ - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort(sortOrder); - - if ( hasDuplicate ) { - for ( var i = 1; i < results.length; i++ ) { - if ( results[i] === results[i-1] ) { - results.splice(i--, 1); - } - } - } - } - - return results; -}; - -Sizzle.matches = function(expr, set){ - return Sizzle(expr, null, null, set); -}; - -Sizzle.find = function(expr, context, isXML){ - var set, match; - - if ( !expr ) { - return []; - } - - for ( var i = 0, l = Expr.order.length; i < l; i++ ) { - var type = Expr.order[i], match; - - if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { - var left = match[1]; - match.splice(1,1); - - if ( left.substr( left.length - 1 ) !== "\\" ) { - match[1] = (match[1] || "").replace(/\\/g, ""); - set = Expr.find[ type ]( match, context, isXML ); - if ( set != null ) { - expr = expr.replace( Expr.match[ type ], "" ); - break; - } - } - } - } - - if ( !set ) { - set = context.getElementsByTagName("*"); - } - - return {set: set, expr: expr}; -}; - -Sizzle.filter = function(expr, set, inplace, not){ - var old = expr, result = [], curLoop = set, match, anyFound, - isXMLFilter = set && set[0] && isXML(set[0]); - - while ( expr && set.length ) { - for ( var type in Expr.filter ) { - if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { - var filter = Expr.filter[ type ], found, item, left = match[1]; - anyFound = false; - - match.splice(1,1); - - if ( left.substr( left.length - 1 ) === "\\" ) { - continue; - } - - if ( curLoop === result ) { - result = []; - } - - if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); - - if ( !match ) { - anyFound = found = true; - } else if ( match === true ) { - continue; - } - } - - if ( match ) { - for ( var i = 0; (item = curLoop[i]) != null; i++ ) { - if ( item ) { - found = filter( item, match, i, curLoop ); - var pass = not ^ !!found; - - if ( inplace && found != null ) { - if ( pass ) { - anyFound = true; - } else { - curLoop[i] = false; - } - } else if ( pass ) { - result.push( item ); - anyFound = true; - } - } - } - } - - if ( found !== undefined ) { - if ( !inplace ) { - curLoop = result; - } - - expr = expr.replace( Expr.match[ type ], "" ); - - if ( !anyFound ) { - return []; - } - - break; - } - } - } - - // Improper expression - if ( expr === old ) { - if ( anyFound == null ) { - Sizzle.error( expr ); - } else { - break; - } - } - - old = expr; - } - - return curLoop; -}; - -Sizzle.error = function( msg ) { - throw "Syntax error, unrecognized expression: " + msg; -}; - -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - match: { - ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - leftMatch: {}, - attrMap: { - "class": "className", - "for": "htmlFor" - }, - attrHandle: { - href: function(elem){ - return elem.getAttribute("href"); - } - }, - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !/\W/.test(part), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} - - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } - - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); - } - }, - ">": function(checkSet, part){ - var isPartStr = typeof part === "string"; - - if ( isPartStr && !/\W/.test(part) ) { - part = part.toLowerCase(); - - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; - } - } - } else { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; - } - } - - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, - "": function(checkSet, part, isXML){ - var doneName = done++, checkFn = dirCheck; - - if ( typeof part === "string" && !/\W/.test(part) ) { - var nodeCheck = part = part.toLowerCase(); - checkFn = dirNodeCheck; - } - - checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); - }, - "~": function(checkSet, part, isXML){ - var doneName = done++, checkFn = dirCheck; - - if ( typeof part === "string" && !/\W/.test(part) ) { - var nodeCheck = part = part.toLowerCase(); - checkFn = dirNodeCheck; - } - - checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); - } - }, - find: { - ID: function(match, context, isXML){ - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - return m ? [m] : []; - } - }, - NAME: function(match, context){ - if ( typeof context.getElementsByName !== "undefined" ) { - var ret = [], results = context.getElementsByName(match[1]); - - for ( var i = 0, l = results.length; i < l; i++ ) { - if ( results[i].getAttribute("name") === match[1] ) { - ret.push( results[i] ); - } - } - - return ret.length === 0 ? null : ret; - } - }, - TAG: function(match, context){ - return context.getElementsByTagName(match[1]); - } - }, - preFilter: { - CLASS: function(match, curLoop, inplace, result, not, isXML){ - match = " " + match[1].replace(/\\/g, "") + " "; - - if ( isXML ) { - return match; - } - - for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { - if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { - if ( !inplace ) { - result.push( elem ); - } - } else if ( inplace ) { - curLoop[i] = false; - } - } - } - - return false; - }, - ID: function(match){ - return match[1].replace(/\\/g, ""); - }, - TAG: function(match, curLoop){ - return match[1].toLowerCase(); - }, - CHILD: function(match){ - if ( match[1] === "nth" ) { - // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' - var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( - match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || - !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); - - // calculate the numbers (first)n+(last) including if they are negative - match[2] = (test[1] + (test[2] || 1)) - 0; - match[3] = test[3] - 0; - } - - // TODO: Move to normal caching system - match[0] = done++; - - return match; - }, - ATTR: function(match, curLoop, inplace, result, not, isXML){ - var name = match[1].replace(/\\/g, ""); - - if ( !isXML && Expr.attrMap[name] ) { - match[1] = Expr.attrMap[name]; - } - - if ( match[2] === "~=" ) { - match[4] = " " + match[4] + " "; - } - - return match; - }, - PSEUDO: function(match, curLoop, inplace, result, not){ - if ( match[1] === "not" ) { - // If we're dealing with a complex expression, or a simple one - if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { - match[3] = Sizzle(match[3], null, null, curLoop); - } else { - var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); - if ( !inplace ) { - result.push.apply( result, ret ); - } - return false; - } - } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { - return true; - } - - return match; - }, - POS: function(match){ - match.unshift( true ); - return match; - } - }, - filters: { - enabled: function(elem){ - return elem.disabled === false && elem.type !== "hidden"; - }, - disabled: function(elem){ - return elem.disabled === true; - }, - checked: function(elem){ - return elem.checked === true; - }, - selected: function(elem){ - // Accessing this property makes selected-by-default - // options in Safari work properly - elem.parentNode.selectedIndex; - return elem.selected === true; - }, - parent: function(elem){ - return !!elem.firstChild; - }, - empty: function(elem){ - return !elem.firstChild; - }, - has: function(elem, i, match){ - return !!Sizzle( match[3], elem ).length; - }, - header: function(elem){ - return /h\d/i.test( elem.nodeName ); - }, - text: function(elem){ - return "text" === elem.type; - }, - radio: function(elem){ - return "radio" === elem.type; - }, - checkbox: function(elem){ - return "checkbox" === elem.type; - }, - file: function(elem){ - return "file" === elem.type; - }, - password: function(elem){ - return "password" === elem.type; - }, - submit: function(elem){ - return "submit" === elem.type; - }, - image: function(elem){ - return "image" === elem.type; - }, - reset: function(elem){ - return "reset" === elem.type; - }, - button: function(elem){ - return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; - }, - input: function(elem){ - return /input|select|textarea|button/i.test(elem.nodeName); - } - }, - setFilters: { - first: function(elem, i){ - return i === 0; - }, - last: function(elem, i, match, array){ - return i === array.length - 1; - }, - even: function(elem, i){ - return i % 2 === 0; - }, - odd: function(elem, i){ - return i % 2 === 1; - }, - lt: function(elem, i, match){ - return i < match[3] - 0; - }, - gt: function(elem, i, match){ - return i > match[3] - 0; - }, - nth: function(elem, i, match){ - return match[3] - 0 === i; - }, - eq: function(elem, i, match){ - return match[3] - 0 === i; - } - }, - filter: { - PSEUDO: function(elem, match, i, array){ - var name = match[1], filter = Expr.filters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; - } else if ( name === "not" ) { - var not = match[3]; - - for ( var i = 0, l = not.length; i < l; i++ ) { - if ( not[i] === elem ) { - return false; - } - } - - return true; - } else { - Sizzle.error( "Syntax error, unrecognized expression: " + name ); - } - }, - CHILD: function(elem, match){ - var type = match[1], node = elem; - switch (type) { - case 'only': - case 'first': - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - if ( type === "first" ) { - return true; - } - node = elem; - case 'last': - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - return true; - case 'nth': - var first = match[2], last = match[3]; - - if ( first === 1 && last === 0 ) { - return true; - } - - var doneName = match[0], - parent = elem.parentNode; - - if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { - var count = 0; - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.nodeIndex = ++count; - } - } - parent.sizcache = doneName; - } - - var diff = elem.nodeIndex - last; - if ( first === 0 ) { - return diff === 0; - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - } - }, - ID: function(elem, match){ - return elem.nodeType === 1 && elem.getAttribute("id") === match; - }, - TAG: function(elem, match){ - return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; - }, - CLASS: function(elem, match){ - return (" " + (elem.className || elem.getAttribute("class")) + " ") - .indexOf( match ) > -1; - }, - ATTR: function(elem, match){ - var name = match[1], - result = Expr.attrHandle[ name ] ? - Expr.attrHandle[ name ]( elem ) : - elem[ name ] != null ? - elem[ name ] : - elem.getAttribute( name ), - value = result + "", - type = match[2], - check = match[4]; - - return result == null ? - type === "!=" : - type === "=" ? - value === check : - type === "*=" ? - value.indexOf(check) >= 0 : - type === "~=" ? - (" " + value + " ").indexOf(check) >= 0 : - !check ? - value && result !== false : - type === "!=" ? - value !== check : - type === "^=" ? - value.indexOf(check) === 0 : - type === "$=" ? - value.substr(value.length - check.length) === check : - type === "|=" ? - value === check || value.substr(0, check.length + 1) === check + "-" : - false; - }, - POS: function(elem, match, i, array){ - var name = match[2], filter = Expr.setFilters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } - } - } -}; - -var origPOS = Expr.match.POS; - -for ( var type in Expr.match ) { - Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); - Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){ - return "\\" + (num - 0 + 1); - })); -} - -var makeArray = function(array, results) { - array = Array.prototype.slice.call( array, 0 ); - - if ( results ) { - results.push.apply( results, array ); - return results; - } - - return array; -}; - -// Perform a simple check to determine if the browser is capable of -// converting a NodeList to an array using builtin methods. -// Also verifies that the returned array holds DOM nodes -// (which is not the case in the Blackberry browser) -try { - Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; - -// Provide a fallback method if it does not work -} catch(e){ - makeArray = function(array, results) { - var ret = results || []; - - if ( toString.call(array) === "[object Array]" ) { - Array.prototype.push.apply( ret, array ); - } else { - if ( typeof array.length === "number" ) { - for ( var i = 0, l = array.length; i < l; i++ ) { - ret.push( array[i] ); - } - } else { - for ( var i = 0; array[i]; i++ ) { - ret.push( array[i] ); - } - } - } - - return ret; - }; -} - -var sortOrder; - -if ( document.documentElement.compareDocumentPosition ) { - sortOrder = function( a, b ) { - if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { - if ( a == b ) { - hasDuplicate = true; - } - return a.compareDocumentPosition ? -1 : 1; - } - - var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; - if ( ret === 0 ) { - hasDuplicate = true; - } - return ret; - }; -} else if ( "sourceIndex" in document.documentElement ) { - sortOrder = function( a, b ) { - if ( !a.sourceIndex || !b.sourceIndex ) { - if ( a == b ) { - hasDuplicate = true; - } - return a.sourceIndex ? -1 : 1; - } - - var ret = a.sourceIndex - b.sourceIndex; - if ( ret === 0 ) { - hasDuplicate = true; - } - return ret; - }; -} else if ( document.createRange ) { - sortOrder = function( a, b ) { - if ( !a.ownerDocument || !b.ownerDocument ) { - if ( a == b ) { - hasDuplicate = true; - } - return a.ownerDocument ? -1 : 1; - } - - var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); - aRange.setStart(a, 0); - aRange.setEnd(a, 0); - bRange.setStart(b, 0); - bRange.setEnd(b, 0); - var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); - if ( ret === 0 ) { - hasDuplicate = true; - } - return ret; - }; -} - -// Utility function for retrieving the text value of an array of DOM nodes -function getText( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += getText( elem.childNodes ); - } - } - - return ret; -} - -// Check to see if the browser returns elements by name when -// querying by getElementById (and provide a workaround) -(function(){ - // We're going to inject a fake input element with a specified name - var form = document.createElement("div"), - id = "script" + (new Date).getTime(); - form.innerHTML = ""; - - // Inject it into the root element, check its status, and remove it quickly - var root = document.documentElement; - root.insertBefore( form, root.firstChild ); - - // The workaround has to do additional checks after a getElementById - // Which slows things down for other browsers (hence the branching) - if ( document.getElementById( id ) ) { - Expr.find.ID = function(match, context, isXML){ - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; - } - }; - - Expr.filter.ID = function(elem, match){ - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); - return elem.nodeType === 1 && node && node.nodeValue === match; - }; - } - - root.removeChild( form ); - root = form = null; // release memory in IE -})(); - -(function(){ - // Check to see if the browser returns only elements - // when doing getElementsByTagName("*") - - // Create a fake element - var div = document.createElement("div"); - div.appendChild( document.createComment("") ); - - // Make sure no comments are found - if ( div.getElementsByTagName("*").length > 0 ) { - Expr.find.TAG = function(match, context){ - var results = context.getElementsByTagName(match[1]); - - // Filter out possible comments - if ( match[1] === "*" ) { - var tmp = []; - - for ( var i = 0; results[i]; i++ ) { - if ( results[i].nodeType === 1 ) { - tmp.push( results[i] ); - } - } - - results = tmp; - } - - return results; - }; - } - - // Check to see if an attribute returns normalized href attributes - div.innerHTML = ""; - if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && - div.firstChild.getAttribute("href") !== "#" ) { - Expr.attrHandle.href = function(elem){ - return elem.getAttribute("href", 2); - }; - } - - div = null; // release memory in IE -})(); - -if ( document.querySelectorAll ) { - (function(){ - var oldSizzle = Sizzle, div = document.createElement("div"); - div.innerHTML = "

        "; - - // Safari can't handle uppercase or unicode characters when - // in quirks mode. - if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { - return; - } - - Sizzle = function(query, context, extra, seed){ - context = context || document; - - // Only use querySelectorAll on non-XML documents - // (ID selectors don't work in non-HTML documents) - if ( !seed && context.nodeType === 9 && !isXML(context) ) { - try { - return makeArray( context.querySelectorAll(query), extra ); - } catch(e){} - } - - return oldSizzle(query, context, extra, seed); - }; - - for ( var prop in oldSizzle ) { - Sizzle[ prop ] = oldSizzle[ prop ]; - } - - div = null; // release memory in IE - })(); -} - -(function(){ - var div = document.createElement("div"); - - div.innerHTML = "
        "; - - // Opera can't find a second classname (in 9.6) - // Also, make sure that getElementsByClassName actually exists - if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { - return; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - - if ( div.getElementsByClassName("e").length === 1 ) { - return; - } - - Expr.order.splice(1, 0, "CLASS"); - Expr.find.CLASS = function(match, context, isXML) { - if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { - return context.getElementsByClassName(match[1]); - } - }; - - div = null; // release memory in IE -})(); - -function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - elem = elem[dir]; - var match = false; - - while ( elem ) { - if ( elem.sizcache === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 && !isXML ){ - elem.sizcache = doneName; - elem.sizset = i; - } - - if ( elem.nodeName.toLowerCase() === cur ) { - match = elem; - break; - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - elem = elem[dir]; - var match = false; - - while ( elem ) { - if ( elem.sizcache === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 ) { - if ( !isXML ) { - elem.sizcache = doneName; - elem.sizset = i; - } - if ( typeof cur !== "string" ) { - if ( elem === cur ) { - match = true; - break; - } - - } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { - match = elem; - break; - } - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -var contains = document.compareDocumentPosition ? function(a, b){ - return !!(a.compareDocumentPosition(b) & 16); -} : function(a, b){ - return a !== b && (a.contains ? a.contains(b) : true); -}; - -var isXML = function(elem){ - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -var posProcess = function(selector, context){ - var tmpSet = [], later = "", match, - root = context.nodeType ? [context] : context; - - // Position selectors must be done after the filter - // And so must :not(positional) so we move all PSEUDOs to the end - while ( (match = Expr.match.PSEUDO.exec( selector )) ) { - later += match[0]; - selector = selector.replace( Expr.match.PSEUDO, "" ); - } - - selector = Expr.relative[selector] ? selector + "*" : selector; - - for ( var i = 0, l = root.length; i < l; i++ ) { - Sizzle( selector, root[i], tmpSet ); - } - - return Sizzle.filter( later, tmpSet ); -}; - -// EXPOSE -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.filters; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = getText; -jQuery.isXMLDoc = isXML; -jQuery.contains = contains; - -return; - -window.Sizzle = Sizzle; - -})(); -var runtil = /Until$/, - rparentsprev = /^(?:parents|prevUntil|prevAll)/, - // Note: This RegExp should be improved, or likely pulled from Sizzle - rmultiselector = /,/, - slice = Array.prototype.slice; - -// Implement the identical functionality for filter and not -var winnow = function( elements, qualifier, keep ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) === keep; - }); - - } else if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem, i ) { - return (elem === qualifier) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = jQuery.filter( qualifier, filtered ); - } - } - - return jQuery.grep(elements, function( elem, i ) { - return (jQuery.inArray( elem, qualifier ) >= 0) === keep; - }); -}; - -jQuery.fn.extend({ - find: function( selector ) { - var ret = this.pushStack( "", "find", selector ), length = 0; - - for ( var i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( var n = length; n < ret.length; n++ ) { - for ( var r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var targets = jQuery( target ); - return this.filter(function() { - for ( var i = 0, l = targets.length; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && jQuery.filter( selector, this ).length > 0; - }, - - closest: function( selectors, context ) { - if ( jQuery.isArray( selectors ) ) { - var ret = [], cur = this[0], match, matches = {}, selector; - - if ( cur && selectors.length ) { - for ( var i = 0, l = selectors.length; i < l; i++ ) { - selector = selectors[i]; - - if ( !matches[selector] ) { - matches[selector] = jQuery.expr.match.POS.test( selector ) ? - jQuery( selector, context || this.context ) : - selector; - } - } - - while ( cur && cur.ownerDocument && cur !== context ) { - for ( selector in matches ) { - match = matches[selector]; - - if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { - ret.push({ selector: selector, elem: cur }); - delete matches[selector]; - } - } - cur = cur.parentNode; - } - } - - return ret; - } - - var pos = jQuery.expr.match.POS.test( selectors ) ? - jQuery( selectors, context || this.context ) : null; - - return this.map(function( i, cur ) { - while ( cur && cur.ownerDocument && cur !== context ) { - if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) { - return cur; - } - cur = cur.parentNode; - } - return null; - }); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - if ( !elem || typeof elem === "string" ) { - return jQuery.inArray( this[0], - // If it receives a string, the selector is used - // If it receives nothing, the siblings are used - elem ? jQuery( elem ) : this.parent().children() ); - } - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context || this.context ) : - jQuery.makeArray( selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - jQuery.unique( all ) ); - }, - - andSelf: function() { - return this.add( this.prevObject ); - } -}); - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return jQuery.nth( elem, 2, "nextSibling" ); - }, - prev: function( elem ) { - return jQuery.nth( elem, 2, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( elem.parentNode.firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.makeArray( elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - ret = this.length > 1 ? jQuery.unique( ret ) : ret; - - if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, slice.call(arguments).join(",") ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], cur = elem[dir]; - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - nth: function( cur, result, dir, elem ) { - result = result || 1; - var num = 0; - - for ( ; cur; cur = cur[dir] ) { - if ( cur.nodeType === 1 && ++num === result ) { - break; - } - } - - return cur; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); -var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g, - rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i, - rtagName = /<([\w:]+)/, - rtbody = /"; - }, - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
        ", "
        " ], - thead: [ 1, "", "
        " ], - tr: [ 2, "", "
        " ], - td: [ 3, "", "
        " ], - col: [ 2, "", "
        " ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }; - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE can't serialize and \ No newline at end of file diff --git a/src/.jshintrc b/src/.jshintrc index 72a58de2ea..1ba47a2617 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -1,24 +1,30 @@ { + "boss": true, "curly": true, + "eqeqeq": true, + "eqnull": true, "expr": true, - "newcap": false, + "immed": true, + "noarg": true, "quotmark": "double", - "regexdash": true, - "strict": true, - "trailing": true, "undef": true, "unused": true, - "maxerr": 100, - "eqnull": true, "evil": true, "sub": true, - "browser": true, - "wsh": true, + // Support: IE < 10, Android < 4.1 + // The above browsers are failing a lot of tests in the ES5 + // test suite at http://test262.ecmascript.org. + "es3": true, + + "globals": { + "window": true, + "JSON": false, - "predef": [ - "define", - "jQuery" - ] + "jQuery": true, + "define": false, + "module": false, + "noGlobal": true + } } diff --git a/src/ajax.js b/src/ajax.js index 6dd34a7326..c7013c0939 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -1,22 +1,29 @@ -var - // Document location - ajaxLocParts, - ajaxLocation, - - ajax_nonce = jQuery.now(), +define( [ + "./core", + "./var/document", + "./var/rnotwhite", + "./ajax/var/location", + "./ajax/var/nonce", + "./ajax/var/rquery", + + "./core/init", + "./ajax/parseJSON", + "./ajax/parseXML", + "./deferred" +], function( jQuery, document, rnotwhite, location, nonce, rquery ) { - ajax_rquery = /\?/, +var rhash = /#.*$/, rts = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + + // IE leaves an \r character at EOL + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, + // #7653, #8125, #8152: local protocol detection rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, rnoContent = /^(?:GET|HEAD)$/, rprotocol = /^\/\//, - rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, - - // Keep a copy of the old load method - _load = jQuery.fn.load, + rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/, /* Prefilters * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) @@ -37,22 +44,13 @@ var transports = {}, // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = "*/".concat("*"); - -// #8138, IE may throw an exception when accessing -// a field from window.location if document.domain has been set -try { - ajaxLocation = location.href; -} catch( e ) { - // Use the href attribute of an A element - // since IE will modify it given document.location - ajaxLocation = document.createElement( "a" ); - ajaxLocation.href = ""; - ajaxLocation = ajaxLocation.href; -} + allTypes = "*/".concat( "*" ), -// Segment location into parts -ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + // Document location + ajaxLocation = location.href, + + // Segment location into parts + ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport function addToPrefiltersOrTransports( structure ) { @@ -67,19 +65,21 @@ function addToPrefiltersOrTransports( structure ) { var dataType, i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || []; + dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || []; if ( jQuery.isFunction( func ) ) { + // For each dataType in the dataTypeExpression - while ( (dataType = dataTypes[i++]) ) { + while ( ( dataType = dataTypes[ i++ ] ) ) { + // Prepend if requested - if ( dataType[0] === "+" ) { + if ( dataType.charAt( 0 ) === "+" ) { dataType = dataType.slice( 1 ) || "*"; - (structure[ dataType ] = structure[ dataType ] || []).unshift( func ); + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); // Otherwise append } else { - (structure[ dataType ] = structure[ dataType ] || []).push( func ); + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); } } } @@ -97,14 +97,16 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX inspected[ dataType ] = true; jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + options.dataTypes.unshift( dataTypeOrTransport ); inspect( dataTypeOrTransport ); return false; } else if ( seekingTransport ) { return !( selected = dataTypeOrTransport ); } - }); + } ); return selected; } @@ -115,12 +117,12 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX // that takes "flat" options (not to be deep extended) // Fixes #9887 function ajaxExtend( target, src ) { - var key, deep, + var deep, key, flatOptions = jQuery.ajaxSettings.flatOptions || {}; for ( key in src ) { if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ]; + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; } } if ( deep ) { @@ -130,90 +132,164 @@ function ajaxExtend( target, src ) { return target; } -jQuery.fn.load = function( url, params, callback ) { - if ( typeof url !== "string" && _load ) { - return _load.apply( this, arguments ); +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + var firstDataType, ct, finalDataType, type, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } } - var selector, type, response, - self = this, - off = url.indexOf(" "); + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } - if ( off >= 0 ) { - selector = url.slice( off, url.length ); - url = url.slice( 0, off ); + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; } +} - // If it's a function - if ( jQuery.isFunction( params ) ) { +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, - // We assume that it's the callback - callback = params; - params = undefined; + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); - // Otherwise, build a param string - } else if ( params && typeof params === "object" ) { - type = "POST"; + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } } - // If we have elements to modify, make the request - if ( self.length > 0 ) { - jQuery.ajax({ - url: url, + current = dataTypes.shift(); - // if "type" variable is undefined, then "GET" method will be used - type: type, - dataType: "html", - data: params - }).done(function( responseText ) { + // Convert to each sequential dataType + while ( current ) { - // Save response for use in complete callback - response = arguments; + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } - self.html( selector ? + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } - // If a selector was specified, locate the right elements in a dummy div - // Exclude scripts to avoid IE 'Permission Denied' errors - jQuery("
        ").append( jQuery.parseHTML( responseText ) ).find( selector ) : + prev = current; + current = dataTypes.shift(); - // Otherwise use the full result - responseText ); + if ( current ) { - }).complete( callback && function( jqXHR, status ) { - self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); - }); - } + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { - return this; -}; + current = prev; -// Attach a bunch of functions for handling common AJAX events -jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){ - jQuery.fn[ type ] = function( fn ){ - return this.on( type, fn ); - }; -}); + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { -jQuery.each( [ "get", "post" ], function( i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - // shift arguments if data argument was omitted - if ( jQuery.isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s[ "throws" ] ) { // jscs:ignore requireDotNotation + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } } + } - return jQuery.ajax({ - url: url, - type: method, - dataType: type, - data: data, - success: callback - }); - }; -}); + return { state: "success", data: response }; +} -jQuery.extend({ +jQuery.extend( { // Counter for holding the number of active queries active: 0, @@ -251,14 +327,15 @@ jQuery.extend({ }, contents: { - xml: /xml/, - html: /html/, - json: /json/ + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ }, responseFields: { xml: "responseXML", - text: "responseText" + text: "responseText", + json: "responseJSON" }, // Data converters @@ -266,7 +343,7 @@ jQuery.extend({ converters: { // Convert anything to text - "* text": window.String, + "* text": String, // Text to html (true = no transformation) "text html": true, @@ -316,40 +393,60 @@ jQuery.extend({ // Force options to be an object options = options || {}; - var transport, + var + + // Cross-domain detection vars + parts, + + // Loop variable + i, + // URL without anti-cache param cacheURL, - // Response headers + + // Response headers as string responseHeadersString, - responseHeaders, + // timeout handle timeoutTimer, - // Cross-domain detection vars - parts, + // To know if global events are to be dispatched fireGlobals, - // Loop variable - i, + + transport, + + // Response headers + responseHeaders, + // Create the final options object s = jQuery.ajaxSetup( {}, options ), + // Callbacks context callbackContext = s.context || s, + // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + // Deferreds deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks("once memory"), + completeDeferred = jQuery.Callbacks( "once memory" ), + // Status-dependent callbacks statusCode = s.statusCode || {}, + // Headers (they are sent all at once) requestHeaders = {}, requestHeadersNames = {}, + // The jqXHR state state = 0, + // Default abort message strAbort = "canceled", + // Fake xhr jqXHR = { readyState: 0, @@ -360,8 +457,8 @@ jQuery.extend({ if ( state === 2 ) { if ( !responseHeaders ) { responseHeaders = {}; - while ( (match = rheaders.exec( responseHeadersString )) ) { - responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; } } match = responseHeaders[ key.toLowerCase() ]; @@ -398,10 +495,12 @@ jQuery.extend({ if ( map ) { if ( state < 2 ) { for ( code in map ) { + // Lazy-add the new callback in a way that preserves old ones statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; } } else { + // Execute the appropriate callbacks jqXHR.always( map[ jqXHR.status ] ); } @@ -429,21 +528,23 @@ jQuery.extend({ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) // Handle falsy url in the settings object (#10093: consistency with old signature) // We also use the url parameter if available - s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + s.url = ( ( url || s.url || ajaxLocation ) + "" ) + .replace( rhash, "" ) + .replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); // Alias method option to type as per ticket #12004 s.type = options.method || options.type || s.method || s.type; // Extract dataTypes list - s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""]; + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ]; // A cross-domain request is in order when we have a protocol:host:port mismatch if ( s.crossDomain == null ) { parts = rurl.exec( s.url.toLowerCase() ); s.crossDomain = !!( parts && ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || - ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != - ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !== + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) ) ); } @@ -461,11 +562,12 @@ jQuery.extend({ } // We can fire global events as of now if asked to - fireGlobals = s.global; + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; // Watch for a new set of requests if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger("ajaxStart"); + jQuery.event.trigger( "ajaxStart" ); } // Uppercase the type @@ -483,7 +585,8 @@ jQuery.extend({ // If data is available, append data to url if ( s.data ) { - cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data ); + cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data ); + // #9682: remove data so that it's not used in an eventual retry delete s.data; } @@ -493,10 +596,10 @@ jQuery.extend({ s.url = rts.test( cacheURL ) ? // If there is already a '_' parameter, set its value - cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) : + cacheURL.replace( rts, "$1_=" + nonce++ ) : // Otherwise add one to the end - cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++; + cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++; } } @@ -518,8 +621,9 @@ jQuery.extend({ // Set the Accepts header for the server, depending on the dataType jqXHR.setRequestHeader( "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? - s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : s.accepts[ "*" ] ); @@ -529,7 +633,9 @@ jQuery.extend({ } // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already and return return jqXHR.abort(); } @@ -555,10 +661,16 @@ jQuery.extend({ if ( fireGlobals ) { globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); } + + // If request was aborted inside ajaxSend, stop there + if ( state === 2 ) { + return jqXHR; + } + // Timeout if ( s.async && s.timeout > 0 ) { - timeoutTimer = setTimeout(function() { - jqXHR.abort("timeout"); + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); }, s.timeout ); } @@ -566,9 +678,11 @@ jQuery.extend({ state = 1; transport.send( requestHeaders, done ); } catch ( e ) { + // Propagate exception as error if not done if ( state < 2 ) { done( -1, e ); + // Simply rethrow otherwise } else { throw e; @@ -591,7 +705,7 @@ jQuery.extend({ // Clear timeout if it exists if ( timeoutTimer ) { - clearTimeout( timeoutTimer ); + window.clearTimeout( timeoutTimer ); } // Dereference transport for early garbage collection @@ -604,40 +718,49 @@ jQuery.extend({ // Set readyState jqXHR.readyState = status > 0 ? 4 : 0; + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + // Get response data if ( responses ) { response = ajaxHandleResponses( s, jqXHR, responses ); } + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + // If successful, handle type chaining - if ( status >= 200 && status < 300 || status === 304 ) { + if ( isSuccess ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { - modified = jqXHR.getResponseHeader("Last-Modified"); + modified = jqXHR.getResponseHeader( "Last-Modified" ); if ( modified ) { jQuery.lastModified[ cacheURL ] = modified; } - modified = jqXHR.getResponseHeader("etag"); + modified = jqXHR.getResponseHeader( "etag" ); if ( modified ) { jQuery.etag[ cacheURL ] = modified; } } - // If not modified - if ( status === 304 ) { - isSuccess = true; + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { statusText = "notmodified"; - // If we have data + // If we have data, let's convert it } else { - isSuccess = ajaxConvert( s, response ); - statusText = isSuccess.state; - success = isSuccess.data; - error = isSuccess.error; + statusText = response.state; + success = response.data; + error = response.error; isSuccess = !error; } } else { + // We extract error from statusText // then normalize statusText and status for non-aborts error = statusText; @@ -674,9 +797,10 @@ jQuery.extend({ if ( fireGlobals ) { globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter if ( !( --jQuery.active ) ) { - jQuery.event.trigger("ajaxStop"); + jQuery.event.trigger( "ajaxStop" ); } } } @@ -684,163 +808,35 @@ jQuery.extend({ return jqXHR; }, - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - }, - getJSON: function( url, data, callback ) { return jQuery.get( url, data, callback, "json" ); - } -}); - -/* Handles responses to an ajax request: - * - sets all responseXXX fields accordingly - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes, - responseFields = s.responseFields; - - // Fill responseXXX fields - for ( type in responseFields ) { - if ( type in responses ) { - jqXHR[ responseFields[type] ] = responses[ type ]; - } - } - - // Remove auto dataType and get content-type in the process - while( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader("Content-Type"); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - // Or just use first one - finalDataType = finalDataType || firstDataType; - } + }, - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); } -} - -// Chain conversions given the request and the original response -function ajaxConvert( s, response ) { +} ); - var conv, conv2, current, tmp, - converters = {}, - i = 0, - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(), - prev = dataTypes[ 0 ]; - - // Apply the dataFilter if provided - if ( s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; } - } - - // Convert to each sequential dataType, tolerating list modification - for ( ; (current = dataTypes[++i]); ) { - - // There's only work to do if current dataType is non-auto - if ( current !== "*" ) { - - // Convert response if prev dataType is non-auto and differs from current - if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split(" "); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.splice( i--, 0, current ); - } - - break; - } - } - } - } - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s["throws"] ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; - } - } - } - } - - // Update prev for next iteration - prev = current; - } - } + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); - return { state: "success", data: response }; -} +return jQuery; +} ); diff --git a/src/ajax/jsonp.js b/src/ajax/jsonp.js index b8803df444..89ce44d11a 100644 --- a/src/ajax/jsonp.js +++ b/src/ajax/jsonp.js @@ -1,15 +1,22 @@ +define( [ + "../core", + "./var/nonce", + "./var/rquery", + "../ajax" +], function( jQuery, nonce, rquery ) { + var oldCallbacks = [], rjsonp = /(=)\?(?=&|$)|\?\?/; // Default jsonp settings -jQuery.ajaxSetup({ +jQuery.ajaxSetup( { jsonp: "callback", jsonpCallback: function() { - var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) ); + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); this[ callback ] = true; return callback; } -}); +} ); // Detect, normalize options and install callbacks for jsonp requests jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { @@ -17,7 +24,10 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { var callbackName, overwritten, responseContainer, jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? "url" : - typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data" + typeof s.data === "string" && + ( s.contentType || "" ) + .indexOf( "application/x-www-form-urlencoded" ) === 0 && + rjsonp.test( s.data ) && "data" ); // Handle iff the expected data type is "jsonp" or we have a parameter to set @@ -32,11 +42,11 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { if ( jsonProp ) { s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); } else if ( s.jsonp !== false ) { - s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; } // Use data converter to retrieve json after script execution - s.converters["script json"] = function() { + s.converters[ "script json" ] = function() { if ( !responseContainer ) { jQuery.error( callbackName + " was not called" ); } @@ -53,12 +63,20 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { }; // Clean-up function (fires after converters) - jqXHR.always(function() { - // Restore preexisting value - window[ callbackName ] = overwritten; + jqXHR.always( function() { + + // If previous value didn't exist - remove it + if ( overwritten === undefined ) { + jQuery( window ).removeProp( callbackName ); + + // Otherwise restore preexisting value + } else { + window[ callbackName ] = overwritten; + } // Save back as free if ( s[ callbackName ] ) { + // make sure that re-using the options doesn't screw things around s.jsonpCallback = originalSettings.jsonpCallback; @@ -72,9 +90,11 @@ jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { } responseContainer = overwritten = undefined; - }); + } ); // Delegate to script return "script"; } -}); +} ); + +} ); diff --git a/src/ajax/load.js b/src/ajax/load.js new file mode 100644 index 0000000000..2ae4b39b95 --- /dev/null +++ b/src/ajax/load.js @@ -0,0 +1,83 @@ +define( [ + "../core", + "../core/parseHTML", + "../ajax", + "../traversing", + "../manipulation", + "../selector", + + // Optional event/alias dependency + "../event/alias" +], function( jQuery ) { + +// Keep a copy of the old load method +var _load = jQuery.fn.load; + +/** + * Load a url into a page + */ +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + var selector, type, response, + self = this, + off = url.indexOf( " " ); + + if ( off > -1 ) { + selector = jQuery.trim( url.slice( off, url.length ) ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // If we have elements to modify, make the request + if ( self.length > 0 ) { + jQuery.ajax( { + url: url, + + // If "type" variable is undefined, then "GET" method will be used. + // Make value of this field explicit since + // user can override it through ajaxSetup method + type: type || "GET", + dataType: "html", + data: params + } ).done( function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + self.html( selector ? + + // If a selector was specified, locate the right elements in a dummy div + // Exclude scripts to avoid IE 'Permission Denied' errors + jQuery( "
        " ).append( jQuery.parseHTML( responseText ) ).find( selector ) : + + // Otherwise use the full result + responseText ); + + // If the request succeeds, this function gets "data", "status", "jqXHR" + // but they are ignored because response was set above. + // If it fails, this function gets "jqXHR", "status", "error" + } ).always( callback && function( jqXHR, status ) { + self.each( function() { + callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); + } ); + } ); + } + + return this; +}; + +} ); diff --git a/src/ajax/parseJSON.js b/src/ajax/parseJSON.js new file mode 100644 index 0000000000..34efb79b21 --- /dev/null +++ b/src/ajax/parseJSON.js @@ -0,0 +1,53 @@ +define( [ + "../core" +], function( jQuery ) { + +var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g; + +jQuery.parseJSON = function( data ) { + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + + // Support: Android 2.3 + // Workaround failure to string-cast null input + return window.JSON.parse( data + "" ); + } + + var requireNonComma, + depth = null, + str = jQuery.trim( data + "" ); + + // Guard against invalid (and possibly dangerous) input by ensuring that nothing remains + // after removing valid tokens + return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) { + + // Force termination if we see a misplaced comma + if ( requireNonComma && comma ) { + depth = 0; + } + + // Perform no more replacements after returning to outermost depth + if ( depth === 0 ) { + return token; + } + + // Commas must not follow "[", "{", or "," + requireNonComma = open || comma; + + // Determine new depth + // array/object open ("[" or "{"): depth += true - false (increment) + // array/object close ("]" or "}"): depth += false - true (decrement) + // other cases ("," or primitive): depth += true - true (numeric cast) + depth += !close - !open; + + // Remove this token + return ""; + } ) ) ? + ( Function( "return " + str ) )() : + jQuery.error( "Invalid JSON: " + data ); +}; + +return jQuery.parseJSON; + +} ); diff --git a/src/ajax/parseXML.js b/src/ajax/parseXML.js new file mode 100644 index 0000000000..a05e8002ee --- /dev/null +++ b/src/ajax/parseXML.js @@ -0,0 +1,31 @@ +define( [ + "../core" +], function( jQuery ) { + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new window.DOMParser(); + xml = tmp.parseFromString( data, "text/xml" ); + } else { // IE + xml = new window.ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch ( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + +return jQuery.parseXML; + +} ); diff --git a/src/ajax/script.js b/src/ajax/script.js index ac14c44be2..1aa4dbfbd5 100644 --- a/src/ajax/script.js +++ b/src/ajax/script.js @@ -1,10 +1,17 @@ +define( [ + "../core", + "../var/document", + "../ajax" +], function( jQuery, document ) { + // Install script dataType -jQuery.ajaxSetup({ +jQuery.ajaxSetup( { accepts: { - script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" }, contents: { - script: /(?:java|ecma)script/ + script: /\b(?:java|ecma)script\b/ }, converters: { "text script": function( text ) { @@ -12,7 +19,7 @@ jQuery.ajaxSetup({ return text; } } -}); +} ); // Handle cache's special case and global jQuery.ajaxPrefilter( "script", function( s ) { @@ -23,22 +30,22 @@ jQuery.ajaxPrefilter( "script", function( s ) { s.type = "GET"; s.global = false; } -}); +} ); // Bind script tag hack transport -jQuery.ajaxTransport( "script", function(s) { +jQuery.ajaxTransport( "script", function( s ) { // This transport only deals with cross domain requests if ( s.crossDomain ) { var script, - head = document.head || jQuery("head")[0] || document.documentElement; + head = document.head || jQuery( "head" )[ 0 ] || document.documentElement; return { send: function( _, callback ) { - script = document.createElement("script"); + script = document.createElement( "script" ); script.async = true; @@ -83,4 +90,6 @@ jQuery.ajaxTransport( "script", function(s) { } }; } -}); +} ); + +} ); diff --git a/src/ajax/var/location.js b/src/ajax/var/location.js new file mode 100644 index 0000000000..ff9578e993 --- /dev/null +++ b/src/ajax/var/location.js @@ -0,0 +1,3 @@ +define( function() { + return window.location; +} ); diff --git a/src/ajax/var/nonce.js b/src/ajax/var/nonce.js new file mode 100644 index 0000000000..83fd557c87 --- /dev/null +++ b/src/ajax/var/nonce.js @@ -0,0 +1,5 @@ +define( [ + "../../core" +], function( jQuery ) { + return jQuery.now(); +} ); diff --git a/src/ajax/var/rquery.js b/src/ajax/var/rquery.js new file mode 100644 index 0000000000..0502146ca8 --- /dev/null +++ b/src/ajax/var/rquery.js @@ -0,0 +1,3 @@ +define( function() { + return ( /\?/ ); +} ); diff --git a/src/ajax/xhr.js b/src/ajax/xhr.js index 3c1fde374c..7e41409715 100644 --- a/src/ajax/xhr.js +++ b/src/ajax/xhr.js @@ -1,81 +1,99 @@ -var xhrCallbacks, xhrSupported, - xhrId = 0, - // #5280: Internet Explorer will keep connections alive if we don't abort on unload - xhrOnUnloadAbort = window.ActiveXObject && function() { - // Abort all pending requests - var key; - for ( key in xhrCallbacks ) { - xhrCallbacks[ key ]( undefined, true ); - } - }; - -// Functions to create xhrs -function createStandardXHR() { - try { - return new window.XMLHttpRequest(); - } catch( e ) {} -} - -function createActiveXHR() { - try { - return new window.ActiveXObject("Microsoft.XMLHTTP"); - } catch( e ) {} -} +define( [ + "../core", + "../var/document", + "../var/support", + "../ajax" +], function( jQuery, document, support ) { // Create the request object // (This is still attached to ajaxSettings for backward compatibility) -jQuery.ajaxSettings.xhr = window.ActiveXObject ? - /* Microsoft failed to properly - * implement the XMLHttpRequest in IE7 (can't request local files), - * so we use the ActiveXObject when it is available - * Additionally XMLHttpRequest can be disabled in IE7/IE8 so - * we need a fallback. - */ +jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ? + + // Support: IE6-IE8 function() { - return !this.isLocal && createStandardXHR() || createActiveXHR(); + + // XHR cannot access local files, always use ActiveX for that case + if ( this.isLocal ) { + return createActiveXHR(); + } + + // Support: IE 9-11 + // IE seems to error on cross-domain PATCH requests when ActiveX XHR + // is used. In IE 9+ always use the native XHR. + // Note: this condition won't catch Edge as it doesn't define + // document.documentMode but it also doesn't support ActiveX so it won't + // reach this code. + if ( document.documentMode > 8 ) { + return createStandardXHR(); + } + + // Support: IE<9 + // oldIE XHR does not support non-RFC2616 methods (#13240) + // See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx + // and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9 + // Although this check for six methods instead of eight + // since IE also does not support "trace" and "connect" + return /^(get|post|head|put|delete|options)$/i.test( this.type ) && + createStandardXHR() || createActiveXHR(); } : + // For all other browsers, use the standard XMLHttpRequest object createStandardXHR; +var xhrId = 0, + xhrCallbacks = {}, + xhrSupported = jQuery.ajaxSettings.xhr(); + +// Support: IE<10 +// Open requests must be manually aborted on unload (#5280) +// See https://support.microsoft.com/kb/2856746 for more info +if ( window.attachEvent ) { + window.attachEvent( "onunload", function() { + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ]( undefined, true ); + } + } ); +} + // Determine support properties -xhrSupported = jQuery.ajaxSettings.xhr(); -jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -xhrSupported = jQuery.support.ajax = !!xhrSupported; +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +xhrSupported = support.ajax = !!xhrSupported; // Create transport if the browser can provide an xhr if ( xhrSupported ) { - jQuery.ajaxTransport(function( s ) { + jQuery.ajaxTransport( function( options ) { + // Cross domain only allowed if supported through XMLHttpRequest - if ( !s.crossDomain || jQuery.support.cors ) { + if ( !options.crossDomain || support.cors ) { var callback; return { send: function( headers, complete ) { - - // Get a new xhr - var handle, i, - xhr = s.xhr(); + var i, + xhr = options.xhr(), + id = ++xhrId; // Open the socket - // Passing null username, generates a login popup on Opera (#2865) - if ( s.username ) { - xhr.open( s.type, s.url, s.async, s.username, s.password ); - } else { - xhr.open( s.type, s.url, s.async ); - } + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); // Apply custom fields if provided - if ( s.xhrFields ) { - for ( i in s.xhrFields ) { - xhr[ i ] = s.xhrFields[ i ]; + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; } } // Override mime type if needed - if ( s.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( s.mimeType ); + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); } // X-Requested-With header @@ -83,127 +101,105 @@ if ( xhrSupported ) { // akin to a jigsaw puzzle, we simply never set it to be sure. // (it can always be set on a per-request basis or even using ajaxSetup) // For same-domain requests, won't change header if already provided. - if ( !s.crossDomain && !headers["X-Requested-With"] ) { - headers["X-Requested-With"] = "XMLHttpRequest"; + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; } - // Need an extra try/catch for cross domain requests in Firefox 3 - try { - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); + // Set headers + for ( i in headers ) { + + // Support: IE<9 + // IE's ActiveXObject throws a 'Type Mismatch' exception when setting + // request header to a null-value. + // + // To keep consistent with other XHR implementations, cast the value + // to string and ignore `undefined`. + if ( headers[ i ] !== undefined ) { + xhr.setRequestHeader( i, headers[ i ] + "" ); } - } catch( err ) {} + } // Do send the request // This may raise an exception which is actually // handled in jQuery.ajax (so no try/catch here) - xhr.send( ( s.hasContent && s.data ) || null ); + xhr.send( ( options.hasContent && options.data ) || null ); // Listener callback = function( _, isAbort ) { + var status, statusText, responses; + + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { - var status, - statusText, - responseHeaders, - responses, - xml; - - // Firefox throws exceptions when accessing properties - // of an xhr when a network error occurred - // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) - try { - - // Was never called and is aborted or complete - if ( callback && ( isAbort || xhr.readyState === 4 ) ) { - - // Only called once - callback = undefined; - - // Do not keep as active anymore - if ( handle ) { - xhr.onreadystatechange = jQuery.noop; - if ( xhrOnUnloadAbort ) { - delete xhrCallbacks[ handle ]; - } + // Clean up + delete xhrCallbacks[ id ]; + callback = undefined; + xhr.onreadystatechange = jQuery.noop; + + // Abort manually if needed + if ( isAbort ) { + if ( xhr.readyState !== 4 ) { + xhr.abort(); + } + } else { + responses = {}; + status = xhr.status; + + // Support: IE<10 + // Accessing binary-data responseText throws an exception + // (#11426) + if ( typeof xhr.responseText === "string" ) { + responses.text = xhr.responseText; } - // If it's an abort - if ( isAbort ) { - // Abort it manually if needed - if ( xhr.readyState !== 4 ) { - xhr.abort(); - } - } else { - responses = {}; - status = xhr.status; - xml = xhr.responseXML; - responseHeaders = xhr.getAllResponseHeaders(); - - // Construct response list - if ( xml && xml.documentElement /* #4958 */ ) { - responses.xml = xml; - } - - // When requesting binary data, IE6-9 will throw an exception - // on any attempt to access responseText (#11426) - if ( typeof xhr.responseText === "string" ) { - responses.text = xhr.responseText; - } - - // Firefox throws an exception when accessing - // statusText for faulty cross-domain requests - try { - statusText = xhr.statusText; - } catch( e ) { - // We normalize with Webkit giving an empty statusText - statusText = ""; - } - - // Filter status for non standard behaviors - - // If the request is local and we have data: assume a success - // (success with no data won't get notified, that's the best we - // can do given current implementations) - if ( !status && s.isLocal && !s.crossDomain ) { - status = responses.text ? 200 : 404; - // IE - #1450: sometimes returns 1223 when it should be 204 - } else if ( status === 1223 ) { - status = 204; - } + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch ( e ) { + + // We normalize with Webkit giving an empty statusText + statusText = ""; + } + + // Filter status for non standard behaviors + + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + if ( !status && options.isLocal && !options.crossDomain ) { + status = responses.text ? 200 : 404; + + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; } - } - } catch( firefoxAccessException ) { - if ( !isAbort ) { - complete( -1, firefoxAccessException ); } } // Call complete if needed if ( responses ) { - complete( status, statusText, responses, responseHeaders ); + complete( status, statusText, responses, xhr.getAllResponseHeaders() ); } }; - if ( !s.async ) { - // if we're in sync mode we fire the callback + // Do send the request + // `xhr.send` may raise an exception, but it will be + // handled in jQuery.ajax (so no try/catch here) + if ( !options.async ) { + + // If we're in sync mode we fire the callback callback(); } else if ( xhr.readyState === 4 ) { + // (IE6 & IE7) if it's in cache and has been // retrieved directly we need to fire the callback - setTimeout( callback ); + window.setTimeout( callback ); } else { - handle = ++xhrId; - if ( xhrOnUnloadAbort ) { - // Create the active xhrs callbacks list if needed - // and attach the unload handler - if ( !xhrCallbacks ) { - xhrCallbacks = {}; - jQuery( window ).unload( xhrOnUnloadAbort ); - } - // Add to list of active xhrs callbacks - xhrCallbacks[ handle ] = callback; - } - xhr.onreadystatechange = callback; + + // Register the callback, but delay it in case `xhr.send` throws + // Add to the list of active xhr callbacks + xhr.onreadystatechange = xhrCallbacks[ id ] = callback; } }, @@ -214,5 +210,20 @@ if ( xhrSupported ) { } }; } - }); + } ); +} + +// Functions to create xhrs +function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} } + +function createActiveXHR() { + try { + return new window.ActiveXObject( "Microsoft.XMLHTTP" ); + } catch ( e ) {} +} + +} ); diff --git a/src/attributes.js b/src/attributes.js index 4193bbffb2..4a10974577 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -1,657 +1,11 @@ -var nodeHook, boolHook, - rclass = /[\t\r\n]/g, - rreturn = /\r/g, - rfocusable = /^(?:input|select|textarea|button|object)$/i, - rclickable = /^(?:a|area)$/i, - rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i, - ruseDefault = /^(?:checked|selected)$/i, - getSetAttribute = jQuery.support.getSetAttribute, - getSetInput = jQuery.support.input; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - name = jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call( this, j, this.className ) ); - }); - } - - if ( proceed ) { - // The disjunction here is for better compressibility (see removeClass) - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - " " - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - elem.className = jQuery.trim( cur ); - - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = arguments.length === 0 || typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call( this, j, this.className ) ); - }); - } - if ( proceed ) { - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - "" - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - elem.className = value ? jQuery.trim( cur ) : ""; - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.match( core_rnotwhite ) || []; - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - // Toggle whole class name - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery._data( this, "__className__", this.className ); - } - - // If the element has a class name or if we're passed "false", - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var val, - self = jQuery(this); - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, option, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one" || index < 0, - values = one ? null : [], - max = one ? index + 1 : options.length, - i = index < 0 ? - max : - one ? index : 0; - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // oldIE doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - // Don't return options that are disabled or in a disabled optgroup - ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && - ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var values = jQuery.makeArray( value ); - - jQuery(elem).find("option").each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attr: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( notxml ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - - } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, value + "" ); - return value; - } - - } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - ret = elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var name, propName, - i = 0, - attrNames = value && value.match( core_rnotwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( (name = attrNames[i++]) ) { - propName = jQuery.propFix[ name ] || name; - - // Boolean attributes get special treatment (#10870) - if ( rboolean.test( name ) ) { - // Set corresponding property to false for boolean attributes - // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8 - if ( !getSetAttribute && ruseDefault.test( name ) ) { - elem[ jQuery.camelCase( "default-" + name ) ] = - elem[ propName ] = false; - } else { - elem[ propName ] = false; - } - - // See #9699 for explanation of this approach (setting first, then removal) - } else { - jQuery.attr( elem, name, "" ); - } - - elem.removeAttribute( getSetAttribute ? name : propName ); - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to default in case type is set after value during creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - return ( elem[ name ] = value ); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - var - // Use .prop to determine if this attribute is understood as boolean - prop = jQuery.prop( elem, name ), - - // Fetch it accordingly - attr = typeof prop === "boolean" && elem.getAttribute( name ), - detail = typeof prop === "boolean" ? - - getSetInput && getSetAttribute ? - attr != null : - // oldIE fabricates an empty string for missing boolean attributes - // and conflates checked/selected into attroperties - ruseDefault.test( name ) ? - elem[ jQuery.camelCase( "default-" + name ) ] : - !!attr : - - // fetch an attribute node for properties not recognized as boolean - elem.getAttributeNode( name ); - - return detail && detail.value !== false ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { - // IE<8 needs the *property* name - elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); - - // Use defaultChecked and defaultSelected for oldIE - } else { - elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; - } - - return name; - } -}; - -// fix oldIE value attroperty -if ( !getSetInput || !getSetAttribute ) { - jQuery.attrHooks.value = { - get: function( elem, name ) { - var ret = elem.getAttributeNode( name ); - return jQuery.nodeName( elem, "input" ) ? - - // Ignore the value *property* by using defaultValue - elem.defaultValue : - - ret && ret.specified ? ret.value : undefined; - }, - set: function( elem, value, name ) { - if ( jQuery.nodeName( elem, "input" ) ) { - // Does not return so that setAttribute is also used - elem.defaultValue = value; - } else { - // Use nodeHook if defined (#1954); otherwise setAttribute is fine - return nodeHook && nodeHook.set( elem, value, name ); - } - } - }; -} - -// IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !getSetAttribute ) { - - // Use this for any attribute in IE6/7 - // This fixes almost every IE6/7 issue - nodeHook = jQuery.valHooks.button = { - get: function( elem, name ) { - var ret = elem.getAttributeNode( name ); - return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ? - ret.value : - undefined; - }, - set: function( elem, value, name ) { - // Set the existing or create a new attribute node - var ret = elem.getAttributeNode( name ); - if ( !ret ) { - elem.setAttributeNode( - (ret = elem.ownerDocument.createAttribute( name )) - ); - } - - ret.value = value += ""; - - // Break association with cloned elements by also using setAttribute (#9646) - return name === "value" || value === elem.getAttribute( name ) ? - value : - undefined; - } - }; - - // Set contenteditable to false on removals(#10429) - // Setting to empty string throws an error as an invalid value - jQuery.attrHooks.contenteditable = { - get: nodeHook.get, - set: function( elem, value, name ) { - nodeHook.set( elem, value === "" ? false : value, name ); - } - }; - - // Set width and height to auto instead of 0 on empty string( Bug #8150 ) - // This is for removals - jQuery.each([ "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - set: function( elem, value ) { - if ( value === "" ) { - elem.setAttribute( name, "auto" ); - return value; - } - } - }); - }); -} - - -// Some attributes require a special call on IE -// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !jQuery.support.hrefNormalized ) { - jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - get: function( elem ) { - var ret = elem.getAttribute( name, 2 ); - return ret == null ? undefined : ret; - } - }); - }); - - // href/src property should get the full normalized URL (#10299/#12915) - jQuery.each([ "href", "src" ], function( i, name ) { - jQuery.propHooks[ name ] = { - get: function( elem ) { - return elem.getAttribute( name, 4 ); - } - }; - }); -} - -if ( !jQuery.support.style ) { - jQuery.attrHooks.style = { - get: function( elem ) { - // Return undefined in the case of empty string - // Note: IE uppercases css property names, but if we were to .toLowerCase() - // .cssText, that would destroy case senstitivity in URL's, like in "background" - return elem.style.cssText || undefined; - }, - set: function( elem, value ) { - return ( elem.style.cssText = value + "" ); - } - }; -} - -// Safari mis-reports the default selected property of an option -// Accessing the parent's selectedIndex property fixes it -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - return null; - } - }); -} - -// IE6/7 call enctype encoding -if ( !jQuery.support.enctype ) { - jQuery.propFix.enctype = "encoding"; -} - -// Radios and checkboxes getter/setter -if ( !jQuery.support.checkOn ) { - jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }); -}); +define( [ + "./core", + "./attributes/val", + "./attributes/attr", + "./attributes/prop", + "./attributes/classes" +], function( jQuery ) { + +// Return jQuery for attributes-only inclusion +return jQuery; +} ); diff --git a/src/attributes/attr.js b/src/attributes/attr.js new file mode 100644 index 0000000000..127c418f9e --- /dev/null +++ b/src/attributes/attr.js @@ -0,0 +1,281 @@ +define( [ + "../core", + "../core/access", + "./support", + "../var/rnotwhite", + "./val", + "../selector" +], function( jQuery, access, support, rnotwhite ) { + +var nodeHook, boolHook, + attrHandle = jQuery.expr.attrHandle, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = support.getSetAttribute, + getSetInput = support.input; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + jQuery.nodeName( elem, "input" ) ) { + + // Setting the type on a radio button after the value resets the value in IE8-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + + // Set corresponding property to false + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + elem[ propName ] = false; + + // Support: IE<9 + // Also clear defaultChecked/defaultSelected (if appropriate) + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + } else { + + // Support: IE<9 + // Use defaultChecked and defaultSelected for oldIE + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle; + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ name ]; + attrHandle[ name ] = ret; + ret = getter( elem, name, isXML ) != null ? + name.toLowerCase() : + null; + attrHandle[ name ] = handle; + } + return ret; + }; + } else { + attrHandle[ name ] = function( elem, name, isXML ) { + if ( !isXML ) { + return elem[ jQuery.camelCase( "default-" + name ) ] ? + name.toLowerCase() : + null; + } + }; + } +} ); + +// fix oldIE attroperties +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = { + set: function( elem, value, name ) { + + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + ( ret = elem.ownerDocument.createAttribute( name ) ) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + if ( name === "value" || value === elem.getAttribute( name ) ) { + return value; + } + } + }; + + // Some attributes are constructed with empty-string values when not defined + attrHandle.id = attrHandle.name = attrHandle.coords = + function( elem, name, isXML ) { + var ret; + if ( !isXML ) { + return ( ret = elem.getAttributeNode( name ) ) && ret.value !== "" ? + ret.value : + null; + } + }; + + // Fixing value retrieval on a button requires this module + jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + if ( ret && ret.specified ) { + return ret.value; + } + }, + set: nodeHook.set + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each( [ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }; + } ); +} + +if ( !support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case sensitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +} ); diff --git a/src/attributes/classes.js b/src/attributes/classes.js new file mode 100644 index 0000000000..a58f5491c4 --- /dev/null +++ b/src/attributes/classes.js @@ -0,0 +1,174 @@ +define( [ + "../core", + "../var/rnotwhite", + "../core/init" +], function( jQuery, rnotwhite ) { + +var rclass = /[\t\r\n\f]/g; + +function getClass( elem ) { + return jQuery.attr( elem, "class" ) || ""; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( typeof value === "string" && value ) { + classes = value.match( rnotwhite ) || []; + + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && + ( " " + curValue + " " ).replace( rclass, " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // only assign if different to avoid unneeded rendering. + finalValue = jQuery.trim( cur ); + if ( curValue !== finalValue ) { + jQuery.attr( elem, "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + if ( typeof value === "string" && value ) { + classes = value.match( rnotwhite ) || []; + + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && + ( " " + curValue + " " ).replace( rclass, " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = jQuery.trim( cur ); + if ( curValue !== finalValue ) { + jQuery.attr( elem, "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( type === "string" ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = value.match( rnotwhite ) || []; + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // store className if set + jQuery._data( this, "__className__", className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + jQuery.attr( this, "class", + className || value === false ? + "" : + jQuery._data( this, "__className__" ) || "" + ); + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + getClass( elem ) + " " ).replace( rclass, " " ) + .indexOf( className ) > -1 + ) { + return true; + } + } + + return false; + } +} ); + +} ); diff --git a/src/attributes/prop.js b/src/attributes/prop.js new file mode 100644 index 0000000000..1c3a0f9718 --- /dev/null +++ b/src/attributes/prop.js @@ -0,0 +1,156 @@ +define( [ + "../core", + "../core/access", + "./support", + "../selector" +], function( jQuery, access, support ) { + +var rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each( function() { + + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch ( e ) {} + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !support.hrefNormalized ) { + + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each( [ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + } ); +} + +// Support: Safari, IE9+ +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + }, + set: function( elem ) { + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + +// IE6/7 call enctype encoding +if ( !support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +} ); diff --git a/src/attributes/support.js b/src/attributes/support.js new file mode 100644 index 0000000000..2292ab77db --- /dev/null +++ b/src/attributes/support.js @@ -0,0 +1,70 @@ +define( [ + "../var/document", + "../var/support" +], function( document, support ) { + +( function() { + var a, + input = document.createElement( "input" ), + div = document.createElement( "div" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + // Setup + div = document.createElement( "div" ); + div.setAttribute( "className", "t" ); + div.innerHTML = "
        a"; + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Support: Windows Web Apps (WWA) + // `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "checkbox" ); + div.appendChild( input ); + + a = div.getElementsByTagName( "a" )[ 0 ]; + + // First batch of tests. + a.style.cssText = "top:1px"; + + // Test setAttribute on camelCase class. + // If it works, we need attrFixes when doing get/setAttribute (ie6/7) + support.getSetAttribute = div.className !== "t"; + + // Get the style information from getAttribute + // (IE uses .cssText instead) + support.style = /top/.test( a.getAttribute( "style" ) ); + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + support.hrefNormalized = a.getAttribute( "href" ) === "/a"; + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + support.checkOn = !!input.value; + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + support.optSelected = opt.selected; + + // Tests for enctype support on a form (#6743) + support.enctype = !!document.createElement( "form" ).enctype; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE8 only + // Check if we can trust getAttribute("value") + input = document.createElement( "input" ); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; +} )(); + +return support; + +} ); diff --git a/src/attributes/val.js b/src/attributes/val.js new file mode 100644 index 0000000000..33b1dbea63 --- /dev/null +++ b/src/attributes/val.js @@ -0,0 +1,191 @@ +define( [ + "../core", + "./support", + "../core/init" +], function( jQuery, support ) { + +var rreturn = /\r/g, + rspaces = /[\x20\t\r\n\f]+/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, isFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( + hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + + // handle most common string cases + ret.replace( rreturn, "" ) : + + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE10-11+ + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + jQuery.trim( jQuery.text( elem ) ).replace( rspaces, " " ); + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + ( support.optDisabled ? + !option.disabled : + option.getAttribute( "disabled" ) === null ) && + ( !option.parentNode.disabled || + !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + if ( jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 ) { + + // Support: IE6 + // When new option element is added to select box we need to + // force reflow of newly added node in order to workaround delay + // of initialization properties + try { + option.selected = optionSet = true; + + } catch ( _ ) { + + // Will be executed only in IE6 + option.scrollHeight; + } + + } else { + option.selected = false; + } + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + + return options; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + +} ); diff --git a/src/callbacks.js b/src/callbacks.js index 58f1402a84..6516569640 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -1,12 +1,14 @@ -// String to Object options format cache -var optionsCache = {}; +define( [ + "./core", + "./var/rnotwhite" +], function( jQuery, rnotwhite ) { -// Convert String-formatted options into Object-formatted ones and store in cache +// Convert String-formatted options into Object-formatted ones function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + var object = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; - }); + } ); return object; } @@ -37,154 +39,186 @@ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : + createOptions( options ) : jQuery.extend( {}, options ); - var // Last fire value (for non-forgettable lists) + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists memory, + // Flag to know if list was already fired fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, + + // Flag to prevent firing + locked, + // Actual callback list list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; + fire = function() { + + // Enforce single-firing + locked = options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } } } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { list = []; + + // Otherwise, this object is spent } else { - self.disable(); + list = ""; } } }, + // Actual Callbacks object self = { + // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { + if ( jQuery.isFunction( arg ) ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } - } else if ( arg && arg.length && type !== "string" ) { + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + // Inspect recursively add( arg ); } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); } } return this; }, + // Remove a callback from the list remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; } - }); - } + } + } ); return this; }, - // Control if a given callback is in the list + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { - return jQuery.inArray( fn, list ) > -1; + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; }, + // Remove all callbacks from the list empty: function() { - list = []; + if ( list ) { + list = []; + } return this; }, - // Have the list do nothing anymore + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values disable: function() { - list = stack = memory = undefined; + locked = queue = []; + list = memory = ""; return this; }, - // Is it disabled? disabled: function() { return !list; }, - // Lock the list in its current state + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions lock: function() { - stack = undefined; + locked = true; if ( !memory ) { self.disable(); } return this; }, - // Is it locked? locked: function() { - return !stack; + return !!locked; }, + // Call all callbacks with the given context and arguments fireWith: function( context, args ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( list && ( !fired || stack ) ) { - if ( firing ) { - stack.push( args ); - } else { - fire( args ); + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); } } return this; }, + // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, + // To know if the callbacks have already been called at least once fired: function() { return !!fired; @@ -193,3 +227,6 @@ jQuery.Callbacks = function( options ) { return self; }; + +return jQuery; +} ); diff --git a/src/core.js b/src/core.js index 656fc22f14..cdbc1971ce 100644 --- a/src/core.js +++ b/src/core.js @@ -1,66 +1,32 @@ -var - // A central reference to the root jQuery(document) - rootjQuery, - - // The deferred used on DOM ready - readyList, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - location = window.location, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // [[Class]] -> type pairs - class2type = {}, - - // List of deleted data cache ids, so we can reuse them - core_deletedIds = [], +define( [ + "./var/deletedIds", + "./var/document", + "./var/slice", + "./var/concat", + "./var/push", + "./var/indexOf", + "./var/class2type", + "./var/toString", + "./var/hasOwn", + "./var/support" +], function( deletedIds, document, slice, concat, push, indexOf, + class2type, toString, hasOwn, support ) { - core_version = "@VERSION", - - // Save a reference to some core methods - core_concat = core_deletedIds.concat, - core_push = core_deletedIds.push, - core_slice = core_deletedIds.slice, - core_indexOf = core_deletedIds.indexOf, - core_toString = class2type.toString, - core_hasOwn = class2type.hasOwnProperty, - core_trim = core_version.trim, +var + version = "@VERSION", // Define a local copy of jQuery jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); }, - // Used for matching numbers - core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, - - // Used for splitting on whitespace - core_rnotwhite = /\S+/g, - - // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + // Support: Android<4.1, IE<9 + // Make sure we trim BOM and NBSP rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, - - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, - rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, - // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([\da-z])/gi, @@ -68,126 +34,14 @@ var // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); - }, - - // The ready event handler and self cleanup method - DOMContentLoaded = function() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - } else if ( document.readyState === "complete" ) { - // we're here because readyState === "complete" in oldIE - // which is good enough for us to call the dom ready! - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } }; jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used - jquery: core_version, + jquery: version, constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, // Start with an empty selector selector: "", @@ -195,25 +49,20 @@ jQuery.fn = jQuery.prototype = { // The default length of a jQuery object is 0 length: 0, - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - toArray: function() { - return core_slice.call( this ); + return slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { - return num == null ? + return num != null ? - // Return a 'clean' array - this.toArray() : + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); + // Return all the elements in a clean array + slice.call( this ); }, // Take an array of elements and push it onto the stack @@ -232,21 +81,18 @@ jQuery.fn = jQuery.prototype = { }, // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); + each: function( callback ) { + return jQuery.each( this, callback ); }, - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); }, slice: function() { - return this.pushStack( core_slice.apply( this, arguments ) ); + return this.pushStack( slice.apply( this, arguments ) ); }, first: function() { @@ -260,32 +106,23 @@ jQuery.fn = jQuery.prototype = { eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); }, end: function() { - return this.prevObject || this.constructor(null); + return this.prevObject || this.constructor(); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice }; -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, + var src, copyIsArray, copy, name, options, clone, + target = arguments[ 0 ] || {}, i = 1, length = arguments.length, deep = false; @@ -293,25 +130,28 @@ jQuery.extend = jQuery.fn.extend = function() { // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; - target = arguments[1] || {}; + // skip the boolean and the target - i = 2; + target = arguments[ i ] || {}; + i++; } // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { target = {}; } // extend jQuery itself if only one argument is passed - if ( length === i ) { + if ( i === length ) { target = this; - --i; + i--; } for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { + if ( ( options = arguments[ i ] ) != null ) { + // Extend the base object for ( name in options ) { src = target[ name ]; @@ -323,13 +163,15 @@ jQuery.extend = jQuery.fn.extend = function() { } // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + if ( copyIsArray ) { copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; + clone = src && jQuery.isArray( src ) ? src : []; } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; + clone = src && jQuery.isPlainObject( src ) ? src : {}; } // Never move original objects, clone them @@ -347,229 +189,112 @@ jQuery.extend = jQuery.fn.extend = function() { return target; }; -jQuery.extend({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, +jQuery.extend( { - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, + // Assume jQuery is ready without the ready module + isReady: true, - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } + error: function( msg ) { + throw new Error( msg ); }, - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger("ready").off("ready"); - } - }, + noop: function() {}, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { - return jQuery.type(obj) === "function"; + return jQuery.type( obj ) === "function"; }, isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; + return jQuery.type( obj ) === "array"; }, isWindow: function( obj ) { + /* jshint eqeqeq: false */ return obj != null && obj == obj.window; }, isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); + + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; }, - type: function( obj ) { - if ( obj == null ) { - return String( obj ); + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; } - return typeof obj === "object" || typeof obj === "function" ? - class2type[ core_toString.call(obj) ] || "object" : - typeof obj; + return true; }, isPlainObject: function( obj ) { + var key; + // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { + // Not own constructor property must be Object if ( obj.constructor && - !core_hasOwn.call(obj, "constructor") && - !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { return false; } } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 return false; } + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( !support.ownFirst ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. - - var key; for ( key in obj ) {} - return key === undefined || core_hasOwn.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // keepScripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, keepScripts ) { - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - keepScripts = context; - context = false; - } - context = context || document; - - var parsed = rsingleTag.exec( data ), - scripts = !keepScripts && []; - - // Single tag - if ( parsed ) { - return [ context.createElement( parsed[1] ) ]; - } - - parsed = context.createDocumentFragment(); - jQuery.clean( [ data ], context, parsed, scripts ); - if ( scripts ) { - jQuery( scripts ).remove(); - } - return jQuery.merge( [], parsed.childNodes ); + return key === undefined || hasOwn.call( obj, key ); }, - parseJSON: function( data ) { - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } - - if ( data === null ) { - return data; - } - - if ( typeof data === "string" ) { - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); - - if ( data ) { - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { - - return ( new Function( "return " + data ) )(); - } - } - } - - jQuery.error( "Invalid JSON: " + data ); - }, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); + type: function( obj ) { + if ( obj == null ) { + return obj + ""; } - return xml; + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; }, - noop: function() {}, - - // Evaluates a script in a global context // Workarounds based on findings by Jim Driscoll // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); + window[ "eval" ].call( window, data ); // jscs:ignore requireDotNotation } )( data ); } }, @@ -584,49 +309,20 @@ jQuery.extend({ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); - - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); + each: function( obj, callback ) { + var length, i = 0; - if ( value === false ) { - break; - } + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; } } - - // A special, fast, case for the most common use of each } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; } } } @@ -634,33 +330,25 @@ jQuery.extend({ return obj; }, - // Use native String.trim function wherever possible - trim: core_trim && !core_trim.call("\uFEFF\xA0") ? - function( text ) { - return text == null ? - "" : - core_trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, + // Support: Android<4.1, IE<9 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { + if ( isArrayLike( Object( arr ) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { - core_push.call( ret, arr ); + push.call( ret, arr ); } } @@ -671,14 +359,15 @@ jQuery.extend({ var len; if ( arr ) { - if ( core_indexOf ) { - return core_indexOf.call( arr, elem, i ); + if ( indexOf ) { + return indexOf.call( arr, elem, i ); } len = arr.length; i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays if ( i in arr && arr[ i ] === elem ) { return i; @@ -690,16 +379,18 @@ jQuery.extend({ }, merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; + var len = +second.length, + j = 0, + i = first.length; - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - } else { - while ( second[j] !== undefined ) { + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[ j ] !== undefined ) { first[ i++ ] = second[ j++ ]; } } @@ -709,40 +400,39 @@ jQuery.extend({ return first; }, - grep: function( elems, callback, inv ) { - var retVal, - ret = [], + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], i = 0, - length = elems.length; - inv = !!inv; + length = elems.length, + callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); } } - return ret; + return matches; }, // arg is for internal usage only map: function( elems, callback, arg ) { - var value, + var length, value, i = 0, - length = elems.length, - isArray = isArraylike( elems ), ret = []; - // Go through the array, translating each of the items to their - if ( isArray ) { + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { - ret[ ret.length ] = value; + ret.push( value ); } } @@ -752,13 +442,13 @@ jQuery.extend({ value = callback( elems[ i ], i, arg ); if ( value != null ) { - ret[ ret.length ] = value; + ret.push( value ); } } } // Flatten any nested arrays - return core_concat.apply( [], ret ); + return concat.apply( [], ret ); }, // A global GUID counter for objects @@ -767,7 +457,7 @@ jQuery.extend({ // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { - var tmp, args, proxy; + var args, proxy, tmp; if ( typeof context === "string" ) { tmp = fn[ context ]; @@ -782,9 +472,9 @@ jQuery.extend({ } // Simulated bind - args = core_slice.call( arguments, 2 ); + args = slice.call( arguments, 2 ); proxy = function() { - return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed @@ -793,143 +483,47 @@ jQuery.extend({ return proxy; }, - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - length = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, - now: function() { - return ( new Date() ).getTime(); - } -}); - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - // Standards-based browsers support DOMContentLoaded - } else if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + return +( new Date() ); + }, - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); - // If IE event model is used - } else { - // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var top = false; - - try { - top = window.frameElement == null && document.documentElement; - } catch(e) {} - - if ( top && top.doScroll ) { - (function doScrollCheck() { - if ( !jQuery.isReady ) { - - try { - // Use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - top.doScroll("left"); - } catch(e) { - return setTimeout( doScrollCheck, 50 ); - } - - // and execute any waiting functions - jQuery.ready(); - } - })(); - } - } - } - return readyList.promise( obj ); -}; +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ]; +} +/* jshint ignore: end */ // Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); +} ); + +function isArrayLike( obj ) { -function isArraylike( obj ) { - var length = obj.length, + // Support: iOS 8.2 (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, type = jQuery.type( obj ); - if ( jQuery.isWindow( obj ) ) { + if ( type === "function" || jQuery.isWindow( obj ) ) { return false; } - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || type !== "function" && - ( length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj ); + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; } -// All jQuery objects should point back to these -rootjQuery = jQuery(document); +return jQuery; +} ); diff --git a/src/core/access.js b/src/core/access.js new file mode 100644 index 0000000000..7fb3cd4945 --- /dev/null +++ b/src/core/access.js @@ -0,0 +1,65 @@ +define( [ + "../core" +], function( jQuery ) { + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( + elems[ i ], + key, + raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[ 0 ], key ) : emptyGet; +}; + +return access; + +} ); diff --git a/src/core/init.js b/src/core/init.js new file mode 100644 index 0000000000..fbd87d4e89 --- /dev/null +++ b/src/core/init.js @@ -0,0 +1,140 @@ +// Initialize a jQuery object +define( [ + "../core", + "../var/document", + "./var/rsingleTag", + "../traversing/findFilter" +], function( jQuery, document, rsingleTag ) { + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // init accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt( 0 ) === "<" && + selector.charAt( selector.length - 1 ) === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[ 2 ] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[ 0 ] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof root.ready !== "undefined" ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + +return init; + +} ); diff --git a/src/core/parseHTML.js b/src/core/parseHTML.js new file mode 100644 index 0000000000..327d6ef5c7 --- /dev/null +++ b/src/core/parseHTML.js @@ -0,0 +1,41 @@ +define( [ + "../core", + "../var/document", + "./var/rsingleTag", + "../manipulation/buildFragment" +], function( jQuery, document, rsingleTag, buildFragment ) { + +// data: string of html +// context (optional): If specified, the fragment will be created in this context, +// defaults to document +// keepScripts (optional): If true, will include scripts passed in the html string +jQuery.parseHTML = function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[ 1 ] ) ]; + } + + parsed = buildFragment( [ data ], context, scripts ); + + if ( scripts && scripts.length ) { + jQuery( scripts ).remove(); + } + + return jQuery.merge( [], parsed.childNodes ); +}; + +return jQuery.parseHTML; + +} ); diff --git a/src/core/ready.js b/src/core/ready.js new file mode 100644 index 0000000000..da7a0a2922 --- /dev/null +++ b/src/core/ready.js @@ -0,0 +1,163 @@ +define( [ + "../core", + "../var/document", + "../core/init", + "../deferred" +], function( jQuery, document ) { + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +} ); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || + window.event.type === "load" || + document.readyState === "complete" ) { + + detach(); + jQuery.ready(); + } +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // Support: IE6-10 + // Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); + + // If IE event model is used + } else { + + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch ( e ) {} + + if ( top && top.doScroll ) { + ( function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll( "left" ); + } catch ( e ) { + return window.setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + } )(); + } + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + +} ); diff --git a/src/core/var/rsingleTag.js b/src/core/var/rsingleTag.js new file mode 100644 index 0000000000..1a55ee39d2 --- /dev/null +++ b/src/core/var/rsingleTag.js @@ -0,0 +1,5 @@ +define( function() { + + // Match a standalone tag + return ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); +} ); diff --git a/src/css.js b/src/css.js index 210bed2e80..e222ce7b42 100644 --- a/src/css.js +++ b/src/css.js @@ -1,57 +1,74 @@ -var curCSS, getStyles, iframe, +define( [ + "./core", + "./var/pnum", + "./core/access", + "./css/var/rmargin", + "./var/document", + "./var/rcssNum", + "./css/var/rnumnonpx", + "./css/var/cssExpand", + "./css/var/isHidden", + "./css/var/swap", + "./css/curCSS", + "./css/adjustCSS", + "./css/defaultDisplay", + "./css/addGetHookIf", + "./css/support", + + "./core/init", + "./core/ready", + "./selector" // contains +], function( jQuery, pnum, access, rmargin, document, rcssNum, rnumnonpx, cssExpand, + isHidden, swap, curCSS, adjustCSS, defaultDisplay, addGetHookIf, support ) { + +var + + // BuildExclude + getStyles = curCSS.getStyles, ralpha = /alpha\([^)]*\)/i, - ropacity = /opacity\s*=\s*([^)]*)/, - rposition = /^(top|right|bottom|left)$/, - // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" - // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + ropacity = /opacity\s*=\s*([^)]*)/i, + + // swappable if display is none or starts with table except + // "table", "table-cell", or "table-caption" + // see here for display values: + // https://developer.mozilla.org/en-US/docs/CSS/display rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rmargin = /^margin/, - rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), - rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), - rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), - elemdisplay = { BODY: "block" }, + rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ), cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { - letterSpacing: 0, - fontWeight: 400 + letterSpacing: "0", + fontWeight: "400" }, - cssExpand = [ "Top", "Right", "Bottom", "Left" ], - cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; + +// BuildExclude +curCSS = curCSS.curCSS; // return a css property mapped to a potentially vendor prefixed property -function vendorPropName( style, name ) { +function vendorPropName( name ) { // shortcut for names that are not vendor prefixed - if ( name in style ) { + if ( name in emptyStyle ) { return name; } // check for vendor prefixed names - var capName = name.charAt(0).toUpperCase() + name.slice(1), - origName = name, + var capName = name.charAt( 0 ).toUpperCase() + name.slice( 1 ), i = cssPrefixes.length; while ( i-- ) { name = cssPrefixes[ i ] + capName; - if ( name in style ) { + if ( name in emptyStyle ) { return name; } } - - return origName; -} - -function isHidden( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); } function showHide( elements, show ) { - var elem, + var display, elem, hidden, values = [], index = 0, length = elements.length; @@ -61,11 +78,14 @@ function showHide( elements, show ) { if ( !elem.style ) { continue; } + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; if ( show ) { + // Reset the inline display of this element to learn if it is // being hidden by cascaded rules or not - if ( !values[ index ] && elem.style.display === "none" ) { + if ( !values[ index ] && display === "none" ) { elem.style.display = ""; } @@ -73,10 +93,19 @@ function showHide( elements, show ) { // in a stylesheet to whatever the default browser style is // for such an element if ( elem.style.display === "" && isHidden( elem ) ) { - values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + values[ index ] = + jQuery._data( elem, "olddisplay", defaultDisplay( elem.nodeName ) ); + } + } else { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( + elem, + "olddisplay", + hidden ? display : jQuery.css( elem, "display" ) + ); } - } else if ( !values[ index ] && !isHidden( elem ) ) { - jQuery._data( elem, "olddisplay", jQuery.css( elem, "display" ) ); } } @@ -95,55 +124,114 @@ function showHide( elements, show ) { return elements; } -jQuery.fn.extend({ - css: function( name, value ) { - return jQuery.access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? - if ( jQuery.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? - return map; + // If we already have the right measurement, avoid augmentation + 4 : + + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - }, - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - var bool = typeof state === "boolean"; + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { - return this.each(function() { - if ( bool ? state : isHidden( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } - }); + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = support.boxSizing && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test( val ) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && + ( support.boxSizingReliable() || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; } -}); -jQuery.extend({ + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +jQuery.extend( { + // Add in style property hooks for overriding the default // behavior of getting and setting a style property cssHooks: { opacity: { get: function( elem, computed ) { if ( computed ) { + // We should always get a number back from opacity var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; @@ -152,13 +240,17 @@ jQuery.extend({ } }, - // Exclude the following css properties to add px + // Don't automatically add "px" to these possibly-unitless properties cssNumber: { + "animationIterationCount": true, "columnCount": true, "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, "fontWeight": true, "lineHeight": true, "opacity": true, + "order": true, "orphans": true, "widows": true, "zIndex": true, @@ -168,12 +260,14 @@ jQuery.extend({ // Add in properties whose names you wish to fix before // setting or getting the value cssProps: { + // normalize float css property - "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + "float": support.cssFloat ? "cssFloat" : "styleFloat" }, // Get and set the style property on a DOM Node style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { return; @@ -184,7 +278,8 @@ jQuery.extend({ origName = jQuery.camelCase( name ), style = elem.style; - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + name = jQuery.cssProps[ origName ] || + ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); // gets hook for the prefixed version // followed by the unprefixed version @@ -194,42 +289,48 @@ jQuery.extend({ if ( value !== undefined ) { type = typeof value; - // convert relative number strings (+= or -=) to relative numbers. #7345 - if ( type === "string" && (ret = rrelNum.exec( value )) ) { - value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + // Fixes bug #9237 type = "number"; } - // Make sure that NaN and null values aren't set. See: #7116 - if ( value == null || type === "number" && isNaN( value ) ) { + // Make sure that null and NaN values aren't set. See: #7116 + if ( value == null || value !== value ) { return; } - // If a number was passed in, add 'px' to the (except for certain CSS properties) - if ( type === "number" && !jQuery.cssNumber[ origName ] ) { - value += "px"; + // If a number was passed in, add the unit (except for certain CSS properties) + if ( type === "number" ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); } // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, - // but it would mean to define eight (for every problematic property) identical functions - if ( value === "" && name.indexOf("background") === 0 ) { - value = " "; + // but it would mean to define eight + // (for every problematic property) identical functions + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; } // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - // Wrapped to prevent IE from throwing errors when 'invalid' values are provided - // Fixes bug #5509 + // Support: IE + // Swallow errors from 'invalid' CSS values (#5509) try { style[ name ] = value; - } catch(e) {} + } catch ( e ) {} } } else { + // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + return ret; } @@ -239,11 +340,12 @@ jQuery.extend({ }, css: function( elem, name, extra, styles ) { - var val, num, hooks, + var num, val, hooks, origName = jQuery.camelCase( name ); // Make sure that we're working with the right name - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + name = jQuery.cssProps[ origName ] || + ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName ); // gets hook for the prefixed version // followed by the unprefixed version @@ -265,270 +367,27 @@ jQuery.extend({ } // Return, converting to number if forced or a qualifier was provided and val looks numeric - if ( extra ) { + if ( extra === "" || extra ) { num = parseFloat( val ); - return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + return extra === true || isFinite( num ) ? num || 0 : val; } return val; - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - } -}); - -// NOTE: we've included the "window" in window.getComputedStyle -// because jsdom on node.js will break without it. -if ( window.getComputedStyle ) { - getStyles = function( elem ) { - return window.getComputedStyle( elem, null ); - }; - - curCSS = function( elem, name, _computed ) { - var width, minWidth, maxWidth, - computed = _computed || getStyles( elem ), - - // getPropertyValue is only needed for .css('filter') in IE9, see #12537 - ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, - style = elem.style; - - if ( computed ) { - - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right - // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels - // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values - if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret; - }; -} else if ( document.documentElement.currentStyle ) { - getStyles = function( elem ) { - return elem.currentStyle; - }; - - curCSS = function( elem, name, _computed ) { - var left, rs, rsLeft, - computed = _computed || getStyles( elem ), - ret = computed ? computed[ name ] : undefined, - style = elem.style; - - // Avoid setting ret to empty string here - // so we don't default to auto - if ( ret == null && style && style[ name ] ) { - ret = style[ name ]; - } - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - // but not position css attributes, as those are proportional to the parent element instead - // and we can't measure the parent instead because it might trigger a "stacking dolls" problem - if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { - - // Remember the original values - left = style.left; - rs = elem.runtimeStyle; - rsLeft = rs && rs.left; - - // Put in the new values to get a computed value out - if ( rsLeft ) { - rs.left = elem.currentStyle.left; - } - style.left = name === "fontSize" ? "1em" : ret; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - if ( rsLeft ) { - rs.left = rsLeft; - } - } - - return ret === "" ? "auto" : ret; - }; -} - -function setPositiveNumber( elem, value, subtract ) { - var matches = rnumsplit.exec( value ); - return matches ? - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : - value; -} - -function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { - var i = extra === ( isBorderBox ? "border" : "content" ) ? - // If we already have the right measurement, avoid augmentation - 4 : - // Otherwise initialize for horizontal or vertical properties - name === "width" ? 1 : 0, - - val = 0; - - for ( ; i < 4; i += 2 ) { - // both box models exclude margin, so add it if we want it - if ( extra === "margin" ) { - val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); - } - - if ( isBorderBox ) { - // border-box includes padding, so remove it if we want content - if ( extra === "content" ) { - val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // at this point, extra isn't border nor margin, so remove border - if ( extra !== "margin" ) { - val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } else { - // at this point, extra isn't content, so add padding - val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // at this point, extra isn't content nor padding, so add border - if ( extra !== "padding" ) { - val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - return val; -} - -function getWidthOrHeight( elem, name, extra ) { - - // Start with offset property, which is equivalent to the border-box value - var valueIsBorderBox = true, - val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - styles = getStyles( elem ), - isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // some non-html elements return undefined for offsetWidth, so check for null/undefined - // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 - // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 - if ( val <= 0 || val == null ) { - // Fall back to computed then uncomputed css if necessary - val = curCSS( elem, name, styles ); - if ( val < 0 || val == null ) { - val = elem.style[ name ]; - } - - // Computed unit is not pixels. Stop here and return. - if ( rnumnonpx.test(val) ) { - return val; - } - - // we need the check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); - - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - } - - // use the active box-sizing model to add/subtract irrelevant styles - return ( val + - augmentWidthOrHeight( - elem, - name, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles - ) - ) + "px"; -} - -// Try to determine the default display value of an element -function css_defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - // Use the already-created iframe if possible - iframe = ( iframe || - jQuery(" + + + diff --git a/test/data/core/onready.html b/test/data/core/onready.html new file mode 100644 index 0000000000..1e8f127e77 --- /dev/null +++ b/test/data/core/onready.html @@ -0,0 +1,24 @@ + + + + + alias-masked DOM properties (#14074) + + + + +
        + +
        + + + \ No newline at end of file diff --git a/test/data/css/cssWidthBeforeDocReady.html b/test/data/css/cssWidthBeforeDocReady.html new file mode 100644 index 0000000000..3bdd1b5ab5 --- /dev/null +++ b/test/data/css/cssWidthBeforeDocReady.html @@ -0,0 +1,26 @@ + + + + + + + +
        + + + + diff --git a/test/data/data/dataAttrs.html b/test/data/data/dataAttrs.html new file mode 100644 index 0000000000..5e6e442a6b --- /dev/null +++ b/test/data/data/dataAttrs.html @@ -0,0 +1,16 @@ + + + + + IE11 onpageshow strangeness (#14894) + + + + + Test for #14894 + + diff --git a/test/data/dimensions/documentLarge.html b/test/data/dimensions/documentLarge.html index b2fabd144d..a6598fcc86 100644 --- a/test/data/dimensions/documentLarge.html +++ b/test/data/dimensions/documentLarge.html @@ -11,7 +11,7 @@
        - +
        diff --git a/test/data/dimensions/documentSmall.html b/test/data/dimensions/documentSmall.html index d5eeacfc2e..63e1c2a8ff 100644 --- a/test/data/dimensions/documentSmall.html +++ b/test/data/dimensions/documentSmall.html @@ -15,7 +15,7 @@
        - +
        diff --git a/test/data/errorWithJSON.php b/test/data/errorWithJSON.php new file mode 100644 index 0000000000..62b187ecc9 --- /dev/null +++ b/test/data/errorWithJSON.php @@ -0,0 +1,6 @@ + + + + + .focus() (activeElement access #13393) + + + + + + + + diff --git a/test/data/event/focusinCrossFrame.html b/test/data/event/focusinCrossFrame.html new file mode 100644 index 0000000000..487f8de8fd --- /dev/null +++ b/test/data/event/focusinCrossFrame.html @@ -0,0 +1,18 @@ + + + + + focusin event cross-frame (#14180) + + + + + + + + diff --git a/test/data/event/interactiveReady.html b/test/data/event/interactiveReady.html new file mode 100644 index 0000000000..77b37104d0 --- /dev/null +++ b/test/data/event/interactiveReady.html @@ -0,0 +1,23 @@ + + + + +Test case for gh-2100 + + + + + + + + +
        + + diff --git a/test/data/event/promiseReady.html b/test/data/event/promiseReady.html index 3ea11f05ea..17b6e7f2c6 100644 --- a/test/data/event/promiseReady.html +++ b/test/data/event/promiseReady.html @@ -3,7 +3,7 @@ Test case for jQuery ticket #11470 - + + diff --git a/test/data/event/triggerunload.html b/test/data/event/triggerunload.html new file mode 100644 index 0000000000..a7879c7728 --- /dev/null +++ b/test/data/event/triggerunload.html @@ -0,0 +1,18 @@ + + + + + diff --git a/test/data/headers.php b/test/data/headers.php index 968f13f19a..79c1830551 100644 --- a/test/data/headers.php +++ b/test/data/headers.php @@ -14,5 +14,10 @@ } foreach( explode( "_" , $_GET[ "keys" ] ) as $key ) { - echo "$key: " . @$headers[ strtoupper( $key ) ] . "\n"; + + // Only echo if key exists in the header + if ( isset( $headers[ strtoupper( $key ) ] ) ) { + echo "$key: " . @$headers[ strtoupper( $key ) ] . "\n"; + } + } diff --git a/test/data/jquery-1.8.2.ajax_xhr.min.js b/test/data/jquery-1.8.2.ajax_xhr.min.js deleted file mode 100644 index 830d7387cb..0000000000 --- a/test/data/jquery-1.8.2.ajax_xhr.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v1.8.2 -deprecated,-css,-ajax/script,-ajax/jsonp,-effects,-offset,-dimensions jquery.com | jquery.org/license */ -(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.2 -deprecated,-css,-ajax/script,-ajax/jsonp,-effects,-offset,-dimensions",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
        a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0],r.style.cssText="top:1px;float:left;opacity:.5";if(!n||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
        t
        ",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
        ",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r,i,s=e.selectedIndex,o=[],u=e.options,a=e.type==="select-one";if(s<0)return null;n=a?s:0,r=a?s+1:u.length;for(;n=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r,g,!0)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n,r){var i=0,s=t.length;for(;i2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
        ",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e];return t||(t=k(e,new RegExp("(^|"+O+")"+e+"("+O+"|$)"))),function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")}},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&(!!e.type||!!e.href)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(e,t,n){return[0]}),last:st(function(e,t,n){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t,n){for(var r=0;r=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

        ",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&(!i||!i.test(e))){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&(!i||!i.test(n)))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
        ","
        "],thead:[1,"","
        "],tr:[2,"","
        "],td:[3,"","
        "],col:[2,"","
        "],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
        ","
        "]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}});var Dt=/%20/g,Pt=/\[\]$/,Ht=/\r?\n/g,Bt=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,jt=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||jt.test(this.nodeName)||Bt.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(Ht,"\r\n")}}):{name:t.name,value:n.replace(Ht,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)Ft(r,e[r],n,s);return i.join("&").replace(Dt,"+")};var It,qt,Rt=/#.*$/,Ut=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,zt=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,Wt=/^(?:GET|HEAD)$/,Xt=/^\/\//,Vt=/\?/,$t=/)<[^<]*)*<\/script>/gi,Jt=/([?&])_=[^&]*/,Kt=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Qt=v.fn.load,Gt={},Yt={},Zt=["*/"]+["*"];try{qt=s.href}catch(en){qt=i.createElement("a"),qt.href="",qt=qt.href}It=Kt.exec(qt.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&Qt)return Qt.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
        ").append(e.replace($t,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?rn(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),rn(e,t),e},ajaxSettings:{url:qt,isLocal:zt.test(It[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Zt},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:tn(Gt),ajaxTransport:tn(Yt),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=sn(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=on(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=Ut.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(Rt,"").replace(Xt,It[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=Kt.exec(c.url.toLowerCase())||!1,c.crossDomain=a&&a.join(":")+(a[3]?"":a[1]==="http:"?80:443)!==It.join(":")+(It[3]?"":It[1]==="http:"?80:443)),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),nn(Gt,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!Wt.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(Vt.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(Jt,"$1_="+N);c.url=C+(C===c.url?(Vt.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Zt+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=nn(Yt,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var un,an=e.ActiveXObject?function(){for(var e in un)un[e](0,1)}:!1,fn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&ln()||cn()}:ln,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,an&&delete un[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(e){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++fn,an&&(un||(un={},v(e).unload(an)),un[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); diff --git a/test/data/jquery-1.9.1.js b/test/data/jquery-1.9.1.js new file mode 100644 index 0000000000..80c97a226a --- /dev/null +++ b/test/data/jquery-1.9.1.js @@ -0,0 +1,9885 @@ +/*! + * jQuery JavaScript Library v1.9.1 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-2-4 + */ +( function( window, undefined ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; +var + + // The deferred used on DOM ready + readyList, + + // A central reference to the root jQuery(document) + rootjQuery, + + // Support: IE<9 + // For `typeof node.method` instead of `node.method !== undefined` + core_strundefined = typeof undefined, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + location = window.location, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.9.1", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt( 0 ) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[ 2 ] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[ 0 ] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + end: function() { + return this.prevObject || this.constructor( null ); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[ 1 ] || {}; + + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type( obj ) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat( obj ) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call( obj ) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call( obj, "constructor" ) && + !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + } catch ( e ) { + + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[ 1 ] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "" ) ) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data, "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch ( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call( "\uFEFF\xA0" ) ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[ j ] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[ i ], key, raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[ 0 ], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + } +} ); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch ( e ) {} + + if ( top && top.doScroll ) { + ( function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll( "left" ); + } catch ( e ) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + } )(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error".split( " " ), function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery( document ); + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value (for non-forgettable lists) + memory, + + // Flag to know if list was already fired + fired, + + // End of the loop when firing + firingLength, + + // Index of currently firing callback (modified by remove if needed) + firingIndex, + + // First callback to fire (used internally by add and fireWith) + firingStart, + + // Actual callback list + list = [], + + // Stack of fire calls for repeatable lists + stack = !options.once && [], + + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // First, we save the current length + var start = list.length; + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + } ); + } + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + + // Is it disabled? + disabled: function() { + return !list; + }, + + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + + // Is it locked? + locked: function() { + return !stack; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ) ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( function() { + + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +} ); +jQuery.support = ( function() { + + var support, all, a, + input, select, fragment, + opt, eventName, isSupported, i, + div = document.createElement( "div" ); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
        a"; + + // Support tests won't run in some limited or non-browser environments + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + if ( !all || !a || !all.length ) { + return {}; + } + + // First batch of tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement( "option" ) ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + support = { + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: div.firstChild.nodeType === 3, + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName( "tbody" ).length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName( "link" ).length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute( "style" ) ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: a.getAttribute( "href" ) === "/a", + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.5/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + checkOn: !!input.value, + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Tests for enctype support on a form (#6743) + enctype: !!document.createElement( "form" ).enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>", + + // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode + boxModel: document.compatMode === "CSS1Compat", + + // Will be defined later + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + boxSizingReliable: true, + pixelPosition: false + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch ( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement( "input" ); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + } ); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php + for ( i in { submit: true, change: true, focusin: true } ) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Run tests that need a body at doc ready + jQuery( function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName( "body" )[ 0 ]; + + if ( !body ) { + + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement( "div" ); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
        t
        "; + tds = div.getElementsByTagName( "td" ); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + support.boxSizing = ( div.offsetWidth === 4 ); + support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement( "div" ) ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
        "; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + } ); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +} )(); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var i, l, thisCache, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } else { + + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[ i ] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend( { + cache: {}, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute( "classid" ) === noData; + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var attrs, name, + elem = this[ 0 ], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[ i ].name; + + if ( !name.indexOf( "data-" ) ) { + name = jQuery.camelCase( name.slice( 5 ) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + jQuery.data( this, key ); + } ); + } + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + + // Try to fetch any internally stored data first + return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + } + + this.each( function() { + jQuery.data( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + jQuery.removeData( this, key ); + } ); + } +} ); + +function dataAttr( elem, key, data ) { + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = jQuery._data( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + hooks.cur = fn; + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var nodeHook, boolHook, + rclass = /[\t\r\n]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend( { + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each( function() { + + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch ( e ) {} + } ); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + } ); + } + + if ( proceed ) { + + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + } ); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( value.call( this, i, this.className, stateVal ), stateVal ); + } ); + } + + return this.each( function() { + if ( type === "string" ) { + + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.match( core_rnotwhite ) || []; + + while ( ( className = classNames[ i++ ] ) ) { + + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + } ); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[ i ].nodeType === 1 && ( " " + this[ i ].className + " " ).replace( rclass, " " ).indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, "value" ) ) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + + // handle most common string cases + ret.replace( rreturn, "" ) : + + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each( function( i ) { + var val, + self = jQuery( this ); + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery( elem ).find( "option" ).each( function() { + this.selected = jQuery.inArray( jQuery( this ).val(), values ) >= 0; + } ); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, notxml, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && notxml && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && notxml && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + + } else { + + // In IE9+, Flash objects don't have .getAttribute (#12945) + // Support: IE9+ + if ( typeof elem.getAttribute !== core_strundefined ) { + ret = elem.getAttribute( name ); + } + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( rboolean.test( name ) ) { + + // Set corresponding property to false for boolean attributes + // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8 + if ( !getSetAttribute && ruseDefault.test( name ) ) { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } else { + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName( elem, "input" ) ) { + + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode( "tabindex" ); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +} ); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + var + + // Use .prop to determine if this attribute is understood as boolean + prop = jQuery.prop( elem, name ), + + // Fetch it accordingly + attr = typeof prop === "boolean" && elem.getAttribute( name ), + detail = typeof prop === "boolean" ? + + getSetInput && getSetAttribute ? + attr != null : + + // oldIE fabricates an empty string for missing boolean attributes + // and conflates checked/selected into attroperties + ruseDefault.test( name ) ? + elem[ jQuery.camelCase( "default-" + name ) ] : + !!attr : + + // fetch an attribute node for properties not recognized as boolean + elem.getAttributeNode( name ); + + return detail && detail.value !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; + +// fix oldIE value attroperty +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return jQuery.nodeName( elem, "input" ) ? + + // Ignore the value *property* by using defaultValue + elem.defaultValue : + + ret && ret.specified ? ret.value : undefined; + }, + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ? + ret.value : + undefined; + }, + set: function( elem, value, name ) { + + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + ( ret = elem.ownerDocument.createAttribute( name ) ) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each( [ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + } ); + } ); +} + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + jQuery.each( [ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret == null ? undefined : ret; + } + } ); + } ); + + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each( [ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + } ); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + } ); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + } + }; + } ); +} +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) >= 0 ); + } + } + } ); +} ); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && ( !e || jQuery.event.triggered !== e.type ) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = ( types || "" ).match( core_rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) >= 0 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + event.isTrigger = true; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || special._default.apply( elem.ownerDocument, data ) === false ) && + !( type === "click" && jQuery.nodeName( elem, "a" ) ) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && ( !event.button || event.type !== "click" ) ) { + + for ( ; cur != this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push( { elem: cur, handlers: matches } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split( " " ), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split( " " ), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split( " " ), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + } + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== document.activeElement && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === document.activeElement && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + } ); + jQuery._data( form, "submitBubbles", true ); + } + } ); + + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + } ); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + } ); + } + return false; + } + + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + } ); + jQuery._data( elem, "changeBubbles", true ); + } + } ); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || ( elem.type !== "radio" && elem.type !== "checkbox" ) ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + } ); +} + +jQuery.fn.extend( { + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + }, + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +( function( window, undefined ) { + +var i, + cachedruns, + Expr, + getText, + isXML, + compile, + hasDuplicate, + outermostContext, + + // Local document vars + setDocument, + document, + docElem, + documentIsXML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + sortOrder, + + // Instance-specific data + expando = "sizzle" + -( new Date() ), + preferredDoc = window.document, + support = {}, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Array methods + arr = [], + pop = arr.pop, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[ i ] === elem ) { + return i; + } + } + return -1; + }, + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rsibling = /[\x20\t\r\n\f]*[+~]/, + + rnative = /^[^{]+\{\s*\[native code/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g, + funescape = function( _, escaped ) { + var high = "0x" + escaped - 0x10000; + + // NaN means non-codepoint + return high !== high ? + escaped : + + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Use a stripped-down slice if we can't use a native one +try { + slice.call( preferredDoc.documentElement.childNodes, 0 )[ 0 ].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + while ( ( elem = this[ i++ ] ) ) { + results.push( elem ); + } + return results; + }; +} + +/** + * For feature detection + * @param {Function} fn The function to test for native support + */ +function isNative( fn ) { + return rnative.test( fn + "" ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var cache, + keys = []; + + return ( cache = function( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key ] = value ); + } ); +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement( "div" ); + + try { + return fn( div ); + } catch ( e ) { + return false; + } finally { + + // release memory in IE + div = null; + } +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( ( nodeType = context.nodeType ) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( !documentIsXML && !seed ) { + + // Shortcuts + if ( ( match = rquickExpr.exec( selector ) ) ) { + + // Speed-up: Sizzle("#ID") + if ( ( m = match[ 1 ] ) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + + // Context is not a document + if ( context.ownerDocument && ( elem = context.ownerDocument.getElementById( m ) ) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[ 2 ] ) { + push.apply( results, slice.call( context.getElementsByTagName( selector ), 0 ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( ( m = match[ 3 ] ) && support.getByClassName && context.getElementsByClassName ) { + push.apply( results, slice.call( context.getElementsByClassName( m ), 0 ) ); + return results; + } + } + + // QSA path + if ( support.qsa && !rbuggyQSA.test( selector ) ) { + old = true; + nid = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( ( old = context.getAttribute( "id" ) ) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[ i ] = nid + toSelector( groups[ i ] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join( "," ); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch ( qsaError ) { + } finally { + if ( !old ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && ( elem.ownerDocument || elem ).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsXML = isXML( doc ); + + // Check if getElementsByTagName("*") returns only elements + support.tagNameNoComments = assert( function( div ) { + div.appendChild( doc.createComment( "" ) ); + return !div.getElementsByTagName( "*" ).length; + } ); + + // Check if attributes should be retrieved by attribute nodes + support.attributes = assert( function( div ) { + div.innerHTML = ""; + var type = typeof div.lastChild.getAttribute( "multiple" ); + + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + } ); + + // Check if getElementsByClassName can be trusted + support.getByClassName = assert( function( div ) { + + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName( "e" ).length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName( "e" ).length === 2; + } ); + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + support.getByName = assert( function( div ) { + + // Inject content + div.id = expando + 0; + div.innerHTML = "
        "; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = doc.getElementsByName && + + // buggy browsers will return fewer than the correct 2 + doc.getElementsByName( expando ).length === 2 + + + // buggy browsers will return more than the correct 0 + doc.getElementsByName( expando + 0 ).length; + support.getIdNotName = !doc.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + } ); + + // IE6/7 return modified attributes + Expr.attrHandle = assert( function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute( "href" ) === "#"; + } ) ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute( "type" ); + } + }; + + // ID find and filter + if ( support.getIdNotName ) { + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && !documentIsXML ) { + var m = context.getElementById( id ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + } else { + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && !documentIsXML ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode( "id" ).value === id ? + [ m ] : + undefined : + []; + } + }; + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find[ "TAG" ] = support.tagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Name + Expr.find[ "NAME" ] = support.getByName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }; + + // Class + Expr.find[ "CLASS" ] = support.getByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) { + return context.getElementsByClassName( className ); + } + }; + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21), + // no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ]; + + if ( ( support.qsa = isNative( doc.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( div ) { + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + } ); + + assert( function( div ) { + + // Opera 10-12/IE8 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = ""; + if ( div.querySelectorAll( "[i^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll( ":enabled" ).length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = isNative( ( matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( div ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = new RegExp( rbuggyMatches.join( "|" ) ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = isNative( docElem.contains ) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + var compare; + + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( ( compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ) ) ) { + if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) { + if ( a === doc || contains( preferredDoc, a ) ) { + return -1; + } + if ( b === doc || contains( preferredDoc, b ) ) { + return 1; + } + return 0; + } + return compare & 4 ? -1 : 1; + } + + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + ap[ i ] === preferredDoc ? -1 : + bp[ i ] === preferredDoc ? 1 : + 0; + }; + + // Always assume the presence of duplicates if sort doesn't + // pass them to our comparison function (as in Google Chrome). + hasDuplicate = false; + [ 0, 0 ].sort( sortOrder ); + support.detectDuplicates = hasDuplicate; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyQSA always contains :focus, so no need for an existence check + if ( support.matchesSelector && !documentIsXML && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && !rbuggyQSA.test( expr ) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + var val; + + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + if ( !documentIsXML ) { + name = name.toLowerCase(); + } + if ( ( val = Expr.attrHandle[ name ] ) ) { + return val( elem ); + } + if ( documentIsXML || support.attributes ) { + return elem.getAttribute( name ); + } + return ( ( val = elem.getAttributeNode( name ) ) || elem.getAttribute( name ) ) && elem[ name ] === true ? + name : + val && val.specified ? val.value : null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; ( elem = results[ i ] ); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + for ( ; ( node = elem[ i ] ); i++ ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 4 ] || match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? match[ 5 ] + ( match[ 6 ] || 1 ) : 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 5 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 4 ] ) { + match[ 2 ] = match[ 4 ]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + + nodeName = nodeName.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" ) ) && + classCache( className, function( elem ) { + return pattern.test( elem.className || ( typeof elem.getAttribute !== strundefined && elem.getAttribute( "class" ) ) || "" ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || ( parent[ expando ] = {} ); + cache = outerCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = cache[ 0 ] === dirruns && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && ( cache = ( elem[ expando ] || ( elem[ expando ] = {} ) )[ type ] ) && cache[ 0 ] === dirruns ) { + diff = cache[ 1 ]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + ( node[ expando ] || ( node[ expando ] = {} ) )[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifider + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsXML ? + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) : + elem.lang ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && ( !document.hasFocus || document.hasFocus() ) && !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( ( attr = elem.getAttribute( "type" ) ) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + if ( ( cache = outerCache[ dir ] ) && cache[ 0 ] === dirkey ) { + if ( ( data = cache[ 1 ] ) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[ 1 ] = matcher( elem, context, xml ) || cachedruns; + if ( cache[ 1 ] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + + // We must always have either seed elements or context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", expandContext && context.parentNode || context ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && !documentIsXML && + Expr.relative[ tokens[ 1 ].type ] ) { + + context = Expr.find[ "ID" ]( token.matches[ 0 ].replace( runescape, funescape ), context )[ 0 ]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && context.parentNode || context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + documentIsXML, + results, + rsibling.test( selector ) + ); + return results; +} + +// Deprecated +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Easy API for creating new setFilters +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// Initialize with the default document +setDocument(); + +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + +} )( window ); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + isSimple = /^.[^:#\[\.,]*$/, + rneedsContext = jQuery.expr.match.needsContext, + + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, self, + len = this.length; + + if ( typeof selector !== "string" ) { + self = this; + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = []; + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, this[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = ( this.selector ? this.selector + " " : "" ) + selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + not: function( selector ) { + return this.pushStack( winnow( this, selector, false ) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow( this, selector, true ) ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + rneedsContext.test( selector ) ? + jQuery( selector, this.context ).index( this[ 0 ] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + cur = this[ i ]; + + while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { + if ( pos ? pos.index( cur ) > -1 : jQuery.find.matchesSelector( cur, selectors ) ) { + ret.push( cur ); + break; + } + cur = cur.parentNode; + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[ 0 ], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique( all ) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +jQuery.fn.andSelf = jQuery.fn.addBack; + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( this.length > 1 && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret ); + }; +} ); + +jQuery.extend( { + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector( elems[ 0 ], expr ) ? [ elems[ 0 ] ] : [] : + jQuery.find.matches( expr, elems ); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && ( until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until ) ) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[ dir ]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +} ); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + } ); + + } else if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) === keep; + } ); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep( elements, function( elem ) { + return elem.nodeType === 1; + } ); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, filtered, !keep ); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + } ); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
        ", "
        " ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
        " ], + tr: [ 2, "", "
        " ], + col: [ 2, "", "
        " ], + td: [ 3, "", "
        " ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
        ", "
        " ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement( "div" ) ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend( { + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapAll( html.call( this, i ) ); + } ); + } + + if ( this[ 0 ] ) { + + // The elements to wrap the target around + var wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function() { + return this.parent().each( function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + } ).end(); + }, + + append: function() { + return this.domManip( arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return this.domManip( arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.insertBefore( elem, this.firstChild ); + } + } ); + }, + + before: function() { + return this.domManip( arguments, false, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return this.domManip( arguments, false, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for ( ; i < l; i++ ) { + + // Remove element nodes and prevent memory leaks + elem = this[ i ] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + var isFunc = jQuery.isFunction( value ); + + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( !isFunc && typeof value !== "string" ) { + value = jQuery( value ).not( this ).detach(); + } + + return this.domManip( [ value ], true, function( elem ) { + var next = this.nextSibling, + parent = this.parentNode; + + if ( parent ) { + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + } ); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each( function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, table ? self.html() : undefined ); + } + self.domManip( args, table, callback ); + } ); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( + table && jQuery.nodeName( this[ i ], "table" ) ? + findOrAppend( this[ i ], "tbody" ) : + this[ i ], + node, + i + ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Hope ajax is available... + jQuery.ajax( { + url: node.src, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + } ); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +} ); + +function findOrAppend( elem, tag ) { + return elem.getElementsByTagName( tag )[ 0 ] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + var attr = elem.getAttributeNode( "type" ); + elem.type = ( attr && attr.specified ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute( "type" ); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[ i ], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim( dest.innerHTML ) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; ( elem = elems[ i ] ) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend( { + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc( elem ) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( ( !jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked ) && + ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; ( node = srcElements[ i ] ) != null; ++i ) { + + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[ i ] ) { + fixCloneNodeIssues( node, destElements[ i ] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; ( node = srcElements[ i ] ) != null; i++ ) { + cloneCopyEvent( node, destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[ 0 ] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[ 1 ] === "
        " && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + } +} ); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt( 0 ).toUpperCase() + name.slice( 1 ), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay( elem.nodeName ) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend( { + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + var bool = typeof state === "boolean"; + + return this.each( function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && ( ret = rrelNum.exec( value ) ) ) { + value = ( ret[ 1 ] + 1 ) * ret[ 2 ] + parseFloat( jQuery.css( elem, name ) ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch ( e ) {} + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +} ); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + + // If we already have the right measurement, avoid augmentation + 4 : + + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test( val ) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery( " -
        +

        See this blog entry for more information.

        @@ -247,7 +209,7 @@

        -
        hi there
        +
        hi there
        @@ -272,6 +234,12 @@ ...Eat a burger... ...Eat some funyuns... ...Eat some funyuns... + + + + + +
        @@ -318,6 +286,7 @@
        +
        diff --git a/test/integration/data/gh-1764-fullscreen-iframe.css b/test/integration/data/gh-1764-fullscreen-iframe.css new file mode 100644 index 0000000000..ba4b4fa7d3 --- /dev/null +++ b/test/integration/data/gh-1764-fullscreen-iframe.css @@ -0,0 +1,18 @@ +.result { + font-size: 24px; + margin: 0.5em 0; + width: 700px; + height: 56px; +} + +.error { + background-color: red; +} + +.warn { + background-color: yellow; +} + +.success { + background-color: lightgreen; +} diff --git a/test/integration/data/gh-1764-fullscreen-iframe.html b/test/integration/data/gh-1764-fullscreen-iframe.html new file mode 100644 index 0000000000..bed56b8ecf --- /dev/null +++ b/test/integration/data/gh-1764-fullscreen-iframe.html @@ -0,0 +1,21 @@ + + + + + + Test for gh-1764 - test iframe + + + + +
        +
        + +
        + + + + + diff --git a/test/integration/data/gh-1764-fullscreen.js b/test/integration/data/gh-1764-fullscreen.js new file mode 100644 index 0000000000..b2bb4cdb5a --- /dev/null +++ b/test/integration/data/gh-1764-fullscreen.js @@ -0,0 +1,99 @@ +/* exported bootstrapFrom */ + +// `mode` may be "iframe" or not specified. +function bootstrapFrom( mainSelector, mode ) { + if ( mode === "iframe" && window.parent === window ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result warn" ) + .text( "This test should be run in an iframe. Open ../gh-1764-fullscreen.html." ); + jQuery( mainSelector + " .toggle-fullscreen" ).remove(); + return; + } + + var fullscreenSupported = document.exitFullscreen || + document.exitFullscreen || + document.msExitFullscreen || + document.mozCancelFullScreen || + document.webkitExitFullscreen; + + function isFullscreen() { + return !!( document.fullscreenElement || + document.mozFullScreenElement || + document.webkitFullscreenElement || + document.msFullscreenElement ); + } + + function requestFullscreen( element ) { + if ( !isFullscreen() ) { + if ( element.requestFullscreen ) { + element.requestFullscreen(); + } else if ( element.msRequestFullscreen ) { + element.msRequestFullscreen(); + } else if ( element.mozRequestFullScreen ) { + element.mozRequestFullScreen(); + } else if ( element.webkitRequestFullscreen ) { + element.webkitRequestFullscreen(); + } + } + } + + function exitFullscreen() { + if ( document.exitFullscreen ) { + document.exitFullscreen(); + } else if ( document.msExitFullscreen ) { + document.msExitFullscreen(); + } else if ( document.mozCancelFullScreen ) { + document.mozCancelFullScreen(); + } else if ( document.webkitExitFullscreen ) { + document.webkitExitFullscreen(); + } + } + + function runTest() { + var dimensions; + if ( !fullscreenSupported ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result success" ) + .text( "Fullscreen mode is not supported in this browser. Test not run." ); + } else if ( !isFullscreen() ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result warn" ) + .text( "Enable fullscreen mode to fire the test." ); + } else { + dimensions = jQuery( mainSelector + " .result" ).css( [ "width", "height" ] ); + dimensions.width = parseFloat( dimensions.width ).toFixed( 3 ); + dimensions.height = parseFloat( dimensions.height ).toFixed( 3 ); + if ( dimensions.width === "700.000" && dimensions.height === "56.000" ) { + jQuery( mainSelector + " .result" ) + .attr( "class", "result success" ) + .text( "Dimensions in fullscreen mode are computed correctly." ); + } else { + jQuery( mainSelector + " .result" ) + .attr( "class", "result error" ) + .html( "Incorrect dimensions; " + + "expected: { width: '700.000', height: '56.000' };
        " + + "got: { width: '" + dimensions.width + "', height: '" + + dimensions.height + "' }." ); + } + } + } + + function toggleFullscreen() { + if ( isFullscreen() ) { + exitFullscreen(); + } else { + requestFullscreen( jQuery( mainSelector + " .container" )[ 0 ] ); + } + } + + $( mainSelector + " .toggle-fullscreen" ).on( "click", toggleFullscreen ); + + $( document ).on( [ + "webkitfullscreenchange", + "mozfullscreenchange", + "fullscreenchange", + "MSFullscreenChange" + ].join( " " ), runTest ); + + runTest(); +} diff --git a/test/integration/gh-1684-ajax.html b/test/integration/gh-1684-ajax.html new file mode 100644 index 0000000000..55e31d7fee --- /dev/null +++ b/test/integration/gh-1684-ajax.html @@ -0,0 +1,52 @@ + + + + + + Test for gh-1684 + + + + +
        +
        + + + + diff --git a/test/integration/gh-1764-fullscreen.html b/test/integration/gh-1764-fullscreen.html new file mode 100644 index 0000000000..1cba659438 --- /dev/null +++ b/test/integration/gh-1764-fullscreen.html @@ -0,0 +1,34 @@ + + + + + + Test for gh-1764 + + + + + +
        +
        + +
        + + + + + + diff --git a/test/integration/gh-2343-ie-radio-click.html b/test/integration/gh-2343-ie-radio-click.html new file mode 100644 index 0000000000..4bc6956c3e --- /dev/null +++ b/test/integration/gh-2343-ie-radio-click.html @@ -0,0 +1,33 @@ + + + + + + Test for gh-2343 (IE11) + + + + + + +

        Test for gh-2343 (IE11)

        +

        +Instructions: In IE11, click on or focus the first radio button. +Then use the left/right arrow keys to select the other radios. +You should see events logged in the results below. +

        +
        + 0 + 1 + 2 +
        +
        + + + diff --git a/test/jquery.js b/test/jquery.js new file mode 100644 index 0000000000..e418a7e9ea --- /dev/null +++ b/test/jquery.js @@ -0,0 +1,68 @@ +// Use the right jQuery source on the test page (and iframes) +( function() { + /* global loadTests: false */ + + var src, + path = window.location.pathname.split( "test" )[ 0 ], + QUnit = window.QUnit || parent.QUnit, + require = window.require || parent.require; + + // Config parameter to force basic code paths + QUnit.config.urlConfig.push( { + id: "basic", + label: "Bypass optimizations", + tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition; JSON.parse" + } ); + if ( QUnit.urlParams.basic ) { + document.querySelectorAll = null; + document.documentElement.contains = null; + document.documentElement.compareDocumentPosition = null; + window.JSON = null; + } + + // iFrames won't load AMD (the iframe tests synchronously expect jQuery to be there) + QUnit.config.urlConfig.push( { + id: "amd", + label: "Load with AMD", + tooltip: "Load the AMD jQuery file (and its dependencies)" + } ); + + // If QUnit is on window, this is the main window + // This detection allows AMD tests to be run in an iframe + if ( QUnit.urlParams.amd && window.QUnit ) { + require.config( { + baseUrl: path + } ); + src = "src/jquery"; + + // Include tests if specified + if ( typeof loadTests !== "undefined" ) { + require( [ src ], loadTests ); + } else { + require( [ src ] ); + } + return; + } + + // Config parameter to use minified jQuery + QUnit.config.urlConfig.push( { + id: "dev", + label: "Load unminified", + tooltip: "Load the development (unminified) jQuery file" + } ); + if ( QUnit.urlParams.dev ) { + src = "dist/jquery.js"; + } else { + src = "dist/jquery.min.js"; + } + + // Load jQuery + document.write( " + - + \n"; - if( $lib == "prototype" ) { // prototype must be included first - array_unshift( $includes, $include ); - } else { - array_push( $includes, $include ); - } - } - - $includes = implode( "\n", $includes ); - $suite = file_get_contents( "index.html" ); - echo str_replace( "", $includes, $suite ); - exit; - } -?> - - - - - Run jQuery Test Suite Polluted - - - - -

        jQuery Test Suite

        - -

        Choose other libraries to include

        - -
        - $data ) { - echo "
        $name"; - $i = 0; - foreach( $data[ "versions" ] as $ver ) { - $i++; - echo ""; - if( !($i % 4) ) echo "
        "; - } - echo "
        "; - } - ?> - - - - diff --git a/test/promises_aplus_adapter.js b/test/promises_aplus_adapter.js new file mode 100644 index 0000000000..c7440b9691 --- /dev/null +++ b/test/promises_aplus_adapter.js @@ -0,0 +1,22 @@ +/* jshint node: true */ + +"use strict"; + +require( "jsdom" ).env( "", function( errors, window ) { + if ( errors ) { + console.error( errors ); + return; + } + + var jQuery = require( ".." )( window ); + + exports.deferred = function() { + var deferred = jQuery.Deferred(); + + return { + promise: deferred.promise(), + resolve: deferred.resolve.bind( deferred ), + reject: deferred.reject.bind( deferred ) + }; + }; +} ); diff --git a/test/qunit b/test/qunit deleted file mode 160000 index 900f72051b..0000000000 --- a/test/qunit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 900f72051b0112342feda3d700a7a049d886b9ce diff --git a/test/readywait.html b/test/readywait.html index 9983be4322..7a736bef5a 100644 --- a/test/readywait.html +++ b/test/readywait.html @@ -14,7 +14,7 @@ #output { background-color: green } #expectedOutput { background-color: green } - + diff --git a/test/unit/ajax.js b/test/unit/ajax.js index f88b5d7ccb..efe54ba6cf 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -1,5 +1,11 @@ -module( "ajax", { +var isoldIE = /msie [876]\.0/i.test( window.navigator.userAgent ); + +QUnit.module( "ajax", { setup: function() { + if ( !isoldIE ) { + return; + } + var jsonpCallback = this.jsonpCallback = jQuery.ajaxSettings.jsonpCallback; jQuery.ajaxSettings.jsonpCallback = function() { var callback = jsonpCallback.apply( this, arguments ); @@ -11,151 +17,178 @@ module( "ajax", { jQuery( document ).off( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError ajaxSuccess" ); moduleTeardown.apply( this, arguments ); } -}); +} ); + +( function() { + QUnit.test( "Unit Testing Environment", function( assert ) { + assert.expect( 2 ); -(function() { + assert.ok( hasPHP, "Running in an environment with PHP support. The AJAX tests only run if the environment supports PHP!" ); + assert.ok( !isLocal, "Unit tests are not ran from file:// (especially in Chrome. If you must test from file:// with Chrome, run it with the --allow-file-access-from-files flag!)" ); + } ); if ( !jQuery.ajax || ( isLocal && !hasPHP ) ) { return; } - function addGlobalEvents( expected ) { + function addGlobalEvents( expected, assert ) { return function() { expected = expected || ""; jQuery( document ).on( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError ajaxSuccess", function( e ) { - ok( expected.indexOf(e.type) !== -1, e.type ); - }); + assert.ok( expected.indexOf( e.type ) !== -1, e.type ); + } ); }; } //----------- jQuery.ajax() - ajaxTest( "jQuery.ajax() - success callbacks", 8, { - setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), - url: url("data/name.html"), - beforeSend: function() { - ok( true, "beforeSend" ); - }, - success: function() { - ok( true, "success" ); - }, - complete: function() { - ok( true, "complete"); + testIframeWithCallback( + "XMLHttpRequest - Attempt to block tests because of dangling XHR requests (IE)", + "ajax/unreleasedXHR.html", + function( assert ) { + assert.expect( 1 ); + assert.ok( true, "done" ); } - }); + ); - ajaxTest( "jQuery.ajax() - success callbacks - (url, options) syntax", 8, { - setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), - create: function( options ) { - return jQuery.ajax( url("data/name.html"), options ); - }, - beforeSend: function() { - ok( true, "beforeSend" ); - }, - success: function() { - ok( true, "success" ); - }, - complete: function() { - ok( true, "complete" ); - } - }); + ajaxTest( "jQuery.ajax() - success callbacks", 8, function( assert ) { + return { + setup: addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess", assert ), + url: url( "data/name.html" ), + beforeSend: function() { + assert.ok( true, "beforeSend" ); + }, + success: function() { + assert.ok( true, "success" ); + }, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - success callbacks (late binding)", 8, { - setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), - url: url("data/name.html"), - beforeSend: function() { - ok( true, "beforeSend" ); - }, - success: true, - afterSend: function( request ) { - request.complete(function() { - ok( true, "complete" ); - }).success(function() { - ok( true, "success" ); - }).error(function() { - ok( false, "error" ); - }); - } - }); + ajaxTest( "jQuery.ajax() - success callbacks - (url, options) syntax", 8, function( assert ) { + return { + setup: addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess", assert ), + create: function( options ) { + return jQuery.ajax( url( "data/name.html" ), options ); + }, + beforeSend: function() { + assert.ok( true, "beforeSend" ); + }, + success: function() { + assert.ok( true, "success" ); + }, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - success callbacks (oncomplete binding)", 8, { - setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess"), - url: url("data/name.html"), - beforeSend: function() { - ok( true, "beforeSend" ); - }, - success: true, - complete: function( xhr ) { - xhr.complete(function() { - ok( true, "complete" ); - }).success(function() { - ok( true, "success" ); - }).error(function() { - ok( false, "error" ); - }); - } - }); + ajaxTest( "jQuery.ajax() - success callbacks (late binding)", 8, function( assert ) { + return { + setup: addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess", assert ), + url: url( "data/name.html" ), + beforeSend: function() { + assert.ok( true, "beforeSend" ); + }, + success: true, + afterSend: function( request ) { + request.always( function() { + assert.ok( true, "complete" ); + } ).done( function() { + assert.ok( true, "success" ); + } ).fail( function() { + assert.ok( false, "error" ); + } ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - error callbacks", 8, { - setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError"), - url: url("data/name.php?wait=5"), - beforeSend: function() { - ok( true, "beforeSend" ); - }, - afterSend: function( request ) { - request.abort(); - }, - error: function() { - ok( true, "error" ); - }, - complete: function() { - ok( true, "complete" ); - } - }); + ajaxTest( "jQuery.ajax() - success callbacks (oncomplete binding)", 8, function( assert ) { + return { + setup: addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxSuccess", assert ), + url: url( "data/name.html" ), + beforeSend: function() { + assert.ok( true, "beforeSend" ); + }, + success: true, + complete: function( xhr ) { + xhr.always( function() { + assert.ok( true, "complete" ); + } ).done( function() { + assert.ok( true, "success" ); + } ).fail( function() { + assert.ok( false, "error" ); + } ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - textStatus and errorThrown values", 4, [ - { - url: url("data/name.php?wait=5"), + ajaxTest( "jQuery.ajax() - error callbacks", 8, function( assert ) { + return { + setup: addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError", assert ), + url: url( "data/name.php?wait=5" ), + beforeSend: function() { + assert.ok( true, "beforeSend" ); + }, + afterSend: function( request ) { + request.abort(); + }, + error: function() { + assert.ok( true, "error" ); + }, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - textStatus and errorThrown values", 4, function( assert ) { + return [ { + url: url( "data/name.php?wait=5" ), error: function( _, textStatus, errorThrown ) { - strictEqual( textStatus, "abort", "textStatus is 'abort' for abort" ); - strictEqual( errorThrown, "abort", "errorThrown is 'abort' for abort" ); + assert.strictEqual( textStatus, "abort", "textStatus is 'abort' for abort" ); + assert.strictEqual( errorThrown, "abort", "errorThrown is 'abort' for abort" ); }, afterSend: function( request ) { request.abort(); } }, { - url: url("data/name.php?wait=5"), + url: url( "data/name.php?wait=5" ), error: function( _, textStatus, errorThrown ) { - strictEqual( textStatus, "mystatus", "textStatus is 'mystatus' for abort('mystatus')" ); - strictEqual( errorThrown, "mystatus", "errorThrown is 'mystatus' for abort('mystatus')" ); + assert.strictEqual( textStatus, "mystatus", "textStatus is 'mystatus' for abort('mystatus')" ); + assert.strictEqual( errorThrown, "mystatus", "errorThrown is 'mystatus' for abort('mystatus')" ); }, afterSend: function( request ) { - request.abort("mystatus"); + request.abort( "mystatus" ); } - } - ]); + } ]; + } ); - ajaxTest( "jQuery.ajax() - responseText on error", 1, { - url: url("data/errorWithText.php"), - error: function( xhr ) { - strictEqual( xhr.responseText, "plain text message", "Test jqXHR.responseText is filled for HTTP errors" ); - } - }); + ajaxTest( "jQuery.ajax() - responseText on error", 1, function( assert ) { + return { + url: url( "data/errorWithText.php" ), + error: function( xhr ) { + assert.strictEqual( xhr.responseText, "plain text message", "Test jqXHR.responseText is filled for HTTP errors" ); + } + }; + } ); - asyncTest( "jQuery.ajax() - retry with jQuery.ajax( this )", 2, function() { + QUnit.asyncTest( "jQuery.ajax() - retry with jQuery.ajax( this )", 2, function( assert ) { var previousUrl, firstTime = true; - jQuery.ajax({ - url: url("data/errorWithText.php"), + jQuery.ajax( { + url: url( "data/errorWithText.php" ), error: function() { if ( firstTime ) { firstTime = false; jQuery.ajax( this ); } else { - ok ( true, "Test retrying with jQuery.ajax(this) works" ); - jQuery.ajax({ - url: url("data/errorWithText.php"), + assert.ok( true, "Test retrying with jQuery.ajax(this) works" ); + jQuery.ajax( { + url: url( "data/errorWithText.php" ), data: { "x": 1 }, @@ -163,132 +196,153 @@ module( "ajax", { if ( !previousUrl ) { previousUrl = this.url; } else { - strictEqual( this.url, previousUrl, "url parameters are not re-appended" ); - start(); + assert.strictEqual( this.url, previousUrl, "url parameters are not re-appended" ); + QUnit.start(); return false; } }, error: function() { jQuery.ajax( this ); } - }); + } ); } } - }); - }); + } ); + } ); - ajaxTest( "jQuery.ajax() - headers", 4, { - setup: function() { - jQuery( document ).ajaxSend(function( evt, xhr ) { - xhr.setRequestHeader( "ajax-send", "test" ); - }); - }, - url: url("data/headers.php?keys=siMPle_SometHing-elsE_OthEr_ajax-send"), - headers: { - "siMPle": "value", - "SometHing-elsE": "other value", - "OthEr": "something else" - }, - success: function( data, _, xhr ) { - var i, emptyHeader, - requestHeaders = jQuery.extend( this.headers, { - "ajax-send": "test" - }), - tmp = []; - for ( i in requestHeaders ) { - tmp.push( i, ": ", requestHeaders[ i ], "\n" ); - } - tmp = tmp.join(""); - - strictEqual( data, tmp, "Headers were sent" ); - strictEqual( xhr.getResponseHeader("Sample-Header"), "Hello World", "Sample header received" ); - - emptyHeader = xhr.getResponseHeader("Empty-Header"); - if ( emptyHeader === null ) { - ok( true, "Firefox doesn't support empty headers" ); - } else { - strictEqual( emptyHeader, "", "Empty header received" ); - } - strictEqual( xhr.getResponseHeader("Sample-Header2"), "Hello World 2", "Second sample header received" ); - } - }); + ajaxTest( "jQuery.ajax() - headers", 5, function( assert ) { + return { + setup: function() { + jQuery( document ).ajaxSend( function( evt, xhr ) { + xhr.setRequestHeader( "ajax-send", "test" ); + } ); + }, + url: url( "data/headers.php?keys=siMPle_SometHing-elsE_OthEr_Nullable_undefined_Empty_ajax-send" ), + headers: { + "siMPle": "value", + "SometHing-elsE": "other value", + "OthEr": "something else", + "Nullable": null, + "undefined": undefined + + // Support: Firefox + // Not all browsers allow empty-string headers + // https://bugzilla.mozilla.org/show_bug.cgi?id=815299 + //"Empty": "" + }, + success: function( data, _, xhr ) { + var i, emptyHeader, + requestHeaders = jQuery.extend( this.headers, { + "ajax-send": "test" + } ), + tmp = []; + for ( i in requestHeaders ) { + tmp.push( i, ": ", requestHeaders[ i ] + "", "\n" ); + } + tmp = tmp.join( "" ); - ajaxTest( "jQuery.ajax() - Accept header", 1, { - url: url("data/headers.php?keys=accept"), - headers: { - Accept: "very wrong accept value" - }, - beforeSend: function( xhr ) { - xhr.setRequestHeader("Accept", "*/*"); - }, - success: function( data ) { - strictEqual( data, "accept: */*\n", "Test Accept header is set to last value provided" ); - } - }); + assert.strictEqual( data, tmp, "Headers were sent" ); + assert.strictEqual( xhr.getResponseHeader( "Sample-Header" ), "Hello World", "Sample header received" ); + assert.ok( data.indexOf( "undefined" ) < 0, "Undefined header value was not sent" ); - ajaxTest( "jQuery.ajax() - contentType", 2, [ - { - url: url("data/headers.php?keys=content-type"), - contentType: "test", - success: function( data ) { - strictEqual( data, "content-type: test\n", "Test content-type is sent when options.contentType is set" ); + emptyHeader = xhr.getResponseHeader( "Empty-Header" ); + if ( emptyHeader === null ) { + assert.ok( true, "Firefox doesn't support empty headers" ); + } else { + assert.strictEqual( emptyHeader, "", "Empty header received" ); + } + assert.strictEqual( xhr.getResponseHeader( "Sample-Header2" ), "Hello World 2", "Second sample header received" ); } - }, - { - url: url("data/headers.php?keys=content-type"), - contentType: false, + }; + } ); + + ajaxTest( "jQuery.ajax() - Accept header", 1, function( assert ) { + return { + url: url( "data/headers.php?keys=accept" ), + headers: { + Accept: "very wrong accept value" + }, + beforeSend: function( xhr ) { + xhr.setRequestHeader( "Accept", "*/*" ); + }, success: function( data ) { - strictEqual( data, "content-type: \n", "Test content-type is not sent when options.contentType===false" ); + assert.strictEqual( data, "accept: */*\n", "Test Accept header is set to last value provided" ); } - } - ]); - - ajaxTest( "jQuery.ajax() - protocol-less urls", 1, { - url: "//somedomain.com", - beforeSend: function( xhr, settings ) { - equal( settings.url, location.protocol + "//somedomain.com", "Make sure that the protocol is added." ); - return false; - }, - error: true - }); + }; + } ); - ajaxTest( "jQuery.ajax() - hash", 3, [ - { - url: "data/name.html#foo", - beforeSend: function( xhr, settings ) { - equal( settings.url, "data/name.html", "Make sure that the URL is trimmed." ); - return false; + ajaxTest( "jQuery.ajax() - contentType", 2, function( assert ) { + return [ + { + url: url( "data/headers.php?keys=content-type" ), + contentType: "test", + success: function( data ) { + assert.strictEqual( data, "content-type: test\n", "Test content-type is sent when options.contentType is set" ); + } }, - error: true - }, - { - url: "data/name.html?abc#foo", + { + url: url( "data/headers.php?keys=content-type" ), + contentType: false, + success: function( data ) { + + // Some server/interpreter combinations always supply a Content-Type to scripts + data = data || "content-type: \n"; + assert.strictEqual( data, "content-type: \n", "Test content-type is not set when options.contentType===false" ); + } + } + ]; + } ); + + ajaxTest( "jQuery.ajax() - protocol-less urls", 1, function( assert ) { + return { + url: "//somedomain.com", beforeSend: function( xhr, settings ) { - equal( settings.url, "data/name.html?abc", "Make sure that the URL is trimmed." ); + assert.equal( settings.url, location.protocol + "//somedomain.com", "Make sure that the protocol is added." ); return false; }, error: true - }, - { - url: "data/name.html?abc#foo", - data: { - "test": 123 + }; + } ); + + ajaxTest( "jQuery.ajax() - hash", 3, function( assert ) { + return [ + { + url: "data/name.html#foo", + beforeSend: function( xhr, settings ) { + assert.equal( settings.url, "data/name.html", "Make sure that the URL is trimmed." ); + return false; + }, + error: true }, - beforeSend: function( xhr, settings ) { - equal( settings.url, "data/name.html?abc&test=123", "Make sure that the URL is trimmed." ); - return false; + { + url: "data/name.html?abc#foo", + beforeSend: function( xhr, settings ) { + assert.equal( settings.url, "data/name.html?abc", "Make sure that the URL is trimmed." ); + return false; + }, + error: true }, - error: true - } - ]); + { + url: "data/name.html?abc#foo", + data: { + "test": 123 + }, + beforeSend: function( xhr, settings ) { + assert.equal( settings.url, "data/name.html?abc&test=123", "Make sure that the URL is trimmed." ); + return false; + }, + error: true + } + ]; + } ); - ajaxTest( "jQuery.ajax() - cross-domain detection", 7, function() { + ajaxTest( "jQuery.ajax() - cross-domain detection", 7, function( assert ) { function request( url, title, crossDomainOrOptions ) { return jQuery.extend( { dataType: "jsonp", url: url, beforeSend: function( _, s ) { - ok( crossDomainOrOptions === false ? !s.crossDomain : s.crossDomain, title ); + assert.ok( crossDomainOrOptions === false ? !s.crossDomain : s.crossDomain, title ); return false; }, error: true @@ -299,10 +353,10 @@ module( "ajax", { samePort = loc.port || ( loc.protocol === "http:" ? 80 : 443 ), otherPort = loc.port === 666 ? 667 : 666, otherProtocol = loc.protocol === "http:" ? "https:" : "http:"; - + return [ request( - loc.protocol + "//" + loc.host + ":" + samePort, + loc.protocol + "//" + loc.hostname + ":" + samePort, "Test matching ports are not detected as cross-domain", false ), @@ -334,293 +388,348 @@ module( "ajax", { } ) ]; - }); - - ajaxTest( "jQuery.ajax() - abort", 9, { - setup: addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxError ajaxComplete"), - url: url("data/name.php?wait=5"), - beforeSend: function() { - ok( true, "beforeSend" ); - }, - afterSend: function( xhr ) { - strictEqual( xhr.readyState, 1, "XHR readyState indicates successful dispatch" ); - xhr.abort(); - strictEqual( xhr.readyState, 0, "XHR readyState indicates successful abortion" ); - }, - error: true, - complete: function() { - ok( true, "complete" ); - } - }); + } ); - ajaxTest( "jQuery.ajax() - events with context", 12, function() { + ajaxTest( "jQuery.ajax() - abort", 9, function( assert ) { + return { + setup: addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxError ajaxComplete", assert ), + url: url( "data/name.php?wait=5" ), + beforeSend: function() { + assert.ok( true, "beforeSend" ); + }, + afterSend: function( xhr ) { + assert.strictEqual( xhr.readyState, 1, "XHR readyState indicates successful dispatch" ); + xhr.abort(); + assert.strictEqual( xhr.readyState, 0, "XHR readyState indicates successful abortion" ); + }, + error: true, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } ); - var context = document.createElement("div"); + ajaxTest( "jQuery.ajax() - events with context", 12, function( assert ) { + var context = document.createElement( "div" ); function event( e ) { - equal( this, context, e.type ); + assert.equal( this, context, e.type ); } function callback( msg ) { return function() { - equal( this, context, "context is preserved on callback " + msg ); + assert.equal( this, context, "context is preserved on callback " + msg ); }; } return { setup: function() { - jQuery( context ).appendTo("#foo") + jQuery( context ).appendTo( "#foo" ) .ajaxSend( event ) .ajaxComplete( event ) .ajaxError( event ) .ajaxSuccess( event ); }, - requests: [{ - url: url("data/name.html"), + requests: [ { + url: url( "data/name.html" ), context: context, - beforeSend: callback("beforeSend"), - success: callback("success"), - complete: callback("complete") + beforeSend: callback( "beforeSend" ), + success: callback( "success" ), + complete: callback( "complete" ) }, { - url: url("data/404.html"), + url: url( "data/404.html" ), context: context, - beforeSend: callback("beforeSend"), - error: callback("error"), - complete: callback("complete") - }] + beforeSend: callback( "beforeSend" ), + error: callback( "error" ), + complete: callback( "complete" ) + } ] }; - }); - - ajaxTest( "jQuery.ajax() - events without context", 3, function() { + } ); + + ajaxTest( "jQuery.ajax() - events without context", 3, function( assert ) { function nocallback( msg ) { return function() { - equal( typeof this.url, "string", "context is settings on callback " + msg ); + assert.equal( typeof this.url, "string", "context is settings on callback " + msg ); }; } return { - url: url("data/404.html"), - beforeSend: nocallback("beforeSend"), - error: nocallback("error"), - complete: nocallback("complete") + url: url( "data/404.html" ), + beforeSend: nocallback( "beforeSend" ), + error: nocallback( "error" ), + complete: nocallback( "complete" ) }; - }); + } ); - ajaxTest( "jQuery.ajax() - context modification", 1, { - url: url("data/name.html"), - context: {}, - beforeSend: function() { - this.test = "foo"; - }, - afterSend: function() { - strictEqual( this.context.test, "foo", "Make sure the original object is maintained." ); - }, - success: true - }); + ajaxTest( "#15118 - jQuery.ajax() - function without jQuery.event", 1, function( assert ) { + var holder; + return { + url: url( "data/json.php" ), + setup: function() { + holder = jQuery.event; + delete jQuery.event; + }, + complete: function() { + assert.ok( true, "Call can be made without jQuery.event" ); + jQuery.event = holder; + }, + success: true + }; + } ); + + ajaxTest( "#15160 - jQuery.ajax() - request manually aborted in ajaxSend", 3, function( assert ) { + return { + setup: function() { + jQuery( document ).on( "ajaxSend", function( e, jqXHR ) { + jqXHR.abort(); + } ); + + jQuery( document ).on( "ajaxError ajaxComplete", function( e, jqXHR ) { + assert.equal( jqXHR.statusText, "abort", "jqXHR.statusText equals abort on global ajaxComplete and ajaxError events" ); + } ); + }, + url: url( "data/name.html" ), + error: true, + complete: function() { + assert.ok( true, "complete" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - context modification", 1, function( assert ) { + return { + url: url( "data/name.html" ), + context: {}, + beforeSend: function() { + this.test = "foo"; + }, + afterSend: function() { + assert.strictEqual( this.context.test, "foo", "Make sure the original object is maintained." ); + }, + success: true + }; + } ); - ajaxTest( "jQuery.ajax() - context modification through ajaxSetup", 3, function() { + ajaxTest( "jQuery.ajax() - context modification through ajaxSetup", 3, function( assert ) { var obj = {}; return { setup: function() { - jQuery.ajaxSetup({ + jQuery.ajaxSetup( { context: obj - }); - strictEqual( jQuery.ajaxSettings.context, obj, "Make sure the context is properly set in ajaxSettings." ); + } ); + assert.strictEqual( jQuery.ajaxSettings.context, obj, "Make sure the context is properly set in ajaxSettings." ); }, - requests: [{ - url: url("data/name.html"), + requests: [ { + url: url( "data/name.html" ), success: function() { - strictEqual( this, obj, "Make sure the original object is maintained." ); + assert.strictEqual( this, obj, "Make sure the original object is maintained." ); } }, { - url: url("data/name.html"), + url: url( "data/name.html" ), context: {}, success: function() { - ok( this !== obj, "Make sure overidding context is possible." ); + assert.ok( this !== obj, "Make sure overriding context is possible." ); } - }] + } ] }; - }); - - ajaxTest( "jQuery.ajax() - disabled globals", 3, { - setup: addGlobalEvents(""), - global: false, - url: url("data/name.html"), - beforeSend: function() { - ok( true, "beforeSend" ); - }, - success: function() { - ok( true, "success" ); - }, - complete: function() { - ok( true, "complete" ); - } - }); - - ajaxTest( "jQuery.ajax() - xml: non-namespace elements inside namespaced elements", 3, { - url: url("data/with_fries.xml"), - dataType: "xml", - success: function( resp ) { - equal( jQuery( "properties", resp ).length, 1, "properties in responseXML" ); - equal( jQuery( "jsconf", resp ).length, 1, "jsconf in responseXML" ); - equal( jQuery( "thing", resp ).length, 2, "things in responseXML" ); - } - }); - - ajaxTest( "jQuery.ajax() - xml: non-namespace elements inside namespaced elements (over JSONP)", 3, { - url: url("data/with_fries_over_jsonp.php"), - dataType: "jsonp xml", - success: function( resp ) { - equal( jQuery( "properties", resp ).length, 1, "properties in responseXML" ); - equal( jQuery( "jsconf", resp ).length, 1, "jsconf in responseXML" ); - equal( jQuery( "thing", resp ).length, 2, "things in responseXML" ); - } - }); + } ); - ajaxTest( "jQuery.ajax() - HEAD requests", 2, [ - { - url: url("data/name.html"), - type: "HEAD", - success: function( data, status, xhr ) { - ok( /Date/i.test( xhr.getAllResponseHeaders() ), "No Date in HEAD response" ); - } - }, - { - url: url("data/name.html"), - data: { - "whip_it": "good" + ajaxTest( "jQuery.ajax() - disabled globals", 3, function( assert ) { + return { + setup: addGlobalEvents( "", assert ), + global: false, + url: url( "data/name.html" ), + beforeSend: function() { + assert.ok( true, "beforeSend" ); + }, + success: function() { + assert.ok( true, "success" ); }, - type: "HEAD", - success: function( data, status, xhr ) { - ok( /Date/i.test( xhr.getAllResponseHeaders() ), "No Date in HEAD response with data" ); + complete: function() { + assert.ok( true, "complete" ); } - } - ]); + }; + } ); - ajaxTest( "jQuery.ajax() - beforeSend", 1, { - url: url("data/name.html"), - beforeSend: function( xml ) { - this.check = true; - }, - success: function( data ) { - ok( this.check, "check beforeSend was executed" ); - } - }); + ajaxTest( "jQuery.ajax() - xml: non-namespace elements inside namespaced elements", 3, function( assert ) { + return { + url: url( "data/with_fries.xml" ), + dataType: "xml", + success: function( resp ) { + assert.equal( jQuery( "properties", resp ).length, 1, "properties in responseXML" ); + assert.equal( jQuery( "jsconf", resp ).length, 1, "jsconf in responseXML" ); + assert.equal( jQuery( "thing", resp ).length, 2, "things in responseXML" ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - beforeSend, cancel request manually", 2, { - create: function() { - return jQuery.ajax({ - url: url("data/name.html"), - beforeSend: function( xhr ) { - ok( true, "beforeSend got called, canceling" ); - xhr.abort(); - }, - success: function() { - ok( false, "request didn't get canceled" ); - }, - complete: function() { - ok( false, "request didn't get canceled" ); + ajaxTest( "jQuery.ajax() - xml: non-namespace elements inside namespaced elements (over JSONP)", 3, function( assert ) { + return { + url: url( "data/with_fries_over_jsonp.php" ), + dataType: "jsonp xml", + success: function( resp ) { + assert.equal( jQuery( "properties", resp ).length, 1, "properties in responseXML" ); + assert.equal( jQuery( "jsconf", resp ).length, 1, "jsconf in responseXML" ); + assert.equal( jQuery( "thing", resp ).length, 2, "things in responseXML" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - HEAD requests", 2, function( assert ) { + return [ + { + url: url( "data/name.html" ), + type: "HEAD", + success: function( data, status, xhr ) { + assert.ok( /Date/i.test( xhr.getAllResponseHeaders() ), "No Date in HEAD response" ); + } + }, + { + url: url( "data/name.html" ), + data: { + "whip_it": "good" }, - error: function() { - ok( false, "request didn't get canceled" ); + type: "HEAD", + success: function( data, status, xhr ) { + assert.ok( /Date/i.test( xhr.getAllResponseHeaders() ), "No Date in HEAD response with data" ); } - }); - }, - fail: function( _, reason ) { - strictEqual( reason, "canceled", "canceled request must fail with 'canceled' status text" ); - } - }); + } + ]; + } ); - ajaxTest( "jQuery.ajax() - dataType html", 5, { - setup: function() { - Globals.register("testFoo"); - Globals.register("testBar"); - }, - dataType: "html", - url: url("data/test.html"), - success: function( data ) { - ok( data.match( /^html text/ ), "Check content for datatype html" ); - jQuery("#ap").html( data ); - strictEqual( window["testFoo"], "foo", "Check if script was evaluated for datatype html" ); - strictEqual( window["testBar"], "bar", "Check if script src was evaluated for datatype html" ); - } - }); - - ajaxTest( "jQuery.ajax() - synchronous request", 1, { - url: url("data/json_obj.js"), - dataType: "text", - async: false, - success: true, - afterSend: function( xhr ) { - ok( /^\{ "data"/.test( xhr.responseText ), "check returned text" ); - } - }); - - ajaxTest( "jQuery.ajax() - synchronous request with callbacks", 2, { - url: url("data/json_obj.js"), - async: false, - dataType: "text", - success: true, - afterSend: function( xhr ) { - var result; - xhr.done(function( data ) { - ok( true, "success callback executed" ); - result = data; - }); - ok( /^\{ "data"/.test( result ), "check returned text" ); - } - }); - - asyncTest( "jQuery.ajax(), jQuery.get[Script|JSON](), jQuery.post(), pass-through request object", 8, function() { - var target = "data/name.html"; - var successCount = 0; - var errorCount = 0; - var errorEx = ""; - var success = function() { - successCount++; + ajaxTest( "jQuery.ajax() - beforeSend", 1, function( assert ) { + return { + url: url( "data/name.html" ), + beforeSend: function() { + this.check = true; + }, + success: function() { + assert.ok( this.check, "check beforeSend was executed" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - beforeSend, cancel request manually", 2, function( assert ) { + return { + create: function() { + return jQuery.ajax( { + url: url( "data/name.html" ), + beforeSend: function( xhr ) { + assert.ok( true, "beforeSend got called, canceling" ); + xhr.abort(); + }, + success: function() { + assert.ok( false, "request didn't get canceled" ); + }, + complete: function() { + assert.ok( false, "request didn't get canceled" ); + }, + error: function() { + assert.ok( false, "request didn't get canceled" ); + } + } ); + }, + fail: function( _, reason ) { + assert.strictEqual( reason, "canceled", "canceled request must fail with 'canceled' status text" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - dataType html", 5, function( assert ) { + return { + setup: function() { + Globals.register( "testFoo" ); + Globals.register( "testBar" ); + }, + dataType: "html", + url: url( "data/test.html" ), + success: function( data ) { + assert.ok( data.match( /^html text/ ), "Check content for datatype html" ); + jQuery( "#ap" ).html( data ); + assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated for datatype html" ); + assert.strictEqual( window[ "testBar" ], "bar", "Check if script src was evaluated for datatype html" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - synchronous request", 1, function( assert ) { + return { + url: url( "data/json_obj.js" ), + dataType: "text", + async: false, + success: true, + afterSend: function( xhr ) { + assert.ok( /^\{ "data"/.test( xhr.responseText ), "check returned text" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - synchronous request with callbacks", 2, function( assert ) { + return { + url: url( "data/json_obj.js" ), + async: false, + dataType: "text", + success: true, + afterSend: function( xhr ) { + var result; + xhr.done( function( data ) { + assert.ok( true, "success callback executed" ); + result = data; + } ); + assert.ok( /^\{ "data"/.test( result ), "check returned text" ); + } }; - jQuery( document ).on( "ajaxError.passthru", function( e, xml, s, ex ) { + } ); + + QUnit.asyncTest( "jQuery.ajax(), jQuery.get[Script|JSON](), jQuery.post(), pass-through request object", 8, function( assert ) { + var target = "data/name.html", + successCount = 0, + errorCount = 0, + errorEx = "", + success = function() { + successCount++; + }; + jQuery( document ).on( "ajaxError.passthru", function( e, xml ) { errorCount++; errorEx += ": " + xml.status; - }); + } ); jQuery( document ).one( "ajaxStop", function() { - equal( successCount, 5, "Check all ajax calls successful" ); - equal( errorCount, 0, "Check no ajax errors (status" + errorEx + ")" ); - jQuery( document ).off("ajaxError.passthru"); - start(); - }); - Globals.register("testBar"); - - ok( jQuery.get( url(target), success ), "get" ); - ok( jQuery.post( url(target), success ), "post" ); - ok( jQuery.getScript( url("data/test.js"), success ), "script" ); - ok( jQuery.getJSON( url("data/json_obj.js"), success ), "json" ); - ok( jQuery.ajax({ + assert.equal( successCount, 5, "Check all ajax calls successful" ); + assert.equal( errorCount, 0, "Check no ajax errors (status" + errorEx + ")" ); + jQuery( document ).off( "ajaxError.passthru" ); + QUnit.start(); + } ); + Globals.register( "testBar" ); + + assert.ok( jQuery.get( url( target ), success ), "get" ); + assert.ok( jQuery.post( url( target ), success ), "post" ); + assert.ok( jQuery.getScript( url( "data/testbar.php" ), success ), "script" ); + assert.ok( jQuery.getJSON( url( "data/json_obj.js" ), success ), "json" ); + assert.ok( jQuery.ajax( { url: url( target ), success: success - }), "generic" ); - }); + } ), "generic" ); + } ); - ajaxTest( "jQuery.ajax() - cache", 12, function() { - + ajaxTest( "jQuery.ajax() - cache", 12, function( assert ) { var re = /_=(.*?)(&|$)/g; - + function request( url, title ) { return { url: url, cache: false, beforeSend: function() { var parameter, tmp; - while(( tmp = re.exec( this.url ) )) { - strictEqual( parameter, undefined, title + ": only one 'no-cache' parameter" ); + while ( ( tmp = re.exec( this.url ) ) ) { + assert.strictEqual( parameter, undefined, title + ": only one 'no-cache' parameter" ); parameter = tmp[ 1 ]; - notStrictEqual( parameter, "tobereplaced555", title + ": parameter (if it was there) was replaced" ); + assert.notStrictEqual( parameter, "tobereplaced555", title + ": parameter (if it was there) was replaced" ); } return false; }, error: true }; } - + return [ request( "data/text.php", @@ -647,316 +756,193 @@ module( "ajax", { "2 parameters surrounding _=" ) ]; - }); + } ); jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { - - ajaxTest( "jQuery.ajax() - JSONP - Query String (?n)" + label, 4, [ - { - url: "data/jsonp.php?callback=?", + ajaxTest( "#8205 - jQuery.ajax() - JSONP - re-use callbacks name" + label, 4, function( assert ) { + return { + url: "data/jsonp.php", dataType: "jsonp", crossDomain: crossDomain, - success: function( data ) { - ok( data.data, "JSON results returned (GET, url callback)" ); - } - }, - { - url: "data/jsonp.php?callback=??", - dataType: "jsonp", - crossDomain: crossDomain, - success: function( data ) { - ok( data.data, "JSON results returned (GET, url context-free callback)" ); - } - }, - { - url: "data/jsonp.php/??", - dataType: "jsonp", - crossDomain: crossDomain, - success: function( data ) { - ok( data.data, "JSON results returned (GET, REST-like)" ); - } - }, - { - url: "data/jsonp.php/???json=1", - dataType: "jsonp", - crossDomain: crossDomain, - success: function( data ) { - strictEqual( jQuery.type( data ), "array", "JSON results returned (GET, REST-like with param)" ); - } - } - ]); + beforeSend: function( jqXHR, s ) { + s.callback = s.jsonpCallback; - ajaxTest( "jQuery.ajax() - JSONP - Explicit callback param" + label, 9, { - setup: function() { - Globals.register("functionToCleanUp"); - Globals.register("XXX"); - Globals.register("jsonpResults"); - window["jsonpResults"] = function( data ) { - ok( data["data"], "JSON results returned (GET, custom callback function)" ); - }; - }, - requests: [{ - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - jsonp: "callback", - success: function( data ) { - ok( data["data"], "JSON results returned (GET, data obj callback)" ); - } - }, { - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - jsonpCallback: "jsonpResults", - success: function( data ) { - ok( data.data, "JSON results returned (GET, custom callback name)" ); - } - }, { - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - jsonpCallback: "functionToCleanUp", - success: function( data ) { - ok( data["data"], "JSON results returned (GET, custom callback name to be cleaned up)" ); - strictEqual( window["functionToCleanUp"], undefined, "Callback was removed (GET, custom callback name to be cleaned up)" ); - var xhr; - jQuery.ajax({ + assert.ok( this.callback in window, "JSONP callback name is in the window" ); + }, + success: function() { + var previous = this; + + assert.strictEqual( + previous.jsonpCallback, + undefined, + "jsonpCallback option is set back to default in callbacks" + ); + + if ( isoldIE ) { + assert.ok( true, "IE8 can't remove property from the window" ); + + } else { + assert.ok( + !( this.callback in window ), + "JSONP callback name was removed from the window" + ); + } + + jQuery.ajax( { url: "data/jsonp.php", dataType: "jsonp", crossDomain: crossDomain, - jsonpCallback: "functionToCleanUp", - beforeSend: function( jqXHR ) { - xhr = jqXHR; + beforeSend: function() { + assert.strictEqual( this.jsonpCallback, previous.callback, "JSONP callback name is re-used" ); return false; } - }); - xhr.fail(function() { - ok( true, "Ajax error JSON (GET, custom callback name to be cleaned up)" ); - strictEqual( window["functionToCleanUp"], undefined, "Callback was removed after early abort (GET, custom callback name to be cleaned up)" ); - }); + } ); } - }, { - url: "data/jsonp.php?callback=XXX", - dataType: "jsonp", - jsonp: false, - jsonpCallback: "XXX", - crossDomain: crossDomain, - beforeSend: function() { - ok( /^data\/jsonp.php\?callback=XXX&_=\d+$/.test( this.url ), "The URL wasn't messed with (GET, custom callback name with no url manipulation)" ); - }, - success: function( data ) { - ok( data["data"], "JSON results returned (GET, custom callback name with no url manipulation)" ); - } - }] - }); + }; + } ); + } ); - ajaxTest( "jQuery.ajax() - JSONP - Callback in data" + label, 2, [ - { - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - data: "callback=?", - success: function( data ) { - ok( data.data, "JSON results returned (GET, data callback)" ); - } + ajaxTest( "jQuery.ajax() - script, Remote", 2, function( assert ) { + return { + setup: function() { + Globals.register( "testBar" ); }, - { - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - data: "callback=??", - success: function( data ) { - ok( data.data, "JSON results returned (GET, data context-free callback)" ); - } + url: window.location.href.replace( /[^\/]*$/, "" ) + "data/testbar.php", + dataType: "script", + success: function() { + assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (GET, no callback)" ); } - ]); - + }; + } ); - ajaxTest( "jQuery.ajax() - JSONP - POST" + label, 3, [ - { - type: "POST", - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - success: function( data ) { - ok( data["data"], "JSON results returned (POST, no callback)" ); - } + ajaxTest( "jQuery.ajax() - script, Remote with POST", 3, function( assert ) { + return { + setup: function() { + Globals.register( "testBar" ); }, - { - type: "POST", - url: "data/jsonp.php", - data: "callback=?", - dataType: "jsonp", - crossDomain: crossDomain, - success: function( data ) { - ok( data["data"], "JSON results returned (POST, data callback)" ); - } + url: window.location.href.replace( /[^\/]*$/, "" ) + "data/testbar.php", + type: "POST", + dataType: "script", + success: function( data, status ) { + assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (POST, no callback)" ); + assert.strictEqual( status, "success", "Script results returned (POST, no callback)" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - script, Remote with scheme-less URL", 2, function( assert ) { + return { + setup: function() { + Globals.register( "testBar" ); }, - { - type: "POST", - url: "data/jsonp.php", - jsonp: "callback", - dataType: "jsonp", - crossDomain: crossDomain, - success: function( data ) { - ok( data["data"], "JSON results returned (POST, data obj callback)" ); - } + url: window.location.href.replace( /[^\/]*$/, "" ).replace( /^.*?\/\//, "//" ) + "data/testbar.php", + dataType: "script", + success: function() { + assert.strictEqual( window[ "testBar" ], "bar", "Script results returned (GET, no callback)" ); } - ]); + }; + } ); - ajaxTest( "jQuery.ajax() - JSONP" + label, 3, [ + ajaxTest( "jQuery.ajax() - malformed JSON", 2, function( assert ) { + return { + url: "data/badjson.js", + dataType: "json", + error: function( xhr, msg, detailedMsg ) { + assert.strictEqual( msg, "parsererror", "A parse error occurred." ); + assert.ok( /(invalid|error|exception)/i.test( detailedMsg ), "Detailed parsererror message provided" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - script by content-type", 2, function() { + return [ { - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - success: function( data ) { - ok( data.data, "JSON results returned (GET, no callback)" ); - } + url: "data/script.php", + data: { + "header": "script" + }, + success: true }, { - create: function( options ) { - var request = jQuery.ajax( options ), - promise = request.then(function( data ) { - ok( data.data, "first request: JSON results returned (GET, no callback)" ); - request = jQuery.ajax( this ).done(function( data ) { - ok( data.data, "this re-used: JSON results returned (GET, no callback)" ); - }); - promise.abort = request.abort; - return request; - }); - promise.abort = request.abort; - return promise; + url: "data/script.php", + data: { + "header": "ecma" }, - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, success: true } - ]); - - }); - - ajaxTest( "jQuery.ajax() - script, Remote", 2, { - setup: function() { - Globals.register("testBar"); - }, - url: window.location.href.replace( /[^\/]*$/, "" ) + "data/test.js", - dataType: "script", - success: function( data ) { - strictEqual( window["testBar"], "bar", "Script results returned (GET, no callback)" ); - } - }); - - ajaxTest( "jQuery.ajax() - script, Remote with POST", 3, { - setup: function() { - Globals.register("testBar"); - }, - url: window.location.href.replace( /[^\/]*$/, "" ) + "data/test.js", - type: "POST", - dataType: "script", - success: function( data, status ) { - strictEqual( window["testBar"], "bar", "Script results returned (POST, no callback)" ); - strictEqual( status, "success", "Script results returned (POST, no callback)" ); - } - }); - - ajaxTest( "jQuery.ajax() - script, Remote with scheme-less URL", 2, { - setup: function() { - Globals.register("testBar"); - }, - url: window.location.href.replace( /[^\/]*$/, "" ).replace( /^.*?\/\//, "//" ) + "data/test.js", - dataType: "script", - success: function( data ) { - strictEqual( window["testBar"], "bar", "Script results returned (GET, no callback)" ); - } - }); - - ajaxTest( "jQuery.ajax() - malformed JSON", 2, { - url: "data/badjson.js", - dataType: "json", - error: function( xhr, msg, detailedMsg ) { - strictEqual( msg, "parsererror", "A parse error occurred." ); - ok( /(invalid|error|exception)/i.test( detailedMsg ), "Detailed parsererror message provided" ); - } - }); + ]; + } ); - ajaxTest( "jQuery.ajax() - script by content-type", 2, [ - { - url: "data/script.php", + ajaxTest( "jQuery.ajax() - JSON by content-type", 5, function( assert ) { + return { + url: "data/json.php", data: { - "header": "script" + "header": "json", + "json": "array" }, - success: true - }, - { - url: "data/script.php", + success: function( json ) { + assert.ok( json.length >= 2, "Check length" ); + assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - JSON by content-type disabled with options", 6, function( assert ) { + return { + url: url( "data/json.php" ), data: { - "header": "ecma" + "header": "json", + "json": "array" }, - success: true - } - ]); + contents: { + "json": false + }, + success: function( text ) { + assert.strictEqual( typeof text, "string", "json wasn't auto-determined" ); + var json = jQuery.parseJSON( text ); + assert.ok( json.length >= 2, "Check length" ); + assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - JSON by content-type", 5, { - url: "data/json.php", - data: { - "header": "json", - "json": "array" - }, - success: function( json ) { - ok( json.length >= 2, "Check length" ); - strictEqual( json[ 0 ]["name"], "John", "Check JSON: first, name" ); - strictEqual( json[ 0 ]["age"], 21, "Check JSON: first, age" ); - strictEqual( json[ 1 ]["name"], "Peter", "Check JSON: second, name" ); - strictEqual( json[ 1 ]["age"], 25, "Check JSON: second, age" ); - } - }); + ajaxTest( "jQuery.ajax() - simple get", 1, function( assert ) { + return { + type: "GET", + url: url( "data/name.php?name=foo" ), + success: function( msg ) { + assert.strictEqual( msg, "bar", "Check for GET" ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - JSON by content-type disabled with options", 6, { - url: url("data/json.php"), - data: { - "header": "json", - "json": "array" - }, - contents: { - "json": false - }, - success: function( text ) { - strictEqual( typeof text, "string", "json wasn't auto-determined" ); - var json = jQuery.parseJSON( text ); - ok( json.length >= 2, "Check length"); - strictEqual( json[ 0 ]["name"], "John", "Check JSON: first, name" ); - strictEqual( json[ 0 ]["age"], 21, "Check JSON: first, age" ); - strictEqual( json[ 1 ]["name"], "Peter", "Check JSON: second, name" ); - strictEqual( json[ 1 ]["age"], 25, "Check JSON: second, age" ); - } - }); + ajaxTest( "jQuery.ajax() - simple post", 1, function( assert ) { + return { + type: "POST", + url: url( "data/name.php" ), + data: "name=peter", + success: function( msg ) { + assert.strictEqual( msg, "pan", "Check for POST" ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - simple get", 1, { - type: "GET", - url: url("data/name.php?name=foo"), - success: function( msg ) { - strictEqual( msg, "bar", "Check for GET" ); - } - }); - - ajaxTest( "jQuery.ajax() - simple post", 1, { - type: "POST", - url: url("data/name.php"), - data: "name=peter", - success: function( msg ) { - strictEqual( msg, "pan", "Check for POST" ); - } - }); - - ajaxTest( "jQuery.ajax() - data option - empty bodies for non-GET requests", 1, { - url: "data/echoData.php", - data: undefined, - type: "post", - success: function( result ) { - strictEqual( result, "" ); - } - }); + ajaxTest( "jQuery.ajax() - data option - empty bodies for non-GET requests", 1, function( assert ) { + return { + url: "data/echoData.php", + data: undefined, + type: "post", + success: function( result ) { + assert.strictEqual( result, "" ); + } + }; + } ); var ifModifiedNow = new Date(); @@ -967,141 +953,89 @@ module( "ajax", { " (no cache)": false }, function( label, cache ) { - var isOpera = !!window.opera; - - asyncTest( "jQuery.ajax() - If-Modified-Since support" + label, 3, function() { - var url = "data/if_modified_since.php?ts=" + ifModifiedNow++; - - jQuery.ajax({ - url: url, - ifModified: true, - cache: cache, - success: function( data, status ) { - strictEqual( status, "success" ); - - jQuery.ajax({ - url: url, - ifModified: true, - cache: cache, - success: function( data, status ) { - if ( data === "FAIL" ) { - ok( isOpera, "Opera is incapable of doing .setRequestHeader('If-Modified-Since')." ); - ok( isOpera, "Opera is incapable of doing .setRequestHeader('If-Modified-Since')." ); - } else { - strictEqual( status, "notmodified" ); - ok( data == null, "response body should be empty" ); - } - start(); - }, - error: function() { - // Do this because opera simply refuses to implement 304 handling :( - // A feature-driven way of detecting this would be appreciated - // See: http://gist.github.com/599419 - ok( isOpera, "error" ); - ok( isOpera, "error" ); - start(); - } - }); - }, - error: function() { - strictEqual( false, "error" ); - // Do this because opera simply refuses to implement 304 handling :( - // A feature-driven way of detecting this would be appreciated - // See: http://gist.github.com/599419 - ok( isOpera, "error" ); - start(); - } - }); - }); - - asyncTest( "jQuery.ajax() - Etag support" + label, 3, function() { - var url = "data/etag.php?ts=" + ifModifiedNow++; - - jQuery.ajax({ - url: url, - ifModified: true, - cache: cache, - success: function( data, status ) { - strictEqual( status, "success" ); - - jQuery.ajax({ + jQuery.each( + { + "If-Modified-Since": "if_modified_since.php", + "Etag": "etag.php" + }, + function( type, url ) { + url = "data/" + url + "?ts=" + ifModifiedNow++; + QUnit.asyncTest( "jQuery.ajax() - " + type + " support" + label, 4, function( assert ) { + jQuery.ajax( { url: url, ifModified: true, cache: cache, - success: function( data, status ) { - if ( data === "FAIL" ) { - ok( isOpera, "Opera is incapable of doing .setRequestHeader('If-None-Match')." ); - ok( isOpera, "Opera is incapable of doing .setRequestHeader('If-None-Match')." ); - } else { - strictEqual( status, "notmodified" ); - ok( data == null, "response body should be empty" ); - } - start(); - }, - error: function() { - // Do this because opera simply refuses to implement 304 handling :( - // A feature-driven way of detecting this would be appreciated - // See: http://gist.github.com/599419 - ok( isOpera, "error" ); - ok( isOpera, "error" ); - start(); + success: function( _, status ) { + assert.strictEqual( status, "success", "Initial status is 'success'" ); + jQuery.ajax( { + url: url, + ifModified: true, + cache: cache, + success: function( data, status, jqXHR ) { + assert.strictEqual( status, "notmodified", "Following status is 'notmodified'" ); + assert.strictEqual( jqXHR.status, 304, "XHR status is 304" ); + assert.equal( data, null, "no response body is given" ); + }, + complete: function() { + QUnit.start(); + } + } ); } - }); - }, - error: function() { - // Do this because opera simply refuses to implement 304 handling :( - // A feature-driven way of detecting this would be appreciated - // See: http://gist.github.com/599419 - ok( isOpera, "error" ); - start(); - } - }); - }); + } ); + } ); + } + ); } /* jQuery.each arguments end */ ); - ajaxTest( "jQuery.ajax() - failing cross-domain (non-existing)", 1, { - // see RFC 2606 - url: "http://example.invalid", - error: function( xhr, _, e ) { - ok( true, "file not found: " + xhr.status + " => " + e ); - } - }); + ajaxTest( "jQuery.ajax() - failing cross-domain (non-existing)", 1, function( assert ) { + return { - ajaxTest( "jQuery.ajax() - failing cross-domain", 1, { - url: "http://" + externalHost, - error: function( xhr, _, e ) { - ok( true, "access denied: " + xhr.status + " => " + e ); - } - }); + // see RFC 2606 + url: "http://example.invalid", + error: function( xhr, _, e ) { + assert.ok( true, "file not found: " + xhr.status + " => " + e ); + } + }; + } ); - ajaxTest( "jQuery.ajax() - atom+xml", 1, { - url: url("data/atom+xml.php"), - success: function() { - ok( true, "success" ); - } - }); - - asyncTest( "jQuery.ajax() - statusText", 3, function() { - jQuery.ajax( url("data/statusText.php?status=200&text=Hello") ).done(function( _, statusText, jqXHR ) { - strictEqual( statusText, "success", "callback status text ok for success" ); - ok( jqXHR.statusText === "Hello" || jqXHR.statusText === "OK", "jqXHR status text ok for success (" + jqXHR.statusText + ")" ); - jQuery.ajax( url("data/statusText.php?status=404&text=World") ).fail(function( jqXHR, statusText ) { - strictEqual( statusText, "error", "callback status text ok for error" ); - // ok( jqXHR.statusText === "World" || jQuery.browser.safari && jqXHR.statusText === "Not Found", "jqXHR status text ok for error (" + jqXHR.statusText + ")" ); - start(); - }); - }); - }); - - asyncTest( "jQuery.ajax() - statusCode", 20, function() { + ajaxTest( "jQuery.ajax() - failing cross-domain", 1, function( assert ) { + return { + url: "http://" + externalHost, + error: function( xhr, _, e ) { + assert.ok( true, "access denied: " + xhr.status + " => " + e ); + } + }; + } ); + + ajaxTest( "jQuery.ajax() - atom+xml", 1, function( assert ) { + return { + url: url( "data/atom+xml.php" ), + success: function() { + assert.ok( true, "success" ); + } + }; + } ); + + QUnit.asyncTest( "jQuery.ajax() - statusText", 3, function( assert ) { + jQuery.ajax( url( "data/statusText.php?status=200&text=Hello" ) ).done( function( _, statusText, jqXHR ) { + assert.strictEqual( statusText, "success", "callback status text ok for success" ); + assert.ok( jqXHR.statusText === "Hello" || jqXHR.statusText === "OK", "jqXHR status text ok for success (" + jqXHR.statusText + ")" ); + jQuery.ajax( url( "data/statusText.php?status=404&text=World" ) ).fail( function( jqXHR, statusText ) { + assert.strictEqual( statusText, "error", "callback status text ok for error" ); + QUnit.start(); + } ); + } ); + } ); + + QUnit.asyncTest( "jQuery.ajax() - statusCode", 20, function( assert ) { var count = 12; function countComplete() { - if ( ! --count ) { - start(); + if ( !--count ) { + QUnit.start(); } } @@ -1109,10 +1043,10 @@ module( "ajax", { name = "Test " + name + " " + ( isSuccess ? "success" : "error" ); return { 200: function() { - ok( isSuccess, name ); + assert.ok( isSuccess, name ); }, 404: function() { - ok( !isSuccess, name ); + assert.ok( !isSuccess, name ); } }; } @@ -1125,47 +1059,47 @@ module( "ajax", { }, function( uri, isSuccess ) { - jQuery.ajax( url(uri), { + jQuery.ajax( url( uri ), { statusCode: createStatusCodes( "in options", isSuccess ), complete: countComplete - }); + } ); - jQuery.ajax( url(uri), { + jQuery.ajax( url( uri ), { complete: countComplete - }).statusCode( createStatusCodes("immediately with method", isSuccess) ); + } ).statusCode( createStatusCodes( "immediately with method", isSuccess ) ); - jQuery.ajax( url(uri), { + jQuery.ajax( url( uri ), { complete: function( jqXHR ) { - jqXHR.statusCode( createStatusCodes("on complete", isSuccess) ); + jqXHR.statusCode( createStatusCodes( "on complete", isSuccess ) ); countComplete(); } - }); + } ); - jQuery.ajax( url(uri), { + jQuery.ajax( url( uri ), { complete: function( jqXHR ) { - setTimeout(function() { - jqXHR.statusCode( createStatusCodes("very late binding", isSuccess) ); + setTimeout( function() { + jqXHR.statusCode( createStatusCodes( "very late binding", isSuccess ) ); countComplete(); }, 100 ); } - }); + } ); - jQuery.ajax( url(uri), { + jQuery.ajax( url( uri ), { statusCode: createStatusCodes( "all (options)", isSuccess ), complete: function( jqXHR ) { - jqXHR.statusCode( createStatusCodes("all (on complete)", isSuccess) ); - setTimeout(function() { - jqXHR.statusCode( createStatusCodes("all (very late binding)", isSuccess) ); + jqXHR.statusCode( createStatusCodes( "all (on complete)", isSuccess ) ); + setTimeout( function() { + jqXHR.statusCode( createStatusCodes( "all (very late binding)", isSuccess ) ); countComplete(); }, 100 ); } - }).statusCode( createStatusCodes("all (immediately with method)", isSuccess) ); + } ).statusCode( createStatusCodes( "all (immediately with method)", isSuccess ) ); var testString = ""; - jQuery.ajax( url(uri), { + jQuery.ajax( url( uri ), { success: function( a, b, jqXHR ) { - ok( isSuccess, "success" ); + assert.ok( isSuccess, "success" ); var statusCode = {}; statusCode[ jqXHR.status ] = function() { testString += "B"; @@ -1174,7 +1108,7 @@ module( "ajax", { testString += "A"; }, error: function( jqXHR ) { - ok( !isSuccess, "error" ); + assert.ok( !isSuccess, "error" ); var statusCode = {}; statusCode[ jqXHR.status ] = function() { testString += "B"; @@ -1183,205 +1117,200 @@ module( "ajax", { testString += "A"; }, complete: function() { - strictEqual( + assert.strictEqual( testString, "AB", "Test statusCode callbacks are ordered like " + ( isSuccess ? "success" : "error" ) + " callbacks" ); countComplete(); } - }); + } ); } /* jQuery.each arguments end*/ ); - }); + } ); - ajaxTest( "jQuery.ajax() - transitive conversions", 8, [ - { - url: url("data/json.php"), - converters: { - "json myJson": function( data ) { - ok( true, "converter called" ); - return data; + ajaxTest( "jQuery.ajax() - transitive conversions", 8, function( assert ) { + return [ + { + url: url( "data/json.php" ), + converters: { + "json myJson": function( data ) { + assert.ok( true, "converter called" ); + return data; + } + }, + dataType: "myJson", + success: function() { + assert.ok( true, "Transitive conversion worked" ); + assert.strictEqual( this.dataTypes[ 0 ], "text", "response was retrieved as text" ); + assert.strictEqual( this.dataTypes[ 1 ], "myjson", "request expected myjson dataType" ); } }, - dataType: "myJson", - success: function() { - ok( true, "Transitive conversion worked" ); - strictEqual( this.dataTypes[ 0 ], "text", "response was retrieved as text" ); - strictEqual( this.dataTypes[ 1 ], "myjson", "request expected myjson dataType" ); + { + url: url( "data/json.php" ), + converters: { + "json myJson": function( data ) { + assert.ok( true, "converter called (*)" ); + return data; + } + }, + contents: false, /* headers are wrong so we ignore them */ + dataType: "* myJson", + success: function() { + assert.ok( true, "Transitive conversion worked (*)" ); + assert.strictEqual( this.dataTypes[ 0 ], "text", "response was retrieved as text (*)" ); + assert.strictEqual( this.dataTypes[ 1 ], "myjson", "request expected myjson dataType (*)" ); + } } - }, - { - url: url("data/json.php"), - converters: { - "json myJson": function( data ) { - ok( true, "converter called (*)" ); - return data; + ]; + } ); + + ajaxTest( "jQuery.ajax() - overrideMimeType", 2, function( assert ) { + return [ + { + url: url( "data/json.php" ), + beforeSend: function( xhr ) { + xhr.overrideMimeType( "application/json" ); + }, + success: function( json ) { + assert.ok( json.data, "Mimetype overridden using beforeSend" ); } }, - contents: false, /* headers are wrong so we ignore them */ - dataType: "* myJson", - success: function() { - ok( true, "Transitive conversion worked (*)" ); - strictEqual( this.dataTypes[ 0 ], "text", "response was retrieved as text (*)" ); - strictEqual( this.dataTypes[ 1 ], "myjson", "request expected myjson dataType (*)" ); + { + url: url( "data/json.php" ), + mimeType: "application/json", + success: function( json ) { + assert.ok( json.data, "Mimetype overridden using mimeType option" ); + } } - } - ]); + ]; + } ); - ajaxTest( "jQuery.ajax() - overrideMimeType", 2, [ - { - url: url("data/json.php"), - beforeSend: function( xhr ) { - xhr.overrideMimeType( "application/json" ); + ajaxTest( "jQuery.ajax() - empty json gets to error callback instead of success callback.", 1, function( assert ) { + return { + url: url( "data/echoData.php" ), + error: function( _, __, error ) { + assert.equal( typeof error === "object", true, "Didn't get back error object for empty json response" ); }, - success: function( json ) { - ok( json.data, "Mimetype overriden using beforeSend" ); - } - }, - { - url: url("data/json.php"), - mimeType: "application/json", - success: function( json ) { - ok( json.data, "Mimetype overriden using mimeType option" ); - } - } - ]); + dataType: "json" + }; + } ); - ajaxTest( "jQuery.ajax() - empty json gets to error callback instead of success callback.", 1, { - url: url("data/echoData.php"), - error: function( _, __, error ) { - equal( typeof error === "object", true, "Didn't get back error object for empty json response" ); - }, - dataType: "json" - }); + ajaxTest( "#2688 - jQuery.ajax() - beforeSend, cancel request", 2, function( assert ) { + return { + create: function() { + return jQuery.ajax( { + url: url( "data/name.html" ), + beforeSend: function() { + assert.ok( true, "beforeSend got called, canceling" ); + return false; + }, + success: function() { + assert.ok( false, "request didn't get canceled" ); + }, + complete: function() { + assert.ok( false, "request didn't get canceled" ); + }, + error: function() { + assert.ok( false, "request didn't get canceled" ); + } + } ); + }, + fail: function( _, reason ) { + assert.strictEqual( reason, "canceled", "canceled request must fail with 'canceled' status text" ); + } + }; + } ); - ajaxTest( "#2688 - jQuery.ajax() - beforeSend, cancel request", 2, { - create: function() { - return jQuery.ajax({ - url: url("data/name.html"), - beforeSend: function() { - ok( true, "beforeSend got called, canceling" ); - return false; - }, - success: function() { - ok( false, "request didn't get canceled" ); - }, - complete: function() { - ok( false, "request didn't get canceled" ); - }, - error: function() { - ok( false, "request didn't get canceled" ); + ajaxTest( "#2806 - jQuery.ajax() - data option - evaluate function values", 1, function( assert ) { + return { + url: "data/echoQuery.php", + data: { + key: function() { + return "value"; } - }); - }, - fail: function( _, reason ) { - strictEqual( reason, "canceled", "canceled request must fail with 'canceled' status text" ); - } - }); - - ajaxTest( "#2806 - jQuery.ajax() - data option - evaluate function values", 1, { - url: "data/echoQuery.php", - data: { - key: function() { - return "value"; + }, + success: function( result ) { + assert.strictEqual( result, "key=value" ); } - }, - success: function( result ) { - strictEqual( result, "key=value" ); - } - }); + }; + } ); + + QUnit.test( "#7531 - jQuery.ajax() - Location object as url", function( assert ) { + assert.expect( 1 ); - test( "#7531 - jQuery.ajax() - Location object as url", 1, function () { - var success = false; + var xhr, + success = false; try { - var xhr = jQuery.ajax({ + xhr = jQuery.ajax( { url: window.location - }); + } ); success = true; xhr.abort(); - } catch (e) { + } catch ( e ) { } - ok( success, "document.location did not generate exception" ); - }); + assert.ok( success, "document.location did not generate exception" ); + } ); jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { - ajaxTest( "#7578 - jQuery.ajax() - JSONP - default for cache option" + label, 1, { - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - beforeSend: function( jqXHR, s ) { - strictEqual( this.cache, false, "cache must be false on JSON request" ); - return false; - }, - error: true - }); - }); + ajaxTest( "#7578 - jQuery.ajax() - JSONP - default for cache option" + label, 1, function( assert ) { + return { + url: "data/jsonp.php", + dataType: "jsonp", + crossDomain: crossDomain, + beforeSend: function() { + assert.strictEqual( this.cache, false, "cache must be false on JSON request" ); + return false; + }, + error: true + }; + } ); + } ); - ajaxTest( "#8107 - jQuery.ajax() - multiple method signatures introduced in 1.5", 4, [ - { - create: function() { - return jQuery.ajax(); - }, - done: function() { - ok( true, "With no arguments" ); - } - }, - { - create: function() { - return jQuery.ajax("data/name.html"); - }, - done: function() { - ok( true, "With only string URL argument" ); - } - }, - { - create: function() { - return jQuery.ajax( "data/name.html", {}); + ajaxTest( "#8107 - jQuery.ajax() - multiple method signatures introduced in 1.5", 4, function( assert ) { + return [ + { + create: function() { + return jQuery.ajax(); + }, + done: function() { + assert.ok( true, "With no arguments" ); + } }, - done: function() { - ok( true, "With string URL param and map" ); - } - }, - { - create: function( options ) { - return jQuery.ajax( options ); + { + create: function() { + return jQuery.ajax( "data/name.html" ); + }, + done: function() { + assert.ok( true, "With only string URL argument" ); + } }, - url: "data/name.html", - success: function() { - ok( true, "With only map" ); - } - } - ]); - - jQuery.each( [ " - Same Domain", " - Cross Domain" ], function( crossDomain, label ) { - ajaxTest( "#8205 - jQuery.ajax() - JSONP - re-use callbacks name" + label, 2, { - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - beforeSend: function( jqXHR, s ) { - s.callback = s.jsonpCallback; + { + create: function() { + return jQuery.ajax( "data/name.html", {} ); + }, + done: function() { + assert.ok( true, "With string URL param and map" ); + } }, - success: function() { - var previous = this; - strictEqual( previous.jsonpCallback, undefined, "jsonpCallback option is set back to default in callbacks" ); - jQuery.ajax({ - url: "data/jsonp.php", - dataType: "jsonp", - crossDomain: crossDomain, - beforeSend: function() { - strictEqual( this.jsonpCallback, previous.callback, "JSONP callback name is re-used" ); - return false; - } - }); + { + create: function( options ) { + return jQuery.ajax( options ); + }, + url: "data/name.html", + success: function() { + assert.ok( true, "With only map" ); + } } - }); - }); + ]; + } ); + + QUnit.test( "#9887 - jQuery.ajax() - Context with circular references (#9887)", function( assert ) { + assert.expect( 2 ); - test( "#9887 - jQuery.ajax() - Context with circular references (#9887)", 2, function () { var success = false, context = {}; context.field = context; @@ -1389,435 +1318,751 @@ module( "ajax", { jQuery.ajax( "non-existing", { context: context, beforeSend: function() { - ok( this === context, "context was not deep extended" ); + assert.ok( this === context, "context was not deep extended" ); return false; } - }); + } ); success = true; } catch ( e ) { console.log( e ); } - ok( success, "context with circular reference did not generate an exception" ); - }); + assert.ok( success, "context with circular reference did not generate an exception" ); + } ); jQuery.each( [ "as argument", "in settings object" ], function( inSetting, title ) { - - function request( url, test ) { + + function request( assert, url, test ) { return { create: function() { return jQuery.ajax( inSetting ? { url: url } : url ); }, done: function() { - ok( true, ( test || url ) + " " + title ); + assert.ok( true, ( test || url ) + " " + title ); } }; } - - ajaxTest( "#10093 - jQuery.ajax() - falsy url " + title, 4, [ - request( "", "empty string" ), - request( false ), - request( null ), - request( undefined ) - ]); - - }); - - ajaxTest( "#11426 - jQuery.ajax() - loading binary data shouldn't throw an exception in IE", 1, { - url: url("data/1x1.jpg"), - success: function( data ) { - ok( data === undefined || /JFIF/.test( data ), "success callback reached" ); - } - }); - test( "#11743 - jQuery.ajax() - script, throws exception", 1, function() { - raises(function() { - jQuery.ajax({ + ajaxTest( "#10093 - jQuery.ajax() - falsy url " + title, 4, function( assert ) { + return [ + request( assert, "", "empty string" ), + request( assert, false ), + request( assert, null ), + request( assert, undefined ) + ]; + } ); + } ); + + ajaxTest( "#11151 - jQuery.ajax() - parse error body", 2, function( assert ) { + return { + url: url( "data/errorWithJSON.php" ), + dataFilter: function( string ) { + assert.ok( false, "dataFilter called" ); + return string; + }, + error: function( jqXHR ) { + assert.strictEqual( jqXHR.responseText, "{ \"code\": 40, \"message\": \"Bad Request\" }", "Error body properly set" ); + assert.deepEqual( jqXHR.responseJSON, { code: 40, message: "Bad Request" }, "Error body properly parsed" ); + } + }; + } ); + + ajaxTest( "#11426 - jQuery.ajax() - loading binary data shouldn't throw an exception in IE", 1, function( assert ) { + return { + url: url( "data/1x1.jpg" ), + success: function( data ) { + assert.ok( data === undefined || /JFIF/.test( data ), "success callback reached" ); + } + }; + } ); + + QUnit.test( "#11743 - jQuery.ajax() - script, throws exception", 1, function(assert) { + assert.throws( function() { + jQuery.ajax( { url: "data/badjson.js", dataType: "script", - throws: true, + "throws": true, + // TODO find a way to test this asynchronously, too async: false, + // Global events get confused by the exception global: false, success: function() { - ok( false, "Success." ); + assert.ok( false, "Success." ); }, error: function() { - ok( false, "Error." ); + assert.ok( false, "Error." ); } - }); + } ); }, "exception bubbled" ); - }); + } ); jQuery.each( [ "method", "type" ], function( _, globalOption ) { - - function request( option ) { + function request( assert, option ) { var options = { - url: url("data/echoData.php"), + url: url( "data/echoData.php" ), data: "hello", success: function( msg ) { - strictEqual( msg, "hello", "Check for POST (no override)" ); + assert.strictEqual( msg, "hello", "Check for POST (no override)" ); } }; if ( option ) { options[ option ] = "GET"; options.success = function( msg ) { - strictEqual( msg, "", "Check for no POST (overriding with " + option + ")" ); + assert.strictEqual( msg, "", "Check for no POST (overriding with " + option + ")" ); }; } return options; } - ajaxTest( "#12004 - jQuery.ajax() - method is an alias of type - " + globalOption + " set globally", 3, { - setup: function() { - var options = {}; - options[ globalOption ] = "POST"; - jQuery.ajaxSetup( options ); - }, - requests: [ - request("type"), - request("method"), - request() - ] - }); - - }); + ajaxTest( + "#12004 - jQuery.ajax() - method is an alias of type - " + + globalOption + " set globally", 3, + function( assert ) { + return { + setup: function() { + var options = {}; + options[ globalOption ] = "POST"; + jQuery.ajaxSetup( options ); + }, + requests: [ + request( assert, "type" ), + request( assert, "method" ), + request( assert ) + ] + }; + } + ); + } ); -//----------- jQuery.ajaxPrefilter() + ajaxTest( "#13276 - jQuery.ajax() - compatibility between XML documents from ajax requests and parsed string", 1, function( assert ) { + return { + url: "data/dashboard.xml", + dataType: "xml", + success: function( ajaxXML ) { + var parsedXML = jQuery( jQuery.parseXML( "blibli" ) ).find( "tab" ); + ajaxXML = jQuery( ajaxXML ); + try { + + // Android 2.3 doesn't automatically adopt nodes from foreign documents. + // (see the comment in test/manipulation.js) + // Support: Android 2.3 + if ( /android 2\.3/i.test( navigator.userAgent ) ) { + parsedXML = jQuery( ajaxXML[ 0 ].adoptNode( parsedXML[ 0 ] ) ); + } + ajaxXML.find( "infowindowtab" ).append( parsedXML ); + } catch ( e ) { + assert.strictEqual( e, undefined, "error" ); + return; + } + assert.strictEqual( ajaxXML.find( "tab" ).length, 3, "Parsed node was added properly" ); + } + }; + } ); - ajaxTest( "jQuery.ajaxPrefilter() - abort", 1, { - setup: function() { - jQuery.ajaxPrefilter(function( options, _, jqXHR ) { - if ( options.abortInPrefilter ) { - jqXHR.abort(); + ajaxTest( "#13292 - jQuery.ajax() - converter is bypassed for 204 requests", 3, function( assert ) { + return { + url: "data/nocontent.php", + dataType: "testing", + converters: { + "* testing": function() { + throw "converter was called"; } - }); - }, - abortInPrefilter: true, - error: function() { - ok( false, "error callback called" ); - }, - fail: function( _, reason ) { - strictEqual( reason, "canceled", "Request aborted by the prefilter must fail with 'canceled' status text" ); - } - }); + }, + success: function( data, status, jqXHR ) { + assert.strictEqual( jqXHR.status, 204, "status code is 204" ); + assert.strictEqual( status, "nocontent", "status text is 'nocontent'" ); + assert.strictEqual( data, undefined, "data is undefined" ); + }, + error: function( _, status, error ) { + assert.ok( false, "error" ); + assert.strictEqual( status, "parsererror", "Parser Error" ); + assert.strictEqual( error, "converter was called", "Converter was called" ); + } + }; + } ); + + ajaxTest( "#13388 - jQuery.ajax() - responseXML", 3, function( assert ) { + return { + url: url( "data/with_fries.xml" ), + dataType: "xml", + success: function( resp, _, jqXHR ) { + assert.notStrictEqual( resp, undefined, "XML document exists" ); + assert.ok( "responseXML" in jqXHR, "jqXHR.responseXML exists" ); + assert.strictEqual( resp, jqXHR.responseXML, "jqXHR.responseXML is set correctly" ); + } + }; + } ); + + ajaxTest( "#13922 - jQuery.ajax() - converter is bypassed for HEAD requests", 3, function( assert ) { + return { + url: "data/json.php", + method: "HEAD", + data: { + header: "yes" + }, + converters: { + "text json": function() { + throw "converter was called"; + } + }, + success: function( data, status ) { + assert.ok( true, "success" ); + assert.strictEqual( status, "nocontent", "data is undefined" ); + assert.strictEqual( data, undefined, "data is undefined" ); + }, + error: function( _, status, error ) { + assert.ok( false, "error" ); + assert.strictEqual( status, "parsererror", "Parser Error" ); + assert.strictEqual( error, "converter was called", "Converter was called" ); + } + }; + } ); + + // Chrome 78 dropped support for synchronous XHR requests inside of + // beforeunload, unload, pagehide, and visibilitychange event handlers. + // See https://bugs.chromium.org/p/chromium/issues/detail?id=952452 + // Safari 13 did similar changes. The below check will catch them both. + if ( !/safari/i.test( navigator.userAgent ) ) { + testIframeWithCallback( + "#14379 - jQuery.ajax() on unload", + "ajax/onunload.html", + function( status, assert ) { + assert.expect( 1 ); + assert.strictEqual( status, "success", "Request completed" ); + } + ); + } + + // BrowserStack PATCH support sometimes breaks so on TestSwarm run the test in IE only. + // Unfortunately, all IE versions gets special treatment in request object creation + // so we need to test in all supported IE versions to be sure. + if ( location.search.indexOf( "swarmURL=" ) === -1 || document.documentMode ) { + ajaxTest( "#13240 - jQuery.ajax() - support non-RFC2616 methods", 1, function( assert ) { + return { + url: "data/echoQuery.php", + method: "PATCH", + success: function() { + assert.ok( true, "success" ); + }, + error: function() { + assert.ok( false, "error" ); + } + }; + } ); + } + + ajaxTest( "#14683 - jQuery.ajax() - Exceptions thrown synchronously by xhr.send should be caught", 4, function( assert ) { + return [ { + url: "data/params_html.php", + method: "POST", + data: { + toString: function() { + throw "Can't parse"; + } + }, + processData: false, + done: function( data ) { + assert.ok( false, "done: " + data ); + }, + fail: function( jqXHR, status, error ) { + assert.ok( true, "exception caught: " + error ); + assert.strictEqual( jqXHR.status, 0, "proper status code" ); + assert.strictEqual( status, "error", "proper status" ); + } + }, { + url: "http://" + externalHost + ":80q", + done: function( data ) { + assert.ok( false, "done: " + data ); + }, + fail: function( _, status, error ) { + assert.ok( true, "fail: " + status + " - " + error ); + } + } ]; + } ); + + ajaxTest( "gh-2587 - when content-type not xml, but looks like one", 1, function( assert ) { + return { + url: url( "data/ajax/content-type.php" ), + data: { + "content-type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "response": "" + }, + success: function( result ) { + assert.strictEqual( + typeof result, + "string", + "Should handle it as a string, not xml" + ); + } + }; + } ); + + ajaxTest( "gh-2587 - when content-type not xml, but looks like one", 1, function( assert ) { + return { + url: url( "data/ajax/content-type.php" ), + data: { + "content-type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "response": "" + }, + success: function( result ) { + assert.strictEqual( + typeof result, + "string", + "Should handle it as a string, not xml" + ); + } + }; + } ); + + ajaxTest( "gh-2587 - when content-type not json, but looks like one", 1, function( assert ) { + return { + url: url( "data/ajax/content-type.php" ), + data: { + "content-type": "test/jsontest", + "response": "{ \"test\": \"test\" }" + }, + success: function( result ) { + assert.strictEqual( + typeof result, + "string", + "Should handle it as a string, not json" + ); + } + }; + } ); + + ajaxTest( "gh-2587 - when content-type not html, but looks like one", 1, function( assert ) { + return { + url: url( "data/ajax/content-type.php" ), + data: { + "content-type": "test/htmltest", + "response": "

        test

        " + }, + success: function( result ) { + assert.strictEqual( + typeof result, + "string", + "Should handle it as a string, not html" + ); + } + }; + } ); + + ajaxTest( "gh-2587 - when content-type not javascript, but looks like one", 1, function( assert ) { + return { + url: url( "data/ajax/content-type.php" ), + data: { + "content-type": "test/testjavascript", + "response": "alert(1)" + }, + success: function( result ) { + assert.strictEqual( + typeof result, + "string", + "Should handle it as a string, not javascript" + ); + } + }; + } ); + + ajaxTest( "gh-2587 - when content-type not ecmascript, but looks like one", 1, function( assert ) { + return { + url: url( "data/ajax/content-type.php" ), + data: { + "content-type": "test/testjavascript", + "response": "alert(1)" + }, + success: function( result ) { + assert.strictEqual( + typeof result, + "string", + "Should handle it as a string, not ecmascript" + ); + } + }; + } ); + +//----------- jQuery.ajaxPrefilter() + + ajaxTest( "jQuery.ajaxPrefilter() - abort", 1, function( assert ) { + return { + dataType: "prefix", + setup: function() { + + // Ensure prefix does not throw an error + jQuery.ajaxPrefilter( "+prefix", function( options, _, jqXHR ) { + if ( options.abortInPrefilter ) { + jqXHR.abort(); + } + } ); + }, + abortInPrefilter: true, + error: function() { + assert.ok( false, "error callback called" ); + }, + fail: function( _, reason ) { + assert.strictEqual( reason, "canceled", "Request aborted by the prefilter must fail with 'canceled' status text" ); + } + }; + } ); //----------- jQuery.ajaxSetup() - asyncTest( "jQuery.ajaxSetup()", 1, function() { - jQuery.ajaxSetup({ - url: url("data/name.php?name=foo"), + QUnit.asyncTest( "jQuery.ajaxSetup()", 1, function( assert ) { + jQuery.ajaxSetup( { + url: url( "data/name.php?name=foo" ), success: function( msg ) { - strictEqual( msg, "bar", "Check for GET" ); - start(); + assert.strictEqual( msg, "bar", "Check for GET" ); + QUnit.start(); } - }); + } ); jQuery.ajax(); - }); + } ); - asyncTest( "jQuery.ajaxSetup({ timeout: Number }) - with global timeout", 2, function() { + QUnit.asyncTest( "jQuery.ajaxSetup({ timeout: Number }) - with global timeout", 2, function( assert ) { var passed = 0, pass = function() { - ok( passed++ < 2, "Error callback executed" ); - if ( passed == 2 ) { - jQuery( document ).off("ajaxError.setupTest"); - start(); + assert.ok( passed++ < 2, "Error callback executed" ); + if ( passed === 2 ) { + jQuery( document ).off( "ajaxError.setupTest" ); + QUnit.start(); } }, - fail = function( a, b, c ) { - ok( false, "Check for timeout failed " + a + " " + b ); - start(); + fail = function( a, b ) { + assert.ok( false, "Check for timeout failed " + a + " " + b ); + QUnit.start(); }; jQuery( document ).on( "ajaxError.setupTest", pass ); - jQuery.ajaxSetup({ + jQuery.ajaxSetup( { timeout: 1000 - }); + } ); - jQuery.ajax({ + jQuery.ajax( { type: "GET", - url: url("data/name.php?wait=5"), + url: url( "data/name.php?wait=5" ), error: pass, success: fail - }); - }); + } ); + } ); - asyncTest( "jQuery.ajaxSetup({ timeout: Number }) with localtimeout", 1, function() { - jQuery.ajaxSetup({ + QUnit.asyncTest( "jQuery.ajaxSetup({ timeout: Number }) with localtimeout", 1, function( assert ) { + jQuery.ajaxSetup( { timeout: 50 - }); - jQuery.ajax({ + } ); + jQuery.ajax( { type: "GET", timeout: 15000, - url: url("data/name.php?wait=1"), + url: url( "data/name.php?wait=1" ), error: function() { - ok( false, "Check for local timeout failed" ); - start(); + assert.ok( false, "Check for local timeout failed" ); + QUnit.start(); }, success: function() { - ok( true, "Check for local timeout" ); - start(); + assert.ok( true, "Check for local timeout" ); + QUnit.start(); } - }); - }); + } ); + } ); //----------- jQuery.domManip() - test( "#11264 - jQuery.domManip() - no side effect because of ajaxSetup or global events", 1, function() { - jQuery.ajaxSetup({ + QUnit.test( "#11264 - jQuery.domManip() - no side effect because of ajaxSetup or global events", function( assert ) { + assert.expect( 1 ); + + jQuery.ajaxSetup( { type: "POST" - }); + } ); + + jQuery( document ).on( "ajaxStart ajaxStop", function() { + assert.ok( false, "Global event triggered" ); + } ); + + jQuery( "#qunit-fixture" ).append( "" ); + + jQuery( document ).off( "ajaxStart ajaxStop" ); + } ); - jQuery( document ).bind( "ajaxStart ajaxStop", function() { - ok( false, "Global event triggered" ); - }); + QUnit.test( + "jQuery#load() - always use GET method even if it overrided through ajaxSetup (#11264)", 1, + function( assert ) { + var done = assert.async(); - jQuery("#qunit-fixture").append(""); + jQuery.ajaxSetup( { + type: "POST" + } ); - jQuery( document ).unbind("ajaxStart ajaxStop"); - }); + jQuery( "#qunit-fixture" ).load( "data/ajax/method.php", function( method ) { + assert.equal( method, "GET" ); + done(); + } ); + } + ); + + QUnit.test( + "jQuery#load() - should resolve with correct context", 2, + function( assert ) { + var done = assert.async(); + var ps = jQuery( "

        " ); + var i = 0; + + ps.appendTo( "#qunit-fixture" ); - asyncTest( "#11402 - jQuery.domManip() - script in comments are properly evaluated", 2, function() { - jQuery("#qunit-fixture").load( "data/cleanScript.html", start ); - }); + ps.load( "data/ajax/method.php", function() { + assert.strictEqual( this, ps[ i++ ] ); + + if ( i === 2 ) { + done(); + } + } ); + } + ); + + QUnit.test( + "#11402 - jQuery.domManip() - script in comments are properly evaluated", 2, + function( assert ) { + jQuery( "#qunit-fixture" ).load( "data/cleanScript.html", assert.async() ); + } + ); //----------- jQuery.get() - asyncTest( "jQuery.get( String, Hash, Function ) - parse xml and use text() on nodes", 2, function() { - jQuery.get( url("data/dashboard.xml"), function( xml ) { + QUnit.asyncTest( "jQuery.get( String, Hash, Function ) - parse xml and use text() on nodes", 2, function( assert ) { + jQuery.get( url( "data/dashboard.xml" ), function( xml ) { var content = []; - jQuery( "tab", xml ).each(function() { + jQuery( "tab", xml ).each( function() { content.push( jQuery( this ).text() ); - }); - strictEqual( content[ 0 ], "blabla", "Check first tab" ); - strictEqual( content[ 1 ], "blublu", "Check second tab" ); - start(); - }); - }); - - asyncTest( "#8277 - jQuery.get( String, Function ) - data in ajaxSettings", 1, function() { - jQuery.ajaxSetup({ + } ); + assert.strictEqual( content[ 0 ], "blabla", "Check first tab" ); + assert.strictEqual( content[ 1 ], "blublu", "Check second tab" ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( "#8277 - jQuery.get( String, Function ) - data in ajaxSettings", 1, function( assert ) { + jQuery.ajaxSetup( { data: "helloworld" - }); - jQuery.get( url("data/echoQuery.php"), function( data ) { - ok( /helloworld$/.test( data ), "Data from ajaxSettings was used" ); - start(); - }); - }); + } ); + jQuery.get( url( "data/echoQuery.php" ), function( data ) { + assert.ok( /helloworld$/.test( data ), "Data from ajaxSettings was used" ); + QUnit.start(); + } ); + } ); //----------- jQuery.getJSON() - asyncTest( "jQuery.getJSON( String, Hash, Function ) - JSON array", 5, function() { + QUnit.asyncTest( "jQuery.getJSON( String, Hash, Function ) - JSON array", 5, function( assert ) { jQuery.getJSON( - url("data/json.php"), + url( "data/json.php" ), { "json": "array" }, function( json ) { - ok( json.length >= 2, "Check length" ); - strictEqual( json[ 0 ]["name"], "John", "Check JSON: first, name" ); - strictEqual( json[ 0 ]["age"], 21, "Check JSON: first, age" ); - strictEqual( json[ 1 ]["name"], "Peter", "Check JSON: second, name" ); - strictEqual( json[ 1 ]["age"], 25, "Check JSON: second, age" ); - start(); + assert.ok( json.length >= 2, "Check length" ); + assert.strictEqual( json[ 0 ][ "name" ], "John", "Check JSON: first, name" ); + assert.strictEqual( json[ 0 ][ "age" ], 21, "Check JSON: first, age" ); + assert.strictEqual( json[ 1 ][ "name" ], "Peter", "Check JSON: second, name" ); + assert.strictEqual( json[ 1 ][ "age" ], 25, "Check JSON: second, age" ); + QUnit.start(); } ); - }); - - asyncTest( "jQuery.getJSON( String, Function ) - JSON object", 2, function() { - jQuery.getJSON( url("data/json.php"), function( json ) { - if ( json && json["data"] ) { - strictEqual( json["data"]["lang"], "en", "Check JSON: lang" ); - strictEqual( json["data"].length, 25, "Check JSON: length" ); - start(); - } - }); - }); - - asyncTest( "jQuery.getJSON() - Using Native JSON", 2, function() { - var restore = "JSON" in window, - old = window.JSON; - if ( !restore ) { - Globals.register("JSON"); - } - window.JSON = { - parse: function( str ) { - ok( true, "Verifying that parse method was run" ); - window.JSON = old; - return true; + } ); + + QUnit.asyncTest( "jQuery.getJSON( String, Function ) - JSON object", 2, function( assert ) { + jQuery.getJSON( url( "data/json.php" ), function( json ) { + if ( json && json[ "data" ] ) { + assert.strictEqual( json[ "data" ][ "lang" ], "en", "Check JSON: lang" ); + assert.strictEqual( json[ "data" ].length, 25, "Check JSON: length" ); + QUnit.start(); } - }; - jQuery.getJSON( url("data/json.php"), function( json ) { - strictEqual( json, true, "Verifying return value" ); - start(); - }); - }); + } ); + } ); - asyncTest( "jQuery.getJSON( String, Function ) - JSON object with absolute url to local content", 2, function() { + QUnit.asyncTest( "jQuery.getJSON( String, Function ) - JSON object with absolute url to local content", 2, function( assert ) { jQuery.getJSON( url( window.location.href.replace( /[^\/]*$/, "" ) + "data/json.php" ), function( json ) { - strictEqual( json.data.lang, "en", "Check JSON: lang" ); - strictEqual( json.data.length, 25, "Check JSON: length" ); - start(); - }); - }); + assert.strictEqual( json.data.lang, "en", "Check JSON: lang" ); + assert.strictEqual( json.data.length, 25, "Check JSON: length" ); + QUnit.start(); + } ); + } ); //----------- jQuery.getScript() - asyncTest( "jQuery.getScript( String, Function ) - with callback", 2, function() { - Globals.register("testBar"); - jQuery.getScript( url("data/test.js"), function( data, _, jqXHR ) { - strictEqual( window["testBar"], "bar", "Check if script was evaluated" ); - start(); - }); - }); - - asyncTest( "jQuery.getScript( String, Function ) - no callback", 1, function() { - Globals.register("testBar"); - jQuery.getScript( url("data/test.js") ).done( start ); - }); - - asyncTest( "#8082 - jQuery.getScript( String, Function ) - source as responseText", 2, function() { - Globals.register("testBar"); - jQuery.getScript( url("data/test.js"), function( data, _, jqXHR ) { - strictEqual( data, jqXHR.responseText, "Same-domain script requests returns the source of the script" ); - start(); - }); - }); - -//----------- jQuery.fn.load() - + QUnit.test( "jQuery.getScript( String, Function ) - with callback", 2, + function( assert ) { + var done = assert.async(); + + Globals.register( "testBar" ); + jQuery.getScript( url( "data/testbar.php" ), function() { + assert.strictEqual( window[ "testBar" ], "bar", "Check if script was evaluated" ); + done(); + } ); + } + ); + + QUnit.test( "jQuery.getScript( String, Function ) - no callback", 1, function( assert ) { + Globals.register( "testBar" ); + jQuery.getScript( url( "data/testbar.php" ) ).done( assert.async() ); + } ); + + QUnit.test( "#8082 - jQuery.getScript( String, Function ) - source as responseText", 2, function( assert ) { + var done = assert.async(); + + Globals.register( "testBar" ); + jQuery.getScript( url( "data/testbar.php" ), function( data, _, jqXHR ) { + assert.strictEqual( data, jqXHR.responseText, "Same-domain script requests returns the source of the script" ); + done(); + } ); + } ); + +// //----------- jQuery.fn.load() + // check if load can be called with only url - asyncTest( "jQuery.fn.load( String )", 2, function() { - jQuery.ajaxSetup({ + QUnit.test( "jQuery.fn.load( String )", 2, function( assert ) { + jQuery.ajaxSetup( { beforeSend: function() { - strictEqual( this.type, "GET", "no data means GET request" ); + assert.strictEqual( this.type, "GET", "no data means GET request" ); } - }); - jQuery("#first").load( "data/name.html", start ); - }); + } ); + jQuery( "#first" ).load( "data/name.html", assert.async() ); + } ); - asyncTest( "jQuery.fn.load() - 404 error callbacks", 6, function() { - addGlobalEvents("ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError")(); - jQuery( document ).ajaxStop( start ); - jQuery("
        ").load( "data/404.html", function() { - ok( true, "complete" ); - }); - }); + QUnit.test( "jQuery.fn.load() - 404 error callbacks", function( assert ) { + assert.expect( 6 ); + var done = assert.async(); + + addGlobalEvents( "ajaxStart ajaxStop ajaxSend ajaxComplete ajaxError", assert )(); + jQuery( document ).ajaxStop( done ); + jQuery( "
        " ).load( "data/404.html", function() { + assert.ok( true, "complete" ); + } ); + } ); // check if load can be called with url and null data - asyncTest( "jQuery.fn.load( String, null )", 2, function() { - jQuery.ajaxSetup({ + QUnit.test( "jQuery.fn.load( String, null )", 2, function( assert ) { + jQuery.ajaxSetup( { beforeSend: function() { - strictEqual( this.type, "GET", "no data means GET request" ); + assert.strictEqual( this.type, "GET", "no data means GET request" ); } - }); - jQuery("#first").load( "data/name.html", null, start ); - }); + } ); + jQuery( "#first" ).load( "data/name.html", null, assert.async() ); + } ); // check if load can be called with url and undefined data - asyncTest( "jQuery.fn.load( String, undefined )", 2, function() { - jQuery.ajaxSetup({ + QUnit.test( "jQuery.fn.load( String, undefined )", 2, function( assert ) { + jQuery.ajaxSetup( { beforeSend: function() { - strictEqual( this.type, "GET", "no data means GET request" ); + assert.strictEqual( this.type, "GET", "no data means GET request" ); } - }); - jQuery("#first").load( "data/name.html", undefined, start ); - }); + } ); + jQuery( "#first" ).load( "data/name.html", undefined, assert.async() ); + } ); // check if load can be called with only url - asyncTest( "jQuery.fn.load( URL_SELECTOR )", 1, function() { - jQuery("#first").load( "data/test3.html div.user", function() { - strictEqual( jQuery( this ).children("div").length, 2, "Verify that specific elements were injected" ); - start(); - }); - }); - - asyncTest( "jQuery.fn.load( String, Function ) - simple: inject text into DOM", 2, function() { - jQuery("#first").load( url("data/name.html"), function() { - ok( /^ERROR/.test(jQuery("#first").text()), "Check if content was injected into the DOM" ); - start(); - }); - }); - - asyncTest( "jQuery.fn.load( String, Function ) - check scripts", 7, function() { + QUnit.asyncTest( "jQuery.fn.load( URL_SELECTOR )", 1, function( assert ) { + jQuery( "#first" ).load( "data/test3.html div.user", function() { + assert.strictEqual( jQuery( this ).children( "div" ).length, 2, "Verify that specific elements were injected" ); + QUnit.start(); + } ); + } ); + + // Selector should be trimmed to avoid leading spaces (#14773) + QUnit.asyncTest( "jQuery.fn.load( URL_SELECTOR with spaces )", 1, function( assert ) { + jQuery( "#first" ).load( "data/test3.html #superuser ", function() { + assert.strictEqual( jQuery( this ).children( "div" ).length, 1, "Verify that specific elements were injected" ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( "jQuery.fn.load( String, Function ) - simple: inject text into DOM", 2, function( assert ) { + jQuery( "#first" ).load( url( "data/name.html" ), function() { + assert.ok( /^ERROR/.test( jQuery( "#first" ).text() ), "Check if content was injected into the DOM" ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( "jQuery.fn.load( String, Function ) - check scripts", 7, function( assert ) { var verifyEvaluation = function() { - strictEqual( window["testBar"], "bar", "Check if script src was evaluated after load" ); - strictEqual( jQuery("#ap").html(), "bar", "Check if script evaluation has modified DOM"); - start(); + assert.strictEqual( window[ "testBar" ], "bar", "Check if script src was evaluated after load" ); + assert.strictEqual( jQuery( "#ap" ).html(), "bar", "Check if script evaluation has modified DOM" ); + QUnit.start(); }; - Globals.register("testFoo"); - Globals.register("testBar"); + Globals.register( "testFoo" ); + Globals.register( "testBar" ); - jQuery("#first").load( url("data/test.html"), function() { - ok( jQuery("#first").html().match( /^html text/ ), "Check content after loading html" ); - strictEqual( jQuery("#foo").html(), "foo", "Check if script evaluation has modified DOM" ); - strictEqual( window["testFoo"], "foo", "Check if script was evaluated after load" ); + jQuery( "#first" ).load( url( "data/test.html" ), function() { + assert.ok( jQuery( "#first" ).html().match( /^html text/ ), "Check content after loading html" ); + assert.strictEqual( jQuery( "#foo" ).html(), "foo", "Check if script evaluation has modified DOM" ); + assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated after load" ); setTimeout( verifyEvaluation, 600 ); - }); - }); + } ); + } ); - asyncTest( "jQuery.fn.load( String, Function ) - check file with only a script tag", 3, function() { - Globals.register("testFoo"); + QUnit.asyncTest( "jQuery.fn.load( String, Function ) - check file with only a script tag", 3, function( assert ) { + Globals.register( "testFoo" ); - jQuery("#first").load( url("data/test2.html"), function() { - strictEqual( jQuery("#foo").html(), "foo", "Check if script evaluation has modified DOM"); - strictEqual( window["testFoo"], "foo", "Check if script was evaluated after load" ); - start(); - }); - }); + jQuery( "#first" ).load( url( "data/test2.html" ), function() { + assert.strictEqual( jQuery( "#foo" ).html(), "foo", "Check if script evaluation has modified DOM" ); + assert.strictEqual( window[ "testFoo" ], "foo", "Check if script was evaluated after load" ); + QUnit.start(); + } ); + } ); - asyncTest( "jQuery.fn.load( String, Function ) - dataFilter in ajaxSettings", 2, function() { - jQuery.ajaxSetup({ + QUnit.asyncTest( "jQuery.fn.load( String, Function ) - dataFilter in ajaxSettings", 2, function( assert ) { + jQuery.ajaxSetup( { dataFilter: function() { return "Hello World"; } - }); - jQuery("
        ").load( url("data/name.html"), function( responseText ) { - strictEqual( jQuery( this ).html(), "Hello World", "Test div was filled with filtered data" ); - strictEqual( responseText, "Hello World", "Test callback receives filtered data" ); - start(); - }); - }); - - asyncTest( "jQuery.fn.load( String, Object, Function )", 2, function() { - jQuery("
        ").load( url("data/params_html.php"), { + } ); + jQuery( "
        " ).load( url( "data/name.html" ), function( responseText ) { + assert.strictEqual( jQuery( this ).html(), "Hello World", "Test div was filled with filtered data" ); + assert.strictEqual( responseText, "Hello World", "Test callback receives filtered data" ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( "jQuery.fn.load( String, Object, Function )", 2, function( assert ) { + jQuery( "
        " ).load( url( "data/params_html.php" ), { "foo": 3, "bar": "ok" }, function() { - var $post = jQuery( this ).find("#post"); - strictEqual( $post.find("#foo").text(), "3", "Check if a hash of data is passed correctly" ); - strictEqual( $post.find("#bar").text(), "ok", "Check if a hash of data is passed correctly" ); - start(); - }); - }); - - asyncTest( "jQuery.fn.load( String, String, Function )", 2, function() { - jQuery("
        ").load( url("data/params_html.php"), "foo=3&bar=ok", function() { - var $get = jQuery( this ).find("#get"); - strictEqual( $get.find("#foo").text(), "3", "Check if a string of data is passed correctly" ); - strictEqual( $get.find("#bar").text(), "ok", "Check if a of data is passed correctly" ); - start(); - }); - }); - - asyncTest( "jQuery.fn.load() - callbacks get the correct parameters", 8, function() { - var slice = [].slice, - completeArgs = {}; - - jQuery.ajaxSetup({ + var $post = jQuery( this ).find( "#post" ); + assert.strictEqual( $post.find( "#foo" ).text(), "3", "Check if a hash of data is passed correctly" ); + assert.strictEqual( $post.find( "#bar" ).text(), "ok", "Check if a hash of data is passed correctly" ); + QUnit.start(); + } ); + } ); + + QUnit.test( "jQuery.fn.load( String, String, Function )", 2, function( assert ) { + var done = assert.async(); + + jQuery( "
        " ).load( url( "data/params_html.php" ), "foo=3&bar=ok", function() { + var $get = jQuery( this ).find( "#get" ); + assert.strictEqual( $get.find( "#foo" ).text(), "3", "Check if a string of data is passed correctly" ); + assert.strictEqual( $get.find( "#bar" ).text(), "ok", "Check if a of data is passed correctly" ); + done(); + } ); + } ); + + QUnit.test( "jQuery.fn.load() - callbacks get the correct parameters", 8, function( assert ) { + var completeArgs = {}; + var done = assert.async(); + + jQuery.ajaxSetup( { success: function( _, status, jqXHR ) { completeArgs[ this.url ] = [ jqXHR.responseText, status, jqXHR ]; }, error: function( jqXHR, status ) { completeArgs[ this.url ] = [ jqXHR.responseText, status, jqXHR ]; } - }); + } ); jQuery.when.apply( jQuery, - jQuery.map([ + jQuery.map( [ { type: "success", url: "data/echoQuery.php?arg=pop" @@ -1828,67 +2073,73 @@ module( "ajax", { } ], function( options ) { - return jQuery.Deferred(function( defer ) { - jQuery("#foo").load( options.url, function() { + return jQuery.Deferred( function( defer ) { + jQuery( "#foo" ).load( options.url, function() { var args = arguments; - strictEqual( completeArgs[ options.url ].length, args.length, "same number of arguments (" + options.type + ")" ); + assert.strictEqual( completeArgs[ options.url ].length, args.length, "same number of arguments (" + options.type + ")" ); jQuery.each( completeArgs[ options.url ], function( i, value ) { - strictEqual( args[ i ], value, "argument #" + i + " is the same (" + options.type + ")" ); - }); + assert.strictEqual( args[ i ], value, "argument #" + i + " is the same (" + options.type + ")" ); + } ); defer.resolve(); - }); - }); - }) - ).always( start ); - }); - - asyncTest( "#2046 - jQuery.fn.load( String, Function ) with ajaxSetup on dataType json", 1, function() { - jQuery.ajaxSetup({ + } ); + } ); + } ) + ).always( done ); + } ); + + QUnit.test( "#2046 - jQuery.fn.load( String, Function ) with ajaxSetup on dataType json", 1, function( assert ) { + var done = assert.async(); + + jQuery.ajaxSetup( { dataType: "json" - }); - jQuery( document ).ajaxComplete(function( e, xml, s ) { - strictEqual( s.dataType, "html", "Verify the load() dataType was html" ); - jQuery( document ).unbind("ajaxComplete"); - start(); - }); - jQuery("#first").load("data/test3.html"); - }); - - asyncTest( "#10524 - jQuery.fn.load() - data specified in ajaxSettings is merged in", 1, function() { + } ); + jQuery( document ).ajaxComplete( function( e, xml, s ) { + assert.strictEqual( s.dataType, "html", "Verify the load() dataType was html" ); + jQuery( document ).off( "ajaxComplete" ); + done(); + } ); + jQuery( "#first" ).load( "data/test3.html" ); + } ); + + QUnit.test( "#10524 - jQuery.fn.load() - data specified in ajaxSettings is merged in", 1, function( assert ) { + var done = assert.async(); + var data = { "baz": 1 }; - jQuery.ajaxSetup({ + jQuery.ajaxSetup( { data: { "foo": "bar" } - }); - jQuery("#foo").load( "data/echoQuery.php", data ); - jQuery( document ).ajaxComplete(function( event, jqXHR, options ) { - ok( ~options.data.indexOf("foo=bar"), "Data from ajaxSettings was used" ); - start(); - }); - }); + } ); + jQuery( "#foo" ).load( "data/echoQuery.php", data ); + jQuery( document ).ajaxComplete( function( event, jqXHR, options ) { + assert.ok( ~options.data.indexOf( "foo=bar" ), "Data from ajaxSettings was used" ); + done(); + } ); + } ); + +// //----------- jQuery.post() -//----------- jQuery.post() + QUnit.test( "jQuery.post() - data", 3, function( assert ) { + var done = assert.async(); - asyncTest( "jQuery.post() - data", 3, function() { jQuery.when( jQuery.post( - url("data/name.php"), + url( "data/name.php" ), { xml: "5-2", length: 3 }, function( xml ) { - jQuery( "math", xml ).each(function() { - strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); - strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); - }); + jQuery( "math", xml ).each( function() { + assert.strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); + assert.strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); + } ); } ), - jQuery.ajax({ - url: url("data/echoData.php"), + jQuery.ajax( { + url: url( "data/echoData.php" ), type: "POST", data: { "test": { @@ -1897,39 +2148,68 @@ module( "ajax", { } }, success: function( data ) { - strictEqual( data, "test%5Blength%5D=7&test%5Bfoo%5D=bar", "Check if a sub-object with a length param is serialized correctly" ); + assert.strictEqual( data, "test%5Blength%5D=7&test%5Bfoo%5D=bar", "Check if a sub-object with a length param is serialized correctly" ); } - }) - ).always( start ); - }); + } ) + ).always( function() { + done(); + } ); + } ); + + QUnit.test( "jQuery.post( String, Hash, Function ) - simple with xml", 4, function( assert ) { + var done = assert.async(); - asyncTest( "jQuery.post( String, Hash, Function ) - simple with xml", 4, function() { jQuery.when( jQuery.post( - url("data/name.php"), + url( "data/name.php" ), { "xml": "5-2" }, function( xml ) { - jQuery( "math", xml ).each(function() { - strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); - strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); - }); + jQuery( "math", xml ).each( function() { + assert.strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); + assert.strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); + } ); } ), - jQuery.post( url("data/name.php?xml=5-2"), {}, function( xml ) { - jQuery( "math", xml ).each(function() { - strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); - strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); - }); - }) - ).always( start ); - }); + jQuery.post( url( "data/name.php?xml=5-2" ), {}, function( xml ) { + jQuery( "math", xml ).each( function() { + assert.strictEqual( jQuery( "calculation", this ).text(), "5-2", "Check for XML" ); + assert.strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); + } ); + } ) + ).always( function() { + done(); + } ); + } ); + + QUnit.test( "jQuery[get|post]( options ) - simple with xml", 2, function( assert ) { + var done = assert.async(); + + jQuery.when.apply( jQuery, + jQuery.map( [ "get", "post" ], function( method ) { + return jQuery[ method ]( { + url: url( "data/name.php" ), + data: { + "xml": "5-2" + }, + success: function( xml ) { + jQuery( "math", xml ).each( function() { + assert.strictEqual( jQuery( "result", this ).text(), "3", "Check for XML" ); + } ); + } + } ); + } ) + ).always( function() { + done(); + } ); + } ); //----------- jQuery.active - test( "jQuery.active", 1, function() { - ok( jQuery.active === 0, "ajax active counter should be zero: " + jQuery.active ); - }); + QUnit.test( "jQuery.active", function( assert ) { + assert.expect( 1 ); + assert.ok( jQuery.active === 0, "ajax active counter should be zero: " + jQuery.active ); + } ); -})(); +} )(); diff --git a/test/unit/animation.js b/test/unit/animation.js new file mode 100644 index 0000000000..78db5f31bf --- /dev/null +++ b/test/unit/animation.js @@ -0,0 +1,246 @@ +( function() { + +// Can't test what ain't there +if ( !jQuery.fx ) { + return; +} + +var oldRaf = window.requestAnimationFrame, + defaultPrefilter = jQuery.Animation.prefilters[ 0 ], + defaultTweener = jQuery.Animation.tweeners[ "*" ][ 0 ], + startTime = 505877050; + +// This module tests jQuery.Animation and the corresponding 1.8+ effects APIs +QUnit.module( "animation", { + setup: function() { + window.requestAnimationFrame = null; + this.sandbox = sinon.sandbox.create(); + this.clock = this.sandbox.useFakeTimers( startTime ); + this._oldInterval = jQuery.fx.interval; + this._oldNow = jQuery.now; + jQuery.fx.step = {}; + jQuery.fx.interval = 10; + jQuery.now = Date.now || this._oldNow; + jQuery.Animation.prefilters = [ defaultPrefilter ]; + jQuery.Animation.tweeners = { "*": [ defaultTweener ] }; + }, + teardown: function() { + this.sandbox.restore(); + jQuery.now = this._oldNow; + jQuery.fx.stop(); + jQuery.fx.interval = this._oldInterval; + window.requestAnimationFrame = oldRaf; + return moduleTeardown.apply( this, arguments ); + } +} ); + +QUnit.test( "Animation( subject, props, opts ) - shape", function( assert ) { + assert.expect( 20 ); + + var subject = { test: 0 }, + props = { test: 1 }, + opts = { queue: "fx", duration: 100 }, + animation = jQuery.Animation( subject, props, opts ); + + assert.equal( animation.elem, subject, ".elem is set to the exact object passed" ); + assert.equal( animation.originalOptions, opts, ".originalOptions is set to options passed" ); + assert.equal( + animation.originalProperties, props, ".originalProperties is set to props passed" + ); + + assert.notEqual( animation.props, props, ".props is not the original however" ); + assert.deepEqual( animation.props, props, ".props is a copy of the original" ); + + assert.deepEqual( animation.opts, { + duration: 100, + queue: "fx", + specialEasing: { test: undefined }, + easing: jQuery.easing._default + }, ".options is filled with default easing and specialEasing" ); + + assert.equal( animation.startTime, startTime, "startTime was set" ); + assert.equal( animation.duration, 100, ".duration is set" ); + + assert.equal( animation.tweens.length, 1, ".tweens has one Tween" ); + assert.equal( typeof animation.tweens[ 0 ].run, "function", "which has a .run function" ); + + assert.equal( typeof animation.createTween, "function", ".createTween is a function" ); + assert.equal( typeof animation.stop, "function", ".stop is a function" ); + + assert.equal( typeof animation.done, "function", ".done is a function" ); + assert.equal( typeof animation.fail, "function", ".fail is a function" ); + assert.equal( typeof animation.always, "function", ".always is a function" ); + assert.equal( typeof animation.progress, "function", ".progress is a function" ); + + assert.equal( jQuery.timers.length, 1, "Added a timers function" ); + assert.equal( jQuery.timers[ 0 ].elem, subject, "...with .elem as the subject" ); + assert.equal( jQuery.timers[ 0 ].anim, animation, "...with .anim as the animation" ); + assert.equal( jQuery.timers[ 0 ].queue, opts.queue, "...with .queue" ); + + // Cleanup after ourselves by ticking to the end + this.clock.tick( 100 ); +} ); + +QUnit.test( "Animation.prefilter( fn ) - calls prefilter after defaultPrefilter", + function( assert ) { + assert.expect( 1 ); + + var prefilter = this.sandbox.stub(), + defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, 0 ); + + jQuery.Animation.prefilter( prefilter ); + + jQuery.Animation( {}, {}, {} ); + assert.ok( prefilter.calledAfter( defaultSpy ), "our prefilter called after" ); + } +); + +QUnit.test( "Animation.prefilter( fn, true ) - calls prefilter before defaultPrefilter", + function( assert ) { + assert.expect( 1 ); + + var prefilter = this.sandbox.stub(), + defaultSpy = this.sandbox.spy( jQuery.Animation.prefilters, 0 ); + + jQuery.Animation.prefilter( prefilter, true ); + + jQuery.Animation( {}, {}, {} ); + assert.ok( prefilter.calledBefore( defaultSpy ), "our prefilter called before" ); + } +); + +QUnit.test( "Animation.prefilter - prefilter return hooks", function( assert ) { + assert.expect( 34 ); + + var animation, realAnimation, element, + sandbox = this.sandbox, + ourAnimation = { stop: this.sandbox.spy() }, + target = { height: 50 }, + props = { height: 100 }, + opts = { duration: 100 }, + prefilter = this.sandbox.spy( function() { + realAnimation = this; + sandbox.spy( realAnimation, "createTween" ); + + assert.deepEqual( realAnimation.originalProperties, props, "originalProperties" ); + assert.equal( arguments[ 0 ], this.elem, "first param elem" ); + assert.equal( arguments[ 1 ], this.props, "second param props" ); + assert.equal( arguments[ 2 ], this.opts, "third param opts" ); + return ourAnimation; + } ), + defaultSpy = sandbox.spy( jQuery.Animation.prefilters, 0 ), + queueSpy = sandbox.spy( function( next ) { + next(); + } ), + TweenSpy = sandbox.spy( jQuery, "Tween" ); + + jQuery.Animation.prefilter( prefilter, true ); + + sandbox.stub( jQuery.fx, "timer" ); + + animation = jQuery.Animation( target, props, opts ); + + assert.equal( prefilter.callCount, 1, "Called prefilter" ); + + assert.equal( defaultSpy.callCount, 0, + "Returning something from a prefilter caused remaining prefilters to not run" ); + assert.equal( jQuery.fx.timer.callCount, 0, "Returning something never queues a timer" ); + assert.equal( + animation, ourAnimation, "Returning something returned it from jQuery.Animation" + ); + assert.equal( + realAnimation.createTween.callCount, 0, "Returning something never creates tweens" + ); + assert.equal( TweenSpy.callCount, 0, "Returning something never creates tweens" ); + + // Test overriden usage on queues: + prefilter.reset(); + element = jQuery( "
        " ) + .css( "height", 50 ) + .animate( props, 100 ) + .queue( queueSpy ) + .animate( props, 100 ) + .queue( queueSpy ) + .animate( props, 100 ) + .queue( queueSpy ); + + assert.equal( prefilter.callCount, 1, "Called prefilter" ); + assert.equal( queueSpy.callCount, 0, "Next function in queue not called" ); + + realAnimation.opts.complete.call( realAnimation.elem ); + assert.equal( queueSpy.callCount, 1, "Next function in queue called after complete" ); + + assert.equal( prefilter.callCount, 2, "Called prefilter again - animation #2" ); + assert.equal( ourAnimation.stop.callCount, 0, ".stop() on our animation hasn't been called" ); + + element.stop(); + assert.equal( ourAnimation.stop.callCount, 1, ".stop() called ourAnimation.stop()" ); + assert.ok( + !ourAnimation.stop.args[ 0 ][ 0 ], ".stop( falsy ) (undefined or false are both valid)" + ); + + assert.equal( queueSpy.callCount, 2, "Next queue function called" ); + assert.ok( queueSpy.calledAfter( ourAnimation.stop ), "After our animation was told to stop" ); + + // ourAnimation.stop.reset(); + assert.equal( prefilter.callCount, 3, "Got the next animation" ); + + ourAnimation.stop.reset(); + + // do not clear queue, gotoEnd + element.stop( false, true ); + assert.ok( ourAnimation.stop.calledWith( true ), ".stop(true) calls .stop(true)" ); + assert.ok( queueSpy.calledAfter( ourAnimation.stop ), + "and the next queue function ran after we were told" ); +} ); + +QUnit.test( "Animation.tweener( fn ) - unshifts a * tweener", function( assert ) { + assert.expect( 2 ); + var starTweeners = jQuery.Animation.tweeners[ "*" ]; + + jQuery.Animation.tweener( jQuery.noop ); + assert.equal( starTweeners.length, 2 ); + assert.deepEqual( starTweeners, [ jQuery.noop, defaultTweener ] ); +} ); + +QUnit.test( "Animation.tweener( 'prop', fn ) - unshifts a 'prop' tweener", function( assert ) { + assert.expect( 4 ); + var tweeners = jQuery.Animation.tweeners, + fn = function() {}; + + jQuery.Animation.tweener( "prop", jQuery.noop ); + assert.equal( tweeners.prop.length, 1 ); + assert.deepEqual( tweeners.prop, [ jQuery.noop ] ); + + jQuery.Animation.tweener( "prop", fn ); + assert.equal( tweeners.prop.length, 2 ); + assert.deepEqual( tweeners.prop, [ fn, jQuery.noop ] ); +} ); + +QUnit.test( + "Animation.tweener( 'list of props', fn ) - unshifts a tweener to each prop", + function( assert ) { + assert.expect( 2 ); + var tweeners = jQuery.Animation.tweeners, + fn = function() {}; + + jQuery.Animation.tweener( "list of props", jQuery.noop ); + assert.deepEqual( tweeners, { + list: [ jQuery.noop ], + of: [ jQuery.noop ], + props: [ jQuery.noop ], + "*": [ defaultTweener ] + } ); + + // Test with extra whitespaces + jQuery.Animation.tweener( " list\t of \tprops\n*", fn ); + assert.deepEqual( tweeners, { + list: [ fn, jQuery.noop ], + of: [ fn, jQuery.noop ], + props: [ fn, jQuery.noop ], + "*": [ fn, defaultTweener ] + } ); + } +); + +} )(); diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 072240049c..def62cb4a2 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -1,16 +1,16 @@ -module( "attributes", { +QUnit.module( "attributes", { teardown: moduleTeardown -}); +} ); -var bareObj = function( value ) { +function bareObj( value ) { return value; -}; +} -var functionReturningObj = function( value ) { - return (function() { +function functionReturningObj( value ) { + return function() { return value; - }); -}; + }; +} /* ======== local reference ======= @@ -24,8 +24,8 @@ var functionReturningObj = function( value ) { Returns a function that returns the value */ -test( "jQuery.propFix integrity test", function() { - expect( 1 ); +QUnit.test( "jQuery.propFix integrity test", function( assert ) { + assert.expect( 1 ); // This must be maintained and equal jQuery.attrFix when appropriate // Ensure that accidental or erroneous property @@ -51,521 +51,545 @@ test( "jQuery.propFix integrity test", function() { } deepEqual( props, jQuery.propFix, "jQuery.propFix passes integrity check" ); -}); - -test( "attr(String)", function() { - expect( 50 ); - - equal( jQuery("#text1").attr("type"), "text", "Check for type attribute" ); - equal( jQuery("#radio1").attr("type"), "radio", "Check for type attribute" ); - equal( jQuery("#check1").attr("type"), "checkbox", "Check for type attribute" ); - equal( jQuery("#simon1").attr("rel"), "bookmark", "Check for rel attribute" ); - equal( jQuery("#google").attr("title"), "Google!", "Check for title attribute" ); - equal( jQuery("#mark").attr("hreflang"), "en", "Check for hreflang attribute" ); - equal( jQuery("#en").attr("lang"), "en", "Check for lang attribute" ); - equal( jQuery("#simon").attr("class"), "blog link", "Check for class attribute" ); - equal( jQuery("#name").attr("name"), "name", "Check for name attribute" ); - equal( jQuery("#text1").attr("name"), "action", "Check for name attribute" ); - ok( jQuery("#form").attr("action").indexOf("formaction") >= 0, "Check for action attribute" ); - equal( jQuery("#text1").attr("value", "t").attr("value"), "t", "Check setting the value attribute" ); - equal( jQuery("#text1").attr("value", "").attr("value"), "", "Check setting the value attribute to empty string" ); - equal( jQuery("
        ").attr("value"), "t", "Check setting custom attr named 'value' on a div" ); - equal( jQuery("#form").attr("blah", "blah").attr("blah"), "blah", "Set non-existant attribute on a form" ); - equal( jQuery("#foo").attr("height"), undefined, "Non existent height attribute should return undefined" ); +} ); + +QUnit.test( "attr(String)", function( assert ) { + assert.expect( 50 ); + + var extras, body, $body, + select, optgroup, option, $img, styleElem, + $button, $form, $a; + + assert.equal( jQuery( "#text1" ).attr( "type" ), "text", "Check for type attribute" ); + assert.equal( jQuery( "#radio1" ).attr( "type" ), "radio", "Check for type attribute" ); + assert.equal( jQuery( "#check1" ).attr( "type" ), "checkbox", "Check for type attribute" ); + assert.equal( jQuery( "#simon1" ).attr( "rel" ), "bookmark", "Check for rel attribute" ); + assert.equal( jQuery( "#google" ).attr( "title" ), "Google!", "Check for title attribute" ); + assert.equal( jQuery( "#mark" ).attr( "hreflang" ), "en", "Check for hreflang attribute" ); + assert.equal( jQuery( "#en" ).attr( "lang" ), "en", "Check for lang attribute" ); + assert.equal( jQuery( "#simon" ).attr( "class" ), "blog link", "Check for class attribute" ); + assert.equal( jQuery( "#name" ).attr( "name" ), "name", "Check for name attribute" ); + assert.equal( jQuery( "#text1" ).attr( "name" ), "action", "Check for name attribute" ); + assert.ok( jQuery( "#form" ).attr( "action" ).indexOf( "formaction" ) >= 0, "Check for action attribute" ); + assert.equal( jQuery( "#text1" ).attr( "value", "t" ).attr( "value" ), "t", "Check setting the value attribute" ); + assert.equal( jQuery( "#text1" ).attr( "value", "" ).attr( "value" ), "", "Check setting the value attribute to empty string" ); + assert.equal( jQuery( "
        " ).attr( "value" ), "t", "Check setting custom attr named 'value' on a div" ); + assert.equal( jQuery( "#form" ).attr( "blah", "blah" ).attr( "blah" ), "blah", "Set non-existent attribute on a form" ); + assert.equal( jQuery( "#foo" ).attr( "height" ), undefined, "Non existent height attribute should return undefined" ); // [7472] & [3113] (form contains an input with name="action" or name="id") - var extras = jQuery("").appendTo("#testForm"); - equal( jQuery("#form").attr("action","newformaction").attr("action"), "newformaction", "Check that action attribute was changed" ); - equal( jQuery("#testForm").attr("target"), undefined, "Retrieving target does not equal the input with name=target" ); - equal( jQuery("#testForm").attr("target", "newTarget").attr("target"), "newTarget", "Set target successfully on a form" ); - equal( jQuery("#testForm").removeAttr("id").attr("id"), undefined, "Retrieving id does not equal the input with name=id after id is removed [#7472]" ); + extras = jQuery( "" ).appendTo( "#testForm" ); + assert.equal( jQuery( "#form" ).attr( "action", "newformaction" ).attr( "action" ), "newformaction", "Check that action attribute was changed" ); + assert.equal( jQuery( "#testForm" ).attr( "target" ), undefined, "Retrieving target does not equal the input with name=target" ); + assert.equal( jQuery( "#testForm" ).attr( "target", "newTarget" ).attr( "target" ), "newTarget", "Set target successfully on a form" ); + assert.equal( jQuery( "#testForm" ).removeAttr( "id" ).attr( "id" ), undefined, "Retrieving id does not equal the input with name=id after id is removed [#7472]" ); + // Bug #3685 (form contains input with name="name") - equal( jQuery("#testForm").attr("name"), undefined, "Retrieving name does not retrieve input with name=name" ); + assert.equal( jQuery( "#testForm" ).attr( "name" ), undefined, "Retrieving name does not retrieve input with name=name" ); extras.remove(); - equal( jQuery("#text1").attr("maxlength"), "30", "Check for maxlength attribute" ); - equal( jQuery("#text1").attr("maxLength"), "30", "Check for maxLength attribute" ); - equal( jQuery("#area1").attr("maxLength"), "30", "Check for maxLength attribute" ); + assert.equal( jQuery( "#text1" ).attr( "maxlength" ), "30", "Check for maxlength attribute" ); + assert.equal( jQuery( "#text1" ).attr( "maxLength" ), "30", "Check for maxLength attribute" ); + assert.equal( jQuery( "#area1" ).attr( "maxLength" ), "30", "Check for maxLength attribute" ); // using innerHTML in IE causes href attribute to be serialized to the full path - jQuery("").attr({ + jQuery( "" ).attr( { "id": "tAnchor5", "href": "#5" - }).appendTo("#qunit-fixture"); - equal( jQuery("#tAnchor5").attr("href"), "#5", "Check for non-absolute href (an anchor)" ); - jQuery("").appendTo("#qunit-fixture"); - equal( jQuery("#tAnchor5").prop("href"), jQuery("#tAnchor6").prop("href"), "Check for absolute href prop on an anchor" ); + } ).appendTo( "#qunit-fixture" ); + assert.equal( jQuery( "#tAnchor5" ).attr( "href" ), "#5", "Check for non-absolute href (an anchor)" ); + jQuery( "" ).appendTo( "#qunit-fixture" ); + assert.equal( jQuery( "#tAnchor5" ).prop( "href" ), jQuery( "#tAnchor6" ).prop( "href" ), "Check for absolute href prop on an anchor" ); - $("").appendTo("#qunit-fixture"); - equal( jQuery("#tAnchor5").prop("href"), jQuery("#scriptSrc").prop("src"), "Check for absolute src prop on a script" ); + jQuery( "" ).appendTo( "#qunit-fixture" ); + assert.equal( jQuery( "#tAnchor5" ).prop( "href" ), jQuery( "#scriptSrc" ).prop( "src" ), "Check for absolute src prop on a script" ); // list attribute is readonly by default in browsers that support it - jQuery("#list-test").attr( "list", "datalist" ); - equal( jQuery("#list-test").attr("list"), "datalist", "Check setting list attribute" ); + jQuery( "#list-test" ).attr( "list", "datalist" ); + assert.equal( jQuery( "#list-test" ).attr( "list" ), "datalist", "Check setting list attribute" ); // Related to [5574] and [5683] - var body = document.body, $body = jQuery( body ); + body = document.body; + $body = jQuery( body ); - strictEqual( $body.attr("foo"), undefined, "Make sure that a non existent attribute returns undefined" ); + assert.strictEqual( $body.attr( "foo" ), undefined, "Make sure that a non existent attribute returns undefined" ); body.setAttribute( "foo", "baz" ); - equal( $body.attr("foo"), "baz", "Make sure the dom attribute is retrieved when no expando is found" ); + assert.equal( $body.attr( "foo" ), "baz", "Make sure the dom attribute is retrieved when no expando is found" ); - $body.attr( "foo","cool" ); - equal( $body.attr("foo"), "cool", "Make sure that setting works well when both expando and dom attribute are available" ); + $body.attr( "foo", "cool" ); + assert.equal( $body.attr( "foo" ), "cool", "Make sure that setting works well when both expando and dom attribute are available" ); - body.removeAttribute("foo"); // Cleanup + body.removeAttribute( "foo" ); // Cleanup - var select = document.createElement("select"), - optgroup = document.createElement("optgroup"), - option = document.createElement("option"); + select = document.createElement( "select" ); + optgroup = document.createElement( "optgroup" ); + option = document.createElement( "option" ); optgroup.appendChild( option ); select.appendChild( optgroup ); - equal( jQuery( option ).prop("selected"), true, "Make sure that a single option is selected, even when in an optgroup." ); + assert.equal( jQuery( option ).prop( "selected" ), true, "Make sure that a single option is selected, even when in an optgroup." ); - var $img = jQuery("").appendTo("body"); - equal( $img.attr("width"), "215", "Retrieve width attribute an an element with display:none." ); - equal( $img.attr("height"), "53", "Retrieve height attribute an an element with display:none." ); + $img = jQuery( "" ).appendTo( "body" ); + assert.equal( $img.attr( "width" ), "215", "Retrieve width attribute an an element with display:none." ); + assert.equal( $img.attr( "height" ), "53", "Retrieve height attribute an an element with display:none." ); // Check for style support - var styleElem = jQuery("
        ").appendTo("#qunit-fixture").css({ + styleElem = jQuery( "
        " ).appendTo( "#qunit-fixture" ).css( { background: "url(UPPERlower.gif)" - }); - ok( !!~styleElem.attr("style").indexOf("UPPERlower.gif"), "Check style attribute getter" ); - ok( !!~styleElem.attr("style", "position:absolute;").attr("style").indexOf("absolute"), "Check style setter" ); + } ); + assert.ok( !!~styleElem.attr( "style" ).indexOf( "UPPERlower.gif" ), "Check style attribute getter" ); + assert.ok( !!~styleElem.attr( "style", "position:absolute;" ).attr( "style" ).indexOf( "absolute" ), "Check style setter" ); // Check value on button element (#1954) - var $button = jQuery("").insertAfter("#button"); - strictEqual( $button.attr("value"), undefined, "Absence of value attribute on a button" ); - equal( $button.attr( "value", "foobar" ).attr("value"), "foobar", "Value attribute on a button does not return innerHTML" ); - equal( $button.attr("value", "baz").html(), "text", "Setting the value attribute does not change innerHTML" ); + $button = jQuery( "" ).insertAfter( "#button" ); + assert.strictEqual( $button.attr( "value" ), undefined, "Absence of value attribute on a button" ); + assert.equal( $button.attr( "value", "foobar" ).attr( "value" ), "foobar", "Value attribute on a button does not return innerHTML" ); + assert.equal( $button.attr( "value", "baz" ).html(), "text", "Setting the value attribute does not change innerHTML" ); // Attributes with a colon on a table element (#1591) - equal( jQuery("#table").attr("test:attrib"), undefined, "Retrieving a non-existent attribute on a table with a colon does not throw an error." ); - equal( jQuery("#table").attr( "test:attrib", "foobar" ).attr("test:attrib"), "foobar", "Setting an attribute on a table with a colon does not throw an error." ); + assert.equal( jQuery( "#table" ).attr( "test:attrib" ), undefined, "Retrieving a non-existent attribute on a table with a colon does not throw an error." ); + assert.equal( jQuery( "#table" ).attr( "test:attrib", "foobar" ).attr( "test:attrib" ), "foobar", "Setting an attribute on a table with a colon does not throw an error." ); - var $form = jQuery("
        ").appendTo("#qunit-fixture"); - equal( $form.attr("class"), "something", "Retrieve the class attribute on a form." ); + $form = jQuery( "
        " ).appendTo( "#qunit-fixture" ); + assert.equal( $form.attr( "class" ), "something", "Retrieve the class attribute on a form." ); - var $a = jQuery("
        Click").appendTo("#qunit-fixture"); - equal( $a.attr("onclick"), "something()", "Retrieve ^on attribute without anonymous function wrapper." ); + $a = jQuery( "Click" ).appendTo( "#qunit-fixture" ); + assert.equal( $a.attr( "onclick" ), "something()", "Retrieve ^on attribute without anonymous function wrapper." ); - ok( jQuery("
        ").attr("doesntexist") === undefined, "Make sure undefined is returned when no attribute is found." ); - ok( jQuery("
        ").attr("title") === undefined, "Make sure undefined is returned when no attribute is found." ); - equal( jQuery("
        ").attr( "title", "something" ).attr("title"), "something", "Set the title attribute." ); - ok( jQuery().attr("doesntexist") === undefined, "Make sure undefined is returned when no element is there." ); - equal( jQuery("
        ").attr("value"), undefined, "An unset value on a div returns undefined." ); - strictEqual( jQuery("").attr("value"), undefined, "An unset value on a select returns undefined." ); + assert.ok( jQuery( "
        " ).attr( "doesntexist" ) === undefined, "Make sure undefined is returned when no attribute is found." ); + assert.ok( jQuery( "
        " ).attr( "title" ) === undefined, "Make sure undefined is returned when no attribute is found." ); + assert.equal( jQuery( "
        " ).attr( "title", "something" ).attr( "title" ), "something", "Set the title attribute." ); + assert.ok( jQuery().attr( "doesntexist" ) === undefined, "Make sure undefined is returned when no element is there." ); + assert.equal( jQuery( "
        " ).attr( "value" ), undefined, "An unset value on a div returns undefined." ); + assert.strictEqual( jQuery( "" ).attr( "value" ), undefined, "An unset value on a select returns undefined." ); - $form = jQuery("#form").attr( "enctype", "multipart/form-data" ); - equal( $form.prop("enctype"), "multipart/form-data", "Set the enctype of a form (encoding in IE6/7 #6743)" ); + $form = jQuery( "#form" ).attr( "enctype", "multipart/form-data" ); + assert.equal( $form.prop( "enctype" ), "multipart/form-data", "Set the enctype of a form (encoding in IE6/7 #6743)" ); -}); +} ); -test( "attr(String) on cloned elements, #9646", function() { - expect( 4 ); +QUnit.test( "attr(String) on cloned elements, #9646", function( assert ) { + assert.expect( 4 ); var div, - input = jQuery(""); + input = jQuery( "" ); - input.attr("name"); + input.attr( "name" ); - strictEqual( input.clone( true ).attr( "name", "test" )[ 0 ].name, "test", "Name attribute should be changed on cloned element" ); + assert.strictEqual( input.clone( true ).attr( "name", "test" )[ 0 ].name, "test", "Name attribute should be changed on cloned element" ); - div = jQuery("
        "); - div.attr("id"); + div = jQuery( "
        " ); + div.attr( "id" ); - strictEqual( div.clone( true ).attr( "id", "test" )[ 0 ].id, "test", "Id attribute should be changed on cloned element" ); + assert.strictEqual( div.clone( true ).attr( "id", "test" )[ 0 ].id, "test", "Id attribute should be changed on cloned element" ); - input = jQuery(""); - input.attr("value"); + input = jQuery( "" ); + input.attr( "value" ); - strictEqual( input.clone( true ).attr( "value", "test" )[ 0 ].value, "test", "Value attribute should be changed on cloned element" ); + assert.strictEqual( input.clone( true ).attr( "value", "test" )[ 0 ].value, "test", "Value attribute should be changed on cloned element" ); - strictEqual( input.clone( true ).attr( "value", 42 )[ 0 ].value, "42", "Value attribute should be changed on cloned element" ); -}); + assert.strictEqual( input.clone( true ).attr( "value", 42 )[ 0 ].value, "42", "Value attribute should be changed on cloned element" ); +} ); -test( "attr(String) in XML Files", function() { - expect( 3 ); +QUnit.test( "attr(String) in XML Files", function( assert ) { + assert.expect( 3 ); var xml = createDashboardXML(); - equal( jQuery( "locations", xml ).attr("class"), "foo", "Check class attribute in XML document" ); - equal( jQuery( "location", xml ).attr("for"), "bar", "Check for attribute in XML document" ); - equal( jQuery( "location", xml ).attr("checked"), "different", "Check that hooks are not attached in XML document" ); -}); + assert.equal( jQuery( "locations", xml ).attr( "class" ), "foo", "Check class attribute in XML document" ); + assert.equal( jQuery( "location", xml ).attr( "for" ), "bar", "Check for attribute in XML document" ); + assert.equal( jQuery( "location", xml ).attr( "checked" ), "different", "Check that hooks are not attached in XML document" ); +} ); -test( "attr(String, Function)", function() { - expect( 2 ); +QUnit.test( "attr(String, Function)", function( assert ) { + assert.expect( 2 ); - equal( - jQuery("#text1").attr( "value", function() { + assert.equal( + jQuery( "#text1" ).attr( "value", function() { return this.id; - }).attr("value"), + } ).attr( "value" ), "text1", "Set value from id" ); - equal( - jQuery("#text1").attr( "title", function(i) { + assert.equal( + jQuery( "#text1" ).attr( "title", function( i ) { return i; - }).attr("title"), + } ).attr( "title" ), "0", "Set value with an index" ); -}); +} ); -test( "attr(Hash)", function() { - expect( 3 ); +QUnit.test( "attr(Hash)", function( assert ) { + assert.expect( 3 ); var pass = true; - jQuery("div").attr({ + jQuery( "div" ).attr( { "foo": "baz", "zoo": "ping" - }).each(function() { - if ( this.getAttribute("foo") != "baz" && this.getAttribute("zoo") != "ping" ) { + } ).each( function() { + if ( this.getAttribute( "foo" ) !== "baz" && this.getAttribute( "zoo" ) !== "ping" ) { pass = false; } - }); + } ); - ok( pass, "Set Multiple Attributes" ); + assert.ok( pass, "Set Multiple Attributes" ); - equal( - jQuery("#text1").attr({ + assert.equal( + jQuery( "#text1" ).attr( { "value": function() { - return this["id"]; - }}).attr("value"), + return this[ "id" ]; + } } ).attr( "value" ), "text1", "Set attribute to computed value #1" ); - equal( - jQuery("#text1").attr({ - "title": function(i) { + assert.equal( + jQuery( "#text1" ).attr( { + "title": function( i ) { return i; } - }).attr("title"), + } ).attr( "title" ), "0", "Set attribute to computed value #2" ); -}); +} ); -test( "attr(String, Object)", function() { - expect( 71 ); +QUnit.test( "attr(String, Object)", function( assert ) { + assert.expect( 71 ); - var div = jQuery("div").attr("foo", "bar"), + var $input, $text, $details, + attributeNode, commentNode, textNode, obj, + table, td, j, type, + check, thrown, button, $radio, $radios, $svg, + div = jQuery( "div" ).attr( "foo", "bar" ), i = 0, fail = false; for ( ; i < div.length; i++ ) { - if ( div[ i ].getAttribute("foo") !== "bar" ) { + if ( div[ i ].getAttribute( "foo" ) !== "bar" ) { fail = i; break; } } - equal( fail, false, "Set Attribute, the #" + fail + " element didn't get the attribute 'foo'" ); + assert.equal( fail, false, "Set Attribute, the #" + fail + " element didn't get the attribute 'foo'" ); - ok( - jQuery("#foo").attr({ + assert.ok( + jQuery( "#foo" ).attr( { "width": null - }), + } ), "Try to set an attribute to nothing" ); - jQuery("#name").attr( "name", "something" ); - equal( jQuery("#name").attr("name"), "something", "Set name attribute" ); - jQuery("#name").attr( "name", null ); - equal( jQuery("#name").attr("name"), undefined, "Remove name attribute" ); + jQuery( "#name" ).attr( "name", "something" ); + assert.equal( jQuery( "#name" ).attr( "name" ), "something", "Set name attribute" ); + jQuery( "#name" ).attr( "name", null ); + assert.equal( jQuery( "#name" ).attr( "name" ), undefined, "Remove name attribute" ); - var $input = jQuery( "", { + $input = jQuery( "", { name: "something", id: "specified" - }); - equal( $input.attr("name"), "something", "Check element creation gets/sets the name attribute." ); - equal( $input.attr("id"), "specified", "Check element creation gets/sets the id attribute." ); + } ); + assert.equal( $input.attr( "name" ), "something", "Check element creation gets/sets the name attribute." ); + assert.equal( $input.attr( "id" ), "specified", "Check element creation gets/sets the id attribute." ); // As of fixing #11115, we only guarantee boolean property update for checked and selected - $input = jQuery("").attr( "checked", true ); - equal( $input.prop("checked"), true, "Setting checked updates property (verified by .prop)" ); - equal( $input[0].checked, true, "Setting checked updates property (verified by native property)" ); - $input = jQuery("
        "), - td = table.find("td:first"); + // Register the property on the window for the previous assertion so it will be clean up + Globals.register( "nonexisting" ); + + table = jQuery( "#table" ).append( "" ); + td = table.find( "td" ).eq( 0 ); td.attr( "rowspan", "2" ); - equal( td[ 0 ]["rowSpan"], 2, "Check rowspan is correctly set" ); + assert.equal( td[ 0 ][ "rowSpan" ], 2, "Check rowspan is correctly set" ); td.attr( "colspan", "2" ); - equal( td[ 0 ]["colSpan"], 2, "Check colspan is correctly set" ); - table.attr("cellspacing", "2"); - equal( table[ 0 ]["cellSpacing"], "2", "Check cellspacing is correctly set" ); + assert.equal( td[ 0 ][ "colSpan" ], 2, "Check colspan is correctly set" ); + table.attr( "cellspacing", "2" ); + assert.equal( table[ 0 ][ "cellSpacing" ], "2", "Check cellspacing is correctly set" ); - equal( jQuery("#area1").attr("value"), undefined, "Value attribute is distinct from value property." ); + assert.equal( jQuery( "#area1" ).attr( "value" ), undefined, "Value attribute is distinct from value property." ); // for #1070 - jQuery("#name").attr( "someAttr", "0" ); - equal( jQuery("#name").attr("someAttr"), "0", "Set attribute to a string of '0'" ); - jQuery("#name").attr( "someAttr", 0 ); - equal( jQuery("#name").attr("someAttr"), "0", "Set attribute to the number 0" ); - jQuery("#name").attr( "someAttr", 1 ); - equal( jQuery("#name").attr("someAttr"), "1", "Set attribute to the number 1" ); + jQuery( "#name" ).attr( "someAttr", "0" ); + assert.equal( jQuery( "#name" ).attr( "someAttr" ), "0", "Set attribute to a string of '0'" ); + jQuery( "#name" ).attr( "someAttr", 0 ); + assert.equal( jQuery( "#name" ).attr( "someAttr" ), "0", "Set attribute to the number 0" ); + jQuery( "#name" ).attr( "someAttr", 1 ); + assert.equal( jQuery( "#name" ).attr( "someAttr" ), "1", "Set attribute to the number 1" ); // using contents will get comments regular, text, and comment nodes - var j = jQuery("#nonnodes").contents(); + j = jQuery( "#nonnodes" ).contents(); j.attr( "name", "attrvalue" ); - equal( j.attr("name"), "attrvalue", "Check node,textnode,comment for attr" ); - j.removeAttr("name"); + assert.equal( j.attr( "name" ), "attrvalue", "Check node,textnode,comment for attr" ); + j.removeAttr( "name" ); // Type - var type = jQuery("#check2").attr("type"); + type = jQuery( "#check2" ).attr( "type" ); try { - jQuery("#check2").attr( "type", "hidden" ); - ok( true, "No exception thrown on input type change" ); - } catch( e ) { - ok( true, "Exception thrown on input type change: " + e ); + jQuery( "#check2" ).attr( "type", "hidden" ); + assert.ok( true, "No exception thrown on input type change" ); + } catch ( e ) { + assert.ok( true, "Exception thrown on input type change: " + e ); } - var check = document.createElement("input"); - var thrown = true; + check = document.createElement( "input" ); + thrown = true; try { jQuery( check ).attr( "type", "checkbox" ); - } catch( e ) { + } catch ( e ) { thrown = false; } - ok( thrown, "Exception thrown when trying to change type property" ); - equal( "checkbox", jQuery( check ).attr("type"), "Verify that you can change the type of an input element that isn't in the DOM" ); + assert.ok( thrown, "Exception thrown when trying to change type property" ); + assert.equal( "checkbox", jQuery( check ).attr( "type" ), "Verify that you can change the type of an input element that isn't in the DOM" ); - check = jQuery(""); + check = jQuery( "" ); thrown = true; try { check.attr( "type", "checkbox" ); - } catch( e ) { + } catch ( e ) { thrown = false; } - ok( thrown, "Exception thrown when trying to change type property" ); - equal( "checkbox", check.attr("type"), "Verify that you can change the type of an input element that isn't in the DOM" ); + assert.ok( thrown, "Exception thrown when trying to change type property" ); + assert.equal( "checkbox", check.attr( "type" ), "Verify that you can change the type of an input element that isn't in the DOM" ); - var button = jQuery("#button"); + button = jQuery( "#button" ); try { button.attr( "type", "submit" ); - ok( true, "No exception thrown on button type change" ); - } catch( e ) { - ok( true, "Exception thrown on button type change: " + e ); + assert.ok( true, "No exception thrown on button type change" ); + } catch ( e ) { + assert.ok( true, "Exception thrown on button type change: " + e ); } - var $radio = jQuery( "", { + $radio = jQuery( "", { "value": "sup", "type": "radio" - }).appendTo("#testForm"); - equal( $radio.val(), "sup", "Value is not reset when type is set after value on a radio" ); + } ).appendTo( "#testForm" ); + assert.equal( $radio.val(), "sup", "Value is not reset when type is set after value on a radio" ); // Setting attributes on svg elements (bug #3116) - var $svg = jQuery( - "" + - + $svg = jQuery( + "" + "" + "" - ).appendTo("body"); - equal( $svg.attr( "cx", 100 ).attr("cx"), "100", "Set attribute on svg element" ); + ).appendTo( "body" ); + assert.equal( $svg.attr( "cx", 100 ).attr( "cx" ), "100", "Set attribute on svg element" ); $svg.remove(); // undefined values are chainable - jQuery("#name").attr( "maxlength", "5" ).removeAttr("nonexisting"); - equal( typeof jQuery("#name").attr( "maxlength", undefined ), "object", ".attr('attribute', undefined) is chainable (#5571)" ); - equal( jQuery("#name").attr( "maxlength", undefined ).attr("maxlength"), "5", ".attr('attribute', undefined) does not change value (#5571)" ); - equal( jQuery("#name").attr( "nonexisting", undefined ).attr("nonexisting"), undefined, ".attr('attribute', undefined) does not create attribute (#5571)" ); -}); - -test( "attr(String, Object) - Loaded via XML document", function() { - expect( 2 ); - var xml = createDashboardXML(); - var titles = []; - jQuery( "tab", xml ).each(function() { - titles.push( jQuery( this ).attr("title") ); - }); - equal( titles[ 0 ], "Location", "attr() in XML context: Check first title" ); - equal( titles[ 1 ], "Users", "attr() in XML context: Check second title" ); -}); - -test( "attr(String, Object) - Loaded via XML fragment", function() { - expect( 2 ); + jQuery( "#name" ).attr( "maxlength", "5" ).removeAttr( "nonexisting" ); + assert.equal( typeof jQuery( "#name" ).attr( "maxlength", undefined ), "object", ".attr('attribute', undefined) is chainable (#5571)" ); + assert.equal( jQuery( "#name" ).attr( "maxlength", undefined ).attr( "maxlength" ), "5", ".attr('attribute', undefined) does not change value (#5571)" ); + assert.equal( jQuery( "#name" ).attr( "nonexisting", undefined ).attr( "nonexisting" ), undefined, ".attr('attribute', undefined) does not create attribute (#5571)" ); +} ); + +QUnit.test( "attr - extending the boolean attrHandle", function( assert ) { + assert.expect( 1 ); + var called = false, + _handle = jQuery.expr.attrHandle.checked || $.noop; + jQuery.expr.attrHandle.checked = function() { + called = true; + _handle.apply( this, arguments ); + }; + jQuery( "input" ).attr( "checked" ); + called = false; + jQuery( "input" ).attr( "checked" ); + assert.ok( called, "The boolean attrHandle does not drop custom attrHandles" ); +} ); + +QUnit.test( "attr(String, Object) - Loaded via XML document", function( assert ) { + assert.expect( 2 ); + var xml = createDashboardXML(), + titles = []; + jQuery( "tab", xml ).each( function() { + titles.push( jQuery( this ).attr( "title" ) ); + } ); + assert.equal( titles[ 0 ], "Location", "attr() in XML context: Check first title" ); + assert.equal( titles[ 1 ], "Users", "attr() in XML context: Check second title" ); +} ); + +QUnit.test( "attr(String, Object) - Loaded via XML fragment", function( assert ) { + assert.expect( 2 ); var frag = createXMLFragment(), $frag = jQuery( frag ); $frag.attr( "test", "some value" ); - equal( $frag.attr("test"), "some value", "set attribute" ); + assert.equal( $frag.attr( "test" ), "some value", "set attribute" ); $frag.attr( "test", null ); - equal( $frag.attr("test"), undefined, "remove attribute" ); -}); + assert.equal( $frag.attr( "test" ), undefined, "remove attribute" ); +} ); -test( "attr('tabindex')", function() { - expect( 8 ); +QUnit.test( "attr('tabindex')", function( assert ) { + assert.expect( 8 ); // elements not natively tabbable - equal( jQuery("#listWithTabIndex").attr("tabindex"), "5", "not natively tabbable, with tabindex set to 0" ); - equal( jQuery("#divWithNoTabIndex").attr("tabindex"), undefined, "not natively tabbable, no tabindex set" ); + assert.equal( jQuery( "#listWithTabIndex" ).attr( "tabindex" ), "5", "not natively tabbable, with tabindex set to 0" ); + assert.equal( jQuery( "#divWithNoTabIndex" ).attr( "tabindex" ), undefined, "not natively tabbable, no tabindex set" ); // anchor with href - equal( jQuery("#linkWithNoTabIndex").attr("tabindex"), undefined, "anchor with href, no tabindex set" ); - equal( jQuery("#linkWithTabIndex").attr("tabindex"), "2", "anchor with href, tabindex set to 2" ); - equal( jQuery("#linkWithNegativeTabIndex").attr("tabindex"), "-1", "anchor with href, tabindex set to -1" ); + assert.equal( jQuery( "#linkWithNoTabIndex" ).attr( "tabindex" ), undefined, "anchor with href, no tabindex set" ); + assert.equal( jQuery( "#linkWithTabIndex" ).attr( "tabindex" ), "2", "anchor with href, tabindex set to 2" ); + assert.equal( jQuery( "#linkWithNegativeTabIndex" ).attr( "tabindex" ), "-1", "anchor with href, tabindex set to -1" ); // anchor without href - equal( jQuery("#linkWithNoHrefWithNoTabIndex").attr("tabindex"), undefined, "anchor without href, no tabindex set" ); - equal( jQuery("#linkWithNoHrefWithTabIndex").attr("tabindex"), "1", "anchor without href, tabindex set to 2" ); - equal( jQuery("#linkWithNoHrefWithNegativeTabIndex").attr("tabindex"), "-1", "anchor without href, no tabindex set" ); -}); + assert.equal( jQuery( "#linkWithNoHrefWithNoTabIndex" ).attr( "tabindex" ), undefined, "anchor without href, no tabindex set" ); + assert.equal( jQuery( "#linkWithNoHrefWithTabIndex" ).attr( "tabindex" ), "1", "anchor without href, tabindex set to 2" ); + assert.equal( jQuery( "#linkWithNoHrefWithNegativeTabIndex" ).attr( "tabindex" ), "-1", "anchor without href, no tabindex set" ); +} ); -test( "attr('tabindex', value)", function() { - expect( 9 ); +QUnit.test( "attr('tabindex', value)", function( assert ) { + assert.expect( 9 ); - var element = jQuery("#divWithNoTabIndex"); - equal( element.attr("tabindex"), undefined, "start with no tabindex" ); + var element = jQuery( "#divWithNoTabIndex" ); + assert.equal( element.attr( "tabindex" ), undefined, "start with no tabindex" ); // set a positive string element.attr( "tabindex", "1" ); - equal( element.attr("tabindex"), "1", "set tabindex to 1 (string)" ); + assert.equal( element.attr( "tabindex" ), "1", "set tabindex to 1 (string)" ); // set a zero string element.attr( "tabindex", "0" ); - equal( element.attr("tabindex"), "0", "set tabindex to 0 (string)" ); + assert.equal( element.attr( "tabindex" ), "0", "set tabindex to 0 (string)" ); // set a negative string element.attr( "tabindex", "-1" ); - equal( element.attr("tabindex"), "-1", "set tabindex to -1 (string)" ); + assert.equal( element.attr( "tabindex" ), "-1", "set tabindex to -1 (string)" ); // set a positive number element.attr( "tabindex", 1 ); - equal( element.attr("tabindex"), "1", "set tabindex to 1 (number)" ); + assert.equal( element.attr( "tabindex" ), "1", "set tabindex to 1 (number)" ); // set a zero number element.attr( "tabindex", 0 ); - equal(element.attr("tabindex"), "0", "set tabindex to 0 (number)"); + assert.equal( element.attr( "tabindex" ), "0", "set tabindex to 0 (number)" ); // set a negative number element.attr( "tabindex", -1 ); - equal( element.attr("tabindex"), "-1", "set tabindex to -1 (number)" ); + assert.equal( element.attr( "tabindex" ), "-1", "set tabindex to -1 (number)" ); - element = jQuery("#linkWithTabIndex"); - equal( element.attr("tabindex"), "2", "start with tabindex 2" ); + element = jQuery( "#linkWithTabIndex" ); + assert.equal( element.attr( "tabindex" ), "2", "start with tabindex 2" ); element.attr( "tabindex", -1 ); - equal( element.attr("tabindex"), "-1", "set negative tabindex" ); -}); + assert.equal( element.attr( "tabindex" ), "-1", "set negative tabindex" ); +} ); -test( "removeAttr(String)", function() { - expect( 12 ); +QUnit.test( "removeAttr(String)", function( assert ) { + assert.expect( 12 ); var $first; - equal( jQuery("#mark").removeAttr("class").attr("class"), undefined, "remove class" ); - equal( jQuery("#form").removeAttr("id").attr("id"), undefined, "Remove id" ); - equal( jQuery("#foo").attr( "style", "position:absolute;" ).removeAttr("style").attr("style"), undefined, "Check removing style attribute" ); - equal( jQuery("#form").attr( "style", "position:absolute;" ).removeAttr("style").attr("style"), undefined, "Check removing style attribute on a form" ); - equal( jQuery("
        ").appendTo("#foo").removeAttr("style").prop("style").cssText, "", "Check removing style attribute (#9699 Webkit)" ); - equal( jQuery("#fx-test-group").attr( "height", "3px" ).removeAttr("height").get( 0 ).style.height, "1px", "Removing height attribute has no effect on height set with style attribute" ); + assert.equal( jQuery( "#mark" ).removeAttr( "class" ).attr( "class" ), undefined, "remove class" ); + assert.equal( jQuery( "#form" ).removeAttr( "id" ).attr( "id" ), undefined, "Remove id" ); + assert.equal( jQuery( "#foo" ).attr( "style", "position:absolute;" ).removeAttr( "style" ).attr( "style" ), undefined, "Check removing style attribute" ); + assert.equal( jQuery( "#form" ).attr( "style", "position:absolute;" ).removeAttr( "style" ).attr( "style" ), undefined, "Check removing style attribute on a form" ); + assert.equal( jQuery( "
        " ).appendTo( "#foo" ).removeAttr( "style" ).prop( "style" ).cssText, "", "Check removing style attribute (#9699 Webkit)" ); + assert.equal( jQuery( "#fx-test-group" ).attr( "height", "3px" ).removeAttr( "height" ).get( 0 ).style.height, "1px", "Removing height attribute has no effect on height set with style attribute" ); - jQuery("#check1").removeAttr("checked").prop( "checked", true ).removeAttr("checked"); - equal( document.getElementById("check1").checked, false, "removeAttr sets boolean properties to false" ); - jQuery("#text1").prop( "readOnly", true ).removeAttr("readonly"); - equal( document.getElementById("text1").readOnly, false, "removeAttr sets boolean properties to false" ); + jQuery( "#check1" ).removeAttr( "checked" ).prop( "checked", true ).removeAttr( "checked" ); + assert.equal( document.getElementById( "check1" ).checked, false, "removeAttr sets boolean properties to false" ); + jQuery( "#text1" ).prop( "readOnly", true ).removeAttr( "readonly" ); + assert.equal( document.getElementById( "text1" ).readOnly, false, "removeAttr sets boolean properties to false" ); - jQuery("#option2c").removeAttr("selected"); - equal( jQuery("#option2d").attr("selected"), "selected", "Removing `selected` from an option that is not selected does not remove selected from the currently selected option (#10870)" ); + jQuery( "#option2c" ).removeAttr( "selected" ); + assert.equal( jQuery( "#option2d" ).attr( "selected" ), "selected", "Removing `selected` from an option that is not selected does not remove selected from the currently selected option (#10870)" ); try { - $first = jQuery("#first").attr( "contenteditable", "true" ).removeAttr("contenteditable"); - equal( $first.attr("contenteditable"), undefined, "Remove the contenteditable attribute" ); - } catch( e ) { - ok( false, "Removing contenteditable threw an error (#10429)" ); + $first = jQuery( "#first" ).attr( "contenteditable", "true" ).removeAttr( "contenteditable" ); + assert.equal( $first.attr( "contenteditable" ), undefined, "Remove the contenteditable attribute" ); + } catch ( e ) { + assert.ok( false, "Removing contenteditable threw an error (#10429)" ); } $first = jQuery("
        "); @@ -575,29 +599,29 @@ test( "removeAttr(String)", function() { ok( !$first.attr("Case"), "mixed-case attribute was removed" ); }); -test( "removeAttr(String) in XML", function() { - expect( 7 ); +QUnit.test( "removeAttr(String) in XML", function( assert ) { + assert.expect( 7 ); var xml = createDashboardXML(), iwt = jQuery( "infowindowtab", xml ); - equal( iwt.attr("normal"), "ab", "Check initial value" ); - iwt.removeAttr("Normal"); - equal( iwt.attr("normal"), "ab", "Should still be there" ); - iwt.removeAttr("normal"); - equal( iwt.attr("normal"), undefined, "Removed" ); - - equal( iwt.attr("mixedCase"), "yes", "Check initial value" ); - equal( iwt.attr("mixedcase"), undefined, "toLowerCase not work good" ); - iwt.removeAttr("mixedcase"); - equal( iwt.attr("mixedCase"), "yes", "Should still be there" ); - iwt.removeAttr("mixedCase"); - equal( iwt.attr("mixedCase"), undefined, "Removed" ); -}); + assert.equal( iwt.attr( "normal" ), "ab", "Check initial value" ); + iwt.removeAttr( "Normal" ); + assert.equal( iwt.attr( "normal" ), "ab", "Should still be there" ); + iwt.removeAttr( "normal" ); + assert.equal( iwt.attr( "normal" ), undefined, "Removed" ); + + assert.equal( iwt.attr( "mixedCase" ), "yes", "Check initial value" ); + assert.equal( iwt.attr( "mixedcase" ), undefined, "toLowerCase not work good" ); + iwt.removeAttr( "mixedcase" ); + assert.equal( iwt.attr( "mixedCase" ), "yes", "Should still be there" ); + iwt.removeAttr( "mixedCase" ); + assert.equal( iwt.attr( "mixedCase" ), undefined, "Removed" ); +} ); -test( "removeAttr(Multi String, variable space width)", function() { - expect( 8 ); +QUnit.test( "removeAttr(Multi String, variable space width)", function( assert ) { + assert.expect( 8 ); - var div = jQuery("
        "), + var div = jQuery( "
        " ), tests = { id: "a", alt: "b", @@ -606,727 +630,1017 @@ test( "removeAttr(Multi String, variable space width)", function() { }; jQuery.each( tests, function( key, val ) { - equal( div.attr( key ), val, "Attribute `" + key + "` exists, and has a value of `" + val + "`" ); - }); + assert.equal( div.attr( key ), val, "Attribute `" + key + "` exists, and has a value of `" + val + "`" ); + } ); div.removeAttr( "id alt title rel " ); - jQuery.each( tests, function( key, val ) { - equal( div.attr( key ), undefined, "Attribute `" + key + "` was removed" ); - }); -}); - -test( "prop(String, Object)", function() { - expect( 31 ); - - equal( jQuery("#text1").prop("value"), "Test", "Check for value attribute" ); - equal( jQuery("#text1").prop( "value", "Test2" ).prop("defaultValue"), "Test", "Check for defaultValue attribute" ); - equal( jQuery("#select2").prop("selectedIndex"), 3, "Check for selectedIndex attribute" ); - equal( jQuery("#foo").prop("nodeName").toUpperCase(), "DIV", "Check for nodeName attribute" ); - equal( jQuery("#foo").prop("tagName").toUpperCase(), "DIV", "Check for tagName attribute" ); - equal( jQuery("").val(), + assert.strictEqual( + jQuery( "" ).val(), null, "Select-one with only option disabled (#12584)" ); if ( jQuery.fn.serialize ) { - var checks = jQuery("").appendTo("#form"); + checks = jQuery( "" ).appendTo( "#form" ); - deepEqual( checks.serialize(), "", "Get unchecked values." ); + assert.deepEqual( checks.serialize(), "", "Get unchecked values." ); - equal( checks.eq( 3 ).val(), "on", "Make sure a value of 'on' is provided if none is specified." ); + assert.equal( checks.eq( 3 ).val(), "on", "Make sure a value of 'on' is provided if none is specified." ); - checks.val([ "2" ]); - deepEqual( checks.serialize(), "test=2", "Get a single checked value." ); + checks.val( [ "2" ] ); + assert.deepEqual( checks.serialize(), "test=2", "Get a single checked value." ); - checks.val([ "1", "" ]); - deepEqual( checks.serialize(), "test=1&test=", "Get multiple checked values." ); + checks.val( [ "1", "" ] ); + assert.deepEqual( checks.serialize(), "test=1&test=", "Get multiple checked values." ); - checks.val([ "", "2" ]); - deepEqual( checks.serialize(), "test=2&test=", "Get multiple checked values." ); + checks.val( [ "", "2" ] ); + assert.deepEqual( checks.serialize(), "test=2&test=", "Get multiple checked values." ); - checks.val([ "1", "on" ]); - deepEqual( checks.serialize(), "test=1&test=on", "Get multiple checked values." ); + checks.val( [ "1", "on" ] ); + assert.deepEqual( checks.serialize(), "test=1&test=on", "Get multiple checked values." ); checks.remove(); } - var $button = jQuery("").insertAfter("#button"); - equal( $button.val(), "foobar", "Value retrieval on a button does not return innerHTML" ); - equal( $button.val("baz").html(), "text", "Setting the value does not change innerHTML" ); + $button = jQuery( "" ).insertAfter( "#button" ); + assert.equal( $button.val(), "foobar", "Value retrieval on a button does not return innerHTML" ); + assert.equal( $button.val( "baz" ).html(), "text", "Setting the value does not change innerHTML" ); - equal( jQuery(""); + $select1.append( "" ); $select1.val( valueObj( 4 ) ); - equal( $select1.val(), "4", "Should be possible to set the val() to a newly created option" ); + assert.equal( $select1.val(), "4", "Should be possible to set the val() to a newly created option" ); // using contents will get comments regular, text, and comment nodes - var j = jQuery("#nonnodes").contents(); + j = jQuery( "#nonnodes" ).contents(); j.val( valueObj( "asdf" ) ); - equal( j.val(), "asdf", "Check node,textnode,comment with val()" ); - j.removeAttr("value"); + assert.equal( j.val(), "asdf", "Check node,textnode,comment with val()" ); + j.removeAttr( "value" ); + + $select.val( valueObj( [ "1", "2" ] ) ); + assert.deepEqual( $select.val(), [ "1", "2" ], "Should set array of values" ); }; -test( "val(String/Number)", function() { - testVal( bareObj ); -}); +QUnit.test( "val(String/Number)", function( assert ) { + testVal( bareObj, assert ); +} ); -test( "val(Function)", function() { - testVal( functionReturningObj ); -}); +QUnit.test( "val(Function)", function( assert ) { + testVal( functionReturningObj, assert ); +} ); -test( "val(Array of Numbers) (Bug #7123)", function() { - expect( 4 ); - jQuery("#form").append(""); - var elements = jQuery("input[name=arrayTest]").val([ 1, 2 ]); - ok( elements[ 0 ].checked, "First element was checked" ); - ok( elements[ 1 ].checked, "Second element was checked" ); - ok( !elements[ 2 ].checked, "Third element was unchecked" ); - ok( !elements[ 3 ].checked, "Fourth element remained unchecked" ); +QUnit.test( "val(Array of Numbers) (Bug #7123)", function( assert ) { + assert.expect( 4 ); + jQuery( "#form" ).append( "" ); + var elements = jQuery( "input[name=arrayTest]" ).val( [ 1, 2 ] ); + assert.ok( elements[ 0 ].checked, "First element was checked" ); + assert.ok( elements[ 1 ].checked, "Second element was checked" ); + assert.ok( !elements[ 2 ].checked, "Third element was unchecked" ); + assert.ok( !elements[ 3 ].checked, "Fourth element remained unchecked" ); elements.remove(); -}); +} ); -test( "val(Function) with incoming value", function() { - expect( 10 ); +QUnit.test( "val(Function) with incoming value", function( assert ) { + assert.expect( 10 ); - QUnit.reset(); - var oldVal = jQuery("#text1").val(); + var oldVal = jQuery( "#text1" ).val(); - jQuery("#text1").val(function( i, val ) { - equal( val, oldVal, "Make sure the incoming value is correct." ); + jQuery( "#text1" ).val( function( i, val ) { + assert.equal( val, oldVal, "Make sure the incoming value is correct." ); return "test"; - }); + } ); - equal( document.getElementById("text1").value, "test", "Check for modified (via val(String)) value of input element" ); + assert.equal( document.getElementById( "text1" ).value, "test", "Check for modified (via val(String)) value of input element" ); - oldVal = jQuery("#text1").val(); + oldVal = jQuery( "#text1" ).val(); - jQuery("#text1").val(function( i, val ) { - equal( val, oldVal, "Make sure the incoming value is correct." ); + jQuery( "#text1" ).val( function( i, val ) { + assert.equal( val, oldVal, "Make sure the incoming value is correct." ); return 67; - }); + } ); - equal( document.getElementById("text1").value, "67", "Check for modified (via val(Number)) value of input element" ); + assert.equal( document.getElementById( "text1" ).value, "67", "Check for modified (via val(Number)) value of input element" ); - oldVal = jQuery("#select1").val(); + oldVal = jQuery( "#select1" ).val(); - jQuery("#select1").val(function( i, val ) { - equal( val, oldVal, "Make sure the incoming value is correct." ); + jQuery( "#select1" ).val( function( i, val ) { + assert.equal( val, oldVal, "Make sure the incoming value is correct." ); return "3"; - }); + } ); - equal( jQuery("#select1").val(), "3", "Check for modified (via val(String)) value of select element" ); + assert.equal( jQuery( "#select1" ).val(), "3", "Check for modified (via val(String)) value of select element" ); - oldVal = jQuery("#select1").val(); + oldVal = jQuery( "#select1" ).val(); - jQuery("#select1").val(function( i, val ) { - equal( val, oldVal, "Make sure the incoming value is correct." ); + jQuery( "#select1" ).val( function( i, val ) { + assert.equal( val, oldVal, "Make sure the incoming value is correct." ); return 2; - }); + } ); - equal( jQuery("#select1").val(), "2", "Check for modified (via val(Number)) value of select element" ); + assert.equal( jQuery( "#select1" ).val(), "2", "Check for modified (via val(Number)) value of select element" ); - jQuery("#select1").append(""); + jQuery( "#select1" ).append( "" ); - oldVal = jQuery("#select1").val(); + oldVal = jQuery( "#select1" ).val(); - jQuery("#select1").val(function( i, val ) { - equal( val, oldVal, "Make sure the incoming value is correct." ); + jQuery( "#select1" ).val( function( i, val ) { + assert.equal( val, oldVal, "Make sure the incoming value is correct." ); return 4; - }); + } ); - equal( jQuery("#select1").val(), "4", "Should be possible to set the val() to a newly created option" ); -}); + assert.equal( jQuery( "#select1" ).val(), "4", "Should be possible to set the val() to a newly created option" ); +} ); // testing if a form.reset() breaks a subsequent call to a select element's .val() (in IE only) -test( "val(select) after form.reset() (Bug #2551)", function() { - expect( 3 ); +QUnit.test( "val(select) after form.reset() (Bug #2551)", function( assert ) { + assert.expect( 3 ); - jQuery("").appendTo("#qunit-fixture"); + jQuery( "" ).appendTo( "#qunit-fixture" ); - jQuery("#kkk").val("gf"); + jQuery( "#kkk" ).val( "gf" ); - document["kk"].reset(); + document[ "kk" ].reset(); - equal( jQuery("#kkk")[ 0 ].value, "cf", "Check value of select after form reset." ); - equal( jQuery("#kkk").val(), "cf", "Check value of select after form reset." ); + assert.equal( jQuery( "#kkk" )[ 0 ].value, "cf", "Check value of select after form reset." ); + assert.equal( jQuery( "#kkk" ).val(), "cf", "Check value of select after form reset." ); // re-verify the multi-select is not broken (after form.reset) by our fix for single-select - deepEqual( jQuery("#select3").val(), ["1", "2"], "Call val() on a multiple='multiple' select" ); + assert.deepEqual( jQuery( "#select3" ).val(), [ "1", "2" ], "Call val() on a multiple='multiple' select" ); + + jQuery( "#kk" ).remove(); +} ); + +QUnit.test( "select.val(space characters) (gh-2978)", function( assert ) { + assert.expect( 35 ); + + var $select = jQuery( "" ).val( "2" ).val(), "2" ); +} ); + +QUnit.test( "Insignificant white space returned for $(option).val() (#14858, gh-2978)", function( assert ) { + assert.expect( 16 ); + + var val = jQuery( "" ).val(); + assert.equal( val.length, 0, "Empty option should have no value" ); + + jQuery.each( [ " ", "\n", "\t", "\f", "\r" ], function( i, character ) { + var val = jQuery( "" ).val(); + assert.equal( val.length, 0, "insignificant white-space returned for value" ); + + val = jQuery( "" ).val(); + assert.equal( val.length, 4, "insignificant white-space returned for value" ); + + val = jQuery( "" ).val(); + assert.equal( val, "te st", "Whitespace is collapsed in values" ); + } ); +} ); + +QUnit.test( "SVG class manipulation (gh-2199)", function( assert ) { + + // Support: IE8 + var svgSupport = !!document.createElementNS && + !!document.createElementNS( "http://www.w3.org/2000/svg", "svg" ).createSVGRect; + + if ( !svgSupport ) { + assert.expect( 1 ); + assert.ok( true, "Environment doesn't support SVG" ); + + return; + } + + assert.expect( 12 ); + + function createSVGElement( nodeName ) { + return document.createElementNS( "http://www.w3.org/2000/svg", nodeName ); + } + + jQuery.each( [ + "svg", + "rect", + "g" + ], function() { + var elem = jQuery( createSVGElement( this ) ); + + elem.addClass( "awesome" ); + assert.ok( elem.hasClass( "awesome" ), "SVG element (" + this + ") has added class" ); + + elem.removeClass( "awesome" ); + assert.ok( !elem.hasClass( "awesome" ), "SVG element (" + this + ") removes the class" ); + + elem.toggleClass( "awesome" ); + assert.ok( elem.hasClass( "awesome" ), "SVG element (" + this + ") toggles the class on" ); + + elem.toggleClass( "awesome" ); + assert.ok( !elem.hasClass( "awesome" ), "SVG element (" + this + ") toggles the class off" ); + } ); +} ); diff --git a/test/unit/basic.js b/test/unit/basic.js new file mode 100644 index 0000000000..a762a8b4f1 --- /dev/null +++ b/test/unit/basic.js @@ -0,0 +1,304 @@ +QUnit.module( "basic", { teardown: moduleTeardown } ); + +if ( jQuery.ajax ) { +QUnit.test( "ajax", function( assert ) { + assert.expect( 4 ); + + var done = jQuery.map( new Array( 3 ), function() { return assert.async(); } ); + + jQuery.ajax( { + type: "GET", + url: url( "data/name.php?name=foo" ), + success: function( msg ) { + assert.strictEqual( msg, "bar", "Check for GET" ); + done.pop()(); + } + } ); + + jQuery.ajax( { + type: "POST", + url: url( "data/name.php" ), + data: "name=peter", + success: function( msg ) { + assert.strictEqual( msg, "pan", "Check for POST" ); + done.pop()(); + } + } ); + + jQuery( "#first" ).load( url( "data/name.html" ), function() { + assert.ok( /^ERROR/.test( jQuery( "#first" ).text() ), + "Check if content was injected into the DOM" ); + done.pop()(); + } ); +} ); +} + +QUnit.test( "attributes", function( assert ) { + assert.expect( 6 ); + + var a = jQuery( "" ).appendTo( "#qunit-fixture" ), + input = jQuery( "" ).appendTo( "#qunit-fixture" ); + + assert.strictEqual( a.attr( "foo", "bar" ).attr( "foo" ), "bar", ".attr getter/setter" ); + assert.strictEqual( a.removeAttr( "foo" ).attr( "foo" ), undefined, ".removeAttr" ); + assert.strictEqual( a.prop( "href", "#5" ).prop( "href" ), + location.href.replace( /\#.*$/, "" ) + "#5", + ".prop getter/setter" ); + + a.addClass( "abc def ghj" ).removeClass( "def ghj" ); + assert.strictEqual( a.hasClass( "abc" ), true, ".(add|remove|has)Class, class present" ); + assert.strictEqual( a.hasClass( "def" ), false, ".(add|remove|has)Class, class missing" ); + + assert.strictEqual( input.val( "xyz" ).val(), "xyz", ".val getter/setter" ); +} ); + +if ( jQuery.css ) { +QUnit.test( "css", function( assert ) { + assert.expect( 3 ); + + var div = jQuery( "
        " ).appendTo( "#qunit-fixture" ); + + assert.strictEqual( div.css( "width", "50px" ).css( "width" ), "50px", ".css getter/setter" ); + + div.hide(); + assert.strictEqual( div.css( "display" ), "none", "div hidden" ); + div.show(); + assert.strictEqual( div.css( "display" ), "block", "div shown" ); +} ); +} + +QUnit.test( "core", function( assert ) { + assert.expect( 28 ); + + var elem = jQuery( "
        " ); + + assert.strictEqual( elem.length, 2, "Correct number of elements" ); + assert.strictEqual( jQuery.trim( " hello " ), "hello", "jQuery.trim" ); + + assert.strictEqual( jQuery.type( null ), "null", "jQuery.type(null)" ); + assert.strictEqual( jQuery.type( undefined ), "undefined", "jQuery.type(undefined)" ); + assert.strictEqual( jQuery.type( "a" ), "string", "jQuery.type(String)" ); + + assert.ok( jQuery.isPlainObject( { "a": 2 } ), "jQuery.isPlainObject(object)" ); + assert.ok( !jQuery.isPlainObject( "foo" ), "jQuery.isPlainObject(String)" ); + + assert.ok( jQuery.isFunction( jQuery.noop ), "jQuery.isFunction(jQuery.noop)" ); + assert.ok( !jQuery.isFunction( 2 ), "jQuery.isFunction(Number)" ); + + assert.ok( jQuery.isNumeric( "-2" ), "jQuery.isNumeric(String representing a number)" ); + assert.ok( !jQuery.isNumeric( "" ), "jQuery.isNumeric(\"\")" ); + + assert.ok( jQuery.isXMLDoc( jQuery.parseXML( + "" + ) ), "jQuery.isXMLDoc" ); + + assert.ok( jQuery.isWindow( window ), "jQuery.isWindow(window)" ); + assert.ok( !jQuery.isWindow( 2 ), "jQuery.isWindow(Number)" ); + + assert.strictEqual( jQuery.inArray( 3, [ "a", 6, false, 3, {} ] ), 3, "jQuery.inArray - true" ); + assert.strictEqual( + jQuery.inArray( 3, [ "a", 6, false, "3", {} ] ), + -1, + "jQuery.inArray - false" + ); + + assert.strictEqual( elem.get( 1 ), elem[ 1 ], ".get" ); + assert.strictEqual( elem.first()[ 0 ], elem[ 0 ], ".first" ); + assert.strictEqual( elem.last()[ 0 ], elem[ 1 ], ".last" ); + + assert.deepEqual( jQuery.map( [ "a", "b", "c" ], function( v, k ) { + return k + v; + } ), [ "0a", "1b", "2c" ], "jQuery.map" ); + + assert.deepEqual( jQuery.merge( [ 1, 2 ], [ "a", "b" ] ), [ 1, 2, "a", "b" ], "jQuery.merge" ); + + assert.deepEqual( jQuery.grep( [ 1, 2, 3 ], function( value ) { + return value % 2 !== 0; + } ), [ 1, 3 ], "jQuery.grep" ); + + assert.deepEqual( jQuery.extend( { a: 2 }, { b: 3 } ), { a: 2, b: 3 }, "jQuery.extend" ); + + jQuery.each( [ 0, 2 ], function( k, v ) { + assert.strictEqual( k * 2, v, "jQuery.each" ); + } ); + + assert.deepEqual( jQuery.makeArray( { 0: "a", 1: "b", 2: "c", length: 3 } ), + [ "a", "b", "c" ], "jQuery.makeArray" ); + + assert.strictEqual( jQuery.parseHTML( "
        " ).length, + 2, "jQuery.parseHTML" ); + + assert.deepEqual( jQuery.parseJSON( "{\"a\": 2}" ), { a: 2 }, "jQuery.parseJON" ); +} ); + +QUnit.test( "data", function( assert ) { + assert.expect( 4 ); + + var elem = jQuery( "
        " ).appendTo( "#qunit-fixture" ); + + assert.ok( !jQuery.hasData( elem[ 0 ] ), "jQuery.hasData - false" ); + assert.strictEqual( elem.data( "a", "b" ).data( "a" ), "b", ".data getter/setter" ); + assert.strictEqual( elem.data( "c" ), "d", ".data from data-* attributes" ); + assert.ok( jQuery.hasData( elem[ 0 ] ), "jQuery.hasData - true" ); +} ); + +QUnit.test( "dimensions", function( assert ) { + assert.expect( 3 ); + + var elem = jQuery( + "
        " + ).appendTo( "#qunit-fixture" ); + + assert.strictEqual( elem.width( 50 ).width(), 50, ".width getter/setter" ); + assert.strictEqual( elem.innerWidth(), 64, ".innerWidth getter" ); + assert.strictEqual( elem.outerWidth(), 68, ".outerWidth getter" ); +} ); + +QUnit.test( "event", function( assert ) { + assert.expect( 1 ); + + var elem = jQuery( "
        " ).appendTo( "#qunit-fixture" ); + + elem + .on( "click", function() { + assert.ok( false, "click should not fire" ); + } ) + .off( "click" ) + .trigger( "click" ) + .on( "click", function() { + assert.ok( true, "click should fire" ); + } ) + .trigger( "click" ); +} ); + +QUnit.test( "manipulation", function( assert ) { + assert.expect( 5 ); + + var child, + elem1 = jQuery( "
        " ).appendTo( "#qunit-fixture" ), + elem2 = jQuery( "
        " ).appendTo( "#qunit-fixture" ); + + assert.strictEqual( elem1.text( "foo" ).text(), "foo", ".html getter/setter" ); + + assert.strictEqual( + + // Support: IE 8 only + // IE 8 prints tag names in upper case. + elem1.html( "" ).html().toLowerCase(), + "", + ".html getter/setter" + ); + + assert.strictEqual( elem1.append( elem2 )[ 0 ].childNodes[ 1 ], elem2[ 0 ], ".append" ); + assert.strictEqual( elem1.prepend( elem2 )[ 0 ].childNodes[ 0 ], elem2[ 0 ], ".prepend" ); + + child = elem1.find( "span" ); + child.after( "" ); + child.before( "" ); + + assert.strictEqual( + + // Support: IE 8 only + // IE 8 prints tag names in upper case. + elem1.html().toLowerCase(), + "
        ", + ".after/.before" + ); +} ); + +QUnit.test( "offset", function( assert ) { + assert.expect( 3 ); + + var parent = + jQuery( "
        " ) + .appendTo( "body" ), + elem = jQuery( "
        " ).appendTo( parent ); + + assert.strictEqual( elem.offset().top, 25, ".offset getter" ); + assert.strictEqual( elem.position().top, 5, ".position getter" ); + assert.strictEqual( elem.offsetParent()[ 0 ], parent[ 0 ], ".offsetParent" ); +} ); + +QUnit.test( "selector", function( assert ) { + assert.expect( 2 ); + + var elem = jQuery( "
        " ) + .appendTo( "#qunit-fixture" ); + + assert.strictEqual( elem.find( ".a a" ).length, 0, ".find - no result" ); + assert.strictEqual( elem.find( "span.b a" )[ 0 ].nodeName, "A", ".find - one result" ); +} ); + +QUnit.test( "serialize", function( assert ) { + assert.expect( 2 ); + + var params = { "someName": [ 1, 2, 3 ], "regularThing": "blah" }; + assert.strictEqual( jQuery.param( params ), + "someName%5B%5D=1&someName%5B%5D=2&someName%5B%5D=3®ularThing=blah", + "jQuery.param" ); + + assert.strictEqual( jQuery( "#form" ).serialize(), + "action=Test&radio2=on&check=on&hidden=&foo%5Bbar%5D=&name=name&search=search" + + "&select1=&select2=3&select3=1&select3=2&select5=3", + "form serialization as query string" ); +} ); + +QUnit.test( "traversing", function( assert ) { + assert.expect( 12 ); + + var elem = jQuery( "
        foo
        " ) + .appendTo( "#qunit-fixture" ); + + assert.strictEqual( elem.find( "em" ).parent()[ 0 ].nodeName, "B", ".parent" ); + assert.strictEqual( elem.find( "em" ).parents()[ 1 ].nodeName, "A", ".parents" ); + assert.strictEqual( elem.find( "em" ).parentsUntil( "div" ).length, 2, ".parentsUntil" ); + assert.strictEqual( elem.find( "i" ).next()[ 0 ].nodeName, "SPAN", ".next" ); + assert.strictEqual( elem.find( "i" ).prev()[ 0 ].nodeName, "A", ".prev" ); + assert.strictEqual( elem.find( "a" ).nextAll()[ 1 ].nodeName, "SPAN", ".nextAll" ); + assert.strictEqual( elem.find( "span" ).prevAll()[ 1 ].nodeName, "A", ".prevAll" ); + assert.strictEqual( elem.find( "a" ).nextUntil( "span" ).length, 1, ".nextUntil" ); + assert.strictEqual( elem.find( "span" ).prevUntil( "a" ).length, 1, ".prevUntil" ); + assert.strictEqual( elem.find( "i" ).siblings().length, 2, ".siblings" ); + assert.strictEqual( elem.children()[ 2 ].nodeName, "SPAN", ".children" ); + assert.strictEqual( elem.contents()[ 3 ].nodeType, 3, ".contents" ); +} ); + +QUnit.test( "wrap", function( assert ) { + assert.expect( 3 ); + + var elem = jQuery( "
        " ); + + elem.find( "b" ).wrap( "" ); + + assert.strictEqual( + + // Support: IE 8 only + // IE 8 prints tag names in upper case. + elem.html().toLowerCase(), + "", + ".wrap" + ); + + elem.find( "span" ).wrapInner( "" ); + + assert.strictEqual( + + // Support: IE 8 only + // IE 8 prints tag names in upper case. + elem.html().toLowerCase(), + "", + ".wrapInner" + ); + + elem.find( "a" ).wrapAll( "" ); + + assert.strictEqual( + + // Support: IE 8 only + // IE 8 prints tag names in upper case. + elem.html().toLowerCase(), + "", + ".wrapAll" + ); + +} ); diff --git a/test/unit/callbacks.js b/test/unit/callbacks.js index bd61207ae3..f2e07f9273 100644 --- a/test/unit/callbacks.js +++ b/test/unit/callbacks.js @@ -1,8 +1,8 @@ -module( "callbacks", { +QUnit.module( "callbacks", { teardown: moduleTeardown -}); +} ); -(function() { +( function() { var output, addToOutput = function( string ) { @@ -10,9 +10,9 @@ var output, output += string; }; }, - outputA = addToOutput("A"), - outputB = addToOutput("B"), - outputC = addToOutput("C"), + outputA = addToOutput( "A" ), + outputB = addToOutput( "B" ), + outputC = addToOutput( "C" ), tests = { "": "XABC X XABCABCC X XBB X XABA X XX", "once": "XABC X X X X X XABA X XX", @@ -54,22 +54,18 @@ jQuery.each( tests, function( strFlags, resultString ) { if ( this.length ) { objectFlags[ this ] = true; } - }); + } ); - jQuery.each( filters, function( filterLabel, filter ) { + jQuery.each( filters, function( filterLabel ) { - jQuery.each({ + jQuery.each( { "string": strFlags, "object": objectFlags }, function( flagsTypes, flags ) { - test( "jQuery.Callbacks( " + showFlags( flags ) + " ) - " + filterLabel, function() { + QUnit.test( "jQuery.Callbacks( " + showFlags( flags ) + " ) - " + filterLabel, function( assert ) { - expect( 21 ); - - // Give qunit a little breathing room - stop(); - setTimeout( start, 0 ); + expect( 28 ); var cblist, results = resultString.split( /\s+/ ); @@ -77,38 +73,60 @@ jQuery.each( tests, function( strFlags, resultString ) { // Basic binding and firing output = "X"; cblist = jQuery.Callbacks( flags ); - cblist.add(function( str ) { + assert.strictEqual( cblist.locked(), false, ".locked() initially false" ); + assert.strictEqual( cblist.disabled(), false, ".disabled() initially false" ); + assert.strictEqual( cblist.fired(), false, ".fired() initially false" ); + cblist.add( function( str ) { output += str; - }); - cblist.fire("A"); - strictEqual( output, "XA", "Basic binding and firing" ); - strictEqual( cblist.fired(), true, ".fired() detects firing" ); + } ); + assert.strictEqual( cblist.fired(), false, ".fired() still false after .add" ); + cblist.fire( "A" ); + assert.strictEqual( output, "XA", "Basic binding and firing" ); + assert.strictEqual( cblist.fired(), true, ".fired() detects firing" ); output = "X"; cblist.disable(); - cblist.add(function( str ) { + cblist.add( function( str ) { output += str; - }); - strictEqual( output, "X", "Adding a callback after disabling" ); - cblist.fire("A"); - strictEqual( output, "X", "Firing after disabling" ); + } ); + assert.strictEqual( output, "X", "Adding a callback after disabling" ); + cblist.fire( "A" ); + assert.strictEqual( output, "X", "Firing after disabling" ); + assert.strictEqual( cblist.disabled(), true, ".disabled() becomes true" ); + assert.strictEqual( cblist.locked(), true, "disabling locks" ); + + // #13517 - Emptying while firing + cblist = jQuery.Callbacks( flags ); + cblist.add( cblist.empty ); + cblist.add( function() { + assert.ok( false, "not emptied" ); + } ); + cblist.fire(); + + // Disabling while firing + cblist = jQuery.Callbacks( flags ); + cblist.add( cblist.disable ); + cblist.add( function() { + assert.ok( false, "not disabled" ); + } ); + cblist.fire(); // Basic binding and firing (context, arguments) output = "X"; cblist = jQuery.Callbacks( flags ); - cblist.add(function() { - equal( this, window, "Basic binding and firing (context)" ); + cblist.add( function() { + assert.equal( this, window, "Basic binding and firing (context)" ); output += Array.prototype.join.call( arguments, "" ); - }); + } ); cblist.fireWith( window, [ "A", "B" ] ); - strictEqual( output, "XAB", "Basic binding and firing (arguments)" ); + assert.strictEqual( output, "XAB", "Basic binding and firing (arguments)" ); // fireWith with no arguments output = ""; cblist = jQuery.Callbacks( flags ); - cblist.add(function() { - equal( this, window, "fireWith with no arguments (context is window)" ); - strictEqual( arguments.length, 0, "fireWith with no arguments (no arguments)" ); - }); + cblist.add( function() { + assert.equal( this, window, "fireWith with no arguments (context is window)" ); + assert.strictEqual( arguments.length, 0, "fireWith with no arguments (no arguments)" ); + } ); cblist.fireWith(); // Basic binding, removing and firing @@ -117,7 +135,7 @@ jQuery.each( tests, function( strFlags, resultString ) { cblist.add( outputA, outputB, outputC ); cblist.remove( outputB, outputC ); cblist.fire(); - strictEqual( output, "XA", "Basic binding, removing and firing" ); + assert.strictEqual( output, "XA", "Basic binding, removing and firing" ); // Empty output = "X"; @@ -127,67 +145,68 @@ jQuery.each( tests, function( strFlags, resultString ) { cblist.add( outputC ); cblist.empty(); cblist.fire(); - strictEqual( output, "X", "Empty" ); + assert.strictEqual( output, "X", "Empty" ); // Locking output = "X"; cblist = jQuery.Callbacks( flags ); - cblist.add(function( str ) { + cblist.add( function( str ) { output += str; - }); + } ); cblist.lock(); - cblist.add(function( str ) { + cblist.add( function( str ) { output += str; - }); - cblist.fire("A"); - cblist.add(function( str ) { + } ); + cblist.fire( "A" ); + cblist.add( function( str ) { output += str; - }); - strictEqual( output, "X", "Lock early" ); + } ); + assert.strictEqual( output, "X", "Lock early" ); + assert.strictEqual( cblist.locked(), true, "Locking reflected in accessor" ); // Ordering output = "X"; cblist = jQuery.Callbacks( flags ); - cblist.add(function() { + cblist.add( function() { cblist.add( outputC ); outputA(); }, outputB ); cblist.fire(); - strictEqual( output, results.shift(), "Proper ordering" ); + assert.strictEqual( output, results.shift(), "Proper ordering" ); // Add and fire again output = "X"; - cblist.add(function() { + cblist.add( function() { cblist.add( outputC ); outputA(); }, outputB ); - strictEqual( output, results.shift(), "Add after fire" ); + assert.strictEqual( output, results.shift(), "Add after fire" ); output = "X"; cblist.fire(); - strictEqual( output, results.shift(), "Fire again" ); + assert.strictEqual( output, results.shift(), "Fire again" ); // Multiple fire output = "X"; cblist = jQuery.Callbacks( flags ); - cblist.add(function( str ) { + cblist.add( function( str ) { output += str; - }); - cblist.fire("A"); - strictEqual( output, "XA", "Multiple fire (first fire)" ); + } ); + cblist.fire( "A" ); + assert.strictEqual( output, "XA", "Multiple fire (first fire)" ); output = "X"; - cblist.add(function( str ) { + cblist.add( function( str ) { output += str; - }); - strictEqual( output, results.shift(), "Multiple fire (first new callback)" ); + } ); + assert.strictEqual( output, results.shift(), "Multiple fire (first new callback)" ); output = "X"; - cblist.fire("B"); - strictEqual( output, results.shift(), "Multiple fire (second fire)" ); + cblist.fire( "B" ); + assert.strictEqual( output, results.shift(), "Multiple fire (second fire)" ); output = "X"; - cblist.add(function( str ) { + cblist.add( function( str ) { output += str; - }); - strictEqual( output, results.shift(), "Multiple fire (second new callback)" ); + } ); + assert.strictEqual( output, results.shift(), "Multiple fire (second new callback)" ); // Return false output = "X"; @@ -195,16 +214,16 @@ jQuery.each( tests, function( strFlags, resultString ) { cblist.add( outputA, function() { return false; }, outputB ); cblist.add( outputA ); cblist.fire(); - strictEqual( output, results.shift(), "Callback returning false" ); + assert.strictEqual( output, results.shift(), "Callback returning false" ); // Add another callback (to control lists with memory do not fire anymore) output = "X"; cblist.add( outputC ); - strictEqual( output, results.shift(), "Adding a callback after one returned false" ); + assert.strictEqual( output, results.shift(), "Adding a callback after one returned false" ); // Callbacks are not iterated output = ""; - function handler( tmp ) { + function handler() { output += "X"; } handler.method = function() { @@ -214,17 +233,17 @@ jQuery.each( tests, function( strFlags, resultString ) { cblist.add( handler ); cblist.add( handler ); cblist.fire(); - strictEqual( output, results.shift(), "No callback iteration" ); - }); - }); - }); -}); + assert.strictEqual( output, results.shift(), "No callback iteration" ); + } ); + } ); + } ); +} ); -})(); +} )(); -test( "jQuery.Callbacks( options ) - options are copied", function() { +QUnit.test( "jQuery.Callbacks( options ) - options are copied", function( assert ) { - expect( 1 ); + assert.expect( 1 ); var options = { "unique": true @@ -232,48 +251,108 @@ test( "jQuery.Callbacks( options ) - options are copied", function() { cb = jQuery.Callbacks( options ), count = 0, fn = function() { - ok( !( count++ ), "called once" ); + assert.ok( !( count++ ), "called once" ); }; - options["unique"] = false; + options[ "unique" ] = false; cb.add( fn, fn ); cb.fire(); -}); +} ); -test( "jQuery.Callbacks.fireWith - arguments are copied", function() { +QUnit.test( "jQuery.Callbacks.fireWith - arguments are copied", function( assert ) { - expect( 1 ); + assert.expect( 1 ); - var cb = jQuery.Callbacks("memory"), - args = ["hello"]; + var cb = jQuery.Callbacks( "memory" ), + args = [ "hello" ]; cb.fireWith( null, args ); args[ 0 ] = "world"; - cb.add(function( hello ) { - strictEqual( hello, "hello", "arguments are copied internally" ); - }); -}); + cb.add( function( hello ) { + assert.strictEqual( hello, "hello", "arguments are copied internally" ); + } ); +} ); -test( "jQuery.Callbacks.remove - should remove all instances", function() { +QUnit.test( "jQuery.Callbacks.remove - should remove all instances", function( assert ) { - expect( 1 ); + assert.expect( 1 ); var cb = jQuery.Callbacks(); function fn() { - ok( false, "function wasn't removed" ); + assert.ok( false, "function wasn't removed" ); } cb.add( fn, fn, function() { - ok( true, "end of test" ); - }).remove( fn ).fire(); -}); + assert.ok( true, "end of test" ); + } ).remove( fn ).fire(); +} ); + +QUnit.test( "jQuery.Callbacks.has", function( assert ) { + + assert.expect( 13 ); + + var cb = jQuery.Callbacks(); + function getA() { + return "A"; + } + function getB() { + return "B"; + } + function getC() { + return "C"; + } + cb.add( getA, getB, getC ); + assert.strictEqual( cb.has(), true, "No arguments to .has() returns whether callback function(s) are attached or not" ); + assert.strictEqual( cb.has( getA ), true, "Check if a specific callback function is in the Callbacks list" ); + + cb.remove( getB ); + assert.strictEqual( cb.has( getB ), false, "Remove a specific callback function and make sure its no longer there" ); + assert.strictEqual( cb.has( getA ), true, "Remove a specific callback function and make sure other callback function is still there" ); + + cb.empty(); + assert.strictEqual( cb.has(), false, "Empty list and make sure there are no callback function(s)" ); + assert.strictEqual( cb.has( getA ), false, "Check for a specific function in an empty() list" ); + + cb.add( getA, getB, function() { + assert.strictEqual( cb.has(), true, "Check if list has callback function(s) from within a callback function" ); + assert.strictEqual( cb.has( getA ), true, "Check if list has a specific callback from within a callback function" ); + } ).fire(); + + assert.strictEqual( cb.has(), true, "Callbacks list has callback function(s) after firing" ); + + cb.disable(); + assert.strictEqual( cb.has(), false, "disabled() list has no callback functions (returns false)" ); + assert.strictEqual( cb.has( getA ), false, "Check for a specific function in a disabled() list" ); -test( "jQuery.Callbacks() - adding a string doesn't cause a stack overflow", function() { + cb = jQuery.Callbacks( "unique" ); + cb.add( getA ); + cb.add( getA ); + assert.strictEqual( cb.has(), true, "Check if unique list has callback function(s) attached" ); + cb.lock(); + strictEqual( cb.has(), false, "locked() list is empty and returns false" ); +} ); - expect( 1 ); +QUnit.test( "jQuery.Callbacks() - adding a string doesn't cause a stack overflow", function( assert ) { + + assert.expect( 1 ); jQuery.Callbacks().add( "hello world" ); - ok( true, "no stack overflow" ); -}); + assert.ok( true, "no stack overflow" ); +} ); + +QUnit.test( "jQuery.Callbacks() - disabled callback doesn't fire (gh-1790)", function( assert ) { + + assert.expect( 1 ); + + var cb = jQuery.Callbacks(), + fired = false, + shot = function() { fired = true; }; + + cb.disable(); + cb.empty(); + cb.add( shot ); + cb.fire(); + assert.ok( !fired, "Disabled callback function didn't fire" ); +} ); diff --git a/test/unit/core.js b/test/unit/core.js index db21c41ccb..d6c8e33260 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1,35 +1,26 @@ -module("core", { teardown: moduleTeardown }); - -test("Unit Testing Environment", function () { - expect(2); - ok( hasPHP, "Running in an environment with PHP support. The AJAX tests only run if the environment supports PHP!" ); - ok( !isLocal, "Unit tests are not ran from file:// (especially in Chrome. If you must test from file:// with Chrome, run it with the --allow-file-access-from-files flag!)" ); -}); - -test("Basic requirements", function() { - expect(7); - ok( Array.prototype.push, "Array.push()" ); - ok( Function.prototype.apply, "Function.apply()" ); - ok( document.getElementById, "getElementById" ); - ok( document.getElementsByTagName, "getElementsByTagName" ); - ok( RegExp, "RegExp" ); - ok( jQuery, "jQuery" ); - ok( $, "$" ); -}); - -test("jQuery()", function() { +QUnit.module( "core", { teardown: moduleTeardown } ); + +QUnit.test( "Basic requirements", function( assert ) { + assert.expect( 7 ); + assert.ok( Array.prototype.push, "Array.push()" ); + assert.ok( Function.prototype.apply, "Function.apply()" ); + assert.ok( document.getElementById, "getElementById" ); + assert.ok( document.getElementsByTagName, "getElementsByTagName" ); + assert.ok( RegExp, "RegExp" ); + assert.ok( jQuery, "jQuery" ); + assert.ok( $, "$" ); +} ); + +QUnit.test( "jQuery()", function( assert ) { var elem, i, - obj = jQuery("div"), - main = jQuery("#qunit-fixture"), - code = jQuery(""), - img = jQuery(""), - div = jQuery("

        "), + obj = jQuery( "div" ), + code = jQuery( "" ), + img = jQuery( "" ), + div = jQuery( "

        " ), exec = false, - lng = "", - expected = 22, + expected = 23, attrObj = { - "click": function() { ok( exec, "Click executed." ); }, "text": "test", "class": "test2", "id": "test3" @@ -37,38 +28,44 @@ test("jQuery()", function() { // The $(html, props) signature can stealth-call any $.fn method, check for a // few here but beware of modular builds where these methods may be excluded. + if ( jQuery.fn.click ) { + expected++; + attrObj[ "click" ] = function() { assert.ok( exec, "Click executed." ); }; + } if ( jQuery.fn.width ) { expected++; - attrObj["width"] = 10; + attrObj[ "width" ] = 10; } if ( jQuery.fn.offset ) { expected++; - attrObj["offset"] = { "top": 1, "left": 1 }; + attrObj[ "offset" ] = { "top": 1, "left": 1 }; + } + if ( jQuery.fn.offset ) { + expected++; + attrObj[ "offset" ] = { "top": 1, "left": 1 }; } if ( jQuery.fn.css ) { expected += 2; - attrObj["css"] = { "paddingLeft": 1, "paddingRight": 1 }; + attrObj[ "css" ] = { "paddingLeft": 1, "paddingRight": 1 }; } if ( jQuery.fn.attr ) { expected++; attrObj.attr = { "desired": "very" }; } - expect( expected ); + assert.expect( expected ); // Basic constructor's behavior equal( jQuery().length, 0, "jQuery() === jQuery([])" ); - equal( jQuery(undefined).length, 0, "jQuery(undefined) === jQuery([])" ); - equal( jQuery(null).length, 0, "jQuery(null) === jQuery([])" ); - equal( jQuery("").length, 0, "jQuery('') === jQuery([])" ); - equal( jQuery("#").length, 0, "jQuery('#') === jQuery([])" ); + equal( jQuery( undefined ).length, 0, "jQuery(undefined) === jQuery([])" ); + equal( jQuery( null ).length, 0, "jQuery(null) === jQuery([])" ); + equal( jQuery( "" ).length, 0, "jQuery('') === jQuery([])" ); + equal( jQuery( "#" ).length, 0, "jQuery('#') === jQuery([])" ); - equal( jQuery(obj).selector, "div", "jQuery(jQueryObj) == jQueryObj" ); + equal( jQuery( obj ).selector, "div", "jQuery(jQueryObj) == jQueryObj" ); // can actually yield more than one, when iframes are included, the window is an array as well - equal( jQuery(window).length, 1, "Correct number of elements generated for jQuery(window)" ); - - deepEqual( jQuery("div p", main).get(), q("sndp", "en", "sap"), "Basic selector with jQuery object as context" ); + assert.equal( jQuery( window ).length, 1, "Correct number of elements generated for jQuery(window)" ); /* // disabled since this test was doing nothing. i tried to fix it but i'm not sure @@ -76,7 +73,7 @@ test("jQuery()", function() { // make sure this is handled var crlfContainer = jQuery('

        \r\n

        '); var x = crlfContainer.contents().get(0).nodeValue; - equal( x, what???, "Check for \\r and \\n in jQuery()" ); + assert.equal( x, what???, "Check for \\r and \\n in jQuery()" ); */ /* // Disabled until we add this functionality in @@ -86,63 +83,77 @@ test("jQuery()", function() { } catch(e){ pass = false; } - ok( pass, "jQuery('<tag>') needs optional document parameter to ease cross-frame DOM wrangling, see #968" );*/ + assert.ok( pass, "jQuery('<tag>') needs optional document parameter to ease cross-frame DOM wrangling, see #968" );*/ - equal( code.length, 1, "Correct number of elements generated for code" ); - equal( code.parent().length, 0, "Make sure that the generated HTML has no parent." ); + assert.equal( code.length, 1, "Correct number of elements generated for code" ); + assert.equal( code.parent().length, 0, "Make sure that the generated HTML has no parent." ); - equal( img.length, 1, "Correct number of elements generated for img" ); - equal( img.parent().length, 0, "Make sure that the generated HTML has no parent." ); + assert.equal( img.length, 1, "Correct number of elements generated for img" ); + assert.equal( img.parent().length, 0, "Make sure that the generated HTML has no parent." ); - equal( div.length, 4, "Correct number of elements generated for div hr code b" ); - equal( div.parent().length, 0, "Make sure that the generated HTML has no parent." ); + assert.equal( div.length, 4, "Correct number of elements generated for div hr code b" ); + assert.equal( div.parent().length, 0, "Make sure that the generated HTML has no parent." ); - equal( jQuery([1,2,3]).get(1), 2, "Test passing an array to the factory" ); + assert.equal( jQuery( [ 1, 2, 3 ] ).get( 1 ), 2, "Test passing an array to the factory" ); - equal( jQuery(document.body).get(0), jQuery("body").get(0), "Test passing an html node to the factory" ); + assert.equal( jQuery( document.body ).get( 0 ), jQuery( "body" ).get( 0 ), "Test passing an html node to the factory" ); - elem = jQuery("
        ", attrObj ); + elem = jQuery( " hello" )[ 0 ]; + assert.equal( elem.nodeName.toLowerCase(), "em", "leading space" ); + + elem = jQuery( "\n\nworld" )[ 0 ]; + assert.equal( elem.nodeName.toLowerCase(), "em", "leading newlines" ); + + elem = jQuery( "
        ", attrObj ); if ( jQuery.fn.width ) { - equal( elem[0].style.width, "10px", "jQuery() quick setter width"); + assert.equal( elem[ 0 ].style.width, "10px", "jQuery() quick setter width" ); + } + + if ( jQuery.fn.offset ) { + assert.equal( elem[ 0 ].style.top, "1px", "jQuery() quick setter offset" ); } if ( jQuery.fn.offset ) { - equal( elem[0].style.top, "1px", "jQuery() quick setter offset"); + equal( elem[ 0 ].style.top, "1px", "jQuery() quick setter offset" ); } if ( jQuery.fn.css ) { - equal( elem[0].style.paddingLeft, "1px", "jQuery quick setter css"); - equal( elem[0].style.paddingRight, "1px", "jQuery quick setter css"); + assert.equal( elem[ 0 ].style.paddingLeft, "1px", "jQuery quick setter css" ); + assert.equal( elem[ 0 ].style.paddingRight, "1px", "jQuery quick setter css" ); } if ( jQuery.fn.attr ) { - equal( elem[0].getAttribute("desired"), "very", "jQuery quick setter attr"); + assert.equal( elem[ 0 ].getAttribute( "desired" ), "very", "jQuery quick setter attr" ); } - equal( elem[0].childNodes.length, 1, "jQuery quick setter text"); - equal( elem[0].firstChild.nodeValue, "test", "jQuery quick setter text"); - equal( elem[0].className, "test2", "jQuery() quick setter class"); - equal( elem[0].id, "test3", "jQuery() quick setter id"); + assert.equal( elem[ 0 ].childNodes.length, 1, "jQuery quick setter text" ); + assert.equal( elem[ 0 ].firstChild.nodeValue, "test", "jQuery quick setter text" ); + assert.equal( elem[ 0 ].className, "test2", "jQuery() quick setter class" ); + assert.equal( elem[ 0 ].id, "test3", "jQuery() quick setter id" ); exec = true; - elem.click(); + elem.trigger( "click" ); // manually clean up detached elements elem.remove(); for ( i = 0; i < 3; ++i ) { - elem = jQuery(""); + elem = jQuery( "" ); } - equal( elem[0].defaultValue, "TEST", "Ensure cached nodes are cloned properly (Bug #6655)" ); + assert.equal( elem[ 0 ].defaultValue, "TEST", "Ensure cached nodes are cloned properly (Bug #6655)" ); - // manually clean up detached elements - elem.remove(); + elem = jQuery( "", {} ); + assert.strictEqual( elem[ 0 ].ownerDocument, document, + "Empty attributes object is not interpreted as a document (trac-8950)" ); +} ); - for ( i = 0; i < 128; i++ ) { - lng += "12345678"; - } -}); +QUnit.test( "jQuery(selector, context)", function( assert ) { + assert.expect( 3 ); + assert.deepEqual( jQuery( "div p", "#qunit-fixture" ).get(), q( "sndp", "en", "sap" ), "Basic selector with string as context" ); + assert.deepEqual( jQuery( "div p", q( "qunit-fixture" )[ 0 ] ).get(), q( "sndp", "en", "sap" ), "Basic selector with element as context" ); + assert.deepEqual( jQuery( "div p", jQuery( "#qunit-fixture" ) ).get(), q( "sndp", "en", "sap" ), "Basic selector with jQuery object as context" ); +} ); test( "selector state", function() { expect( 18 ); @@ -161,11 +172,11 @@ test( "selector state", function() { equal( test.selector, "", "Body Selector" ); equal( test.context, document.body, "Body Context" ); - test = jQuery("#qunit-fixture"); + test = jQuery( "#qunit-fixture" ); equal( test.selector, "#qunit-fixture", "#qunit-fixture Selector" ); equal( test.context, document, "#qunit-fixture Context" ); - test = jQuery("#notfoundnono"); + test = jQuery( "#notfoundnono" ); equal( test.selector, "#notfoundnono", "#notfoundnono Selector" ); equal( test.context, document, "#notfoundnono Context" ); @@ -182,225 +193,297 @@ test( "selector state", function() { equal( test.selector, "#qunit-fixture", "#qunit-fixture Selector" ); equal( test.context, document.body, "#qunit-fixture Context" ); - test = jQuery( document.body ).find("#qunit-fixture"); + test = jQuery( document.body ).find( "#qunit-fixture" ); equal( test.selector, "#qunit-fixture", "#qunit-fixture find Selector" ); equal( test.context, document.body, "#qunit-fixture find Context" ); -}); - -test( "globalEval", function() { - expect( 3 ); - Globals.register("globalEvalTest"); - - jQuery.globalEval("globalEvalTest = 1;"); - equal( window.globalEvalTest, 1, "Test variable assignments are global" ); - - jQuery.globalEval("var globalEvalTest = 2;"); - equal( window.globalEvalTest, 2, "Test variable declarations are global" ); - - jQuery.globalEval("this.globalEvalTest = 3;"); - equal( window.globalEvalTest, 3, "Test context (this) is the window object" ); -}); - -test("noConflict", function() { - expect(7); - - var $$ = jQuery; - - strictEqual( jQuery, jQuery.noConflict(), "noConflict returned the jQuery object" ); - strictEqual( window["jQuery"], $$, "Make sure jQuery wasn't touched." ); - strictEqual( window["$"], original$, "Make sure $ was reverted." ); - - jQuery = $ = $$; - - strictEqual( jQuery.noConflict(true), $$, "noConflict returned the jQuery object" ); - strictEqual( window["jQuery"], originaljQuery, "Make sure jQuery was reverted." ); - strictEqual( window["$"], original$, "Make sure $ was reverted." ); - ok( $$().pushStack([]), "Make sure that jQuery still works." ); - - window["jQuery"] = jQuery = $$; -}); - -test("trim", function() { - expect(13); - - var nbsp = String.fromCharCode(160); - - equal( jQuery.trim("hello "), "hello", "trailing space" ); - equal( jQuery.trim(" hello"), "hello", "leading space" ); - equal( jQuery.trim(" hello "), "hello", "space on both sides" ); - equal( jQuery.trim(" " + nbsp + "hello " + nbsp + " "), "hello", " " ); - - equal( jQuery.trim(), "", "Nothing in." ); - equal( jQuery.trim( undefined ), "", "Undefined" ); - equal( jQuery.trim( null ), "", "Null" ); - equal( jQuery.trim( 5 ), "5", "Number" ); - equal( jQuery.trim( false ), "false", "Boolean" ); - - equal( jQuery.trim(" "), "", "space should be trimmed" ); - equal( jQuery.trim("ipad\xA0"), "ipad", "nbsp should be trimmed" ); - equal( jQuery.trim("\uFEFF"), "", "zwsp should be trimmed" ); - equal( jQuery.trim("\uFEFF \xA0! | \uFEFF"), "! |", "leading/trailing should be trimmed" ); -}); - -test("type", function() { - expect( 28 ); - - equal( jQuery.type(null), "null", "null" ); - equal( jQuery.type(undefined), "undefined", "undefined" ); - equal( jQuery.type(true), "boolean", "Boolean" ); - equal( jQuery.type(false), "boolean", "Boolean" ); - equal( jQuery.type(Boolean(true)), "boolean", "Boolean" ); - equal( jQuery.type(0), "number", "Number" ); - equal( jQuery.type(1), "number", "Number" ); - equal( jQuery.type(Number(1)), "number", "Number" ); - equal( jQuery.type(""), "string", "String" ); - equal( jQuery.type("a"), "string", "String" ); - equal( jQuery.type(String("a")), "string", "String" ); - equal( jQuery.type({}), "object", "Object" ); - equal( jQuery.type(/foo/), "regexp", "RegExp" ); - equal( jQuery.type(new RegExp("asdf")), "regexp", "RegExp" ); - equal( jQuery.type([1]), "array", "Array" ); - equal( jQuery.type(new Date()), "date", "Date" ); - equal( jQuery.type(new Function("return;")), "function", "Function" ); - equal( jQuery.type(function(){}), "function", "Function" ); - equal( jQuery.type(new Error()), "error", "Error" ); - equal( jQuery.type(window), "object", "Window" ); - equal( jQuery.type(document), "object", "Document" ); - equal( jQuery.type(document.body), "object", "Element" ); - equal( jQuery.type(document.createTextNode("foo")), "object", "TextNode" ); - equal( jQuery.type(document.getElementsByTagName("*")), "object", "NodeList" ); +} ); + +QUnit.test( "globalEval", function( assert ) { + expect( 2 ); + Globals.register( "globalEvalTest" ); + + jQuery.globalEval( "var globalEvalTest = 2;" ); + assert.equal( window.globalEvalTest, 2, "Test variable declarations are global" ); + + jQuery.globalEval( "this.globalEvalTest = 3;" ); + assert.equal( window.globalEvalTest, 3, "Test context (this) is the window object" ); +} ); + +if ( jQuery.noConflict ) { + QUnit.test( "noConflict", function( assert ) { + assert.expect( 7 ); + + var $$ = jQuery; + + assert.strictEqual( jQuery, jQuery.noConflict(), "noConflict returned the jQuery object" ); + assert.strictEqual( window[ "jQuery" ], $$, "Make sure jQuery wasn't touched." ); + assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." ); + + jQuery = $ = $$; + + assert.strictEqual( jQuery.noConflict( true ), $$, "noConflict returned the jQuery object" ); + assert.strictEqual( window[ "jQuery" ], originaljQuery, "Make sure jQuery was reverted." ); + assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." ); + assert.ok( $$().pushStack( [] ), "Make sure that jQuery still works." ); + + window[ "jQuery" ] = jQuery = $$; + } ); +} + +// This is not run in AMD mode +if ( jQuery.noConflict ) { + QUnit.test( "noConflict", function( assert ) { + assert.expect( 7 ); + + var $$ = jQuery; + + assert.strictEqual( jQuery, jQuery.noConflict(), "noConflict returned the jQuery object" ); + assert.strictEqual( window[ "jQuery" ], $$, "Make sure jQuery wasn't touched." ); + assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." ); + + jQuery = $ = $$; + + assert.strictEqual( jQuery.noConflict( true ), $$, "noConflict returned the jQuery object" ); + assert.strictEqual( window[ "jQuery" ], originaljQuery, "Make sure jQuery was reverted." ); + assert.strictEqual( window[ "$" ], original$, "Make sure $ was reverted." ); + assert.ok( $$().pushStack( [] ), "Make sure that jQuery still works." ); + + window[ "jQuery" ] = jQuery = $$; + } ); +} + +QUnit.test( "trim", function( assert ) { + assert.expect( 13 ); + + var nbsp = String.fromCharCode( 160 ); + + assert.equal( jQuery.trim( "hello " ), "hello", "trailing space" ); + assert.equal( jQuery.trim( " hello" ), "hello", "leading space" ); + assert.equal( jQuery.trim( " hello " ), "hello", "space on both sides" ); + assert.equal( jQuery.trim( " " + nbsp + "hello " + nbsp + " " ), "hello", " " ); + + assert.equal( jQuery.trim(), "", "Nothing in." ); + assert.equal( jQuery.trim( undefined ), "", "Undefined" ); + assert.equal( jQuery.trim( null ), "", "Null" ); + assert.equal( jQuery.trim( 5 ), "5", "Number" ); + assert.equal( jQuery.trim( false ), "false", "Boolean" ); + + assert.equal( jQuery.trim( " " ), "", "space should be trimmed" ); + assert.equal( jQuery.trim( "ipad\xA0" ), "ipad", "nbsp should be trimmed" ); + assert.equal( jQuery.trim( "\uFEFF" ), "", "zwsp should be trimmed" ); + assert.equal( jQuery.trim( "\uFEFF \xA0! | \uFEFF" ), "! |", "leading/trailing should be trimmed" ); +} ); + +QUnit.test( "type", function( assert ) { + assert.expect( 28 ); + + assert.equal( jQuery.type( null ), "null", "null" ); + assert.equal( jQuery.type( undefined ), "undefined", "undefined" ); + assert.equal( jQuery.type( true ), "boolean", "Boolean" ); + assert.equal( jQuery.type( false ), "boolean", "Boolean" ); + assert.equal( jQuery.type( Boolean( true ) ), "boolean", "Boolean" ); + assert.equal( jQuery.type( 0 ), "number", "Number" ); + assert.equal( jQuery.type( 1 ), "number", "Number" ); + assert.equal( jQuery.type( Number( 1 ) ), "number", "Number" ); + assert.equal( jQuery.type( "" ), "string", "String" ); + assert.equal( jQuery.type( "a" ), "string", "String" ); + assert.equal( jQuery.type( String( "a" ) ), "string", "String" ); + assert.equal( jQuery.type( {} ), "object", "Object" ); + assert.equal( jQuery.type( /foo/ ), "regexp", "RegExp" ); + assert.equal( jQuery.type( new RegExp( "asdf" ) ), "regexp", "RegExp" ); + assert.equal( jQuery.type( [ 1 ] ), "array", "Array" ); + assert.equal( jQuery.type( new Date() ), "date", "Date" ); + assert.equal( jQuery.type( new Function( "return;" ) ), "function", "Function" ); + assert.equal( jQuery.type( function() {} ), "function", "Function" ); + assert.equal( jQuery.type( new Error() ), "error", "Error" ); + assert.equal( jQuery.type( window ), "object", "Window" ); + assert.equal( jQuery.type( document ), "object", "Document" ); + assert.equal( jQuery.type( document.body ), "object", "Element" ); + assert.equal( jQuery.type( document.createTextNode( "foo" ) ), "object", "TextNode" ); + assert.equal( jQuery.type( document.getElementsByTagName( "*" ) ), "object", "NodeList" ); // Avoid Lint complaints - var MyString = String; - var MyNumber = Number; - var MyBoolean = Boolean; - var MyObject = Object; - equal( jQuery.type(new MyBoolean(true)), "boolean", "Boolean" ); - equal( jQuery.type(new MyNumber(1)), "number", "Number" ); - equal( jQuery.type(new MyString("a")), "string", "String" ); - equal( jQuery.type(new MyObject()), "object", "Object" ); -}); - -asyncTest("isPlainObject", function() { - expect(15); - - var pass, iframe, doc, + var MyString = String, + MyNumber = Number, + MyBoolean = Boolean, + MyObject = Object; + assert.equal( jQuery.type( new MyBoolean( true ) ), "boolean", "Boolean" ); + assert.equal( jQuery.type( new MyNumber( 1 ) ), "number", "Number" ); + assert.equal( jQuery.type( new MyString( "a" ) ), "string", "String" ); + assert.equal( jQuery.type( new MyObject() ), "object", "Object" ); +} ); + +QUnit.test( "type for `Symbol`", function( assert ) { + + // Prevent reference errors + if ( typeof Symbol !== "function" ) { + assert.expect( 0 ); + return; + } + + assert.expect( 2 ); + + assert.equal( jQuery.type( Symbol() ), "symbol", "Symbol" ); + assert.equal( jQuery.type( Object( Symbol() ) ), "symbol", "Symbol" ); +} ); + +QUnit.asyncTest( "isPlainObject", function( assert ) { + assert.expect( 18 ); + + var pass, iframe, doc, deep, fn = function() {}; // The use case that we want to match - ok( jQuery.isPlainObject({}), "{}" ); + assert.ok( jQuery.isPlainObject( {} ), "{}" ); + assert.ok( jQuery.isPlainObject( new window.Object() ), "new Object" ); // Not objects shouldn't be matched - ok( !jQuery.isPlainObject(""), "string" ); - ok( !jQuery.isPlainObject(0) && !jQuery.isPlainObject(1), "number" ); - ok( !jQuery.isPlainObject(true) && !jQuery.isPlainObject(false), "boolean" ); - ok( !jQuery.isPlainObject(null), "null" ); - ok( !jQuery.isPlainObject(undefined), "undefined" ); + assert.ok( !jQuery.isPlainObject( "" ), "string" ); + assert.ok( !jQuery.isPlainObject( 0 ) && !jQuery.isPlainObject( 1 ), "number" ); + assert.ok( !jQuery.isPlainObject( true ) && !jQuery.isPlainObject( false ), "boolean" ); + assert.ok( !jQuery.isPlainObject( null ), "null" ); + assert.ok( !jQuery.isPlainObject( undefined ), "undefined" ); // Arrays shouldn't be matched - ok( !jQuery.isPlainObject([]), "array" ); + assert.ok( !jQuery.isPlainObject( [] ), "array" ); // Instantiated objects shouldn't be matched - ok( !jQuery.isPlainObject(new Date()), "new Date" ); + assert.ok( !jQuery.isPlainObject( new Date() ), "new Date" ); // Functions shouldn't be matched - ok( !jQuery.isPlainObject(fn), "fn" ); + assert.ok( !jQuery.isPlainObject( fn ), "fn" ); // Again, instantiated objects shouldn't be matched - ok( !jQuery.isPlainObject(new fn()), "new fn (no methods)" ); + assert.ok( !jQuery.isPlainObject( new fn() ), "new fn (no methods)" ); // Makes the function a little more realistic // (and harder to detect, incidentally) - fn.prototype["someMethod"] = function(){}; + fn.prototype[ "someMethod" ] = function() {}; // Again, instantiated objects shouldn't be matched - ok( !jQuery.isPlainObject(new fn()), "new fn" ); + assert.ok( !jQuery.isPlainObject( new fn() ), "new fn" ); + + // Make it even harder to detect in IE < 9 + fn = function() { + this.a = "a"; + }; + fn.prototype = { + b: "b" + }; + + assert.ok( !jQuery.isPlainObject( new fn() ), "fn (inherited and own properties)" ); + + // Deep object + deep = { "foo": { "baz": true }, "foo2": document }; + assert.ok( jQuery.isPlainObject( deep ), "Object with objects is still plain" ); // DOM Element - ok( !jQuery.isPlainObject( document.createElement("div") ), "DOM Element" ); + assert.ok( !jQuery.isPlainObject( document.createElement( "div" ) ), "DOM Element" ); // Window - ok( !jQuery.isPlainObject( window ), "window" ); + assert.ok( !jQuery.isPlainObject( window ), "window" ); pass = false; try { jQuery.isPlainObject( window.location ); pass = true; } catch ( e ) {} - ok( pass, "Does not throw exceptions on host objects" ); + assert.ok( pass, "Does not throw exceptions on host objects" ); // Objects from other windows should be matched - window.iframeCallback = function( otherObject, detail ) { - window.iframeCallback = undefined; + Globals.register( "iframeDone" ); + window.iframeDone = function( otherObject, detail ) { + window.iframeDone = undefined; iframe.parentNode.removeChild( iframe ); - ok( jQuery.isPlainObject(new otherObject()), "new otherObject" + ( detail ? " - " + detail : "" ) ); - start(); + assert.ok( jQuery.isPlainObject( new otherObject() ), "new otherObject" + ( detail ? " - " + detail : "" ) ); + QUnit.start(); }; try { - iframe = jQuery("#qunit-fixture")[0].appendChild( document.createElement("iframe") ); + iframe = jQuery( "#qunit-fixture" )[ 0 ].appendChild( document.createElement( "iframe" ) ); doc = iframe.contentDocument || iframe.contentWindow.document; doc.open(); - doc.write(""); + doc.write( "" ); doc.close(); - } catch(e) { + } catch ( e ) { window.iframeDone( Object, "iframes not supported" ); } -}); +} ); + +QUnit[ typeof Object.create !== "undefined" ? "test" : "skip" ]( "isPlainObject(Object.create()", function( assert ) { + assert.expect( 2 ); + + var parentObj = { foo: "bar" }, + childObj = Object.create( parentObj ); + + assert.ok( !jQuery.isPlainObject( childObj ), "isPlainObject(Object.create({}))" ); + childObj.bar = "foo"; + assert.ok( !jQuery.isPlainObject( childObj ), "isPlainObject(Object.create({}))" ); +} ); + +// +QUnit[ typeof Symbol === "function" ? "test" : "skip" ]( "isPlainObject(Symbol)", function( assert ) { + assert.expect( 2 ); -test("isFunction", function() { - expect(19); + assert.equal( jQuery.isPlainObject( Symbol() ), false, "Symbol" ); + assert.equal( jQuery.isPlainObject( Object( Symbol() ) ), false, "Symbol inside an object" ); +} ); + +QUnit.test( "isFunction", function( assert ) { + assert.expect( 19 ); + + var mystr, myarr, myfunction, fn, obj, nodes, first, input, a; // Make sure that false values return false - ok( !jQuery.isFunction(), "No Value" ); - ok( !jQuery.isFunction( null ), "null Value" ); - ok( !jQuery.isFunction( undefined ), "undefined Value" ); - ok( !jQuery.isFunction( "" ), "Empty String Value" ); - ok( !jQuery.isFunction( 0 ), "0 Value" ); + assert.ok( !jQuery.isFunction(), "No Value" ); + assert.ok( !jQuery.isFunction( null ), "null Value" ); + assert.ok( !jQuery.isFunction( undefined ), "undefined Value" ); + assert.ok( !jQuery.isFunction( "" ), "Empty String Value" ); + assert.ok( !jQuery.isFunction( 0 ), "0 Value" ); // Check built-ins // Safari uses "(Internal Function)" - ok( jQuery.isFunction(String), "String Function("+String+")" ); - ok( jQuery.isFunction(Array), "Array Function("+Array+")" ); - ok( jQuery.isFunction(Object), "Object Function("+Object+")" ); - ok( jQuery.isFunction(Function), "Function Function("+Function+")" ); + assert.ok( jQuery.isFunction( String ), "String Function(" + String + ")" ); + assert.ok( jQuery.isFunction( Array ), "Array Function(" + Array + ")" ); + assert.ok( jQuery.isFunction( Object ), "Object Function(" + Object + ")" ); + assert.ok( jQuery.isFunction( Function ), "Function Function(" + Function + ")" ); // When stringified, this could be misinterpreted - var mystr = "function"; - ok( !jQuery.isFunction(mystr), "Function String" ); + mystr = "function"; + assert.ok( !jQuery.isFunction( mystr ), "Function String" ); // When stringified, this could be misinterpreted - var myarr = [ "function" ]; - ok( !jQuery.isFunction(myarr), "Function Array" ); + myarr = [ "function" ]; + assert.ok( !jQuery.isFunction( myarr ), "Function Array" ); // When stringified, this could be misinterpreted - var myfunction = { "function": "test" }; - ok( !jQuery.isFunction(myfunction), "Function Object" ); + myfunction = { "function": "test" }; + assert.ok( !jQuery.isFunction( myfunction ), "Function Object" ); // Make sure normal functions still work - var fn = function(){}; - ok( jQuery.isFunction(fn), "Normal Function" ); + fn = function() {}; + assert.ok( jQuery.isFunction( fn ), "Normal Function" ); - var obj = document.createElement("object"); + obj = document.createElement( "object" ); // Firefox says this is a function - ok( !jQuery.isFunction(obj), "Object Element" ); + assert.ok( !jQuery.isFunction( obj ), "Object Element" ); // IE says this is an object // Since 1.3, this isn't supported (#2968) //ok( jQuery.isFunction(obj.getAttribute), "getAttribute Function" ); - var nodes = document.body.childNodes; + nodes = document.body.childNodes; // Safari says this is a function - ok( !jQuery.isFunction(nodes), "childNodes Property" ); + assert.ok( !jQuery.isFunction( nodes ), "childNodes Property" ); - var first = document.body.firstChild; + first = document.body.firstChild; // Normal elements are reported ok everywhere - ok( !jQuery.isFunction(first), "A normal DOM Element" ); + assert.ok( !jQuery.isFunction( first ), "A normal DOM Element" ); - var input = document.createElement("input"); + input = document.createElement( "input" ); input.type = "text"; document.body.appendChild( input ); @@ -410,413 +493,436 @@ test("isFunction", function() { document.body.removeChild( input ); - var a = document.createElement("a"); + a = document.createElement( "a" ); a.href = "some-function"; document.body.appendChild( a ); // This serializes with the word 'function' in it - ok( !jQuery.isFunction(a), "Anchor Element" ); + assert.ok( !jQuery.isFunction( a ), "Anchor Element" ); document.body.removeChild( a ); // Recursive function calls have lengths and array-like properties - function callme(callback){ - function fn(response){ - callback(response); + function callme( callback ) { + function fn( response ) { + callback( response ); } - ok( jQuery.isFunction(fn), "Recursive Function Call" ); + assert.ok( jQuery.isFunction( fn ), "Recursive Function Call" ); - fn({ some: "data" }); + fn( { some: "data" } ); } - callme(function(){ - callme(function(){}); - }); -}); + callme( function() { + callme( function() {} ); + } ); +} ); -test( "isNumeric", function() { - expect( 36 ); +QUnit.test( "isNumeric", function( assert ) { + assert.expect( 38 ); var t = jQuery.isNumeric, - Traditionalists = /** @constructor */ function(n) { - this.value = n; - this.toString = function(){ - return String(this.value); + ToString = function( value ) { + this.toString = function() { + return String( value ); }; - }, - answer = new Traditionalists( "42" ), - rong = new Traditionalists( "Devo" ); - - ok( t("-10"), "Negative integer string"); - ok( t("0"), "Zero string"); - ok( t("5"), "Positive integer string"); - ok( t(-16), "Negative integer number"); - ok( t(0), "Zero integer number"); - ok( t(32), "Positive integer number"); - ok( t("040"), "Octal integer literal string"); - // OctalIntegerLiteral has been deprecated since ES3/1999 - // It doesn't pass lint, so disabling until a solution can be found - //ok( t(0144), "Octal integer literal"); - ok( t("0xFF"), "Hexadecimal integer literal string"); - ok( t(0xFFF), "Hexadecimal integer literal"); - ok( t("-1.6"), "Negative floating point string"); - ok( t("4.536"), "Positive floating point string"); - ok( t(-2.6), "Negative floating point number"); - ok( t(3.1415), "Positive floating point number"); - ok( t(8e5), "Exponential notation"); - ok( t("123e-2"), "Exponential notation string"); - ok( t(answer), "Custom .toString returning number"); - equal( t(""), false, "Empty string"); - equal( t(" "), false, "Whitespace characters string"); - equal( t("\t\t"), false, "Tab characters string"); - equal( t("abcdefghijklm1234567890"), false, "Alphanumeric character string"); - equal( t("xabcdefx"), false, "Non-numeric character string"); - equal( t(true), false, "Boolean true literal"); - equal( t(false), false, "Boolean false literal"); - equal( t("bcfed5.2"), false, "Number with preceding non-numeric characters"); - equal( t("7.2acdgs"), false, "Number with trailling non-numeric characters"); - equal( t(undefined), false, "Undefined value"); - equal( t(null), false, "Null value"); - equal( t(NaN), false, "NaN value"); - equal( t(Infinity), false, "Infinity primitive"); - equal( t(Number.POSITIVE_INFINITY), false, "Positive Infinity"); - equal( t(Number.NEGATIVE_INFINITY), false, "Negative Infinity"); - equal( t(rong), false, "Custom .toString returning non-number"); - equal( t({}), false, "Empty object"); - equal( t(function(){} ), false, "Instance of a function"); - equal( t( new Date() ), false, "Instance of a Date"); - equal( t(function(){} ), false, "Instance of a function"); -}); - -test("isXMLDoc - HTML", function() { - expect(4); - - ok( !jQuery.isXMLDoc( document ), "HTML document" ); - ok( !jQuery.isXMLDoc( document.documentElement ), "HTML documentElement" ); - ok( !jQuery.isXMLDoc( document.body ), "HTML Body Element" ); - - var iframe = document.createElement("iframe"); + }; + + assert.ok( t( "-10" ), "Negative integer string" ); + assert.ok( t( "0" ), "Zero string" ); + assert.ok( t( "5" ), "Positive integer string" ); + assert.ok( t( -16 ), "Negative integer number" ); + assert.ok( t( 0 ), "Zero integer number" ); + assert.ok( t( 32 ), "Positive integer number" ); + assert.ok( t( "040" ), "Octal integer literal string" ); + assert.ok( t( "0xFF" ), "Hexadecimal integer literal string" ); + assert.ok( t( 0xFFF ), "Hexadecimal integer literal" ); + assert.ok( t( "-1.6" ), "Negative floating point string" ); + assert.ok( t( "4.536" ), "Positive floating point string" ); + assert.ok( t( -2.6 ), "Negative floating point number" ); + assert.ok( t( 3.1415 ), "Positive floating point number" ); + assert.ok( t( 1.5999999999999999 ), "Very precise floating point number" ); + assert.ok( t( 8e5 ), "Exponential notation" ); + assert.ok( t( "123e-2" ), "Exponential notation string" ); + assert.ok( t( new ToString( "42" ) ), "Custom .toString returning number" ); + + assert.equal( t( "" ), false, "Empty string" ); + assert.equal( t( " " ), false, "Whitespace characters string" ); + assert.equal( t( "\t\t" ), false, "Tab characters string" ); + assert.equal( t( "abcdefghijklm1234567890" ), false, "Alphanumeric character string" ); + assert.equal( t( "xabcdefx" ), false, "Non-numeric character string" ); + assert.equal( t( true ), false, "Boolean true literal" ); + assert.equal( t( false ), false, "Boolean false literal" ); + assert.equal( t( "bcfed5.2" ), false, "Number with preceding non-numeric characters" ); + assert.equal( t( "7.2acdgs" ), false, "Number with trailling non-numeric characters" ); + assert.equal( t( undefined ), false, "Undefined value" ); + assert.equal( t( null ), false, "Null value" ); + assert.equal( t( NaN ), false, "NaN value" ); + assert.equal( t( Infinity ), false, "Infinity primitive" ); + assert.equal( t( Number.POSITIVE_INFINITY ), false, "Positive Infinity" ); + assert.equal( t( Number.NEGATIVE_INFINITY ), false, "Negative Infinity" ); + assert.equal( t( new ToString( "Devo" ) ), false, "Custom .toString returning non-number" ); + assert.equal( t( {} ), false, "Empty object" ); + assert.equal( t( [] ), false, "Empty array" ); + assert.equal( t( [ 42 ] ), false, "Array with one number" ); + assert.equal( t( function() {} ), false, "Instance of a function" ); + assert.equal( t( new Date() ), false, "Instance of a Date" ); +} ); + +QUnit[ typeof Symbol === "function" ? "test" : "skip" ]( "isNumeric(Symbol)", function( assert ) { + assert.expect( 2 ); + + assert.equal( jQuery.isNumeric( Symbol() ), false, "Symbol" ); + assert.equal( jQuery.isNumeric( Object( Symbol() ) ), false, "Symbol inside an object" ); +} ); + +QUnit.test( "isXMLDoc - HTML", function( assert ) { + assert.expect( 4 ); + + assert.ok( !jQuery.isXMLDoc( document ), "HTML document" ); + assert.ok( !jQuery.isXMLDoc( document.documentElement ), "HTML documentElement" ); + assert.ok( !jQuery.isXMLDoc( document.body ), "HTML Body Element" ); + + var body, + iframe = document.createElement( "iframe" ); document.body.appendChild( iframe ); try { - var body = jQuery(iframe).contents()[0]; + body = jQuery( iframe ).contents()[ 0 ]; try { - ok( !jQuery.isXMLDoc( body ), "Iframe body element" ); - } catch(e) { - ok( false, "Iframe body element exception" ); + assert.ok( !jQuery.isXMLDoc( body ), "Iframe body element" ); + } catch ( e ) { + assert.ok( false, "Iframe body element exception" ); } - } catch(e) { - ok( true, "Iframe body element - iframe not working correctly" ); + } catch ( e ) { + assert.ok( true, "Iframe body element - iframe not working correctly" ); } document.body.removeChild( iframe ); -}); +} ); -test("XSS via location.hash", function() { - expect(1); +QUnit.test( "XSS via location.hash", function( assert ) { + assert.expect( 1 ); - stop(); - jQuery["_check9521"] = function(x){ - ok( x, "script called from #id-like selector with inline handler" ); - jQuery("#check9521").remove(); - delete jQuery["_check9521"]; - start(); + QUnit.stop(); + jQuery[ "_check9521" ] = function( x ) { + assert.ok( x, "script called from #id-like selector with inline handler" ); + jQuery( "#check9521" ).remove(); + delete jQuery[ "_check9521" ]; + QUnit.start(); }; try { + // This throws an error because it's processed like an id - jQuery( "#" ).appendTo("#qunit-fixture"); - } catch (err) { - jQuery["_check9521"](true); + jQuery( "#" ).appendTo( "#qunit-fixture" ); + } catch ( err ) { + jQuery[ "_check9521" ]( true ); } -}); +} ); -test("isXMLDoc - XML", function() { - expect(3); +QUnit.test( "isXMLDoc - XML", function( assert ) { + assert.expect( 3 ); var xml = createDashboardXML(); - ok( jQuery.isXMLDoc( xml ), "XML document" ); - ok( jQuery.isXMLDoc( xml.documentElement ), "XML documentElement" ); - ok( jQuery.isXMLDoc( jQuery("tab", xml)[0] ), "XML Tab Element" ); -}); - -test("isWindow", function() { - expect( 14 ); - - ok( jQuery.isWindow(window), "window" ); - ok( jQuery.isWindow(document.getElementsByTagName("iframe")[0].contentWindow), "iframe.contentWindow" ); - ok( !jQuery.isWindow(), "empty" ); - ok( !jQuery.isWindow(null), "null" ); - ok( !jQuery.isWindow(undefined), "undefined" ); - ok( !jQuery.isWindow(document), "document" ); - ok( !jQuery.isWindow(document.documentElement), "documentElement" ); - ok( !jQuery.isWindow(""), "string" ); - ok( !jQuery.isWindow(1), "number" ); - ok( !jQuery.isWindow(true), "boolean" ); - ok( !jQuery.isWindow({}), "object" ); - ok( !jQuery.isWindow({ setInterval: function(){} }), "fake window" ); - ok( !jQuery.isWindow(/window/), "regexp" ); - ok( !jQuery.isWindow(function(){}), "function" ); -}); - -test("jQuery('html')", function() { - expect( 15 ); - - QUnit.reset(); - jQuery["foo"] = false; - var s = jQuery("")[0]; - ok( s, "Creating a script" ); - ok( !jQuery["foo"], "Make sure the script wasn't executed prematurely" ); - jQuery("body").append(""); - ok( jQuery["foo"], "Executing a scripts contents in the right context" ); + assert.ok( jQuery.isXMLDoc( xml ), "XML document" ); + assert.ok( jQuery.isXMLDoc( xml.documentElement ), "XML documentElement" ); + assert.ok( jQuery.isXMLDoc( jQuery( "tab", xml )[ 0 ] ), "XML Tab Element" ); +} ); + +QUnit.test( "isWindow", function( assert ) { + assert.expect( 14 ); + + assert.ok( jQuery.isWindow( window ), "window" ); + assert.ok( jQuery.isWindow( document.getElementsByTagName( "iframe" )[ 0 ].contentWindow ), "iframe.contentWindow" ); + assert.ok( !jQuery.isWindow(), "empty" ); + assert.ok( !jQuery.isWindow( null ), "null" ); + assert.ok( !jQuery.isWindow( undefined ), "undefined" ); + assert.ok( !jQuery.isWindow( document ), "document" ); + assert.ok( !jQuery.isWindow( document.documentElement ), "documentElement" ); + assert.ok( !jQuery.isWindow( "" ), "string" ); + assert.ok( !jQuery.isWindow( 1 ), "number" ); + assert.ok( !jQuery.isWindow( true ), "boolean" ); + assert.ok( !jQuery.isWindow( {} ), "object" ); + assert.ok( !jQuery.isWindow( { setInterval: function() {} } ), "fake window" ); + assert.ok( !jQuery.isWindow( /window/ ), "regexp" ); + assert.ok( !jQuery.isWindow( function() {} ), "function" ); +} ); + +QUnit.test( "jQuery('html')", function( assert ) { + assert.expect( 18 ); + + var s, div, j; + + jQuery[ "foo" ] = false; + s = jQuery( "" )[ 0 ]; + assert.ok( s, "Creating a script" ); + assert.ok( !jQuery[ "foo" ], "Make sure the script wasn't executed prematurely" ); + jQuery( "body" ).append( "" ); + assert.ok( jQuery[ "foo" ], "Executing a scripts contents in the right context" ); // Test multi-line HTML - var div = jQuery("
        \r\nsome text\n

        some p

        \nmore text\r\n
        ")[0]; - equal( div.nodeName.toUpperCase(), "DIV", "Make sure we're getting a div." ); - equal( div.firstChild.nodeType, 3, "Text node." ); - equal( div.lastChild.nodeType, 3, "Text node." ); - equal( div.childNodes[1].nodeType, 1, "Paragraph." ); - equal( div.childNodes[1].firstChild.nodeType, 3, "Paragraph text." ); + div = jQuery( "
        \r\nsome text\n

        some p

        \nmore text\r\n
        " )[ 0 ]; + assert.equal( div.nodeName.toUpperCase(), "DIV", "Make sure we're getting a div." ); + assert.equal( div.firstChild.nodeType, 3, "Text node." ); + assert.equal( div.lastChild.nodeType, 3, "Text node." ); + assert.equal( div.childNodes[ 1 ].nodeType, 1, "Paragraph." ); + assert.equal( div.childNodes[ 1 ].firstChild.nodeType, 3, "Paragraph text." ); - QUnit.reset(); - ok( jQuery("")[0], "Creating a link" ); + assert.ok( jQuery( "" )[ 0 ], "Creating a link" ); - ok( !jQuery(""; - equal( jQuery.parseHTML( html ).length, 0, "Ignore scripts by default" ); - equal( jQuery.parseHTML( html, true )[0].nodeName.toLowerCase(), "script", "Preserve scripts when requested" ); + assert.equal( jQuery.parseHTML( html ).length, 0, "Ignore scripts by default" ); + assert.equal( jQuery.parseHTML( html, true )[ 0 ].nodeName.toLowerCase(), "script", "Preserve scripts when requested" ); html += "
        "; - equal( jQuery.parseHTML( html )[0].nodeName.toLowerCase(), "div", "Preserve non-script nodes" ); - equal( jQuery.parseHTML( html, true )[0].nodeName.toLowerCase(), "script", "Preserve script position"); + assert.equal( jQuery.parseHTML( html )[ 0 ].nodeName.toLowerCase(), "div", "Preserve non-script nodes" ); + assert.equal( jQuery.parseHTML( html, true )[ 0 ].nodeName.toLowerCase(), "script", "Preserve script position" ); + + assert.equal( jQuery.parseHTML( "text" )[ 0 ].nodeType, 3, "Parsing text returns a text node" ); + assert.equal( jQuery.parseHTML( "\t
        " )[ 0 ].nodeValue, "\t", "Preserve leading whitespace" ); - equal( jQuery.parseHTML("text")[0].nodeType, 3, "Parsing text returns a text node" ); - equal( jQuery.parseHTML( "\t
        " )[0].nodeValue, "\t", "Preserve leading whitespace" ); + assert.equal( jQuery.parseHTML( "
        " )[ 0 ].nodeType, 3, "Leading spaces are treated as text nodes (#11290)" ); - equal( jQuery.parseHTML("
        ")[0].nodeType, 3, "Leading spaces are treated as text nodes (#11290)" ); -}); + html = jQuery.parseHTML( "
        test div
        " ); + assert.equal( html[ 0 ].parentNode.nodeType, 11, "parentNode should be documentFragment" ); + assert.equal( html[ 0 ].innerHTML, "test div", "Content should be preserved" ); -test("jQuery.parseJSON", function(){ - expect( 9 ); + assert.equal( jQuery.parseHTML( "" ).length, 1, "Incorrect html-strings should not break anything" ); + assert.equal( jQuery.parseHTML( "
        cell
        cellcell
        cellcell
        cell
        cellcell
        cellcell
        " )[ 1 ].parentNode.nodeType, 11, "parentNode should be documentFragment" ); +} ); - equal( jQuery.parseJSON( null ), null, "Actual null returns null" ); - equal( jQuery.isEmptyObject( jQuery.parseJSON("{}") ), true, "Empty object returns empty object" ); - deepEqual( jQuery.parseJSON("{\"test\":1}"), { "test": 1 }, "Plain object parses" ); - deepEqual( jQuery.parseJSON("\n{\"test\":1}"), { "test": 1 }, "Leading whitespaces are ignored." ); - raises(function() { +QUnit.test( "jQuery.parseJSON", function( assert ) { + assert.expect( 20 ); + + assert.strictEqual( jQuery.parseJSON( null ), null, "primitive null" ); + assert.strictEqual( jQuery.parseJSON( "0.88" ), 0.88, "Number" ); + assert.strictEqual( + jQuery.parseJSON( "\" \\\" \\\\ \\/ \\b \\f \\n \\r \\t \\u007E \\u263a \"" ), + " \" \\ / \b \f \n \r \t ~ \u263A ", + "String escapes" + ); + assert.deepEqual( jQuery.parseJSON( "{}" ), {}, "Empty object" ); + assert.deepEqual( jQuery.parseJSON( "{\"test\":1}" ), { "test": 1 }, "Plain object" ); + assert.deepEqual( jQuery.parseJSON( "[0]" ), [ 0 ], "Simple array" ); + + assert.deepEqual( + jQuery.parseJSON( "[ \"string\", -4.2, 2.7180e0, 3.14E-1, {}, [], true, false, null ]" ), + [ "string", -4.2, 2.718, 0.314, {}, [], true, false, null ], + "Array of all data types" + ); + assert.deepEqual( + jQuery.parseJSON( "{ \"string\": \"\", \"number\": 4.2e+1, \"object\": {}," + + "\"array\": [[]], \"boolean\": [ true, false ], \"null\": null }" ), + { string: "", number: 42, object: {}, array: [ [] ], "boolean": [ true, false ], "null": null }, + "Dictionary of all data types" + ); + + assert.deepEqual( jQuery.parseJSON( "\n{\"test\":1}\t" ), { "test": 1 }, + "Leading and trailing whitespace are ignored" ); + + assert.throws( function() { jQuery.parseJSON(); }, null, "Undefined raises an error" ); - raises( function() { + assert.throws( function() { jQuery.parseJSON( "" ); }, null, "Empty string raises an error" ); - raises(function() { - jQuery.parseJSON("''"); + assert.throws( function() { + jQuery.parseJSON( "''" ); }, null, "Single-quoted string raises an error" ); - raises(function() { - jQuery.parseJSON("{a:1}"); + /* + + // Broken on IE8 + assert.throws(function() { + jQuery.parseJSON("\" \\a \""); + }, null, "Invalid string escape raises an error" ); + + // Broken on IE8, Safari 5.1 Windows + assert.throws(function() { + jQuery.parseJSON("\"\t\""); + }, null, "Unescaped control character raises an error" ); + + // Broken on IE8 + assert.throws(function() { + jQuery.parseJSON(".123"); + }, null, "Number with no integer component raises an error" ); + + */ + assert.throws( function() { + var result = jQuery.parseJSON( "0101" ); + + // Support: IE9+ + // Ensure base-10 interpretation on browsers that erroneously accept leading-zero numbers + if ( result === 101 ) { + throw new Error( "close enough" ); + } + }, null, "Leading-zero number raises an error or is parsed as decimal" ); + assert.throws( function() { + jQuery.parseJSON( "{a:1}" ); }, null, "Unquoted property raises an error" ); - raises(function() { - jQuery.parseJSON("{'a':1}"); + assert.throws( function() { + jQuery.parseJSON( "{'a':1}" ); }, null, "Single-quoted property raises an error" ); -}); + assert.throws( function() { + jQuery.parseJSON( "[,]" ); + }, null, "Array element elision raises an error" ); + assert.throws( function() { + jQuery.parseJSON( "{},[]" ); + }, null, "Comma expression raises an error" ); + assert.throws( function() { + jQuery.parseJSON( "[]\n,{}" ); + }, null, "Newline-containing comma expression raises an error" ); + assert.throws( function() { + jQuery.parseJSON( "\"\"\n\"\"" ); + }, null, "Automatic semicolon insertion raises an error" ); + + assert.strictEqual( jQuery.parseJSON( [ 0 ] ), 0, "Input cast to string" ); +} ); + +QUnit.test( "jQuery.parseXML", function( assert ) { + assert.expect( 8 ); -test("jQuery.parseXML", 8, function(){ var xml, tmp; try { xml = jQuery.parseXML( "

        A well-formed xml string

        " ); tmp = xml.getElementsByTagName( "p" )[ 0 ]; - ok( !!tmp, "

        present in document" ); + assert.ok( !!tmp, "

        present in document" ); tmp = tmp.getElementsByTagName( "b" )[ 0 ]; - ok( !!tmp, " present in document" ); - strictEqual( tmp.childNodes[ 0 ].nodeValue, "well-formed", " text is as expected" ); - } catch (e) { - strictEqual( e, undefined, "unexpected error" ); + assert.ok( !!tmp, " present in document" ); + assert.strictEqual( tmp.childNodes[ 0 ].nodeValue, "well-formed", " text is as expected" ); + } catch ( e ) { + assert.strictEqual( e, undefined, "unexpected error" ); } try { xml = jQuery.parseXML( "

        Not a <well-formed xml string

        " ); - ok( false, "invalid xml not detected" ); - } catch( e ) { - strictEqual( e.message, "Invalid XML:

        Not a <well-formed xml string

        ", "invalid xml detected" ); + assert.ok( false, "invalid xml not detected" ); + } catch ( e ) { + assert.strictEqual( e.message, "Invalid XML:

        Not a <well-formed xml string

        ", "invalid xml detected" ); } try { xml = jQuery.parseXML( "" ); - strictEqual( xml, null, "empty string => null document" ); + assert.strictEqual( xml, null, "empty string => null document" ); xml = jQuery.parseXML(); - strictEqual( xml, null, "undefined string => null document" ); + assert.strictEqual( xml, null, "undefined string => null document" ); xml = jQuery.parseXML( null ); - strictEqual( xml, null, "null string => null document" ); + assert.strictEqual( xml, null, "null string => null document" ); xml = jQuery.parseXML( true ); - strictEqual( xml, null, "non-string => null document" ); - } catch( e ) { - ok( false, "empty input throws exception" ); + assert.strictEqual( xml, null, "non-string => null document" ); + } catch ( e ) { + assert.ok( false, "empty input throws exception" ); } -}); +} ); -test("jQuery.camelCase()", function() { +QUnit.test( "jQuery.camelCase()", function( assert ) { var tests = { "foo-bar": "fooBar", @@ -1307,9 +1690,72 @@ test("jQuery.camelCase()", function() { "-ms-take": "msTake" }; - expect(7); + assert.expect( 7 ); jQuery.each( tests, function( key, val ) { - equal( jQuery.camelCase( key ), val, "Converts: " + key + " => " + val ); - }); -}); + assert.equal( jQuery.camelCase( key ), val, "Converts: " + key + " => " + val ); + } ); +} ); + +testIframeWithCallback( + "Conditional compilation compatibility (#13274)", + "core/cc_on.html", + function( cc_on, errors, $, assert ) { + assert.expect( 3 ); + assert.ok( true, "JScript conditional compilation " + ( cc_on ? "supported" : "not supported" ) ); + assert.deepEqual( errors, [], "No errors" ); + assert.ok( $(), "jQuery executes" ); + } +); + +// iOS7 doesn't fire the load event if the long-loading iframe gets its source reset to about:blank. +// This makes this test fail but it doesn't seem to cause any real-life problems so blacklisting +// this test there is preferred to complicating the hard-to-test core/ready code further. +if ( !/iphone os 7_/i.test( navigator.userAgent ) ) { + testIframeWithCallback( + "document ready when jQuery loaded asynchronously (#13655)", + "core/dynamic_ready.html", + function( ready, assert ) { + assert.expect( 1 ); + assert.equal( true, ready, "document ready correctly fired when jQuery is loaded after DOMContentLoaded" ); + } + ); +} + +testIframeWithCallback( + "Tolerating alias-masked DOM properties (#14074)", + "core/aliased.html", + function( errors, assert ) { + assert.expect( 1 ); + assert.deepEqual( errors, [], "jQuery loaded" ); + } +); + +testIframeWithCallback( + "Don't call window.onready (#14802)", + "core/onready.html", + function( error, assert ) { + assert.expect( 1 ); + assert.equal( error, false, "no call to user-defined onready" ); + } +); + +QUnit.test( "Iterability of jQuery objects (gh-1693)", function( assert ) { + /* jshint unused: false */ + assert.expect( 1 ); + + var i, elem, result; + + if ( typeof Symbol === "function" ) { + + elem = jQuery( "
        " ); + result = ""; + + try { + eval( "for ( i of elem ) { result += i.nodeName; }" ); + } catch ( e ) {} + assert.equal( result, "DIVSPANA", "for-of works on jQuery objects" ); + } else { + assert.ok( true, "The browser doesn't support Symbols" ); + } +} ); diff --git a/test/unit/css.js b/test/unit/css.js index e7ada8bf90..e1cfea19ea 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -1,264 +1,344 @@ if ( jQuery.css ) { -module("css", { teardown: moduleTeardown }); +QUnit.module( "css", { teardown: moduleTeardown } ); -test("css(String|Hash)", function() { - expect( 46 ); +QUnit.test( "css(String|Hash)", function( assert ) { + assert.expect( 43 ); - equal( jQuery("#qunit-fixture").css("display"), "block", "Check for css property \"display\"" ); + assert.equal( jQuery( "#qunit-fixture" ).css( "display" ), "block", "Check for css property \"display\"" ); - ok( jQuery("#nothiddendiv").is(":visible"), "Modifying CSS display: Assert element is visible" ); - jQuery("#nothiddendiv").css({ display: "none" }); - ok( !jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is hidden" ); - var $child = jQuery("#nothiddendivchild").css({ "width": "20%", "height": "20%" }); - notEqual( $child.css("width"), "20px", "Retrieving a width percentage on the child of a hidden div returns percentage" ); - notEqual( $child.css("height"), "20px", "Retrieving a height percentage on the child of a hidden div returns percentage" ); + var $child, div, div2, width, height, child, prctval, checkval, old; - jQuery("#nothiddendiv").css({"display": "block"}); - ok( jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is visible"); - ok( jQuery(window).is(":visible"), "Calling is(':visible') on window does not throw an error in IE."); - ok( jQuery(document).is(":visible"), "Calling is(':visible') on document does not throw an error in IE."); + $child = jQuery( "#nothiddendivchild" ).css( { "width": "20%", "height": "20%" } ); + assert.notEqual( $child.css( "width" ), "20px", "Retrieving a width percentage on the child of a hidden div returns percentage" ); + assert.notEqual( $child.css( "height" ), "20px", "Retrieving a height percentage on the child of a hidden div returns percentage" ); - var div = jQuery( "
        " ); + div = jQuery( "
        " ); // These should be "auto" (or some better value) // temporarily provide "0px" for backwards compat - equal( div.css("width"), "0px", "Width on disconnected node." ); - equal( div.css("height"), "0px", "Height on disconnected node." ); + assert.equal( div.css( "width" ), "0px", "Width on disconnected node." ); + assert.equal( div.css( "height" ), "0px", "Height on disconnected node." ); - div.css({ "width": 4, "height": 4 }); + div.css( { "width": 4, "height": 4 } ); - equal( div.css("width"), "4px", "Width on disconnected node." ); - equal( div.css("height"), "4px", "Height on disconnected node." ); + assert.equal( div.css( "width" ), "4px", "Width on disconnected node." ); + assert.equal( div.css( "height" ), "4px", "Height on disconnected node." ); - var div2 = jQuery( "
        "); + element = jQuery( "" ); clone = element.clone(); - equal( clone[ 0 ].defaultValue, "foo", "Textarea defaultValue cloned correctly" ); -}); + assert.equal( clone[ 0 ].defaultValue, "foo", "Textarea defaultValue cloned correctly" ); +} ); -test( "clone(multiple selected options) (Bug #8129)", function() { +QUnit.test( "clone(multiple selected options) (Bug #8129)", function( assert ) { - expect( 1 ); + assert.expect( 1 ); - var element = jQuery(""); + var element = jQuery( "" ); - equal( element.clone().find("option:selected").length, element.find("option:selected").length, "Multiple selected options cloned correctly" ); + assert.equal( element.clone().find( "option:selected" ).length, element.find( "option:selected" ).length, "Multiple selected options cloned correctly" ); -}); +} ); -test( "clone() on XML nodes", function() { +QUnit.test( "clone() on XML nodes", function( assert ) { - expect( 2 ); + assert.expect( 2 ); var xml = createDashboardXML(), - root = jQuery(xml.documentElement).clone(), - origTab = jQuery("tab", xml).eq( 0 ), - cloneTab = jQuery("tab", root).eq( 0 ); + root = jQuery( xml.documentElement ).clone(), + origTab = jQuery( "tab", xml ).eq( 0 ), + cloneTab = jQuery( "tab", root ).eq( 0 ); - origTab.text("origval"); - cloneTab.text("cloneval"); - equal( origTab.text(), "origval", "Check original XML node was correctly set" ); - equal( cloneTab.text(), "cloneval", "Check cloned XML node was correctly set" ); -}); + origTab.text( "origval" ); + cloneTab.text( "cloneval" ); + assert.equal( origTab.text(), "origval", "Check original XML node was correctly set" ); + assert.equal( cloneTab.text(), "cloneval", "Check cloned XML node was correctly set" ); +} ); -test( "clone() on local XML nodes with html5 nodename", function() { +QUnit.test( "clone() on local XML nodes with html5 nodename", function( assert ) { - expect( 2 ); + assert.expect( 2 ); var $xmlDoc = jQuery( jQuery.parseXML( "" ) ), $meter = $xmlDoc.find( "meter" ).clone(); - equal( $meter[ 0 ].nodeName, "meter", "Check if nodeName was not changed due to cloning" ); - equal( $meter[ 0 ].nodeType, 1, "Check if nodeType is not changed due to cloning" ); -}); + assert.equal( $meter[ 0 ].nodeName, "meter", "Check if nodeName was not changed due to cloning" ); + assert.equal( $meter[ 0 ].nodeType, 1, "Check if nodeType is not changed due to cloning" ); +} ); -test( "html(undefined)", function() { +QUnit.test( "html(undefined)", function( assert ) { - expect( 1 ); + assert.expect( 1 ); - equal( jQuery("#foo").html("test").html(undefined).html().toLowerCase(), "test", ".html(undefined) is chainable (#5571)" ); -}); + assert.equal( jQuery( "#foo" ).html( "test" ).html( undefined ).html().toLowerCase(), "test", ".html(undefined) is chainable (#5571)" ); +} ); -test( "html() on empty set", function() { +QUnit.test( "html() on empty set", function( assert ) { - expect( 1 ); + assert.expect( 1 ); - strictEqual( jQuery().html(), undefined, ".html() returns undefined for empty sets (#11962)" ); -}); + assert.strictEqual( jQuery().html(), undefined, ".html() returns undefined for empty sets (#11962)" ); +} ); -var childNodeNames = function( node ) { +function childNodeNames( node ) { return jQuery.map( node.childNodes, function( child ) { return child.nodeName.toUpperCase(); - }).join(" "); -}; + } ).join( " " ); +} -var testHtml = function( valueObj ) { - expect( 37 ); +function testHtml( valueObj, assert ) { + assert.expect( 39 ); var actual, expected, tmp, - div = jQuery("
        "), - fixture = jQuery("#qunit-fixture"); + div = jQuery( "
        " ), + fixture = jQuery( "#qunit-fixture" ); - div.html( valueObj("
        ") ); - equal( div.children().length, 2, "Found children" ); - equal( div.children().children().length, 1, "Found grandchild" ); + div.html( valueObj( "
        " ) ); + assert.equal( div.children().length, 2, "Found children" ); + assert.equal( div.children().children().length, 1, "Found grandchild" ); actual = []; expected = []; - tmp = jQuery("").html( valueObj("area") ).each(function() { - expected.push("AREA"); + tmp = jQuery( "" ).html( valueObj( "area" ) ).each( function() { + expected.push( "AREA" ); actual.push( childNodeNames( this ) ); - }); - equal( expected.length, 1, "Expecting one parent" ); - deepEqual( actual, expected, "Found the inserted area element" ); + } ); + assert.equal( expected.length, 1, "Expecting one parent" ); + assert.deepEqual( actual, expected, "Found the inserted area element" ); - equal( div.html(valueObj(5)).html(), "5", "Setting a number as html" ); - equal( div.html(valueObj(0)).html(), "0", "Setting a zero as html" ); + assert.equal( div.html( valueObj( 5 ) ).html(), "5", "Setting a number as html" ); + assert.equal( div.html( valueObj( 0 ) ).html(), "0", "Setting a zero as html" ); + assert.equal( div.html( valueObj( Infinity ) ).html(), "Infinity", "Setting Infinity as html" ); + assert.equal( div.html( valueObj( NaN ) ).html(), "", "Setting NaN as html" ); + assert.equal( div.html( valueObj( 1e2 ) ).html(), "100", "Setting exponential number notation as html" ); - div.html( valueObj(" &") ); - equal( + div.html( valueObj( " &" ) ); + assert.equal( div[ 0 ].innerHTML.replace( /\xA0/, " " ), " &", "Entities are passed through correctly" ); tmp = "<div>hello1</div>"; - equal( div.html(valueObj(tmp) ).html().replace( />/g, ">" ), tmp, "Escaped html" ); + assert.equal( div.html( valueObj( tmp ) ).html().replace( />/g, ">" ), tmp, "Escaped html" ); tmp = "x" + tmp; - equal( div.html(valueObj( tmp )).html().replace( />/g, ">" ), tmp, "Escaped html, leading x" ); + assert.equal( div.html( valueObj( tmp ) ).html().replace( />/g, ">" ), tmp, "Escaped html, leading x" ); tmp = " " + tmp.slice( 1 ); - equal( div.html(valueObj( tmp )).html().replace( />/g, ">" ), tmp, "Escaped html, leading space" ); + assert.equal( div.html( valueObj( tmp ) ).html().replace( />/g, ">" ), tmp, "Escaped html, leading space" ); actual = []; expected = []; tmp = {}; - jQuery("#nonnodes").contents().html( valueObj("bold") ).each(function() { + jQuery( "#nonnodes" ).contents().html( valueObj( "bold" ) ).each( function() { var html = jQuery( this ).html(); tmp[ this.nodeType ] = true; expected.push( this.nodeType === 1 ? "bold" : undefined ); actual.push( html ? html.toLowerCase() : html ); - }); - deepEqual( actual, expected, "Set containing element, text node, comment" ); - ok( tmp[ 1 ], "element" ); - ok( tmp[ 3 ], "text node" ); - ok( tmp[ 8 ], "comment" ); + } ); + assert.deepEqual( actual, expected, "Set containing element, text node, comment" ); + assert.ok( tmp[ 1 ], "element" ); + assert.ok( tmp[ 3 ], "text node" ); + assert.ok( tmp[ 8 ], "comment" ); actual = []; expected = []; - fixture.find("> div").html( valueObj("test") ).each(function() { - expected.push("B"); + fixture.children( "div" ).html( valueObj( "test" ) ).each( function() { + expected.push( "B" ); actual.push( childNodeNames( this ) ); - }); - equal( expected.length, 7, "Expecting many parents" ); - deepEqual( actual, expected, "Correct childNodes after setting HTML" ); + } ); + assert.equal( expected.length, 7, "Expecting many parents" ); + assert.deepEqual( actual, expected, "Correct childNodes after setting HTML" ); actual = []; expected = []; - fixture.html( valueObj("") ).each(function() { - expected.push("STYLE"); + fixture.html( valueObj( "" ) ).each( function() { + expected.push( "STYLE" ); actual.push( childNodeNames( this ) ); - }); - equal( expected.length, 1, "Expecting one parent" ); - deepEqual( actual, expected, "Found the inserted style element" ); + } ); + assert.equal( expected.length, 1, "Expecting one parent" ); + assert.deepEqual( actual, expected, "Found the inserted style element" ); - fixture.html( valueObj("" ) ); + jQuery( "#qunit-fixture select" ).html( valueObj( "" ) ); + assert.equal( jQuery( "#qunit-fixture select" ).val(), "O2", "Selected option correct" ); tmp = fixture.html( - valueObj([ + valueObj( [ "", "", "", @@ -1636,213 +1749,329 @@ var testHtml = function( valueObj ) { "", "", "
        " - ].join("")) - ).find("script"); - equal( tmp.length, 8, "All script tags remain." ); - equal( tmp[ 0 ].type, "something/else", "Non-evaluated type." ); - equal( tmp[ 1 ].type, "text/javascript", "Evaluated type." ); + ].join( "" ) ) + ).find( "script" ); + assert.equal( tmp.length, 8, "All script tags remain." ); + assert.equal( tmp[ 0 ].type, "something/else", "Non-evaluated type." ); + assert.equal( tmp[ 1 ].type, "text/javascript", "Evaluated type." ); - fixture.html( valueObj("") ); - fixture.html( valueObj("") ); - fixture.html( valueObj("") ); - fixture.html( valueObj("foo
        ") ); + fixture.html( valueObj( "" ) ); + fixture.html( valueObj( "" ) ); + fixture.html( valueObj( "" ) ); + fixture.html( valueObj( "foo
        " ) ); jQuery.scriptorder = 0; - fixture.html( valueObj([ + fixture.html( valueObj( [ "", "", "" - ].join("")) ); + ].join( "" ) ) ); +} - QUnit.reset(); - fixture.html( valueObj( fixture.text() ) ); - ok( /^[^<]*[^<\s][^<]*$/.test( fixture.html() ), "Replace html with text" ); -}; +QUnit.test( "html(String|Number)", function( assert ) { + testHtml( manipulationBareObj, assert ); +} ); -test( "html(String)", function() { - testHtml( manipulationBareObj ); -}); +QUnit.test( "html(Function)", function( assert ) { + testHtml( manipulationFunctionReturningObj, assert ); +} ); -test( "html(Function)", function() { - testHtml( manipulationFunctionReturningObj ); -}); +QUnit.test( "html( $.text() )", function( assert ) { -test( "html(Function) with incoming value", function() { + assert.expect( 1 ); - expect( 18 ); + var fixture = jQuery( "#qunit-fixture" ); + fixture.html( fixture.text() ); + assert.ok( /^[^<]*[^<\s][^<]*$/.test( fixture.html() ), "Replace html with text" ); +} ); - var els, actualhtml, pass, j, $div, $div2, insert; +QUnit.test( "html( fn ) returns $.text()", function( assert ) { - els = jQuery("#foo > p"); - actualhtml = els.map(function() { + assert.expect( 1 ); + + var fixture = jQuery( "#qunit-fixture" ); + fixture.html( manipulationFunctionReturningObj( fixture.text() ) ); + assert.ok( /^[^<]*[^<\s][^<]*$/.test( fixture.html() ), "Replace html with text" ); +} ); + +QUnit.test( "html(Function) with incoming value -- direct selection", function( assert ) { + + assert.expect( 4 ); + + var els, actualhtml, pass; + + els = jQuery( "#foo > p" ); + actualhtml = els.map( function() { return jQuery( this ).html(); - }); + } ); - els.html(function( i, val ) { - equal( val, actualhtml[ i ], "Make sure the incoming value is correct." ); + els.html( function( i, val ) { + assert.equal( val, actualhtml[ i ], "Make sure the incoming value is correct." ); return "test"; - }); + } ); pass = true; - els.each(function() { + els.each( function() { if ( this.childNodes.length !== 1 ) { pass = false; } - }); - ok( pass, "Set HTML" ); + } ); + assert.ok( pass, "Set HTML" ); +} ); - QUnit.reset(); - // using contents will get comments regular, text, and comment nodes - j = jQuery("#nonnodes").contents(); - actualhtml = j.map(function() { +QUnit.test( "html(Function) with incoming value -- jQuery.contents()", function( assert ) { + + assert.expect( 14 ); + + var actualhtml, j, $div, $div2, insert; + + j = jQuery( "#nonnodes" ).contents(); + actualhtml = j.map( function() { return jQuery( this ).html(); - }); + } ); - j.html(function( i, val ) { - equal( val, actualhtml[ i ], "Make sure the incoming value is correct." ); + j.html( function( i, val ) { + assert.equal( val, actualhtml[ i ], "Make sure the incoming value is correct." ); return "bold"; - }); + } ); // Handle the case where no comment is in the document if ( j.length === 2 ) { - equal( null, null, "Make sure the incoming value is correct." ); + assert.equal( null, null, "Make sure the incoming value is correct." ); } - j.find("b").removeData(); - equal( j.html().replace( / xmlns="[^"]+"/g, "" ).toLowerCase(), "bold", "Check node,textnode,comment with html()" ); + assert.equal( j.html().replace( / xmlns="[^"]+"/g, "" ).toLowerCase(), "bold", "Check node,textnode,comment with html()" ); - $div = jQuery("
        "); + $div = jQuery( "
        " ); - equal( $div.html(function( i, val ) { - equal( val, "", "Make sure the incoming value is correct." ); + assert.equal( $div.html( function( i, val ) { + assert.equal( val, "", "Make sure the incoming value is correct." ); return 5; - }).html(), "5", "Setting a number as html" ); + } ).html(), "5", "Setting a number as html" ); - equal( $div.html(function( i, val ) { - equal( val, "5", "Make sure the incoming value is correct." ); + assert.equal( $div.html( function( i, val ) { + assert.equal( val, "5", "Make sure the incoming value is correct." ); return 0; - }).html(), "0", "Setting a zero as html" ); + } ).html(), "0", "Setting a zero as html" ); - $div2 = jQuery("
        "); + $div2 = jQuery( "
        " ); insert = "<div>hello1</div>"; - equal( $div2.html(function( i, val ) { - equal( val, "", "Make sure the incoming value is correct." ); + assert.equal( $div2.html( function( i, val ) { + assert.equal( val, "", "Make sure the incoming value is correct." ); return insert; - }).html().replace(/>/g, ">"), insert, "Verify escaped insertion." ); + } ).html().replace( />/g, ">" ), insert, "Verify escaped insertion." ); - equal( $div2.html(function( i, val ) { - equal( val.replace(/>/g, ">"), insert, "Make sure the incoming value is correct." ); + assert.equal( $div2.html( function( i, val ) { + assert.equal( val.replace( />/g, ">" ), insert, "Make sure the incoming value is correct." ); return "x" + insert; - }).html().replace( />/g, ">" ), "x" + insert, "Verify escaped insertion." ); + } ).html().replace( />/g, ">" ), "x" + insert, "Verify escaped insertion." ); - equal( $div2.html(function( i, val ) { - equal( val.replace( />/g, ">" ), "x" + insert, "Make sure the incoming value is correct." ); + assert.equal( $div2.html( function( i, val ) { + assert.equal( val.replace( />/g, ">" ), "x" + insert, "Make sure the incoming value is correct." ); return " " + insert; - }).html().replace( />/g, ">" ), " " + insert, "Verify escaped insertion." ); -}); + } ).html().replace( />/g, ">" ), " " + insert, "Verify escaped insertion." ); +} ); -test( "clone()/html() don't expose jQuery/Sizzle expandos (#12858)", function() { +QUnit.test( "clone()/html() don't expose jQuery/Sizzle expandos (#12858)", function( assert ) { - expect( 2 ); + assert.expect( 2 ); - var $content = jQuery("
        text
        ").appendTo("#qunit-fixture"), + var $content = jQuery( "
        text
        " ).appendTo( "#qunit-fixture" ), expected = /^text<\/i><\/b>$/i; - // Attach jQuery and Sizzle data (the latter by conducting a non-qSA search) - $content.find(":nth-child(1):lt(4)").data( "test", true ); + // Attach jQuery and Sizzle data (the latter with a non-qSA nth-child) + try { + $content.find( ":nth-child(1):lt(4)" ).data( "test", true ); + + // But don't break on a non-Sizzle build + } catch ( e ) { + $content.find( "*" ).data( "test", true ); + } + + assert.ok( expected.test( $content.clone( false )[ 0 ].innerHTML ), "clone()" ); + assert.ok( expected.test( $content.html() ), "html()" ); +} ); - ok( expected.test( $content.clone( false )[ 0 ].innerHTML ), "clone()" ); - ok( expected.test( $content.html() ), "html()" ); -}); +QUnit.test( "remove() no filters", function( assert ) { -var testRemove = function( method ) { - var first = jQuery("#ap").children(":first"); + assert.expect( 3 ); - first.data("foo", "bar"); + var first = jQuery( "#ap" ).children().first(); - jQuery("#ap").children()[ method ](); - ok( jQuery("#ap").text().length > 10, "Check text is not removed" ); - equal( jQuery("#ap").children().length, 0, "Check remove" ); + first.data( "foo", "bar" ); - equal( first.data("foo"), method == "remove" ? null : "bar", "first data" ); + jQuery( "#ap" ).children().remove(); + assert.ok( jQuery( "#ap" ).text().length > 10, "Check text is not removed" ); + assert.equal( jQuery( "#ap" ).children().length, 0, "Check remove" ); - QUnit.reset(); - jQuery("#ap").children()[ method ]("a"); - ok( jQuery("#ap").text().length > 10, "Check text is not removed" ); - equal( jQuery("#ap").children().length, 1, "Check filtered remove" ); + assert.equal( first.data( "foo" ), null, "first data" ); - jQuery("#ap").children()[ method ]("a, code"); - equal( jQuery("#ap").children().length, 0, "Check multi-filtered remove" ); +} ); + +QUnit.test( "remove() with filters", function( assert ) { + + assert.expect( 8 ); + + var markup, div; + jQuery( "#ap" ).children().remove( "a" ); + assert.ok( jQuery( "#ap" ).text().length > 10, "Check text is not removed" ); + assert.equal( jQuery( "#ap" ).children().length, 1, "Check filtered remove" ); + + jQuery( "#ap" ).children().remove( "a, code" ); + assert.equal( jQuery( "#ap" ).children().length, 0, "Check multi-filtered remove" ); + + // Positional and relative selectors + markup = "
        1234
        "; + div = jQuery( markup ); + div.children().remove( "span:nth-child(2n)" ); + assert.equal( div.text(), "13", "relative selector in remove" ); + div = jQuery( markup ); + div.children().remove( "span:first" ); + assert.equal( div.text(), "234", "positional selector in remove" ); + div = jQuery( markup ); + div.children().remove( "span:last" ); + assert.equal( div.text(), "123", "positional selector in remove" ); // using contents will get comments regular, text, and comment nodes // Handle the case where no comment is in the document - ok( jQuery("#nonnodes").contents().length >= 2, "Check node,textnode,comment remove works" ); - jQuery("#nonnodes").contents()[ method ](); - equal( jQuery("#nonnodes").contents().length, 0, "Check node,textnode,comment remove works" ); + assert.ok( jQuery( "#nonnodes" ).contents().length >= 2, "Check node,textnode,comment remove works" ); + jQuery( "#nonnodes" ).contents().remove(); + assert.equal( jQuery( "#nonnodes" ).contents().length, 0, "Check node,textnode,comment remove works" ); +} ); - // manually clean up detached elements - if (method === "detach") { - first.remove(); - } -}; - -test( "remove()", 8, function() { - testRemove("remove"); -}); +QUnit.test( "remove() event cleaning ", function( assert ) { + assert.expect( 1 ); -test( "remove() event cleaning ", 1, function() { var count, first, cleanUp; count = 0; - first = jQuery("#ap").children(":first"); - cleanUp = first.click(function() { + first = jQuery( "#ap" ).children().first(); + cleanUp = first.on( "click", function() { count++; - }).remove().appendTo("#qunit-fixture").click(); + } ).remove().appendTo( "#qunit-fixture" ).trigger( "click" ); - strictEqual( 0, count, "Event handler has been removed" ); + assert.strictEqual( 0, count, "Event handler has been removed" ); // Clean up detached data cleanUp.remove(); -}); +} ); + +QUnit.test( "remove() in document order #13779", function( assert ) { + assert.expect( 1 ); + + var last, + cleanData = jQuery.cleanData; + + jQuery.cleanData = function( nodes ) { + last = jQuery.text( nodes[ 0 ] ); + cleanData.call( this, nodes ); + }; + + jQuery( "#qunit-fixture" ).append( + jQuery.parseHTML( + "
        1
        " + + "
        2
        " + + "
        3
        " + ) + ); + + jQuery( ".removal-fixture" ).remove(); + + assert.equal( last, 3, "The removal fixtures were removed in document order" ); + + jQuery.cleanData = cleanData; +} ); + +QUnit.test( "detach() no filters", function( assert ) { + + assert.expect( 3 ); + + var first = jQuery( "#ap" ).children().first(); + + first.data( "foo", "bar" ); + + jQuery( "#ap" ).children().detach(); + assert.ok( jQuery( "#ap" ).text().length > 10, "Check text is not removed" ); + assert.equal( jQuery( "#ap" ).children().length, 0, "Check remove" ); + + assert.equal( first.data( "foo" ), "bar" ); + first.remove(); + +} ); + +QUnit.test( "detach() with filters", function( assert ) { + + assert.expect( 8 ); -test( "detach()", 8, function() { - testRemove("detach"); -}); + var markup, div; + jQuery( "#ap" ).children().detach( "a" ); + assert.ok( jQuery( "#ap" ).text().length > 10, "Check text is not removed" ); + assert.equal( jQuery( "#ap" ).children().length, 1, "Check filtered remove" ); + + jQuery( "#ap" ).children().detach( "a, code" ); + assert.equal( jQuery( "#ap" ).children().length, 0, "Check multi-filtered remove" ); + + // Positional and relative selectors + markup = "
        1234
        "; + div = jQuery( markup ); + div.children().detach( "span:nth-child(2n)" ); + assert.equal( div.text(), "13", "relative selector in detach" ); + div = jQuery( markup ); + div.children().detach( "span:first" ); + assert.equal( div.text(), "234", "positional selector in detach" ); + div = jQuery( markup ); + div.children().detach( "span:last" ); + assert.equal( div.text(), "123", "positional selector in detach" ); + + // using contents will get comments regular, text, and comment nodes + // Handle the case where no comment is in the document + assert.ok( jQuery( "#nonnodes" ).contents().length >= 2, "Check node,textnode,comment remove works" ); + jQuery( "#nonnodes" ).contents().detach(); + assert.equal( jQuery( "#nonnodes" ).contents().length, 0, "Check node,textnode,comment remove works" ); +} ); + +QUnit.test( "detach() event cleaning ", function( assert ) { + assert.expect( 1 ); -test( "detach() event cleaning ", 1, function() { var count, first, cleanUp; count = 0; - first = jQuery("#ap").children(":first"); - cleanUp = first.click(function() { + first = jQuery( "#ap" ).children().first(); + cleanUp = first.on( "click", function() { count++; - }).detach().appendTo("#qunit-fixture").click(); + } ).detach().appendTo( "#qunit-fixture" ).trigger( "click" ); - strictEqual( 1, count, "Event handler has not been removed" ); + assert.strictEqual( 1, count, "Event handler has not been removed" ); // Clean up detached data cleanUp.remove(); -}); +} ); -test("empty()", function() { +QUnit.test( "empty()", function( assert ) { - expect( 3 ); + assert.expect( 6 ); - equal( jQuery("#ap").children().empty().text().length, 0, "Check text is removed" ); - equal( jQuery("#ap").children().length, 4, "Check elements are not removed" ); + assert.equal( jQuery( "#ap" ).children().empty().text().length, 0, "Check text is removed" ); + assert.equal( jQuery( "#ap" ).children().length, 4, "Check elements are not removed" ); // using contents will get comments regular, text, and comment nodes - var j = jQuery("#nonnodes").contents(); + var j = jQuery( "#nonnodes" ).contents(); j.empty(); - equal( j.html(), "", "Check node,textnode,comment empty works" ); -}); + assert.equal( j.html(), "", "Check node,textnode,comment empty works" ); -test( "jQuery.cleanData", function() { + // Ensure oldIE empties selects (#12336) + assert.notEqual( jQuery( "#select1" ).find( "option" ).length, 0, "Have some initial options" ); + jQuery( "#select1" ).empty(); + assert.equal( jQuery( "#select1" ).find( "option" ).length, 0, "No more option elements found" ); + assert.equal( jQuery( "#select1" )[ 0 ].options.length, 0, "options.length cleared as well" ); +} ); - expect( 14 ); +QUnit.test( "jQuery.cleanData", function( assert ) { + + assert.expect( 14 ); var type, pos, div, child; @@ -1853,10 +2082,10 @@ test( "jQuery.cleanData", function() { // Should both do nothing pos = "Outer"; - div.trigger("click"); + div.trigger( "click" ); pos = "Inner"; - div.children().trigger("click"); + div.children().trigger( "click" ); type = "empty"; div = getDiv(); @@ -1867,11 +2096,11 @@ test( "jQuery.cleanData", function() { // Should trigger 1 pos = "Outer"; - div.trigger("click"); + div.trigger( "click" ); // Should do nothing pos = "Inner"; - child.trigger("click"); + child.trigger( "click" ); // Should trigger 2 div.remove(); @@ -1882,45 +2111,84 @@ test( "jQuery.cleanData", function() { child = div.children(); // Should trigger 2 remove event - div.html("
        "); + div.html( "
        " ); // Should trigger 1 pos = "Outer"; - div.trigger("click"); + div.trigger( "click" ); // Should do nothing pos = "Inner"; - child.trigger("click"); + child.trigger( "click" ); // Should trigger 2 div.remove(); function getDiv() { - var div = jQuery("
        ").click(function() { - ok( true, type + " " + pos + " Click event fired." ); - }).focus(function() { - ok( true, type + " " + pos + " Focus event fired." ); - }).find("div").click(function() { - ok( false, type + " " + pos + " Click event fired." ); - }).focus(function() { - ok( false, type + " " + pos + " Focus event fired." ); - }).end().appendTo("body"); + var div = jQuery( "
        " ).on( "click", function() { + assert.ok( true, type + " " + pos + " Click event fired." ); + } ).on( "focus", function() { + assert.ok( true, type + " " + pos + " Focus event fired." ); + } ).find( "div" ).on( "click", function() { + assert.ok( false, type + " " + pos + " Click event fired." ); + } ).on( "focus", function() { + assert.ok( false, type + " " + pos + " Focus event fired." ); + } ).end().appendTo( "body" ); div[ 0 ].detachEvent = div[ 0 ].removeEventListener = function( t ) { - ok( true, type + " Outer " + t + " event unbound" ); + assert.ok( true, type + " Outer " + t + " event unbound" ); }; div[ 0 ].firstChild.detachEvent = div[ 0 ].firstChild.removeEventListener = function( t ) { - ok( true, type + " Inner " + t + " event unbound" ); + assert.ok( true, type + " Inner " + t + " event unbound" ); }; return div; } -}); +} ); + +QUnit.test( "jQuery.cleanData eliminates all private data (gh-2127)", function( assert ) { + assert.expect( 3 ); + + var div = jQuery( "
        " ).appendTo( "#qunit-fixture" ); + + jQuery._data( div[ 0 ], "gh-2127", "testing" ); + + assert.ok( !jQuery.isEmptyObject( jQuery._data( div[ 0 ] ) ), "Ensure some private data exists" ); + + div.remove(); + + assert.ok( !jQuery.hasData( div[ 0 ] ), "Removed element hasData should return false" ); + + assert.ok( jQuery.isEmptyObject( jQuery._data( div[ 0 ] ) ), + "Private data is empty after node is removed" ); + + div.remove(); +} ); + +QUnit.test( "jQuery.cleanData eliminates all public data", function( assert ) { + assert.expect( 2 ); + + var key, + div = jQuery( "
        " ); + div.data( "some", "data" ); + assert.ok( !jQuery.isEmptyObject( jQuery.data( div[ 0 ] ) ), "Ensure some public data exists" ); + + div.remove(); + + assert.ok( !jQuery.hasData( div[ 0 ] ), "Removed element hasData should return false" ); + + // Make sure the expando is gone + for ( key in div[ 0 ] ) { + if ( /^jQuery/.test( key ) && jQuery[ key ] !== undefined ) { + assert.ok( false, "Expando was not removed when there was no more data" ); + } + } +} ); -test( "jQuery.buildFragment - no plain-text caching (Bug #6779)", function() { +QUnit.test( "domManip plain-text caching (trac-6779)", function( assert ) { - expect( 1 ); + assert.expect( 1 ); // DOM manipulation fails if added text matches an Object method var i, @@ -1931,77 +2199,65 @@ test( "jQuery.buildFragment - no plain-text caching (Bug #6779)", function() { try { $f.append( bad[ i ] ); } - catch( e ) {} + catch ( e ) {} } - equal( $f.text(), bad.join(""), "Cached strings that match Object properties" ); + assert.equal( $f.text(), bad.join( "" ), "Cached strings that match Object properties" ); $f.remove(); -}); - -test( "jQuery.html - execute scripts escaped with html comment or CDATA (#9221)", function() { - - expect( 3 ); - - jQuery([ - "" - ].join("\n")).appendTo("#qunit-fixture"); - jQuery([ - "" - ].join("\n")).appendTo("#qunit-fixture"); - jQuery([ - "" - ].join("\n")).appendTo("#qunit-fixture"); -}); - -test( "jQuery.buildFragment - plain objects are not a document #8950", function() { - - expect( 1 ); - - try { - jQuery( "", {} ); - ok( true, "Does not allow attribute object to be treated like a doc object" ); - } catch ( e ) {} -}); +} ); + +QUnit.test( "domManip executes scripts containing html comments or CDATA (trac-9221)", function( assert ) { + + assert.expect( 3 ); + + jQuery( [ + "" + ].join( "\n" ) ).appendTo( "#qunit-fixture" ); + + jQuery( [ + "" + ].join( "\n" ) ).appendTo( "#qunit-fixture" ); + + jQuery( [ + "" + ].join( "\n" ) ).appendTo( "#qunit-fixture" ); +} ); + +testIframeWithCallback( + "domManip tolerates window-valued document[0] in IE9/10 (trac-12266)", + "manipulation/iframe-denied.html", + function( test, assert ) { + assert.expect( 1 ); + assert.ok( test.status, test.description ); + } +); -test( "jQuery.clone - no exceptions for object elements #9587", function() { +QUnit.test( "jQuery.clone - no exceptions for object elements #9587", function( assert ) { - expect( 1 ); + assert.expect( 1 ); try { - jQuery("#no-clone-exception").clone(); - ok( true, "cloned with no exceptions" ); - } catch( e ) { - ok( false, e.message ); + jQuery( "#no-clone-exception" ).clone(); + assert.ok( true, "cloned with no exceptions" ); + } catch ( e ) { + assert.ok( false, e.message ); } -}); - -test( "jQuery() & wrap[Inner/All]() handle unknown elems (#10667)", function() { - - expect( 2 ); - - var $wraptarget = jQuery( "
        Target
        " ).appendTo( "#qunit-fixture" ), - $section = jQuery( "
        " ).appendTo( "#qunit-fixture" ); - - $wraptarget.wrapAll(""); - - notEqual( $wraptarget.parent("aside").get( 0 ).style.backgroundColor, "transparent", "HTML5 elements created with wrapAll inherit styles" ); - notEqual( $section.get( 0 ).style.backgroundColor, "transparent", "HTML5 elements create with jQuery( string ) inherit styles" ); -}); +} ); -test( "Cloned, detached HTML5 elems (#10667,10670)", function() { +QUnit.test( "Cloned, detached HTML5 elems (#10667,10670)", function( assert ) { - expect( 7 ); + assert.expect( 7 ); var $clone, $section = jQuery( "
        " ).appendTo( "#qunit-fixture" ); @@ -2011,49 +2267,51 @@ test( "Cloned, detached HTML5 elems (#10667,10670)", function() { // Infer that the test is being run in IE<=8 if ( $clone[ 0 ].outerHTML && !jQuery.support.opacity ) { + // This branch tests cloning nodes by reading the outerHTML, used only in IE<=8 - equal( $clone[ 0 ].outerHTML, "
        ", "detached clone outerHTML matches '
        '" ); + assert.equal( $clone[ 0 ].outerHTML, "
        ", "detached clone outerHTML matches '
        '" ); } else { + // This branch tests a known behaviour in modern browsers that should never fail. // Included for expected test count symmetry (expecting 1) - equal( $clone[ 0 ].nodeName, "SECTION", "detached clone nodeName matches 'SECTION' in modern browsers" ); + assert.equal( $clone[ 0 ].nodeName, "SECTION", "detached clone nodeName matches 'SECTION' in modern browsers" ); } // Bind an event - $section.bind( "click", function( event ) { - ok( true, "clone fired event" ); - }); + $section.on( "click", function() { + assert.ok( true, "clone fired event" ); + } ); // Second clone (will have an event bound) $clone = $section.clone( true ); // Trigger an event from the first clone - $clone.trigger("click"); - $clone.unbind("click"); + $clone.trigger( "click" ); + $clone.off( "click" ); // Add a child node with text to the original - $section.append("

        Hello

        "); + $section.append( "

        Hello

        " ); // Third clone (will have child node and text) $clone = $section.clone( true ); - equal( $clone.find("p").text(), "Hello", "Assert text in child of clone" ); + assert.equal( $clone.find( "p" ).text(), "Hello", "Assert text in child of clone" ); // Trigger an event from the third clone - $clone.trigger("click"); - $clone.unbind("click"); + $clone.trigger( "click" ); + $clone.off( "click" ); // Add attributes to copy - $section.attr({ + $section.attr( { "class": "foo bar baz", "title": "This is a title" - }); + } ); // Fourth clone (will have newly added attributes) $clone = $section.clone( true ); - equal( $clone.attr("class"), $section.attr("class"), "clone and element have same class attribute" ); - equal( $clone.attr("title"), $section.attr("title"), "clone and element have same title attribute" ); + assert.equal( $clone.attr( "class" ), $section.attr( "class" ), "clone and element have same class attribute" ); + assert.equal( $clone.attr( "title" ), $section.attr( "title" ), "clone and element have same title attribute" ); // Remove the original $section.remove(); @@ -2065,96 +2323,94 @@ test( "Cloned, detached HTML5 elems (#10667,10670)", function() { $clone.remove(); // Trigger an event from the clone of the clone - $section.trigger("click"); + $section.trigger( "click" ); // Unbind any remaining events - $section.unbind("click"); - $clone.unbind("click"); -}); + $section.off( "click" ); + $clone.off( "click" ); +} ); -test( "Guard against exceptions when clearing safeChildNodes", function() { +QUnit.test( "Guard against exceptions when clearing safeChildNodes", function( assert ) { - expect( 1 ); + assert.expect( 1 ); var div; try { - div = jQuery("

        "); - } catch(e) {} + div = jQuery( "

        " ); + } catch ( e ) {} - ok( div && div.jquery, "Created nodes safely, guarded against exceptions on safeChildNodes[ -1 ]" ); -}); + assert.ok( div && div.jquery, "Created nodes safely, guarded against exceptions on safeChildNodes[ -1 ]" ); +} ); -test( "Ensure oldIE creates a new set on appendTo (#8894)", function() { +QUnit.test( "Ensure oldIE creates a new set on appendTo (#8894)", function( assert ) { - expect( 5 ); + assert.expect( 5 ); - strictEqual( jQuery("
        ").clone().addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after jQuery.clone" ); - strictEqual( jQuery("
        ").find("p").end().addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after jQuery.fn.find" ); - strictEqual( jQuery("
        ").text("test").addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after jQuery.fn.text" ); - strictEqual( jQuery("").clone().addClass("test").appendTo("
        ").end().end().hasClass("test"), false, "Check jQuery.fn.appendTo after clone html5 element" ); - strictEqual( jQuery("

        ").appendTo("

        ").end().length, jQuery("

        test

        ").appendTo("
        ").end().length, "Elements created with createElement and with createDocumentFragment should be treated alike" ); -}); + assert.strictEqual( jQuery( "
        " ).clone().addClass( "test" ).appendTo( "
        " ).end().end().hasClass( "test" ), false, "Check jQuery.fn.appendTo after jQuery.clone" ); + assert.strictEqual( jQuery( "
        " ).find( "p" ).end().addClass( "test" ).appendTo( "
        " ).end().end().hasClass( "test" ), false, "Check jQuery.fn.appendTo after jQuery.fn.find" ); + assert.strictEqual( jQuery( "
        " ).text( "test" ).addClass( "test" ).appendTo( "
        " ).end().end().hasClass( "test" ), false, "Check jQuery.fn.appendTo after jQuery.fn.text" ); + assert.strictEqual( jQuery( "" ).clone().addClass( "test" ).appendTo( "
        " ).end().end().hasClass( "test" ), false, "Check jQuery.fn.appendTo after clone html5 element" ); + assert.strictEqual( jQuery( "

        " ).appendTo( "

        " ).end().length, jQuery( "

        test

        " ).appendTo( "
        " ).end().length, "Elements created with createElement and with createDocumentFragment should be treated alike" ); +} ); -test( "html() - script exceptions bubble (#11743)", function() { +QUnit.test( "html() - script exceptions bubble (#11743)", function( assert ) { - expect( 2 ); + assert.expect( 2 ); - raises(function() { - jQuery("#qunit-fixture").html(""); - ok( false, "error ignored" ); - }, "exception bubbled from inline script" ); + assert.throws( function() { + jQuery( "#qunit-fixture" ).html( "" ); + assert.ok( false, "Exception ignored" ); + }, "Exception bubbled from inline script" ); - raises(function() { - jQuery("#qunit-fixture").html(""); - ok( false, "error ignored" ); - }, "exception bubbled from remote script" ); -}); + if ( jQuery.ajax ) { + assert.throws( function() { + jQuery( "#qunit-fixture" ).html( "" ); + assert.ok( false, "Exception ignored" ); + }, "Exception thrown in remote script" ); + } else { + assert.ok( true, "No jQuery.ajax" ); + } +} ); -test( "checked state is cloned with clone()", function() { +QUnit.test( "checked state is cloned with clone()", function( assert ) { - expect( 2 ); + assert.expect( 2 ); - var elem = jQuery.parseHTML("")[ 0 ]; + var elem = jQuery.parseHTML( "" )[ 0 ]; elem.checked = false; - equal( jQuery(elem).clone().attr("id","clone")[ 0 ].checked, false, "Checked false state correctly cloned" ); + assert.equal( jQuery( elem ).clone().attr( "id", "clone" )[ 0 ].checked, false, "Checked false state correctly cloned" ); - elem = jQuery.parseHTML("")[ 0 ]; + elem = jQuery.parseHTML( "" )[ 0 ]; elem.checked = true; - equal( jQuery(elem).clone().attr("id","clone")[ 0 ].checked, true, "Checked true state correctly cloned" ); -}); + assert.equal( jQuery( elem ).clone().attr( "id", "clone" )[ 0 ].checked, true, "Checked true state correctly cloned" ); +} ); -test( "manipulate mixed jQuery and text (#12384, #12346)", function() { +QUnit.test( "manipulate mixed jQuery and text (#12384, #12346)", function( assert ) { - expect( 2 ); + assert.expect( 2 ); - var div = jQuery("
        a
        ").append( " ", jQuery("b"), " ", jQuery("c") ), + var div = jQuery( "
        a
        " ).append( " ", jQuery( "b" ), " ", jQuery( "c" ) ), nbsp = String.fromCharCode( 160 ); - equal( div.text(), "a" + nbsp + "b" + nbsp+ "c", "Appending mixed jQuery with text nodes" ); + assert.equal( div.text(), "a" + nbsp + "b" + nbsp + "c", "Appending mixed jQuery with text nodes" ); - div = jQuery("
        ") - .find("div") + div = jQuery( "
        " ) + .find( "div" ) .after( "

        a

        ", "

        b

        " ) .parent(); - equal( div.find("*").length, 3, "added 2 paragraphs after inner div" ); -}); - -testIframeWithCallback( "buildFragment works even if document[0] is iframe's window object in IE9/10 (#12266)", "manipulation/iframe-denied.html", function( test ) { - expect( 1 ); + assert.equal( div.find( "*" ).length, 3, "added 2 paragraphs after inner div" ); +} ); - ok( test.status, test.description ); -}); +QUnit.test( "script evaluation (#11795)", function( assert ) { -test( "script evaluation (#11795)", function() { - - expect( 11 ); + assert.expect( 13 ); var scriptsIn, scriptsOut, - fixture = jQuery("#qunit-fixture").empty(), - objGlobal = (function() { + fixture = jQuery( "#qunit-fixture" ).empty(), + objGlobal = ( function() { return this; - })(), + } )(), isOk = objGlobal.ok, notOk = function() { var args = arguments; @@ -2163,7 +2419,7 @@ test( "script evaluation (#11795)", function() { }; objGlobal.ok = notOk; - scriptsIn = jQuery([ + scriptsIn = jQuery( [ "", "", "", @@ -2174,44 +2430,108 @@ test( "script evaluation (#11795)", function() { "", "", "
        " - ].join("")); - scriptsIn.appendTo( jQuery("
        ") ); + ].join( "" ) ); + scriptsIn.appendTo( jQuery( "
        " ) ); objGlobal.ok = isOk; - scriptsOut = fixture.append( scriptsIn ).find("script"); - equal( scriptsOut[ 0 ].type, "something/else", "Non-evaluated type." ); - equal( scriptsOut[ 1 ].type, "text/javascript", "Evaluated type." ); - deepEqual( scriptsOut.get(), fixture.find("script").get(), "All script tags remain." ); + scriptsOut = fixture.append( scriptsIn ).find( "script" ); + assert.equal( scriptsOut[ 0 ].type, "something/else", "Non-evaluated type." ); + assert.equal( scriptsOut[ 1 ].type, "text/javascript", "Evaluated type." ); + assert.deepEqual( scriptsOut.get(), fixture.find( "script" ).get(), "All script tags remain." ); objGlobal.ok = notOk; - scriptsOut = scriptsOut.add( scriptsOut.clone() ).appendTo( fixture.find("div") ); - deepEqual( fixture.find("div script").get(), scriptsOut.get(), "Scripts cloned without reevaluation" ); + scriptsOut = scriptsOut.add( scriptsOut.clone() ).appendTo( fixture.find( "div" ) ); + assert.deepEqual( fixture.find( "div script" ).get(), scriptsOut.get(), "Scripts cloned without reevaluation" ); fixture.append( scriptsOut.detach() ); - deepEqual( fixture.find("> script").get(), scriptsOut.get(), "Scripts detached without reevaluation" ); + assert.deepEqual( fixture.children( "script" ).get(), scriptsOut.get(), "Scripts detached without reevaluation" ); objGlobal.ok = isOk; -}); -test( "wrapping scripts (#10470)", function() { + if ( jQuery.ajax ) { + Globals.register( "testBar" ); + jQuery( "#qunit-fixture" ).append( "", + done = assert.async(); - var script = document.createElement("script"); - script.text = script.textContent = "ok( !document.eval10470, 'script evaluated once' ); document.eval10470 = true;"; + jQuery.htmlPrefilter = function( html ) { + invocations++; + assert.equal( html, expectedArgument, "Expected input" ); - document.eval10470 = false; - jQuery("#qunit-fixture").empty()[0].appendChild( script ); - jQuery("#qunit-fixture script").wrap(""); - strictEqual( script.parentNode, jQuery("#qunit-fixture > b")[ 0 ], "correctly wrapped" ); - jQuery( script ).remove(); -}); + // Remove " ); + + setTimeout( function() { + if ( window[ "testSrcFoo" ] === "foo" ) { + assert.strictEqual( window[ "testFoo" ], window[ "testSrcFoo" ], "data-URI script executed" ); + + } else { + assert.ok( true, "data-URI script is not supported by this environment" ); + } + + QUnit.start(); + }, 100 ); +} ); diff --git a/test/unit/offset.js b/test/unit/offset.js index 08b90c3b8a..15d3895c3b 100644 --- a/test/unit/offset.js +++ b/test/unit/offset.js @@ -1,39 +1,40 @@ -(function() { +( function() { if ( !jQuery.fn.offset ) { return; } var supportsScroll, supportsFixedPosition, - forceScroll = jQuery("
        ").css({ width: 2000, height: 2000 }), + forceScroll = jQuery( "
        " ).css( { width: 2000, height: 2000 } ), checkSupport = function() { + // Only run once checkSupport = false; - var checkFixed = jQuery("
        ").css({ position: "fixed", top: "20px" }).appendTo("#qunit-fixture"); + var checkFixed = jQuery( "
        " ).css( { position: "fixed", top: "20px" } ).appendTo( "#qunit-fixture" ); // Must append to body because #qunit-fixture is hidden and elements inside it don't have a scrollTop - forceScroll.appendTo("body"); + forceScroll.appendTo( "body" ); window.scrollTo( 200, 200 ); supportsScroll = document.documentElement.scrollTop || document.body.scrollTop; forceScroll.detach(); // Safari subtracts parent border width here (which is 5px) - supportsFixedPosition = checkFixed[0].offsetTop === 20 || checkFixed[0].offsetTop === 15; + supportsFixedPosition = checkFixed[ 0 ].offsetTop === 20 || checkFixed[ 0 ].offsetTop === 15; checkFixed.remove(); }; -module("offset", { setup: function(){ +QUnit.module( "offset", { setup: function() { if ( typeof checkSupport === "function" ) { checkSupport(); } // Force a scroll value on the main window to ensure incorrect results // if offset is using the scroll offset of the parent window - forceScroll.appendTo("body"); + forceScroll.appendTo( "body" ); window.scrollTo( 1, 1 ); forceScroll.detach(); -}, teardown: moduleTeardown }); +}, teardown: moduleTeardown } ); /* Closure-compiler will roll static methods off of the jQuery object and so they will @@ -42,32 +43,35 @@ module("offset", { setup: function(){ the iframe window and the "jQuery" symbol is used to access any static methods. */ -test("empty set", function() { - expect(2); +test( "empty set", function() { + expect( 2 ); strictEqual( jQuery().offset(), undefined, "offset() returns undefined for empty set (#11962)" ); strictEqual( jQuery().position(), undefined, "position() returns undefined for empty set (#11962)" ); -}); +} ); -test("object without getBoundingClientRect", function() { - expect(2); +test( "object without getBoundingClientRect", function() { + expect( 2 ); // Simulates a browser without gBCR on elements, we just want to return 0,0 - var result = jQuery({ ownerDocument: document }).offset(); + var result = jQuery( { ownerDocument: document } ).offset(); equal( result.top, 0, "Check top" ); equal( result.left, 0, "Check left" ); -}); +} ); -test("disconnected node", function() { - expect(2); +QUnit.test( "disconnected element", function( assert ) { + assert.expect( 2 ); - var result = jQuery( document.createElement("div") ).offset(); + var result = jQuery( document.createElement( "div" ) ).offset(); - equal( result.top, 0, "Check top" ); - equal( result.left, 0, "Check left" ); -}); + // These tests are solely for 2.x/1.x consistency + // Retrieving offset on disconnected/hidden elements is not officially + // valid input, but will return zeros for back-compat + assert.equal( result.top, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" ); + assert.equal( result.left, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" ); +} ); -testIframe("offset/absolute", "absolute", function($, iframe) { - expect(4); +testIframe( "offset/absolute", "absolute", function( $, iframe, document, assert ) { + assert.expect( 4 ); var doc = iframe.document, tests; @@ -77,36 +81,36 @@ testIframe("offset/absolute", "absolute", function($, iframe) { { "id": "#absolute-1", "top": 1, "left": 1 } ]; jQuery.each( tests, function() { - equal( jQuery( this["id"], doc ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset().top" ); - equal( jQuery( this["id"], doc ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset().left" ); - }); - + assert.equal( jQuery( this[ "id" ], doc ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset().top" ); + assert.equal( jQuery( this[ "id" ], doc ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset().left" ); + } ); // get position tests = [ { "id": "#absolute-1", "top": 0, "left": 0 } ]; jQuery.each( tests, function() { - equal( jQuery( this["id"], doc ).position().top, this["top"], "jQuery('" + this["id"] + "').position().top" ); - equal( jQuery( this["id"], doc ).position().left, this["left"], "jQuery('" + this["id"] + "').position().left" ); - }); -}); + assert.equal( jQuery( this[ "id" ], doc ).position().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').position().top" ); + assert.equal( jQuery( this[ "id" ], doc ).position().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').position().left" ); + } ); +} ); + +testIframe( "offset/absolute", "absolute", function( $, window, document, assert ) { + assert.expect( 178 ); -testIframe("offset/absolute", "absolute", function( $ ) { - expect(178); + var tests, offset; // get offset tests - var tests = [ + tests = [ { "id": "#absolute-1", "top": 1, "left": 1 }, { "id": "#absolute-1-1", "top": 5, "left": 5 }, { "id": "#absolute-1-1-1", "top": 9, "left": 9 }, { "id": "#absolute-2", "top": 20, "left": 20 } ]; jQuery.each( tests, function() { - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset().top" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset().left" ); - }); - + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset().top" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset().left" ); + } ); // get position tests = [ @@ -116,15 +120,14 @@ testIframe("offset/absolute", "absolute", function( $ ) { { "id": "#absolute-2", "top": 19, "left": 19 } ]; jQuery.each( tests, function() { - equal( $( this["id"] ).position().top, this["top"], "jQuery('" + this["id"] + "').position().top" ); - equal( $( this["id"] ).position().left, this["left"], "jQuery('" + this["id"] + "').position().left" ); - }); + assert.equal( $( this[ "id" ] ).position().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').position().top" ); + assert.equal( $( this[ "id" ] ).position().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').position().left" ); + } ); // test #5781 - var offset = $( "#positionTest" ).offset({ "top": 10, "left": 10 }).offset(); - equal( offset.top, 10, "Setting offset on element with position absolute but 'auto' values." ); - equal( offset.left, 10, "Setting offset on element with position absolute but 'auto' values." ); - + offset = $( "#positionTest" ).offset( { "top": 10, "left": 10 } ).offset(); + assert.equal( offset.top, 10, "Setting offset on element with position absolute but 'auto' values." ); + assert.equal( offset.left, 10, "Setting offset on element with position absolute but 'auto' values." ); // set offset tests = [ @@ -146,54 +149,55 @@ testIframe("offset/absolute", "absolute", function( $ ) { { "id": "#absolute-1", "top": 1, "left": 1 } ]; jQuery.each( tests, function() { - $( this["id"] ).offset({ "top": this["top"], "left": this["left"] }); - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset({ top: " + this["top"] + " })" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset({ left: " + this["left"] + " })" ); + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ] } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset({ top: " + this[ "top" ] + " })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset({ left: " + this[ "left" ] + " })" ); - var top = this["top"], left = this["left"]; + var top = this[ "top" ], left = this[ "left" ]; - $( this["id"] ).offset(function(i, val){ - equal( val.top, top, "Verify incoming top position." ); - equal( val.left, left, "Verify incoming top position." ); + $( this[ "id" ] ).offset( function( i, val ) { + assert.equal( val.top, top, "Verify incoming top position." ); + assert.equal( val.left, left, "Verify incoming top position." ); return { "top": top + 1, "left": left + 1 }; - }); - equal( $( this["id"] ).offset().top, this["top"] + 1, "jQuery('" + this["id"] + "').offset({ top: " + (this["top"] + 1) + " })" ); - equal( $( this["id"] ).offset().left, this["left"] + 1, "jQuery('" + this["id"] + "').offset({ left: " + (this["left"] + 1) + " })" ); - - $( this["id"] ) - .offset({ "left": this["left"] + 2 }) - .offset({ "top": this["top"] + 2 }); - equal( $( this["id"] ).offset().top, this["top"] + 2, "Setting one property at a time." ); - equal( $( this["id"] ).offset().left, this["left"] + 2, "Setting one property at a time." ); - - $( this["id"] ).offset({ "top": this["top"], "left": this["left"], "using": function( props ) { - $( this ).css({ + } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ top: " + ( this[ "top" ] + 1 ) + " })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ left: " + ( this[ "left" ] + 1 ) + " })" ); + + $( this[ "id" ] ) + .offset( { "left": this[ "left" ] + 2 } ) + .offset( { "top": this[ "top" ] + 2 } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ] + 2, "Setting one property at a time." ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ] + 2, "Setting one property at a time." ); + + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ], "using": function( props ) { + $( this ).css( { "top": props.top + 1, "left": props.left + 1 - }); - }}); - equal( $( this["id"] ).offset().top, this["top"] + 1, "jQuery('" + this["id"] + "').offset({ top: " + (this["top"] + 1) + ", using: fn })" ); - equal( $( this["id"] ).offset().left, this["left"] + 1, "jQuery('" + this["id"] + "').offset({ left: " + (this["left"] + 1) + ", using: fn })" ); - }); -}); + } ); + } } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ top: " + ( this[ "top" ] + 1 ) + ", using: fn })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ left: " + ( this[ "left" ] + 1 ) + ", using: fn })" ); + } ); +} ); + +testIframe( "offset/relative", "relative", function( $, window, document, assert ) { + assert.expect( 60 ); -testIframe("offset/relative", "relative", function( $ ) { - expect(60); + var ie, tests; // IE is collapsing the top margin of 1px; detect and adjust accordingly - var ie = $("#relative-1").offset().top === 6; + ie = $("#relative-1").offset().top === 6; // get offset - var tests = [ + tests = [ { "id": "#relative-1", "top": ie ? 6 : 7, "left": 7 }, { "id": "#relative-1-1", "top": ie ? 13 : 15, "left": 15 }, { "id": "#relative-2", "top": ie ? 141 : 142, "left": 27 } ]; jQuery.each( tests, function() { - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset().top" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset().left" ); - }); - + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset().top" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset().left" ); + } ); // get position tests = [ @@ -202,10 +206,9 @@ testIframe("offset/relative", "relative", function( $ ) { { "id": "#relative-2", "top": ie ? 140 : 141, "left": 26 } ]; jQuery.each( tests, function() { - equal( $( this["id"] ).position().top, this["top"], "jQuery('" + this["id"] + "').position().top" ); - equal( $( this["id"] ).position().left, this["left"], "jQuery('" + this["id"] + "').position().left" ); - }); - + assert.equal( $( this[ "id" ] ).position().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').position().top" ); + assert.equal( $( this[ "id" ] ).position().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').position().left" ); + } ); // set offset tests = [ @@ -223,40 +226,40 @@ testIframe("offset/relative", "relative", function( $ ) { { "id": "#relative-1", "top": 7, "left": 7 } ]; jQuery.each( tests, function() { - $( this["id"] ).offset({ "top": this["top"], "left": this["left"] }); - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset({ top: " + this["top"] + " })" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset({ left: " + this["left"] + " })" ); + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ] } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset({ top: " + this[ "top" ] + " })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset({ left: " + this[ "left" ] + " })" ); - $( this["id"] ).offset({ "top": this["top"], "left": this["left"], "using": function( props ) { - $( this ).css({ + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ], "using": function( props ) { + $( this ).css( { "top": props.top + 1, "left": props.left + 1 - }); - }}); - equal( $( this["id"] ).offset().top, this["top"] + 1, "jQuery('" + this["id"] + "').offset({ top: " + (this["top"] + 1) + ", using: fn })" ); - equal( $( this["id"] ).offset().left, this["left"] + 1, "jQuery('" + this["id"] + "').offset({ left: " + (this["left"] + 1) + ", using: fn })" ); - }); -}); + } ); + } } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ top: " + ( this[ "top" ] + 1 ) + ", using: fn })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ left: " + ( this[ "left" ] + 1 ) + ", using: fn })" ); + } ); +} ); -testIframe("offset/static", "static", function( $ ) { +testIframe( "offset/static", "static", function( $, window, document, assert ) { + assert.expect( 80 ); - // IE is collapsing the top margin of 1px; detect and adjust accordingly - var ie = $("#static-1").offset().top === 6; + var ie, tests; - expect( 80 ); + // IE is collapsing the top margin of 1px; detect and adjust accordingly + ie = $("#static-1").offset().top === 6; // get offset - var tests = [ + tests = [ { "id": "#static-1", "top": ie ? 6 : 7, "left": 7 }, { "id": "#static-1-1", "top": ie ? 13 : 15, "left": 15 }, { "id": "#static-1-1-1", "top": ie ? 20 : 23, "left": 23 }, { "id": "#static-2", "top": ie ? 121 : 122, left: 7 } ]; jQuery.each( tests, function() { - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset().top" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset().left" ); - }); - + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset().top" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset().left" ); + } ); // get position tests = [ @@ -266,10 +269,9 @@ testIframe("offset/static", "static", function( $ ) { { "id": "#static-2", "top": ie ? 120 : 121, "left": 6 } ]; jQuery.each( tests, function() { - equal( $( this["id"] ).position().top, this["top"], "jQuery('" + this["top"] + "').position().top" ); - equal( $( this["id"] ).position().left, this["left"], "jQuery('" + this["left"] +"').position().left" ); - }); - + assert.equal( $( this[ "id" ] ).position().top, this[ "top" ], "jQuery('" + this[ "top" ] + "').position().top" ); + assert.equal( $( this[ "id" ] ).position().left, this[ "left" ], "jQuery('" + this[ "left" ] + "').position().left" ); + } ); // set offset tests = [ @@ -291,28 +293,30 @@ testIframe("offset/static", "static", function( $ ) { { "id": "#static-1", "top": 7, "left": 7 } ]; jQuery.each( tests, function() { - $( this["id"] ).offset({ "top": this["top"], "left": this["left"] }); - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset({ top: " + this["top"] + " })" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset({ left: " + this["left"] + " })" ); + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ] } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset({ top: " + this[ "top" ] + " })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset({ left: " + this[ "left" ] + " })" ); - $( this["id"] ).offset({ "top": this["top"], "left": this["left"], "using": function( props ) { - $( this ).css({ + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ], "using": function( props ) { + $( this ).css( { "top": props.top + 1, "left": props.left + 1 - }); - }}); - equal( $( this["id"] ).offset().top, this["top"] + 1, "jQuery('" + this["id"] + "').offset({ top: " + (this["top"] + 1) + ", using: fn })" ); - equal( $( this["id"] ).offset().left, this["left"] + 1, "jQuery('" + this["id"] + "').offset({ left: " + (this["left"] + 1) + ", using: fn })" ); - }); -}); - -testIframe("offset/fixed", "fixed", function( $ ) { - // IE is collapsing the top margin of 1px; detect and adjust accordingly - var ie = $("#fixed-1").position().top === 2; + } ); + } } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ top: " + ( this[ "top" ] + 1 ) + ", using: fn })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ left: " + ( this[ "left" ] + 1 ) + ", using: fn })" ); + } ); +} ); + +testIframe( "offset/fixed", "fixed", function( $, window, document, assert ) { + assert.expect( 34 ); - expect(34); + var ie, tests, $noTopLeft; - var tests = [ + // IE is collapsing the top margin of 1px; detect and adjust accordingly + ie = $("#fixed-1").position().top === 2; + + tests = [ { "id": "#fixed-1", "offsetTop": 1001, @@ -331,24 +335,25 @@ testIframe("offset/fixed", "fixed", function( $ ) { jQuery.each( tests, function() { if ( !window.supportsScroll ) { - ok( true, "Browser doesn't support scroll position." ); - ok( true, "Browser doesn't support scroll position." ); - ok( true, "Browser doesn't support scroll position." ); - ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); } else if ( window.supportsFixedPosition ) { - equal( $( this["id"] ).offset().top, this["offsetTop"], "jQuery('" + this["id"] + "').offset().top" ); - equal( $( this["id"] ).position().top, this["positionTop"], "jQuery('" + this["id"] + "').position().top" ); - equal( $( this["id"] ).offset().left, this["offsetLeft"], "jQuery('" + this["id"] + "').offset().left" ); - equal( $( this["id"] ).position().left, this["positionLeft"], "jQuery('" + this["id"] + "').position().left" ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "offsetTop" ], "jQuery('" + this[ "id" ] + "').offset().top" ); + assert.equal( $( this[ "id" ] ).position().top, this[ "positionTop" ], "jQuery('" + this[ "id" ] + "').position().top" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "offsetLeft" ], "jQuery('" + this[ "id" ] + "').offset().left" ); + assert.equal( $( this[ "id" ] ).position().left, this[ "positionLeft" ], "jQuery('" + this[ "id" ] + "').position().left" ); } else { + // need to have same number of assertions - ok( true, "Fixed position is not supported" ); - ok( true, "Fixed position is not supported" ); - ok( true, "Fixed position is not supported" ); - ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); } - }); + } ); tests = [ { "id": "#fixed-1", "top": 100, "left": 100 }, @@ -361,189 +366,254 @@ testIframe("offset/fixed", "fixed", function( $ ) { jQuery.each( tests, function() { if ( window.supportsFixedPosition ) { - $( this["id"] ).offset({ "top": this["top"], "left": this["left"] }); - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset({ top: " + this["top"] + " })" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset({ left: " + this["left"] + " })" ); + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ] } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ], "jQuery('" + this[ "id" ] + "').offset({ top: " + this[ "top" ] + " })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ], "jQuery('" + this[ "id" ] + "').offset({ left: " + this[ "left" ] + " })" ); - $( this["id"] ).offset({ "top": this["top"], "left": this["left"], "using": function( props ) { - $( this ).css({ + $( this[ "id" ] ).offset( { "top": this[ "top" ], "left": this[ "left" ], "using": function( props ) { + $( this ).css( { "top": props.top + 1, "left": props.left + 1 - }); - }}); - equal( $( this["id"] ).offset().top, this["top"] + 1, "jQuery('" + this["id"] + "').offset({ top: " + (this["top"] + 1) + ", using: fn })" ); - equal( $( this["id"] ).offset().left, this["left"] + 1, "jQuery('" + this["id"] + "').offset({ left: " + (this["left"] + 1) + ", using: fn })" ); + } ); + } } ); + assert.equal( $( this[ "id" ] ).offset().top, this[ "top" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ top: " + ( this[ "top" ] + 1 ) + ", using: fn })" ); + assert.equal( $( this[ "id" ] ).offset().left, this[ "left" ] + 1, "jQuery('" + this[ "id" ] + "').offset({ left: " + ( this[ "left" ] + 1 ) + ", using: fn })" ); } else { + // need to have same number of assertions - ok( true, "Fixed position is not supported" ); - ok( true, "Fixed position is not supported" ); - ok( true, "Fixed position is not supported" ); - ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); } - }); + } ); // Bug 8316 - var $noTopLeft = $("#fixed-no-top-left"); + $noTopLeft = $( "#fixed-no-top-left" ); if ( window.supportsFixedPosition ) { - equal( $noTopLeft.offset().top, 1007, "Check offset top for fixed element with no top set" ); - equal( $noTopLeft.offset().left, 1007, "Check offset left for fixed element with no left set" ); + assert.equal( $noTopLeft.offset().top, 1007, "Check offset top for fixed element with no top set" ); + assert.equal( $noTopLeft.offset().left, 1007, "Check offset left for fixed element with no left set" ); } else { + // need to have same number of assertions - ok( true, "Fixed position is not supported" ); - ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); + assert.ok( true, "Fixed position is not supported" ); } -}); +} ); -testIframe("offset/table", "table", function( $ ) { - expect(4); +testIframe( "offset/table", "table", function( $, window, document, assert ) { + assert.expect( 4 ); - equal( $("#table-1").offset().top, 6, "jQuery('#table-1').offset().top" ); - equal( $("#table-1").offset().left, 6, "jQuery('#table-1').offset().left" ); + assert.equal( $( "#table-1" ).offset().top, 6, "jQuery('#table-1').offset().top" ); + assert.equal( $( "#table-1" ).offset().left, 6, "jQuery('#table-1').offset().left" ); - equal( $("#th-1").offset().top, 10, "jQuery('#th-1').offset().top" ); - equal( $("#th-1").offset().left, 10, "jQuery('#th-1').offset().left" ); -}); + assert.equal( $( "#th-1" ).offset().top, 10, "jQuery('#th-1').offset().top" ); + assert.equal( $( "#th-1" ).offset().left, 10, "jQuery('#th-1').offset().left" ); +} ); -testIframe("offset/scroll", "scroll", function( $, win ) { - expect(24); +testIframe( "offset/scroll", "scroll", function( $, win, doc, assert ) { + assert.expect( 24 ); // If we're going to bastardize the tests, let's just DO it var ie = /msie [678]/i.test( navigator.userAgent ); if ( ie ) { - ok( true, "TestSwarm's iframe has hosed this test in oldIE, we surrender" ); + assert.ok( true, "TestSwarm's iframe has hosed this test in oldIE, we surrender" ); } else { - equal( $("#scroll-1").offset().top, 7, "jQuery('#scroll-1').offset().top" ); + assert.equal( $( "#scroll-1" ).offset().top, 7, "jQuery('#scroll-1').offset().top" ); } - equal( $("#scroll-1").offset().left, 7, "jQuery('#scroll-1').offset().left" ); + assert.equal( $( "#scroll-1" ).offset().left, 7, "jQuery('#scroll-1').offset().left" ); if ( ie ) { - ok( true, "TestSwarm's iframe has hosed this test in oldIE, we surrender" ); + assert.ok( true, "TestSwarm's iframe has hosed this test in oldIE, we surrender" ); } else { - equal( $("#scroll-1-1").offset().top, 11, "jQuery('#scroll-1-1').offset().top" ); + assert.equal( $( "#scroll-1-1" ).offset().top, 11, "jQuery('#scroll-1-1').offset().top" ); } - equal( $("#scroll-1-1").offset().left, 11, "jQuery('#scroll-1-1').offset().left" ); + assert.equal( $( "#scroll-1-1" ).offset().left, 11, "jQuery('#scroll-1-1').offset().left" ); + + // These tests are solely for 2.x/1.x consistency + // Retrieving offset on disconnected/hidden elements is not officially + // valid input, but will return zeros for back-compat + // assert.equal( $( "#hidden" ).offset().top, 0, "Hidden elements do not subtract scroll" ); + // assert.equal( $( "#hidden" ).offset().left, 0, "Hidden elements do not subtract scroll" ); // scroll offset tests .scrollTop/Left - equal( $("#scroll-1").scrollTop(), 5, "jQuery('#scroll-1').scrollTop()" ); - equal( $("#scroll-1").scrollLeft(), 5, "jQuery('#scroll-1').scrollLeft()" ); + assert.equal( $( "#scroll-1" ).scrollTop(), 5, "jQuery('#scroll-1').scrollTop()" ); + assert.equal( $( "#scroll-1" ).scrollLeft(), 5, "jQuery('#scroll-1').scrollLeft()" ); - equal( $("#scroll-1-1").scrollTop(), 0, "jQuery('#scroll-1-1').scrollTop()" ); - equal( $("#scroll-1-1").scrollLeft(), 0, "jQuery('#scroll-1-1').scrollLeft()" ); + assert.equal( $( "#scroll-1-1" ).scrollTop(), 0, "jQuery('#scroll-1-1').scrollTop()" ); + assert.equal( $( "#scroll-1-1" ).scrollLeft(), 0, "jQuery('#scroll-1-1').scrollLeft()" ); // scroll method chaining - equal( $("#scroll-1").scrollTop(undefined).scrollTop(), 5, ".scrollTop(undefined) is chainable (#5571)" ); - equal( $("#scroll-1").scrollLeft(undefined).scrollLeft(), 5, ".scrollLeft(undefined) is chainable (#5571)" ); + assert.equal( $( "#scroll-1" ).scrollTop( undefined ).scrollTop(), 5, ".scrollTop(undefined) is chainable (#5571)" ); + assert.equal( $( "#scroll-1" ).scrollLeft( undefined ).scrollLeft(), 5, ".scrollLeft(undefined) is chainable (#5571)" ); win.name = "test"; if ( !window.supportsScroll ) { - ok( true, "Browser doesn't support scroll position." ); - ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); - ok( true, "Browser doesn't support scroll position." ); - ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); + assert.ok( true, "Browser doesn't support scroll position." ); } else { - equal( $(win).scrollTop(), 1000, "jQuery(window).scrollTop()" ); - equal( $(win).scrollLeft(), 1000, "jQuery(window).scrollLeft()" ); + assert.equal( $( win ).scrollTop(), 1000, "jQuery(window).scrollTop()" ); + assert.equal( $( win ).scrollLeft(), 1000, "jQuery(window).scrollLeft()" ); - equal( $(win.document).scrollTop(), 1000, "jQuery(document).scrollTop()" ); - equal( $(win.document).scrollLeft(), 1000, "jQuery(document).scrollLeft()" ); + assert.equal( $( win.document ).scrollTop(), 1000, "jQuery(document).scrollTop()" ); + assert.equal( $( win.document ).scrollLeft(), 1000, "jQuery(document).scrollLeft()" ); } // test jQuery using parent window/document // jQuery reference here is in the iframe - window.scrollTo(0,0); - equal( $(window).scrollTop(), 0, "jQuery(window).scrollTop() other window" ); - equal( $(window).scrollLeft(), 0, "jQuery(window).scrollLeft() other window" ); - equal( $(document).scrollTop(), 0, "jQuery(window).scrollTop() other document" ); - equal( $(document).scrollLeft(), 0, "jQuery(window).scrollLeft() other document" ); + // Support: Android 2.3 only + // Android 2.3 is sometimes off by a few pixels. + window.scrollTo( 0, 0 ); + if ( /android 2\.3/i.test( navigator.userAgent ) ) { + assert.ok( + Math.abs( $( window ).scrollTop() ) < 5, + "jQuery(window).scrollTop() other window" + ); + } else { + assert.equal( $( window ).scrollTop(), 0, "jQuery(window).scrollTop() other window" ); + } + assert.equal( $( window ).scrollLeft(), 0, "jQuery(window).scrollLeft() other window" ); + if ( /android 2\.3/i.test( navigator.userAgent ) ) { + assert.ok( + Math.abs( $( window ).scrollTop() ) < 5, + "jQuery(window).scrollTop() other document" + ); + } else { + assert.equal( $( document ).scrollTop(), 0, "jQuery(window).scrollTop() other document" ); + } + assert.equal( $( document ).scrollLeft(), 0, "jQuery(window).scrollLeft() other document" ); // Tests scrollTop/Left with empty jquery objects - notEqual( $().scrollTop(100), null, "jQuery().scrollTop(100) testing setter on empty jquery object" ); - notEqual( $().scrollLeft(100), null, "jQuery().scrollLeft(100) testing setter on empty jquery object" ); - notEqual( $().scrollTop(null), null, "jQuery().scrollTop(null) testing setter on empty jquery object" ); - notEqual( $().scrollLeft(null), null, "jQuery().scrollLeft(null) testing setter on empty jquery object" ); - strictEqual( $().scrollTop(), null, "jQuery().scrollTop(100) testing setter on empty jquery object" ); - strictEqual( $().scrollLeft(), null, "jQuery().scrollLeft(100) testing setter on empty jquery object" ); -}); - -testIframe("offset/body", "body", function( $ ) { - expect(4); - - equal( $("body").offset().top, 1, "jQuery('#body').offset().top" ); - equal( $("body").offset().left, 1, "jQuery('#body').offset().left" ); - equal( $("#firstElement").position().left, 5, "$('#firstElement').position().left" ); - equal( $("#firstElement").position().top, 5, "$('#firstElement').position().top" ); -}); - -test("chaining", function() { - expect(3); + assert.notEqual( $().scrollTop( 100 ), null, "jQuery().scrollTop(100) testing setter on empty jquery object" ); + assert.notEqual( $().scrollLeft( 100 ), null, "jQuery().scrollLeft(100) testing setter on empty jquery object" ); + assert.notEqual( $().scrollTop( null ), null, "jQuery().scrollTop(null) testing setter on empty jquery object" ); + assert.notEqual( $().scrollLeft( null ), null, "jQuery().scrollLeft(null) testing setter on empty jquery object" ); + assert.strictEqual( $().scrollTop(), null, "jQuery().scrollTop(100) testing setter on empty jquery object" ); + assert.strictEqual( $().scrollLeft(), null, "jQuery().scrollLeft(100) testing setter on empty jquery object" ); +} ); + +testIframe( "offset/body", "body", function( $, window, document, assert ) { + assert.expect( 4 ); + + assert.equal( $( "body" ).offset().top, 1, "jQuery('#body').offset().top" ); + assert.equal( $( "body" ).offset().left, 1, "jQuery('#body').offset().left" ); + assert.equal( $( "#firstElement" ).position().left, 5, "$('#firstElement').position().left" ); + assert.equal( $( "#firstElement" ).position().top, 5, "$('#firstElement').position().top" ); +} ); + +QUnit.test( "chaining", function( assert ) { + assert.expect( 3 ); var coords = { "top": 1, "left": 1 }; - equal( jQuery("#absolute-1").offset(coords).selector, "#absolute-1", "offset(coords) returns jQuery object" ); - equal( jQuery("#non-existent").offset(coords).selector, "#non-existent", "offset(coords) with empty jQuery set returns jQuery object" ); - equal( jQuery("#absolute-1").offset(undefined).selector, "#absolute-1", "offset(undefined) returns jQuery object (#5571)" ); -}); + assert.equal( jQuery( "#absolute-1" ).offset( coords ).selector, "#absolute-1", "offset(coords) returns jQuery object" ); + assert.equal( jQuery( "#non-existent" ).offset( coords ).selector, "#non-existent", "offset(coords) with empty jQuery set returns jQuery object" ); + assert.equal( jQuery( "#absolute-1" ).offset( undefined ).selector, "#absolute-1", "offset(undefined) returns jQuery object (#5571)" ); +} ); + +QUnit.test( "offsetParent", function( assert ) { + assert.expect( 13 ); -test("offsetParent", function(){ - expect(13); + var body, header, div, area; - var body = jQuery("body").offsetParent(); - equal( body.length, 1, "Only one offsetParent found." ); - equal( body[0], document.documentElement, "The html element is the offsetParent of the body." ); + body = jQuery( "body" ).offsetParent(); + assert.equal( body.length, 1, "Only one offsetParent found." ); + assert.equal( body[ 0 ], document.documentElement, "The html element is the offsetParent of the body." ); - var header = jQuery("#qunit").offsetParent(); - equal( header.length, 1, "Only one offsetParent found." ); - equal( header[0], document.documentElement, "The html element is the offsetParent of #qunit." ); + header = jQuery( "#qunit" ).offsetParent(); + assert.equal( header.length, 1, "Only one offsetParent found." ); + assert.equal( header[ 0 ], document.documentElement, "The html element is the offsetParent of #qunit." ); - var div = jQuery("#nothiddendivchild").offsetParent(); - equal( div.length, 1, "Only one offsetParent found." ); - equal( div[0], document.getElementById("qunit-fixture"), "The #qunit-fixture is the offsetParent of #nothiddendivchild." ); + div = jQuery( "#nothiddendivchild" ).offsetParent(); + assert.equal( div.length, 1, "Only one offsetParent found." ); + assert.equal( div[ 0 ], document.getElementById( "qunit-fixture" ), "The #qunit-fixture is the offsetParent of #nothiddendivchild." ); - jQuery("#nothiddendiv").css("position", "relative"); + jQuery( "#nothiddendiv" ).css( "position", "relative" ); - div = jQuery("#nothiddendivchild").offsetParent(); - equal( div.length, 1, "Only one offsetParent found." ); - equal( div[0], jQuery("#nothiddendiv")[0], "The div is the offsetParent." ); + div = jQuery( "#nothiddendivchild" ).offsetParent(); + assert.equal( div.length, 1, "Only one offsetParent found." ); + assert.equal( div[ 0 ], jQuery( "#nothiddendiv" )[ 0 ], "The div is the offsetParent." ); - div = jQuery("body, #nothiddendivchild").offsetParent(); - equal( div.length, 2, "Two offsetParent found." ); - equal( div[0], document.documentElement, "The html element is the offsetParent of the body." ); - equal( div[1], jQuery("#nothiddendiv")[0], "The div is the offsetParent." ); + div = jQuery( "body, #nothiddendivchild" ).offsetParent(); + assert.equal( div.length, 2, "Two offsetParent found." ); + assert.equal( div[ 0 ], document.documentElement, "The html element is the offsetParent of the body." ); + assert.equal( div[ 1 ], jQuery( "#nothiddendiv" )[ 0 ], "The div is the offsetParent." ); - var area = jQuery("#imgmap area").offsetParent(); - equal( area[0], document.documentElement, "The html element is the offsetParent of the body." ); + area = jQuery( "#imgmap area" ).offsetParent(); + assert.equal( area[ 0 ], document.documentElement, "The html element is the offsetParent of the body." ); - div = jQuery("
        ").css({ "position": "absolute" }).appendTo("body"); - equal( div.offsetParent()[0], document.documentElement, "Absolutely positioned div returns html as offset parent, see #12139" ); + div = jQuery( "
        " ).css( { "position": "absolute" } ).appendTo( "body" ); + assert.equal( div.offsetParent()[ 0 ], document.documentElement, "Absolutely positioned div returns html as offset parent, see #12139" ); div.remove(); -}); +} ); -test("fractions (see #7730 and #7885)", function() { - expect(2); +QUnit.test( "fractions (see #7730 and #7885)", function( assert ) { + assert.expect( 2 ); - jQuery("body").append("
        "); + jQuery( "body" ).append( "
        " ); - var expected = { "top": 1000, "left": 1000 }; - var div = jQuery("#fractions"); + var result, + expected = { "top": 1000, "left": 1000 }, + div = jQuery( "#fractions" ); - div.css({ + div.css( { "position": "absolute", "left": "1000.7432222px", "top": "1000.532325px", "width": 100, "height": 100 - }); + } ); - div.offset(expected); + div.offset( expected ); - var result = div.offset(); + result = div.offset(); - equal( result.top, expected.top, "Check top" ); - equal( result.left, expected.left, "Check left" ); + // Support: Chrome 45-46+ + // In recent Chrome these values differ a little. + assert.ok( Math.abs( result.top - expected.top ) < 0.25, "Check top within 0.25 of expected" ); + assert.equal( result.left, expected.left, "Check left" ); div.remove(); -}); +} ); + +QUnit.test( "iframe scrollTop/Left (see gh-1945)", function( assert ) { + assert.expect( 2 ); + + var ifDoc = jQuery( "#iframe" )[ 0 ]; + + ifDoc = ifDoc.contentDocument || ifDoc.contentWindow.document; + + // Mobile Safari and Android 2.3 resize the iframe by its content + // meaning it's not possible to scroll the iframe only its parent element. + // It seems (not confirmed) in android 4.0 it's not possible to scroll iframes from the code. + if ( /iphone os/i.test( navigator.userAgent ) || + /android 2\.3/i.test( navigator.userAgent ) || + /android 4\.0/i.test( navigator.userAgent ) ) { + assert.equal( true, true, "Can't scroll iframes in this environment" ); + assert.equal( true, true, "Can't scroll iframes in this environment" ); + + } else { + + // Tests scrollTop/Left with iframes + jQuery( "#iframe" ).css( "width", "50px" ).css( "height", "50px" ); + + // Support: IE6-8 + // Need a doctype, otherwise IE will scroll it but will still show old values + ifDoc.write( "
        " ); + + // Support: IE6-8 + ifDoc.close(); + + jQuery( ifDoc ).scrollTop( 200 ); + jQuery( ifDoc ).scrollLeft( 500 ); + + assert.equal( jQuery( ifDoc ).scrollTop(), 200, "$($('#iframe')[0].contentDocument).scrollTop()" ); + assert.equal( jQuery( ifDoc ).scrollLeft(), 500, "$($('#iframe')[0].contentDocument).scrollLeft()" ); + } +} ); -})(); +} )(); diff --git a/test/unit/queue.js b/test/unit/queue.js index 9cecad7a53..7ce85cb35a 100644 --- a/test/unit/queue.js +++ b/test/unit/queue.js @@ -1,276 +1,276 @@ -module( "queue", { teardown: moduleTeardown }); +QUnit.module( "queue", { teardown: moduleTeardown } ); -test( "queue() with other types", 14, function() { - var counter = 0; +QUnit.test( "queue() with other types", function( assert ) { + assert.expect( 14 ); - stop(); + QUnit.stop(); - var $div = jQuery({}), - defer; - $div.promise( "foo" ).done(function() { - equal( counter, 0, "Deferred for collection with no queue is automatically resolved" ); - }); + var $div = jQuery( {} ), + counter = 0; + + $div.promise( "foo" ).done( function() { + assert.equal( counter, 0, "Deferred for collection with no queue is automatically resolved" ); + } ); $div - .queue("foo",function(){ - equal( ++counter, 1, "Dequeuing" ); - jQuery.dequeue(this,"foo"); - }) - .queue("foo",function(){ - equal( ++counter, 2, "Dequeuing" ); - jQuery(this).dequeue("foo"); - }) - .queue("foo",function(){ - equal( ++counter, 3, "Dequeuing" ); - }) - .queue("foo",function(){ - equal( ++counter, 4, "Dequeuing" ); - }); + .queue( "foo", function() { + assert.equal( ++counter, 1, "Dequeuing" ); + jQuery.dequeue( this, "foo" ); + } ) + .queue( "foo", function() { + assert.equal( ++counter, 2, "Dequeuing" ); + jQuery( this ).dequeue( "foo" ); + } ) + .queue( "foo", function() { + assert.equal( ++counter, 3, "Dequeuing" ); + } ) + .queue( "foo", function() { + assert.equal( ++counter, 4, "Dequeuing" ); + } ); - defer = $div.promise("foo").done(function() { - equal( counter, 4, "Testing previous call to dequeue in deferred" ); - start(); - }); + $div.promise( "foo" ).done( function() { + assert.equal( counter, 4, "Testing previous call to dequeue in deferred" ); + QUnit.start(); + } ); - equal( $div.queue("foo").length, 4, "Testing queue length" ); + assert.equal( $div.queue( "foo" ).length, 4, "Testing queue length" ); - equal( $div.queue("foo", undefined).queue("foo").length, 4, ".queue('name',undefined) does nothing but is chainable (#5571)"); + assert.equal( $div.queue( "foo", undefined ).queue( "foo" ).length, 4, ".queue('name',undefined) does nothing but is chainable (#5571)" ); - $div.dequeue("foo"); + $div.dequeue( "foo" ); - equal( counter, 3, "Testing previous call to dequeue" ); - equal( $div.queue("foo").length, 1, "Testing queue length" ); + assert.equal( counter, 3, "Testing previous call to dequeue" ); + assert.equal( $div.queue( "foo" ).length, 1, "Testing queue length" ); - $div.dequeue("foo"); + $div.dequeue( "foo" ); - equal( counter, 4, "Testing previous call to dequeue" ); - equal( $div.queue("foo").length, 0, "Testing queue length" ); + assert.equal( counter, 4, "Testing previous call to dequeue" ); + assert.equal( $div.queue( "foo" ).length, 0, "Testing queue length" ); - $div.dequeue("foo"); + $div.dequeue( "foo" ); - equal( counter, 4, "Testing previous call to dequeue" ); - equal( $div.queue("foo").length, 0, "Testing queue length" ); + assert.equal( counter, 4, "Testing previous call to dequeue" ); + assert.equal( $div.queue( "foo" ).length, 0, "Testing queue length" ); -}); +} ); -test("queue(name) passes in the next item in the queue as a parameter", function() { - expect(2); +QUnit.test( "queue(name) passes in the next item in the queue as a parameter", function( assert ) { + assert.expect( 2 ); - var div = jQuery({}); - var counter = 0; + var div = jQuery( {} ), + counter = 0; - div.queue("foo", function(next) { - equal(++counter, 1, "Dequeueing"); + div.queue( "foo", function( next ) { + assert.equal( ++counter, 1, "Dequeueing" ); next(); - }).queue("foo", function(next) { - equal(++counter, 2, "Next was called"); + } ).queue( "foo", function( next ) { + assert.equal( ++counter, 2, "Next was called" ); next(); - }).queue("bar", function() { - equal(++counter, 3, "Other queues are not triggered by next()"); - }); + } ).queue( "bar", function() { + assert.equal( ++counter, 3, "Other queues are not triggered by next()" ); + } ); - div.dequeue("foo"); -}); + div.dequeue( "foo" ); +} ); -test("queue() passes in the next item in the queue as a parameter to fx queues", function() { - expect(3); - stop(); +QUnit.test( "queue() passes in the next item in the queue as a parameter to fx queues", function( assert ) { + assert.expect( 3 ); + QUnit.stop(); - var div = jQuery({}); - var counter = 0; + var div = jQuery( {} ), + counter = 0; - div.queue(function(next) { - equal(++counter, 1, "Dequeueing"); - var self = this; - setTimeout(function() { next(); }, 500); - }).queue(function(next) { - equal(++counter, 2, "Next was called"); + div.queue( function( next ) { + assert.equal( ++counter, 1, "Dequeueing" ); + setTimeout( function() { next(); }, 500 ); + } ).queue( function( next ) { + assert.equal( ++counter, 2, "Next was called" ); next(); - }).queue("bar", function() { - equal(++counter, 3, "Other queues are not triggered by next()"); - }); - - jQuery.when( div.promise("fx"), div ).done(function() { - equal(counter, 2, "Deferreds resolved"); - start(); - }); -}); - -test("callbacks keep their place in the queue", function() { - expect(5); - stop(); - var div = jQuery("
        "), + } ).queue( "bar", function() { + assert.equal( ++counter, 3, "Other queues are not triggered by next()" ); + } ); + + jQuery.when( div.promise( "fx" ), div ).done( function() { + assert.equal( counter, 2, "Deferreds resolved" ); + QUnit.start(); + } ); +} ); + +QUnit.test( "callbacks keep their place in the queue", function( assert ) { + assert.expect( 5 ); + QUnit.stop(); + var div = jQuery( "
        " ), counter = 0; - div.queue(function( next ) { - equal( ++counter, 1, "Queue/callback order: first called" ); + div.queue( function( next ) { + assert.equal( ++counter, 1, "Queue/callback order: first called" ); setTimeout( next, 200 ); - }).delay( 100 ).queue(function( next ) { - equal( ++counter, 2, "Queue/callback order: second called" ); - jQuery( this ).delay( 100 ).queue(function( next ) { - equal( ++counter, 4, "Queue/callback order: fourth called" ); + } ).delay( 100 ).queue( function( next ) { + assert.equal( ++counter, 2, "Queue/callback order: second called" ); + jQuery( this ).delay( 100 ).queue( function( next ) { + assert.equal( ++counter, 4, "Queue/callback order: fourth called" ); next(); - }); + } ); next(); - }).queue(function( next ) { - equal( ++counter, 3, "Queue/callback order: third called" ); + } ).queue( function( next ) { + assert.equal( ++counter, 3, "Queue/callback order: third called" ); next(); - }); + } ); - div.promise("fx").done(function() { - equal(counter, 4, "Deferreds resolved"); - start(); - }); -}); + div.promise( "fx" ).done( function() { + assert.equal( counter, 4, "Deferreds resolved" ); + QUnit.start(); + } ); +} ); -test("delay()", function() { - expect(2); - stop(); +QUnit.test( "delay()", function( assert ) { + assert.expect( 2 ); + QUnit.stop(); - var foo = jQuery({}), run = 0; + var foo = jQuery( {} ), run = 0; - foo.delay(100).queue(function(){ + foo.delay( 100 ).queue( function() { run = 1; - ok( true, "The function was dequeued." ); - start(); - }); + assert.ok( true, "The function was dequeued." ); + QUnit.start(); + } ); - equal( run, 0, "The delay delayed the next function from running." ); -}); + assert.equal( run, 0, "The delay delayed the next function from running." ); +} ); -test("clearQueue(name) clears the queue", function() { - expect(2); +QUnit.test( "clearQueue(name) clears the queue", function( assert ) { + assert.expect( 2 ); - stop(); + QUnit.stop(); - var div = jQuery({}); - var counter = 0; + var div = jQuery( {} ), + counter = 0; - div.queue("foo", function(next) { + div.queue( "foo", function( next ) { counter++; - jQuery(this).clearQueue("foo"); + jQuery( this ).clearQueue( "foo" ); next(); - }).queue("foo", function(next) { + } ).queue( "foo", function() { counter++; - }); + } ); - div.promise("foo").done(function() { - ok( true, "dequeue resolves the deferred" ); - start(); - }); + div.promise( "foo" ).done( function() { + assert.ok( true, "dequeue resolves the deferred" ); + QUnit.start(); + } ); - div.dequeue("foo"); + div.dequeue( "foo" ); - equal(counter, 1, "the queue was cleared"); -}); + assert.equal( counter, 1, "the queue was cleared" ); +} ); -test("clearQueue() clears the fx queue", function() { - expect(1); +QUnit.test( "clearQueue() clears the fx queue", function( assert ) { + assert.expect( 1 ); - var div = jQuery({}); - var counter = 0; + var div = jQuery( {} ), + counter = 0; - div.queue(function(next) { + div.queue( function( next ) { counter++; var self = this; - setTimeout(function() { jQuery(self).clearQueue(); next(); }, 50); - }).queue(function(next) { + setTimeout( function() { jQuery( self ).clearQueue(); next(); }, 50 ); + } ).queue( function() { counter++; - }); + } ); - equal(counter, 1, "the queue was cleared"); + assert.equal( counter, 1, "the queue was cleared" ); div.removeData(); -}); +} ); -asyncTest( "fn.promise() - called when fx queue is empty", 3, function() { - var foo = jQuery( "#foo" ).clone().andSelf(), +QUnit.asyncTest( "fn.promise() - called when fx queue is empty", 3, function( assert ) { + var foo = jQuery( "#foo" ).clone().addBack(), promised = false; foo.queue( function( next ) { + // called twice! - ok( !promised, "Promised hasn't been called" ); + assert.ok( !promised, "Promised hasn't been called" ); setTimeout( next, 10 ); - }); + } ); foo.promise().done( function() { - ok( promised = true, "Promised" ); - start(); - }); -}); + assert.ok( promised = true, "Promised" ); + QUnit.start(); + } ); +} ); -asyncTest( "fn.promise( \"queue\" ) - called whenever last queue function is dequeued", 5, function() { +QUnit.asyncTest( "fn.promise( \"queue\" ) - called whenever last queue function is dequeued", 5, function( assert ) { var foo = jQuery( "#foo" ), test; foo.promise( "queue" ).done( function() { - strictEqual( test, undefined, "called immediately when queue was already empty" ); - }); + assert.strictEqual( test, undefined, "called immediately when queue was already empty" ); + } ); test = 1; foo.queue( "queue", function( next ) { - strictEqual( test++, 1, "step one" ); + assert.strictEqual( test++, 1, "step one" ); setTimeout( next, 0 ); - }).queue( "queue", function( next ) { - strictEqual( test++, 2, "step two" ); + } ).queue( "queue", function( next ) { + assert.strictEqual( test++, 2, "step two" ); setTimeout( function() { next(); - strictEqual( test++, 4, "step four" ); - start(); + assert.strictEqual( test++, 4, "step four" ); + QUnit.start(); }, 10 ); - }).promise( "queue" ).done( function() { - strictEqual( test++, 3, "step three" ); - }); + } ).promise( "queue" ).done( function() { + assert.strictEqual( test++, 3, "step three" ); + } ); foo.dequeue( "queue" ); -}); +} ); -asyncTest( "fn.promise( \"queue\" ) - waits for animation to complete before resolving", 2, function() { +QUnit.asyncTest( "fn.promise( \"queue\" ) - waits for animation to complete before resolving", 2, function( assert ) { var foo = jQuery( "#foo" ), test = 1; - foo.animate({ + foo.animate( { top: 100 }, { duration: 1, queue: "queue", complete: function() { - strictEqual( test++, 1, "step one" ); + assert.strictEqual( test++, 1, "step one" ); } - }).dequeue( "queue" ); + } ).dequeue( "queue" ); foo.promise( "queue" ).done( function() { - strictEqual( test++, 2, "step two" ); - start(); - }); + assert.strictEqual( test++, 2, "step two" ); + QUnit.start(); + } ); -}); +} ); -test( ".promise(obj)", function() { - expect(2); +QUnit.test( ".promise(obj)", function( assert ) { + assert.expect( 2 ); - var obj = {}; - var promise = jQuery( "#foo" ).promise( "promise", obj ); - - ok( jQuery.isFunction( promise.promise ), ".promise(type, obj) returns a promise" ); - strictEqual( promise, obj, ".promise(type, obj) returns obj" ); -}); + var obj = {}, + promise = jQuery( "#foo" ).promise( "promise", obj ); + assert.ok( jQuery.isFunction( promise.promise ), ".promise(type, obj) returns a promise" ); + assert.strictEqual( promise, obj, ".promise(type, obj) returns obj" ); +} ); if ( jQuery.fn.stop ) { - test("delay() can be stopped", function() { - expect( 3 ); - stop(); + QUnit.test( "delay() can be stopped", function( assert ) { + assert.expect( 3 ); + QUnit.stop(); var done = {}; - jQuery({}) + jQuery( {} ) .queue( "alternate", function( next ) { done.alt1 = true; - ok( true, "This first function was dequeued" ); + assert.ok( true, "This first function was dequeued" ); next(); - }) + } ) .delay( 1000, "alternate" ) .queue( "alternate", function() { done.alt2 = true; - ok( true, "The function was dequeued immediately, the delay was stopped" ); - }) + assert.ok( true, "The function was dequeued immediately, the delay was stopped" ); + } ) .dequeue( "alternate" ) // stop( "alternate", false ) will NOT clear the queue, so it should automatically dequeue the next @@ -278,39 +278,39 @@ if ( jQuery.fn.stop ) { // this test .delay( 1 ) - .queue(function() { + .queue( function() { done.default1 = true; - ok( false, "This queue should never run" ); - }) + assert.ok( false, "This queue should never run" ); + } ) // stop( clearQueue ) should clear the queue .stop( true, false ); - deepEqual( done, { alt1: true, alt2: true }, "Queue ran the proper functions" ); + assert.deepEqual( done, { alt1: true, alt2: true }, "Queue ran the proper functions" ); - setTimeout(function() { - start(); + setTimeout( function() { + QUnit.start(); }, 1500 ); - }); + } ); - asyncTest( "queue stop hooks", 2, function() { + QUnit.asyncTest( "queue stop hooks", 2, function( assert ) { var foo = jQuery( "#foo" ); foo.queue( function( next, hooks ) { hooks.stop = function( gotoEnd ) { - equal( !!gotoEnd, false, "Stopped without gotoEnd" ); + assert.equal( !!gotoEnd, false, "Stopped without gotoEnd" ); }; - }); + } ); foo.stop(); foo.queue( function( next, hooks ) { hooks.stop = function( gotoEnd ) { - equal( gotoEnd, true, "Stopped with gotoEnd" ); - start(); + assert.equal( gotoEnd, true, "Stopped with gotoEnd" ); + QUnit.start(); }; - }); + } ); foo.stop( false, true ); - }); + } ); } // if ( jQuery.fn.stop ) diff --git a/test/unit/ready.js b/test/unit/ready.js new file mode 100644 index 0000000000..611b262860 --- /dev/null +++ b/test/unit/ready.js @@ -0,0 +1,74 @@ +QUnit.module( "ready" ); + +( function() { + var notYetReady, noEarlyExecution, + order = [], + args = {}; + + notYetReady = !jQuery.isReady; + + QUnit.test( "jQuery.isReady", function( assert ) { + assert.expect( 2 ); + + assert.equal( notYetReady, true, "jQuery.isReady should not be true before DOM ready" ); + assert.equal( jQuery.isReady, true, "jQuery.isReady should be true once DOM is ready" ); + } ); + + // Create an event handler. + function makeHandler( testId ) { + + // When returned function is executed, push testId onto `order` array + // to ensure execution order. Also, store event handler arg to ensure + // the correct arg is being passed into the event handler. + return function( arg ) { + order.push( testId ); + args[ testId ] = arg; + }; + } + + // Bind to the ready event in every possible way. + jQuery( makeHandler( "a" ) ); + jQuery( document ).ready( makeHandler( "b" ) ); + jQuery( document ).on( "ready.readytest", makeHandler( "c" ) ); + + // Do it twice, just to be sure. + jQuery( makeHandler( "d" ) ); + jQuery( document ).ready( makeHandler( "e" ) ); + jQuery( document ).on( "ready.readytest", makeHandler( "f" ) ); + + noEarlyExecution = order.length === 0; + + // This assumes that QUnit tests are run on DOM ready! + QUnit.test( "jQuery ready", function() { + expect( 10 ); + + ok( noEarlyExecution, "Handlers bound to DOM ready should not execute before DOM ready" ); + + // Ensure execution order. + deepEqual( order, [ "a", "b", "d", "e", "c", "f" ], "Bound DOM ready handlers should execute in on-order, but those bound with jQuery(document).on( 'ready', fn ) will always execute last" ); + + // Ensure handler argument is correct. + equal( args[ "a" ], jQuery, "Argument passed to fn in jQuery( fn ) should be jQuery" ); + equal( args[ "b" ], jQuery, "Argument passed to fn in jQuery(document).ready( fn ) should be jQuery" ); + ok( args[ "c" ] instanceof jQuery.Event, "Argument passed to fn in jQuery(document).on( 'ready', fn ) should be an event object" ); + + order = []; + + // Now that the ready event has fired, again bind to the ready event + // in every possible way. These event handlers should execute immediately. + jQuery( makeHandler( "g" ) ); + equal( order.pop(), "g", "Event handler should execute immediately" ); + equal( args[ "g" ], jQuery, "Argument passed to fn in jQuery( fn ) should be jQuery" ); + + jQuery( document ).ready( makeHandler( "h" ) ); + equal( order.pop(), "h", "Event handler should execute immediately" ); + equal( args[ "h" ], jQuery, "Argument passed to fn in jQuery(document).ready( fn ) should be jQuery" ); + + jQuery( document ).on( "ready.readytest", makeHandler( "never" ) ); + equal( order.length, 0, "Event handler should never execute since DOM ready has already passed" ); + + // Cleanup. + jQuery( document ).off( "ready.readytest" ); + } ); + +} )(); diff --git a/test/unit/selector.js b/test/unit/selector.js index 21113207c6..b330d42267 100644 --- a/test/unit/selector.js +++ b/test/unit/selector.js @@ -1,208 +1,519 @@ -module("selector", { teardown: moduleTeardown }); +QUnit.module( "selector", { teardown: moduleTeardown } ); /** * This test page is for selector tests that require jQuery in order to do the selection */ -test("element - jQuery only", function() { - expect( 7 ); +QUnit.test( "element - jQuery only", function( assert ) { + assert.expect( 7 ); - var fixture = document.getElementById("qunit-fixture"); + var fixture = document.getElementById( "qunit-fixture" ); - deepEqual( jQuery("p", fixture).get(), q("firstp","ap","sndp","en","sap","first"), "Finding elements with a Node context." ); - deepEqual( jQuery("p", "#qunit-fixture").get(), q("firstp","ap","sndp","en","sap","first"), "Finding elements with a selector context." ); - deepEqual( jQuery("p", jQuery("#qunit-fixture")).get(), q("firstp","ap","sndp","en","sap","first"), "Finding elements with a jQuery object context." ); - deepEqual( jQuery("#qunit-fixture").find("p").get(), q("firstp","ap","sndp","en","sap","first"), "Finding elements with a context via .find()." ); + assert.deepEqual( jQuery( "p", fixture ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "Finding elements with a Node context." ); + assert.deepEqual( jQuery( "p", "#qunit-fixture" ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "Finding elements with a selector context." ); + assert.deepEqual( jQuery( "p", jQuery( "#qunit-fixture" ) ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "Finding elements with a jQuery object context." ); + assert.deepEqual( jQuery( "#qunit-fixture" ).find( "p" ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "Finding elements with a context via .find()." ); - ok( jQuery("#length").length, " cannot be found under IE, see #945" ); - ok( jQuery("#lengthtest input").length, " cannot be found under IE, see #945" ); + assert.ok( jQuery( "#length" ).length, " cannot be found under IE, see #945" ); + assert.ok( jQuery( "#lengthtest input" ).length, " cannot be found under IE, see #945" ); // #7533 - equal( jQuery("

        foo

        ").find("p").length, 1, "Find where context root is a node and has an ID with CSS3 meta characters" ); -}); + assert.equal( jQuery( "

        foo

        " ).find( "p" ).length, 1, "Find where context root is a node and has an ID with CSS3 meta characters" ); +} ); -test("class - jQuery only", function() { - expect( 4 ); +QUnit.test( "id", function( assert ) { + assert.expect( 26 ); - deepEqual( jQuery(".blog", document.getElementsByTagName("p")).get(), q("mark", "simon"), "Finding elements with a context." ); - deepEqual( jQuery(".blog", "p").get(), q("mark", "simon"), "Finding elements with a context." ); - deepEqual( jQuery(".blog", jQuery("p")).get(), q("mark", "simon"), "Finding elements with a context." ); - deepEqual( jQuery("p").find(".blog").get(), q("mark", "simon"), "Finding elements with a context." ); -}); + var a; + + assert.t( "ID Selector", "#body", [ "body" ] ); + assert.t( "ID Selector w/ Element", "body#body", [ "body" ] ); + assert.t( "ID Selector w/ Element", "ul#first", [] ); + assert.t( "ID selector with existing ID descendant", "#firstp #simon1", [ "simon1" ] ); + assert.t( "ID selector with non-existant descendant", "#firstp #foobar", [] ); + assert.t( "ID selector using UTF8", "#台北Táiběi", [ "台北Táiběi" ] ); + assert.t( "Multiple ID selectors using UTF8", "#台北Táiběi, #台北", [ "台北Táiběi", "台北" ] ); + assert.t( "Descendant ID selector using UTF8", "div #台北", [ "台北" ] ); + assert.t( "Child ID selector using UTF8", "form > #台北", [ "台北" ] ); -test("attributes - jQuery only", function() { - expect( 6 ); + assert.t( "Escaped ID", "#foo\\:bar", [ "foo:bar" ] ); + assert.t( "Escaped ID", "#test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); + assert.t( "Descendant escaped ID", "div #foo\\:bar", [ "foo:bar" ] ); + assert.t( "Descendant escaped ID", "div #test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); + assert.t( "Child escaped ID", "form > #foo\\:bar", [ "foo:bar" ] ); + assert.t( "Child escaped ID", "form > #test\\.foo\\[5\\]bar", [ "test.foo[5]bar" ] ); - t( "Find elements with a tabindex attribute", "[tabindex]", ["listWithTabIndex", "foodWithNegativeTabIndex", "linkWithTabIndex", "linkWithNegativeTabIndex", "linkWithNoHrefWithTabIndex", "linkWithNoHrefWithNegativeTabIndex"] ); + assert.t( "ID Selector, child ID present", "#form > #radio1", [ "radio1" ] ); // bug #267 + assert.t( "ID Selector, not an ancestor ID", "#form #first", [] ); + assert.t( "ID Selector, not a child ID", "#form > #option1a", [] ); - // #12523 - deepEqual( - jQuery.find( "[title]", null, null, jQuery("#qunit-fixture a").get().concat( document.createTextNode("") ) ), - q("google"), - "Text nodes fail attribute tests without exception" - ); + assert.t( "All Children of ID", "#foo > *", [ "sndp", "en", "sap" ] ); + assert.t( "All Children of ID with no children", "#firstUL > *", [] ); + + a = jQuery( "" ).appendTo( "#qunit-fixture" ); + assert.t( "ID Selector contains backslash", "#backslash\\\\foo", [ "backslash\\foo" ] ); + + assert.t( "ID Selector on Form with an input that has a name of 'id'", "#lengthtest", [ "lengthtest" ] ); + + assert.t( "ID selector with non-existant ancestor", "#asdfasdf #foobar", [] ); // bug #986 + + assert.t( "Underscore ID", "#types_all", [ "types_all" ] ); + assert.t( "Dash ID", "#qunit-fixture", [ "qunit-fixture" ] ); + + assert.t( "ID with weird characters in it", "#name\\+value", [ "name+value" ] ); +} ); + +QUnit.test( "class - jQuery only", function( assert ) { + assert.expect( 4 ); + + assert.deepEqual( jQuery( ".blog", document.getElementsByTagName( "p" ) ).get(), q( "mark", "simon" ), "Finding elements with a context." ); + assert.deepEqual( jQuery( ".blog", "p" ).get(), q( "mark", "simon" ), "Finding elements with a context." ); + assert.deepEqual( jQuery( ".blog", jQuery( "p" ) ).get(), q( "mark", "simon" ), "Finding elements with a context." ); + assert.deepEqual( jQuery( "p" ).find( ".blog" ).get(), q( "mark", "simon" ), "Finding elements with a context." ); +} ); + +QUnit.test( "name", function( assert ) { + assert.expect( 5 ); + + var form; + + assert.t( "Name selector", "input[name=action]", [ "text1" ] ); + assert.t( "Name selector with single quotes", "input[name='action']", [ "text1" ] ); + assert.t( "Name selector with double quotes", "input[name=\"action\"]", [ "text1" ] ); + + assert.t( "Name selector for grouped input", "input[name='types[]']", [ "types_all", "types_anime", "types_movie" ] ); + + form = jQuery( "
        " ).appendTo( "body" ); + assert.equal( jQuery( "input", form[ 0 ] ).length, 1, "Make sure that rooted queries on forms (with possible expandos) work." ); + + form.remove(); +} ); + +QUnit.test( "selectors with comma", function( assert ) { + assert.expect( 4 ); + + var fixture = jQuery( "

        " ); + + assert.equal( fixture.find( "h2, div p" ).filter( "p" ).length, 2, "has to find two

        " ); + assert.equal( fixture.find( "h2, div p" ).filter( "h2" ).length, 1, "has to find one

        " ); + assert.equal( fixture.find( "h2 , div p" ).filter( "p" ).length, 2, "has to find two

        " ); + assert.equal( fixture.find( "h2 , div p" ).filter( "h2" ).length, 1, "has to find one

        " ); +} ); + +QUnit.test( "child and adjacent", function( assert ) { + assert.expect( 27 ); + + var nothiddendiv; + + assert.t( "Child", "p > a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); + assert.t( "Child", "p> a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); + assert.t( "Child", "p >a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); + assert.t( "Child", "p>a", [ "simon1", "google", "groups", "mark", "yahoo", "simon" ] ); + assert.t( "Child w/ Class", "p > a.blog", [ "mark", "simon" ] ); + assert.t( "All Children", "code > *", [ "anchor1", "anchor2" ] ); + assert.t( "All Grandchildren", "p > * > *", [ "anchor1", "anchor2" ] ); + assert.t( "Adjacent", "p + p", [ "ap", "en", "sap" ] ); + assert.t( "Adjacent", "p#firstp + p", [ "ap" ] ); + assert.t( "Adjacent", "p[lang=en] + p", [ "sap" ] ); + assert.t( "Adjacent", "a.GROUPS + code + a", [ "mark" ] ); + assert.t( "Element Preceded By", "#groups ~ a", [ "mark" ] ); + assert.t( "Element Preceded By", "#length ~ input", [ "idTest" ] ); + assert.t( "Element Preceded By", "#siblingfirst ~ em", [ "siblingnext", "siblingthird" ] ); + assert.t( "Element Preceded By (multiple)", "#siblingTest em ~ em ~ em ~ span", [ "siblingspan" ] ); + assert.t( "Element Preceded By, Containing", "#liveHandlerOrder ~ div em:contains('1')", [ "siblingfirst" ] ); + + assert.t( "Multiple combinators selects all levels", "#siblingTest em *", [ "siblingchild", "siblinggrandchild", "siblinggreatgrandchild" ] ); + assert.t( "Multiple combinators selects all levels", "#siblingTest > em *", [ "siblingchild", "siblinggrandchild", "siblinggreatgrandchild" ] ); + assert.t( "Multiple sibling combinators doesn't miss general siblings", "#siblingTest > em:first-child + em ~ span", [ "siblingspan" ] ); + assert.t( "Combinators are not skipped when mixing general and specific", "#siblingTest > em:contains('x') + em ~ span", [] ); + + assert.equal( jQuery( "#listWithTabIndex" ).length, 1, "Parent div for next test is found via ID (#8310)" ); + assert.equal( jQuery( "#listWithTabIndex li:eq(2) ~ li" ).length, 1, "Find by general sibling combinator (#8310)" ); + assert.equal( jQuery( "#__sizzle__" ).length, 0, "Make sure the temporary id assigned by sizzle is cleared out (#8310)" ); + assert.equal( jQuery( "#listWithTabIndex" ).length, 1, "Parent div for previous test is still found via ID (#8310)" ); + + assert.t( "Verify deep class selector", "div.blah > p > a", [] ); + + assert.t( "No element deep selector", "div.foo > span > a", [] ); + + nothiddendiv = document.getElementById( "nothiddendiv" ); + + assert.t( "Non-existant ancestors", ".fototab > .thumbnails > a", [] ); +} ); + +QUnit.test( "attributes", function( assert ) { + assert.expect( 54 ); + + var attrbad, div, withScript; + + assert.t( "Find elements with a tabindex attribute", "[tabindex]", [ "listWithTabIndex", "foodWithNegativeTabIndex", "linkWithTabIndex", "linkWithNegativeTabIndex", "linkWithNoHrefWithTabIndex", "linkWithNoHrefWithNegativeTabIndex" ] ); + + assert.t( "Attribute Exists", "#qunit-fixture a[title]", [ "google" ] ); + assert.t( "Attribute Exists (case-insensitive)", "#qunit-fixture a[TITLE]", [ "google" ] ); + assert.t( "Attribute Exists", "#qunit-fixture *[title]", [ "google" ] ); + assert.t( "Attribute Exists", "#qunit-fixture [title]", [ "google" ] ); + assert.t( "Attribute Exists", "#qunit-fixture a[ title ]", [ "google" ] ); + + assert.t( "Boolean attribute exists", "#select2 option[selected]", [ "option2d" ] ); + assert.t( "Boolean attribute equals", "#select2 option[selected='selected']", [ "option2d" ] ); + + assert.t( "Attribute Equals", "#qunit-fixture a[rel='bookmark']", [ "simon1" ] ); + assert.t( "Attribute Equals", "#qunit-fixture a[rel='bookmark']", [ "simon1" ] ); + assert.t( "Attribute Equals", "#qunit-fixture a[rel=bookmark]", [ "simon1" ] ); + assert.t( "Attribute Equals", "#qunit-fixture a[href='http://www.google.com/']", [ "google" ] ); + assert.t( "Attribute Equals", "#qunit-fixture a[ rel = 'bookmark' ]", [ "simon1" ] ); + assert.t( "Attribute Equals Number", "#qunit-fixture option[value=1]", [ "option1b", "option2b", "option3b", "option4b", "option5c" ] ); + assert.t( "Attribute Equals Number", "#qunit-fixture li[tabIndex=-1]", [ "foodWithNegativeTabIndex" ] ); + + document.getElementById( "anchor2" ).href = "#2"; + assert.t( "href Attribute", "p a[href^='#']", [ "anchor2" ] ); + assert.t( "href Attribute", "p a[href*='#']", [ "simon1", "anchor2" ] ); + + assert.t( "for Attribute", "form label[for]", [ "label-for" ] ); + assert.t( "for Attribute in form", "#form [for=action]", [ "label-for" ] ); + + assert.t( "Attribute containing []", "input[name^='foo[']", [ "hidden2" ] ); + assert.t( "Attribute containing []", "input[name^='foo[bar]']", [ "hidden2" ] ); + assert.t( "Attribute containing []", "input[name*='[bar]']", [ "hidden2" ] ); + assert.t( "Attribute containing []", "input[name$='bar]']", [ "hidden2" ] ); + assert.t( "Attribute containing []", "input[name$='[bar]']", [ "hidden2" ] ); + assert.t( "Attribute containing []", "input[name$='foo[bar]']", [ "hidden2" ] ); + assert.t( "Attribute containing []", "input[name*='foo[bar]']", [ "hidden2" ] ); + + assert.t( "Multiple Attribute Equals", "#form input[type='radio'], #form input[type='hidden']", [ "radio1", "radio2", "hidden1" ] ); + assert.t( "Multiple Attribute Equals", "#form input[type='radio'], #form input[type=\"hidden\"]", [ "radio1", "radio2", "hidden1" ] ); + assert.t( "Multiple Attribute Equals", "#form input[type='radio'], #form input[type=hidden]", [ "radio1", "radio2", "hidden1" ] ); + + assert.t( "Attribute selector using UTF8", "span[lang=中文]", [ "台北" ] ); + + assert.t( "Attribute Begins With", "a[href ^= 'http://www']", [ "google", "yahoo" ] ); + assert.t( "Attribute Ends With", "a[href $= 'org/']", [ "mark" ] ); + assert.t( "Attribute Contains", "a[href *= 'google']", [ "google", "groups" ] ); + assert.t( "Attribute Is Not Equal", "#ap a[hreflang!='en']", [ "google", "groups", "anchor1" ] ); + + assert.t( "Empty values", "#select1 option[value='']", [ "option1a" ] ); + assert.t( "Empty values", "#select1 option[value!='']", [ "option1b", "option1c", "option1d" ] ); + + assert.t( "Select options via :selected", "#select1 option:selected", [ "option1a" ] ); + assert.t( "Select options via :selected", "#select2 option:selected", [ "option2d" ] ); + assert.t( "Select options via :selected", "#select3 option:selected", [ "option3b", "option3c" ] ); + assert.t( "Select options via :selected", "select[name='select2'] option:selected", [ "option2d" ] ); + + assert.t( "Grouped Form Elements", "input[name='foo[bar]']", [ "hidden2" ] ); + + // Make sure attribute value quoting works correctly. See jQuery #6093; #6428; #13894 + // Use seeded results to bypass querySelectorAll optimizations + attrbad = jQuery( + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + ).appendTo( "#qunit-fixture" ).get(); + + assert.t( "Underscores don't need escaping", "input[id=types_all]", [ "types_all" ] ); + + assert.t( "input[type=text]", "#form input[type=text]", [ "text1", "text2", "hidden2", "name" ] ); + assert.t( "input[type=search]", "#form input[type=search]", [ "search" ] ); + + withScript = supportjQuery( "