[Pkg-javascript-commits] [jquery] 01/04: New upstream version 3.1.1

Antonio Terceiro terceiro at moszumanska.debian.org
Sat Oct 22 12:09:22 UTC 2016


This is an automated email from the git hooks/post-receive script.

terceiro pushed a commit to branch master
in repository jquery.

commit 8d9066a5169036ed98d3927425c428a4c261ae83
Author: Antonio Terceiro <terceiro at debian.org>
Date:   Sun Oct 16 13:50:36 2016 -0200

    New upstream version 3.1.1
---
 .babelrc                                           |   3 +
 .editorconfig                                      |  19 +-
 .jshintignore => .eslintignore                     |   4 +-
 .eslintrc.json                                     |   7 +
 .github/ISSUE_TEMPLATE.md                          |  24 +
 .github/PULL_REQUEST_TEMPLATE.md                   |  16 +
 .gitignore                                         |   7 +-
 .jscsrc                                            |  10 -
 .jshintrc                                          |  14 -
 .mailmap                                           |   6 +-
 .npmignore                                         |   4 +-
 .npmrc                                             |   1 +
 .travis.yml                                        |   2 +-
 AUTHORS.txt                                        |  17 +-
 Gruntfile.js                                       | 131 ++---
 README.md                                          |  41 +-
 build/release.js                                   |  21 +-
 build/release/cdn.js                               |   9 +-
 build/release/dist.js                              |  33 +-
 build/tasks/build.js                               |  83 ++-
 build/tasks/install_old_jsdom.js                   |  20 -
 build/tasks/lib/spawn_test.js                      |   6 +-
 build/tasks/promises_aplus_tests.js                |  14 +-
 external/sizzle/dist/sizzle.js                     | 121 +++--
 external/sizzle/dist/sizzle.min.js                 |   4 +-
 external/sizzle/dist/sizzle.min.map                |   2 +-
 package.json                                       |  64 ++-
 src/.eslintrc.json                                 |  18 +
 src/.jshintrc                                      |  29 --
 src/ajax.js                                        |  30 +-
 src/ajax/jsonp.js                                  |   2 +
 src/ajax/load.js                                   |   9 +-
 src/ajax/parseJSON.js                              |   9 -
 src/ajax/parseXML.js                               |   5 +-
 src/ajax/script.js                                 |   2 +
 src/ajax/var/location.js                           |   2 +
 src/ajax/var/nonce.js                              |   2 +
 src/ajax/var/rquery.js                             |   2 +
 src/ajax/xhr.js                                    |  10 +-
 src/attributes.js                                  |   2 +
 src/attributes/attr.js                             |  37 +-
 src/attributes/classes.js                          |  29 +-
 src/attributes/prop.js                             |  46 +-
 src/attributes/support.js                          |   8 +-
 src/attributes/val.js                              |  51 +-
 src/callbacks.js                                   |   8 +-
 src/core.js                                        |  78 +--
 src/core/DOMEval.js                                |   2 +
 src/core/access.js                                 |  17 +-
 src/core/init.js                                   |   2 +
 src/core/parseHTML.js                              |  30 +-
 src/core/ready-no-deferred.js                      | 105 ++++
 src/core/ready.js                                  |  61 ++-
 src/core/readyException.js                         |  13 +
 src/core/stripAndCollapse.js                       |  14 +
 src/core/support.js                                |   4 +-
 src/core/var/rsingleTag.js                         |   1 +
 src/css.js                                         |  33 +-
 src/css/addGetHookIf.js                            |   2 +
 src/css/adjustCSS.js                               |  10 +-
 src/css/curCSS.js                                  |   8 +-
 src/css/hiddenVisibleSelectors.js                  |   8 +-
 src/css/showHide.js                                |  17 +-
 src/css/support.js                                 |  73 ++-
 src/css/var/cssExpand.js                           |   2 +
 src/css/var/getStyles.js                           |   6 +-
 src/css/var/isHidden.js                            |  16 -
 src/css/var/isHiddenWithinTree.js                  |  34 ++
 src/css/var/rmargin.js                             |   2 +
 src/css/var/rnumnonpx.js                           |   2 +
 src/css/var/swap.js                                |   2 +
 src/data.js                                        |  38 +-
 src/data/Data.js                                   |  12 +-
 src/data/var/acceptData.js                         |   3 +-
 src/data/var/dataPriv.js                           |   2 +
 src/data/var/dataUser.js                           |   2 +
 src/deferred.js                                    | 116 +++--
 src/deferred/exceptionHook.js                      |   6 +-
 src/deprecated.js                                  |   4 +
 src/dimensions.js                                  |   2 +
 src/effects.js                                     |  47 +-
 src/effects/Tween.js                               |  21 +-
 src/effects/animatedSelector.js                    |   4 +-
 src/event.js                                       | 268 +++++-----
 src/event/ajax.js                                  |   2 +
 src/event/alias.js                                 |   2 +
 src/event/focusin.js                               |   8 +-
 src/event/support.js                               |   2 +
 src/event/trigger.js                               |  20 +-
 src/exports/amd.js                                 |   2 +
 src/exports/global.js                              |   8 +
 src/jquery.js                                      |   7 +-
 src/manipulation.js                                |  18 +-
 src/manipulation/_evalUrl.js                       |   2 +
 src/manipulation/buildFragment.js                  |   6 +-
 src/manipulation/getAll.js                         |  30 +-
 src/manipulation/setGlobalEval.js                  |   2 +
 src/manipulation/support.js                        |   8 +-
 src/manipulation/var/rcheckableType.js             |   2 +
 src/manipulation/var/rscriptType.js                |   2 +
 src/manipulation/var/rtagName.js                   |   2 +
 src/manipulation/wrapMap.js                        |   6 +-
 src/offset.js                                      |  17 +-
 src/outro.js                                       |   2 -
 src/queue.js                                       |   2 +
 src/queue/delay.js                                 |   2 +
 src/selector-native.js                             |  32 +-
 src/selector-sizzle.js                             |   7 +-
 src/selector.js                                    |   4 +-
 src/serialize.js                                   |  37 +-
 src/traversing.js                                  |   2 +
 src/traversing/findFilter.js                       |  41 +-
 src/traversing/var/dir.js                          |   2 +
 src/traversing/var/rneedsContext.js                |   2 +
 src/traversing/var/siblings.js                     |   2 +
 src/var/ObjectFunctionString.js                    |   7 +
 src/var/arr.js                                     |   2 +
 src/var/class2type.js                              |   1 +
 src/var/concat.js                                  |   2 +
 src/var/document.js                                |   2 +
 src/var/documentElement.js                         |   2 +
 src/var/fnToString.js                              |   7 +
 src/var/getProto.js                                |   5 +
 src/var/hasOwn.js                                  |   2 +
 src/var/indexOf.js                                 |   2 +
 src/var/pnum.js                                    |   2 +
 src/var/push.js                                    |   2 +
 src/var/rcssNum.js                                 |   2 +
 src/var/rnothtmlwhite.js                           |   8 +
 src/var/rnotwhite.js                               |   3 -
 src/var/slice.js                                   |   2 +
 src/var/support.js                                 |   1 +
 src/var/toString.js                                |   2 +
 src/wrap.js                                        |   2 +
 src/{intro.js => wrapper.js}                       |  23 +-
 test/{.jshintrc => .eslintrc.json}                 |  44 +-
 test/data/ajax/onunload.html                       |   3 +-
 test/data/ajax/unreleasedXHR.html                  |   3 +-
 test/data/core/aliased.html                        |   7 +-
 test/data/core/cc_on.html                          |   3 +-
 test/data/core/dynamic_ready.html                  |   5 +-
 test/data/core/onready.html                        |   3 +-
 test/data/css/cssWidthBeforeDocReady.html          |   4 +-
 test/data/data/dataAttrs.html                      |   3 +-
 test/data/dimensions/documentLarge.html            |   4 +
 test/data/event/focusElem.html                     |   3 +-
 test/data/event/focusinCrossFrame.html             |   3 +-
 test/data/event/interactiveReady.html              |   7 +-
 test/data/event/promiseReady.html                  |   3 +-
 test/data/event/syncReady.html                     |   3 +-
 test/data/event/triggerunload.html                 |   3 +-
 test/data/iframeTest.js                            |   8 +
 test/data/manipulation/iframe-denied.html          |   3 +-
 test/data/manipulation/scripts-context.html        |   7 +-
 test/data/offset/absolute.html                     |   2 +
 test/data/offset/body.html                         |   2 +
 test/data/offset/fixed.html                        |   2 +
 test/data/offset/relative.html                     |   2 +
 test/data/offset/scroll.html                       |   2 +
 test/data/offset/static.html                       |   2 +
 test/data/offset/table.html                        |   2 +
 test/data/selector/html5_selector.html             |   4 +-
 test/data/selector/sizzle_cache.html               |   4 +
 test/data/support/bodyBackground.html              |   3 +-
 test/data/support/csp.js                           |   2 +-
 test/data/support/csp.php                          |   1 +
 test/data/test3.html                               |   1 +
 test/data/testinit.js                              | 144 +++---
 test/data/testsuite.css                            |  16 +-
 test/index.html                                    |   2 +-
 test/jquery.js                                     |   5 +-
 test/node_smoke_tests/.eslintrc.json               |   7 +
 test/node_smoke_tests/.jshintrc                    |  14 -
 .../iterable_with_symbol_polyfill.js               |   2 -
 .../node_smoke_tests/lib/ensure_iterability_es6.js |   2 -
 test/promises_aplus_adapters/.eslintrc.json        |   4 +
 .../deferred.js}                                   |   6 +-
 test/promises_aplus_adapters/when.js               |  49 ++
 test/unit/ajax.js                                  | 152 ++++--
 test/unit/attributes.js                            | 208 +++++++-
 test/unit/basic.js                                 |  29 +-
 test/unit/core.js                                  | 292 ++++++-----
 test/unit/css.js                                   | 134 +++--
 test/unit/data.js                                  |  16 +-
 test/unit/deferred.js                              | 557 ++++++++++++++-------
 test/unit/deprecated.js                            |  76 ++-
 test/unit/dimensions.js                            |  82 ++-
 test/unit/effects.js                               | 136 ++---
 test/unit/event.js                                 | 282 +++++++----
 test/unit/manipulation.js                          |  74 ++-
 test/unit/offset.js                                | 156 +++---
 test/unit/queue.js                                 |   2 +-
 test/unit/ready.js                                 | 108 +++-
 test/unit/selector.js                              |  44 +-
 test/unit/serialize.js                             |  68 +--
 test/unit/support.js                               |  42 +-
 test/unit/traversing.js                            |  47 +-
 test/unit/tween.js                                 |   8 +-
 test/unit/wrap.js                                  |   4 +-
 199 files changed, 3472 insertions(+), 1855 deletions(-)

diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..c13c5f6
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+  "presets": ["es2015"]
+}
diff --git a/.editorconfig b/.editorconfig
index 06dbe06..b5bd7f6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -3,25 +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
-
-[test/**.xml]
-indent_style = tab
-
-[test/**.php]
-indent_style = tab
+[package.json]
+indent_style = space
+indent_size = 2
 
-[test/**.html]
-indent_style = tab
-
-[test/**.css]
-indent_style = tab
diff --git a/.jshintignore b/.eslintignore
similarity index 91%
rename from .jshintignore
rename to .eslintignore
index 1ddafd6..484ceee 100644
--- a/.jshintignore
+++ b/.eslintignore
@@ -1,6 +1,6 @@
 external
-src/intro.js
-src/outro.js
+node_modules
+*.min.js
 test/data/jquery-1.9.1.js
 test/data/badcall.js
 test/data/badjson.js
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..6dcb635
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,7 @@
+{
+	"extends": "eslint-config-jquery",
+	"root": true,
+	"env": {
+		"node": true
+	}
+}
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..a3c7d5b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,24 @@
+<!--
+Feature Requests:
+  Please read https://github.com/jquery/jquery/wiki/Adding-new-features
+  Most features should start as plugins outside of jQuery.
+
+Bug Reports:
+  Note that we only can fix bugs in the latest (1.x, 2.x, 3.x) versions of jQuery.
+  Briefly describe the issue you've encountered
+  *  What do you expect to happen?
+  *  What acually happens?
+  *  Which browsers are affected?
+  Provide a *minimal* test case, see https://webkit.org/test-case-reduction/
+  Use the latest shipping version of jQuery in your test case!
+  We prefer test cases on https://jsbin.com or https://jsfiddle.net
+
+Frequently Reported Issues:
+  * Selectors with '#' break: See https://github.com/jquery/jquery/issues/2824
+-->
+
+### Description ###
+
+
+### Link to test case ###
+
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..0ec4516
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,16 @@
+### Summary ###
+<!--
+Describe what this PR does. All but trivial changes (e.g. typos)
+should start with an issue. Mention the issue number here.
+-->
+
+
+### Checklist ###
+Mark an `[x]` for completed items, if you're not sure leave them unchecked and we can assist.
+
+* [ ] All authors have signed the CLA at https://contribute.jquery.com/CLA/
+* [ ] New tests have been added to show the fix or feature works
+* [ ] Grunt build and unit tests pass locally with these changes
+* [ ] If needed, a docs issue/PR was created at https://github.com/jquery/api.jquery.com
+
+Thanks! Bots and humans will be around shortly to check it out.
diff --git a/.gitignore b/.gitignore
index eae5df6..8cb522d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,12 @@
 .bower.json
 .sizecache.json
 
-/dist
+npm-debug.log*
+
+# Ignore everything in dist folder except for eslint config
+/dist/*
+!/dist/.eslintrc.json
+
 /node_modules
 
 /test/node_smoke_tests/lib/ensure_iterability.js
diff --git a/.jscsrc b/.jscsrc
deleted file mode 100644
index c460130..0000000
--- a/.jscsrc
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-	"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",
-		"test/node_smoke_tests/lib/ensure_iterability.js", "node_modules" ]
-}
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index 1445c7b..0000000
--- a/.jshintrc
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"boss": true,
-	"curly": true,
-	"eqeqeq": true,
-	"eqnull": true,
-	"expr": true,
-	"immed": true,
-	"noarg": true,
-	"quotmark": "double",
-	"undef": true,
-	"unused": true,
-
-	"node": true
-}
diff --git a/.mailmap b/.mailmap
index 2949a51..66bd7e7 100644
--- a/.mailmap
+++ b/.mailmap
@@ -86,8 +86,10 @@ Scott Jehl <scottjehl at gmail.com> <scott at scottjehl.com>
 Sebastian Burkhard <sebi.burkhard at gmail.com>
 Senya Pugach <upisfree at outlook.com>
 Thomas Tortorini <thomastortorini at gmail.com> Mr21
-Timmy Willison <timmywillisn at gmail.com>
-Timmy Willison <timmywillisn at gmail.com> <tim.willison at thisismedium.com>
+Timmy Willison <4timmywil at gmail.com>
+Timmy Willison <4timmywil at gmail.com> <timmywillisn at gmail.com>
+Timmy Willison <4timmywil at gmail.com> <tim.willison at thisismedium.com>
+Timmy Willison <4timmywil at gmail.com> <timmywil at users.noreply.github.com>
 Timo Tijhof <krinklemail at gmail.com>
 TJ Holowaychuk <tj at vision-media.ca>
 Tom H Fuertes <tomfuertes at gmail.com>
diff --git a/.npmignore b/.npmignore
index d510949..5af00bb 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,5 +1,5 @@
-.jshintignore
-.jshintrc
+.eslintignore
+.eslintrc.json
 
 /.editorconfig
 /.gitattributes
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..cffe8cd
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+save-exact=true
diff --git a/.travis.yml b/.travis.yml
index 5e3f4a3..7992702 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,4 +4,4 @@ node_js:
 - "0.10"
 - "0.12"
 - "4"
-- "5"
+- "6"
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 685e2e6..334363d 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -77,7 +77,7 @@ Jared Grippe <jared at deadlyicon.com>
 Sylvester Keil <sylvester at keil.or.at>
 Brandon Sterne <bsterne at mozilla.com>
 Mathias Bynens <mathias at qiwi.be>
-Timmy Willison <timmywillisn at gmail.com>
+Timmy Willison <4timmywil at gmail.com>
 Corey Frang <gnarf37 at gmail.com>
 Digitalxero <digitalxero>
 Anton Kovalyov <anton at kovalyov.net>
@@ -278,3 +278,18 @@ Josh Soref <apache at soref.com>
 Henry Wong <henryw4k at gmail.com>
 Jon Dufresne <jon.dufresne at gmail.com>
 Martijn W. van der Lee <martijn at vanderlee.com>
+Devin Wilson <dwilson6.github at gmail.com>
+Steve Mao <maochenyan at gmail.com>
+Zack Hall <zackhall at outlook.com>
+Bernhard M. Wiedemann <jquerybmw at lsmod.de>
+Todor Prikumov <tono_pr at abv.bg>
+Jha Naman <createnaman at gmail.com>
+William Robinet <william.robinet at conostix.com>
+Alexander Lisianoi <all3fox at gmail.com>
+Vitaliy Terziev <vitaliyterziev at gmail.com>
+Joe Trumbull <trumbull.j at gmail.com>
+Alexander K <xpyro at ya.ru>
+Damian Senn <jquery at topaxi.codes>
+Ralin Chimev <ralin.chimev at gmail.com>
+Felipe Sateler <fsateler at gmail.com>
+Christophe Tafani-Dereeper <christophetd at hotmail.fr>
diff --git a/Gruntfile.js b/Gruntfile.js
index 1e204ad..7090a1a 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -2,7 +2,8 @@ module.exports = function( grunt ) {
 	"use strict";
 
 	function readOptionalJSON( filepath ) {
-		var data = {};
+		var stripJSONComments = require( "strip-json-comments" ),
+			data = {};
 		try {
 			data = JSON.parse( stripJSONComments(
 				fs.readFileSync( filepath, { encoding: "utf8" } )
@@ -12,24 +13,15 @@ module.exports = function( grunt ) {
 	}
 
 	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 {
-				require( "jsdom" );
-				return true;
-			} catch ( e ) {
-				return false;
-			}
-		} )();
+		oldNode = /^v0\./.test( process.version );
 
-	// The concatenated file won't pass onevar
-	// But our modules can
-	delete srcHintOptions.onevar;
+	// Support: Node.js <4
+	// Skip running tasks that dropped support for Node.js 0.10 & 0.12
+	// in those Node versions.
+	function runIfNewNode( task ) {
+		return oldNode ? "print_old_node_message:" + task : task;
+	}
 
 	if ( !grunt.option( "filename" ) ) {
 		grunt.option( "filename", "jquery.js" );
@@ -75,6 +67,10 @@ module.exports = function( grunt ) {
 					callbacks: [ "deferred" ],
 					css: [ "effects", "dimensions", "offset" ],
 					"css/showHide": [ "effects" ],
+					deferred: {
+						remove: [ "ajax", "effects", "queue", "core/ready" ],
+						include: [ "core/ready-no-deferred" ]
+					},
 					sizzle: [ "css/hiddenVisibleSelectors", "effects/animatedSelector" ]
 				}
 			}
@@ -101,7 +97,7 @@ module.exports = function( grunt ) {
 
 					"requirejs/require.js": "requirejs/require.js",
 
-					"sinon/fake_timers.js": "sinon/lib/sinon/util/fake_timers.js",
+					"sinon/sinon.js": "sinon/pkg/sinon.js",
 					"sinon/LICENSE.txt": "sinon/LICENSE"
 				}
 			}
@@ -111,34 +107,22 @@ module.exports = function( grunt ) {
 				src: [ "package.json" ]
 			}
 		},
-		jshint: {
-			all: {
-				src: [
-					"src/**/*.js", "Gruntfile.js", "test/**/*.js", "build/**/*.js"
-				],
-				options: {
-					jshintrc: true
-				}
+		eslint: {
+			options: {
+
+				// See https://github.com/sindresorhus/grunt-eslint/issues/119
+				quiet: true
 			},
+
+			// We have to explicitly declare "src" property otherwise "newer"
+			// task wouldn't work properly :/
 			dist: {
-				src: "dist/jquery.js",
-				options: srcHintOptions
+				src: "dist/jquery.js"
+			},
+			dev: {
+				src: [ "src/**/*.js", "Gruntfile.js", "test/**/*.js", "build/**/*.js" ]
 			}
 		},
-		jscs: {
-			src: "src",
-			gruntfile: "Gruntfile.js",
-
-			// Check parts of tests that pass
-			test: [
-				"test/data/testrunner.js",
-				"test/unit/animation.js",
-				"test/unit/basic.js",
-				"test/unit/tween.js",
-				"test/unit/wrap.js"
-			],
-			build: "build"
-		},
 		testswarm: {
 			tests: [
 
@@ -171,7 +155,7 @@ module.exports = function( grunt ) {
 			]
 		},
 		watch: {
-			files: [ "<%= jshint.all.src %>" ],
+			files: [ "<%= eslint.dev.src %>" ],
 			tasks: [ "dev" ]
 		},
 		uglify: {
@@ -203,24 +187,55 @@ module.exports = function( grunt ) {
 	} );
 
 	// Load grunt tasks from NPM packages
-	require( "load-grunt-tasks" )( grunt );
+	// Support: Node.js <4
+	// Don't load the eslint task in old Node.js, it won't parse.
+	require( "load-grunt-tasks" )( grunt, {
+		pattern: oldNode ? [ "grunt-*", "!grunt-eslint" ] : [ "grunt-*" ]
+	} );
 
 	// Integrate jQuery specific tasks
 	grunt.loadTasks( "build/tasks" );
 
-	grunt.registerTask( "lint", [ "jsonlint", "jshint", "jscs" ] );
-
-	// 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" ] : [] );
-
-	grunt.registerTask( "test", [ "test_fast" ].concat(
-		runJsdomTests ? [ "promises_aplus_tests" ] : []
-	) );
-
-	// Short list as a high frequency watch task
-	grunt.registerTask( "dev", [ "build:*:*", "lint", "uglify", "remove_map_comment", "dist:*" ] );
+	grunt.registerTask( "print_old_node_message", function() {
+		var task = [].slice.call( arguments ).join( ":" );
+		grunt.log.writeln( "Old Node.js detected, running the task \"" + task + "\" skipped..." );
+	} );
 
-	grunt.registerTask( "default", [ "dev", "test_fast", "compare_size" ] );
+	grunt.registerTask( "lint", [
+		"jsonlint",
+		runIfNewNode( "eslint" )
+	] );
+
+	grunt.registerTask( "lint:newer", [
+		"newer:jsonlint",
+		runIfNewNode( "newer:eslint" )
+	] );
+
+	grunt.registerTask( "test:fast", runIfNewNode( "node_smoke_tests" ) );
+	grunt.registerTask( "test:slow", runIfNewNode( "promises_aplus_tests" ) );
+
+	grunt.registerTask( "test", [
+		"test:fast",
+		"test:slow"
+	] );
+
+	grunt.registerTask( "dev", [
+		"build:*:*",
+		runIfNewNode( "newer:eslint:dev" ),
+		"newer:uglify",
+		"remove_map_comment",
+		"dist:*",
+		"compare_size"
+	] );
+
+	grunt.registerTask( "default", [
+		runIfNewNode( "eslint:dev" ),
+		"build:*:*",
+		"uglify",
+		"remove_map_comment",
+		"dist:*",
+		runIfNewNode( "eslint:dist" ),
+		"test:fast",
+		"compare_size"
+	] );
 };
diff --git a/README.md b/README.md
index e96f625..9e5b130 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ What you need to build your own jQuery
 
 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.
 
-For Windows, you have to download and install [git](http://git-scm.com/downloads) and [Node.js](https://nodejs.org/en/download/).
+For Windows, you have to download and install [git](https://git-scm.com/downloads) and [Node.js](https://nodejs.org/en/download/).
 
 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.
@@ -340,32 +340,33 @@ url("data/test.php?foo=bar");
 ```
 
 
-### Load tests in an iframe ###
+### Run tests in an iframe ###
 
-Loads a given page constructing a url with fileName: `"./data/" + fileName + ".html"`
-and fires the given callback on jQuery ready (using the jQuery loading from that page)
-and passes the iFrame's jQuery to the callback.
+Some tests may require a document other than the standard test fixture, and
+these can be run in a separate iframe. The actual test code and assertions
+remain in jQuery's main test files; only the minimal test fixture markup
+and setup code should be placed in the iframe file.
 
 ```js
-testIframe( fileName, testName, callback );
+testIframe( testName, fileName,
+  function testCallback(
+      assert, jQuery, window, document,
+	  [ additional args ] ) {
+	...
+  } );
 ```
 
-Callback arguments:
+This loads a page, constructing a url with fileName `"./data/" + fileName`.
+The iframed page determines when the callback occurs in the test by
+including the "/test/data/iframeTest.js" script and calling
+`startIframeTest( [ additional args ] )` when appropriate. Often this
+will be after either document ready or `window.onload` fires.
 
-```js
-callback( jQueryFromIFrame, iFrameWindow, iFrameDocument );
-```
-
-### Load tests in an iframe (window.iframeCallback) ###
+The `testCallback` receives the QUnit `assert` object created by `testIframe`
+for this test, followed by the global `jQuery`, `window`, and `document` from
+the iframe. If the iframe code passes any arguments to `startIframeTest`,
+they follow the `document` argument.
 
-Loads a given page constructing a url with fileName: `"./data/" + fileName + ".html"`
-The given callback is fired when window.iframeCallback is called by the page.
-The arguments passed to the callback are the same as the
-arguments passed to window.iframeCallback, whatever that may be.
-
-```js
-testIframeWithCallback( testName, fileName, callback );
-```
 
 Questions?
 ----------
diff --git a/build/release.js b/build/release.js
index a333d05..22cd90c 100644
--- a/build/release.js
+++ b/build/release.js
@@ -1,3 +1,4 @@
+var fs = require( "fs" );
 
 module.exports = function( Release ) {
 
@@ -8,7 +9,8 @@ module.exports = function( Release ) {
 			"dist/jquery.min.map",
 			"dist/jquery.slim.js",
 			"dist/jquery.slim.min.js",
-			"dist/jquery.slim.min.map"
+			"dist/jquery.slim.min.map",
+			"src/core.js"
 		],
 		cdn = require( "./release/cdn" ),
 		dist = require( "./release/dist" ),
@@ -19,6 +21,7 @@ module.exports = function( Release ) {
 	Release.define( {
 		npmPublish: true,
 		issueTracker: "github",
+
 		/**
 		 * Ensure the repo is in a proper state before release
 		 * @param {Function} callback
@@ -26,6 +29,17 @@ module.exports = function( Release ) {
 		checkRepoState: function( callback ) {
 			ensureSizzle( Release, callback );
 		},
+
+		/**
+		 * Set the version in the src folder for distributing AMD
+		 */
+		_setSrcVersion: function() {
+			var corePath = __dirname + "/../src/core.js",
+				contents = fs.readFileSync( corePath, "utf8" );
+			contents = contents.replace( /@VERSION/g, Release.newVersion );
+			fs.writeFileSync( corePath, contents, "utf8" );
+		},
+
 		/**
 		 * Generates any release artifacts that should be included in the release.
 		 * The callback must be invoked with an array of files that should be
@@ -40,8 +54,10 @@ module.exports = function( Release ) {
 				"Grunt custom failed"
 			);
 			cdn.makeReleaseCopies( Release );
+			Release._setSrcVersion();
 			callback( files );
 		},
+
 		/**
 		 * Acts as insertion point for restoring Release.dir.repo
 		 * It was changed to reuse npm publish code in jquery-release
@@ -53,6 +69,7 @@ module.exports = function( Release ) {
 			Release.dir.repo = Release.dir.origRepo || Release.dir.repo;
 			return npmTags();
 		},
+
 		/**
 		 * Publish to distribution repo and npm
 		 * @param {Function} callback
@@ -67,7 +84,7 @@ module.exports = function( Release ) {
 
 module.exports.dependencies = [
 	"archiver at 0.14.2",
-	"shelljs at 0.2.6",
+	"shelljs at 0.7.0",
 	"npm at 2.3.0",
 	"chalk at 1.1.1"
 ];
diff --git a/build/release/cdn.js b/build/release/cdn.js
index 3b48511..0506062 100644
--- a/build/release/cdn.js
+++ b/build/release/cdn.js
@@ -40,11 +40,12 @@ function makeReleaseCopies( Release ) {
 
 			// Map files need to reference the new uncompressed name;
 			// assume that all files reside in the same directory.
-			// "file":"jquery.min.js","sources":["jquery.js"]
+			// "file":"jquery.min.js" ... "sources":["jquery.js"]
 			text = fs.readFileSync( builtFile, "utf8" )
-				.replace( /"file":"([^"]+)","sources":\["([^"]+)"\]/,
-					"\"file\":\"" + unpathedFile.replace( /\.min\.map/, ".min.js" ) +
-					"\",\"sources\":[\"" + unpathedFile.replace( /\.min\.map/, ".js" ) + "\"]" );
+				.replace( /"file":"([^"]+)"/,
+					"\"file\":\"" + unpathedFile.replace( /\.min\.map/, ".min.js\"" ) )
+				.replace( /"sources":\["([^"]+)"\]/,
+					"\"sources\":[\"" + unpathedFile.replace( /\.min\.map/, ".js" ) + "\"]" );
 			fs.writeFileSync( releaseFile, text );
 		} else if ( builtFile !== releaseFile ) {
 			shell.cp( "-f", builtFile, releaseFile );
diff --git a/build/release/dist.js b/build/release/dist.js
index fc12a1a..e0237fe 100644
--- a/build/release/dist.js
+++ b/build/release/dist.js
@@ -55,24 +55,49 @@ module.exports = function( Release, files, complete ) {
 	function copy() {
 
 		// Copy dist files
-		var distFolder = Release.dir.dist + "/dist";
+		var distFolder = Release.dir.dist + "/dist",
+			externalFolder = Release.dir.dist + "/external",
+			rmIgnore = files
+				.concat( [
+					"README.md",
+					"node_modules"
+				] )
+				.map( function( file ) {
+					return Release.dir.dist + "/" + file;
+				} );
+
+		shell.config.globOptions = {
+			ignore: rmIgnore
+		};
+
+		// Remove extraneous files before copy
+		shell.rm( "-rf", Release.dir.dist + "/**/*" );
+
 		shell.mkdir( "-p", distFolder );
 		files.forEach( function( file ) {
 			shell.cp( "-f", Release.dir.repo + "/" + file, distFolder );
 		} );
 
+		// Copy Sizzle
+		shell.mkdir( "-p", externalFolder );
+		shell.cp( "-rf", Release.dir.repo + "/external/sizzle", externalFolder );
+
 		// Copy other files
 		extras.forEach( function( file ) {
 			shell.cp( "-rf", Release.dir.repo + "/" + file, Release.dir.dist );
 		} );
 
+		// Remove the wrapper from the dist repo
+		shell.rm( "-f", Release.dir.dist + "/src/wrapper.js" );
+
 		// Write generated bower file
 		fs.writeFileSync( Release.dir.dist + "/bower.json", generateBower() );
 
 		console.log( "Adding files to dist..." );
-		Release.exec( "git add .", "Error adding files." );
+
+		Release.exec( "git add -A", "Error adding files." );
 		Release.exec(
-			"git commit -m 'Release " + Release.newVersion + "'",
+			"git commit -m \"Release " + Release.newVersion + "\"",
 			"Error committing files."
 		);
 		console.log();
@@ -80,7 +105,7 @@ module.exports = function( Release, files, complete ) {
 		console.log( "Tagging release on dist..." );
 		Release.exec( "git tag " + Release.newVersion,
 			"Error tagging " + Release.newVersion + " on dist repo." );
-		Release.tagTime = Release.exec( "git log -1 --format='%ad'",
+		Release.tagTime = Release.exec( "git log -1 --format=\"%ad\"",
 			"Error getting tag timestamp." ).trim();
 	}
 
diff --git a/build/tasks/build.js b/build/tasks/build.js
index c9f1dae..1579691 100644
--- a/build/tasks/build.js
+++ b/build/tasks/build.js
@@ -10,12 +10,21 @@ module.exports = function( grunt ) {
 
 	var fs = require( "fs" ),
 		requirejs = require( "requirejs" ),
+		Insight = require( "insight" ),
+		pkg = require( "../../package.json" ),
 		srcFolder = __dirname + "/../../src/",
 		rdefineEnd = /\}\s*?\);[^}\w]*$/,
+		read = function( fileName ) {
+			return grunt.file.read( srcFolder + fileName );
+		},
+		wrapper = read( "wrapper.js" ).split( /\/\/ \@CODE\n\/\/[^\n]+/ ),
 		config = {
 			baseUrl: "src",
 			name: "jquery",
 
+			// Allow strict mode
+			useStrict: true,
+
 			// We have multiple minify steps
 			optimize: "none",
 
@@ -28,11 +37,8 @@ module.exports = function( grunt ) {
 			// Avoid breaking semicolons inserted by r.js
 			skipSemiColonInsertion: true,
 			wrap: {
-				startFile: "src/intro.js",
-				endFile: [ "src/exports/global.js", "src/outro.js" ]
-			},
-			paths: {
-				sizzle: "../external/sizzle/dist/sizzle"
+				start: wrapper[ 0 ].replace( /\/\*\s*eslint(?: |-).*\s*\*\/\n/, "" ),
+				end: wrapper[ 1 ]
 			},
 			rawText: {},
 			onBuildWrite: convert
@@ -54,11 +60,16 @@ module.exports = function( grunt ) {
 		// Convert var modules
 		if ( /.\/var\//.test( path.replace( process.cwd(), "" ) ) ) {
 			contents = contents
-				.replace( /define\([\w\W]*?return/, "var " + ( /var\/([\w-]+)/.exec( name )[ 1 ] ) + " =" )
+				.replace(
+					/define\([\w\W]*?return/,
+					"var " +
+					( /var\/([\w-]+)/.exec( name )[ 1 ] ) +
+					" ="
+				)
 				.replace( rdefineEnd, "" );
 
 		// Sizzle treatment
-		} else if ( /^sizzle$/.test( name ) ) {
+		} else if ( /\/sizzle$/.test( name ) ) {
 			contents = "var Sizzle =\n" + contents
 
 				// Remove EXPOSE lines from Sizzle
@@ -74,7 +85,7 @@ module.exports = function( grunt ) {
 
 			// Remove define wrappers, closure ends, and empty declarations
 			contents = contents
-				.replace( /define\([^{]*?{/, "" )
+				.replace( /define\([^{]*?{\s*(?:("|')use strict\1(?:;|))?/, "" )
 				.replace( rdefineEnd, "" );
 
 			// Remove anything wrapped with
@@ -120,6 +131,7 @@ module.exports = function( grunt ) {
 			excluded = [],
 			included = [],
 			version = grunt.config( "pkg.version" ),
+
 			/**
 			 * Recursively calls the excluder to remove on all modules in the list
 			 * @param {Array} list
@@ -157,6 +169,7 @@ module.exports = function( grunt ) {
 					} );
 				}
 			},
+
 			/**
 			 * Adds the specified module to the excluded or included list, depending on the flag
 			 * @param {String} flag A module path relative to
@@ -164,7 +177,8 @@ module.exports = function( grunt ) {
 			 *  whether it should included or excluded
 			 */
 			excluder = function( flag ) {
-				var m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
+				var additional,
+					m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
 					exclude = m[ 1 ] === "-",
 					module = m[ 2 ];
 
@@ -188,8 +202,16 @@ module.exports = function( grunt ) {
 							}
 						}
 
+						additional = removeWith[ module ];
+
 						// Check removeWith list
-						excludeList( removeWith[ module ] );
+						if ( additional ) {
+							excludeList( additional.remove || additional );
+							if ( additional.include ) {
+								included = included.concat( additional.include );
+								grunt.log.writeln( "+" + additional.include );
+							}
+						}
 					} else {
 						grunt.log.error( "Module \"" + module + "\" is a minimum requirement." );
 						if ( module === "selector" ) {
@@ -310,10 +332,47 @@ module.exports = function( grunt ) {
 	//   grunt build:*:*:+ajax:-dimensions:-effects:-offset
 	grunt.registerTask( "custom", function() {
 		var args = this.args,
-			modules = args.length ? args[ 0 ].replace( /,/g, ":" ) : "";
+			modules = args.length ? args[ 0 ].replace( /,/g, ":" ) : "",
+			done = this.async(),
+			insight = new Insight( {
+				trackingCode: "UA-1076265-4",
+				pkg: pkg
+			} );
+
+		function exec( trackingAllowed ) {
+			var tracks = args.length ? args[ 0 ].split( "," ) : [];
+			var defaultPath = [ "build", "custom" ];
+
+			tracks = tracks.map( function( track ) {
+				return track.replace( /\//g, "+" );
+			} );
+
+			if ( trackingAllowed ) {
+
+				// Track individuals
+				tracks.forEach( function( module ) {
+					var path = defaultPath.concat( [ "individual" ], module );
+
+					insight.track.apply( insight, path );
+				} );
+
+				// Track full command
+				insight.track.apply( insight, defaultPath.concat( [ "full" ], tracks ) );
+			}
+
+			grunt.task.run( [ "build:*:*" + ( modules ? ":" + modules : "" ), "uglify", "dist" ] );
+			done();
+		}
 
 		grunt.log.writeln( "Creating custom build...\n" );
 
-		grunt.task.run( [ "build:*:*" + ( modules ? ":" + modules : "" ), "uglify", "dist" ] );
+		// Ask for permission the first time
+		if ( insight.optOut === undefined ) {
+			insight.askPermission( null, function( error, result ) {
+				exec( result );
+			} );
+		} else {
+			exec( !insight.optOut );
+		}
 	} );
 };
diff --git a/build/tasks/install_old_jsdom.js b/build/tasks/install_old_jsdom.js
deleted file mode 100644
index 271e0cb..0000000
--- a/build/tasks/install_old_jsdom.js
+++ /dev/null
@@ -1,20 +0,0 @@
-module.exports = function( grunt ) {
-
-	"use strict";
-
-	// Run this task to run jsdom-related tests on Node.js < 1.0.0.
-	grunt.registerTask( "old_jsdom", function() {
-		if ( !/^v0/.test( process.version ) ) {
-			console.warn( "The old_jsdom task doesn\'t need to be run in io.js or new Node.js" );
-			return;
-		}
-
-		// Use npm on the command-line
-		// There is no local npm
-		grunt.util.spawn( {
-			cmd: "npm",
-			args: [ "install", "jsdom at 3" ],
-			opts: { stdio: "inherit" }
-		}, this.async() );
-	} );
-};
diff --git a/build/tasks/lib/spawn_test.js b/build/tasks/lib/spawn_test.js
index 6c4596a..34353a7 100644
--- a/build/tasks/lib/spawn_test.js
+++ b/build/tasks/lib/spawn_test.js
@@ -1,5 +1,3 @@
-/* jshint node: true */
-
 "use strict";
 
 // Run Node with provided parameters: the first one being the Grunt
@@ -7,10 +5,10 @@
 // See the comment in ../node_smoke_tests.js for more information.
 module.exports = function spawnTest( done ) {
 	var testPaths = [].slice.call( arguments, 1 ),
-		spawn = require( "win-spawn" );
+		spawn = require( "cross-spawn" );
 
 	spawn( "node", testPaths, { stdio: "inherit" } )
 		.on( "close", function( code ) {
 			done( code === 0 );
 		} );
-} ;
+};
diff --git a/build/tasks/promises_aplus_tests.js b/build/tasks/promises_aplus_tests.js
index 3e770a0..a0118d1 100644
--- a/build/tasks/promises_aplus_tests.js
+++ b/build/tasks/promises_aplus_tests.js
@@ -4,10 +4,20 @@ module.exports = function( grunt ) {
 
 	var spawnTest = require( "./lib/spawn_test.js" );
 
-	grunt.registerTask( "promises_aplus_tests", function() {
+	grunt.registerTask( "promises_aplus_tests",
+		[ "promises_aplus_tests:deferred", "promises_aplus_tests:when" ] );
+
+	grunt.registerTask( "promises_aplus_tests:deferred", function() {
+		spawnTest( this.async(),
+			"./node_modules/.bin/promises-aplus-tests",
+			"test/promises_aplus_adapters/deferred.js"
+		);
+	} );
+
+	grunt.registerTask( "promises_aplus_tests:when", function() {
 		spawnTest( this.async(),
 			"./node_modules/.bin/promises-aplus-tests",
-			"test/promises_aplus_adapter.js"
+			"test/promises_aplus_adapters/when.js"
 		);
 	} );
 };
diff --git a/external/sizzle/dist/sizzle.js b/external/sizzle/dist/sizzle.js
index cb93a5b..bfa0793 100644
--- a/external/sizzle/dist/sizzle.js
+++ b/external/sizzle/dist/sizzle.js
@@ -1,12 +1,12 @@
 /*!
- * Sizzle CSS Selector Engine v2.3.0
+ * Sizzle CSS Selector Engine v2.3.3
  * https://sizzlejs.com/
  *
  * Copyright jQuery Foundation and other contributors
  * Released under the MIT license
  * http://jquery.org/license
  *
- * Date: 2016-01-04
+ * Date: 2016-08-08
  */
 (function( window ) {
 
@@ -152,7 +152,7 @@ var i,
 
 	// CSS string/identifier serialization
 	// https://drafts.csswg.org/cssom/#common-serializing-idioms
-	rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,
+	rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
 	fcssescape = function( ch, asCodePoint ) {
 		if ( asCodePoint ) {
 
@@ -179,7 +179,7 @@ var i,
 
 	disabledAncestor = addCombinator(
 		function( elem ) {
-			return elem.disabled === true;
+			return elem.disabled === true && ("form" in elem || "label" in elem);
 		},
 		{ dir: "parentNode", next: "legend" }
 	);
@@ -465,26 +465,54 @@ function createButtonPseudo( type ) {
  * @param {Boolean} disabled true for :disabled; false for :enabled
  */
 function createDisabledPseudo( disabled ) {
-	// Known :disabled false positives:
-	// IE: *[disabled]:not(button, input, select, textarea, optgroup, option, menuitem, fieldset)
-	// not IE: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
+
+	// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
 	return function( elem ) {
 
-		// Check form elements and option elements for explicit disabling
-		return "label" in elem && elem.disabled === disabled ||
-			"form" in elem && elem.disabled === disabled ||
+		// Only certain elements can match :enabled or :disabled
+		// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
+		// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
+		if ( "form" in elem ) {
+
+			// Check for inherited disabledness on relevant non-disabled elements:
+			// * listed form-associated elements in a disabled fieldset
+			//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
+			//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
+			// * option elements in a disabled optgroup
+			//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
+			// All such elements have a "form" property.
+			if ( elem.parentNode && elem.disabled === false ) {
+
+				// Option elements defer to a parent optgroup if present
+				if ( "label" in elem ) {
+					if ( "label" in elem.parentNode ) {
+						return elem.parentNode.disabled === disabled;
+					} else {
+						return elem.disabled === disabled;
+					}
+				}
 
-			// Check non-disabled form elements for fieldset[disabled] ancestors
-			"form" in elem && elem.disabled === false && (
-				// Support: IE6-11+
-				// Ancestry is covered for us
-				elem.isDisabled === disabled ||
+				// Support: IE 6 - 11
+				// Use the isDisabled shortcut property to check for disabled fieldset ancestors
+				return elem.isDisabled === disabled ||
 
-				// Otherwise, assume any non-<option> under fieldset[disabled] is disabled
-				/* jshint -W018 */
-				elem.isDisabled !== !disabled &&
-					("label" in elem || !disabledAncestor( elem )) !== disabled
-			);
+					// Where there is no isDisabled, check manually
+					/* jshint -W018 */
+					elem.isDisabled !== !disabled &&
+						disabledAncestor( elem ) === disabled;
+			}
+
+			return elem.disabled === disabled;
+
+		// Try to winnow out elements that can't be disabled before trusting the disabled property.
+		// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
+		// even exist on them, let alone have a boolean value.
+		} else if ( "label" in elem ) {
+			return elem.disabled === disabled;
+		}
+
+		// Remaining elements are neither :enabled nor :disabled
+		return false;
 	};
 }
 
@@ -600,25 +628,21 @@ setDocument = Sizzle.setDocument = function( node ) {
 		return !document.getElementsByName || !document.getElementsByName( expando ).length;
 	});
 
-	// ID find and filter
+	// ID filter and find
 	if ( support.getById ) {
-		Expr.find["ID"] = function( id, context ) {
-			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
-				var m = context.getElementById( id );
-				return m ? [ m ] : [];
-			}
-		};
 		Expr.filter["ID"] = function( id ) {
 			var attrId = id.replace( runescape, funescape );
 			return function( elem ) {
 				return elem.getAttribute("id") === attrId;
 			};
 		};
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+				var elem = context.getElementById( id );
+				return elem ? [ elem ] : [];
+			}
+		};
 	} else {
-		// Support: IE6/7
-		// getElementById is not reliable as a find shortcut
-		delete Expr.find["ID"];
-
 		Expr.filter["ID"] =  function( id ) {
 			var attrId = id.replace( runescape, funescape );
 			return function( elem ) {
@@ -627,6 +651,36 @@ setDocument = Sizzle.setDocument = function( node ) {
 				return node && node.value === attrId;
 			};
 		};
+
+		// Support: IE 6 - 7 only
+		// getElementById is not reliable as a find shortcut
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+				var node, i, elems,
+					elem = context.getElementById( id );
+
+				if ( elem ) {
+
+					// Verify the id attribute
+					node = elem.getAttributeNode("id");
+					if ( node && node.value === id ) {
+						return [ elem ];
+					}
+
+					// Fall back on getElementsByName
+					elems = context.getElementsByName( id );
+					i = 0;
+					while ( (elem = elems[i++]) ) {
+						node = elem.getAttributeNode("id");
+						if ( node && node.value === id ) {
+							return [ elem ];
+						}
+					}
+				}
+
+				return [];
+			}
+		};
 	}
 
 	// Tag
@@ -1667,6 +1721,7 @@ function addCombinator( matcher, combinator, base ) {
 					return matcher( elem, context, xml );
 				}
 			}
+			return false;
 		} :
 
 		// Check against all ancestor/preceding elements
@@ -1711,6 +1766,7 @@ function addCombinator( matcher, combinator, base ) {
 					}
 				}
 			}
+			return false;
 		};
 }
 
@@ -2073,8 +2129,7 @@ select = Sizzle.select = function( selector, context, results, seed ) {
 		// Reduce context if the leading compound selector is an ID
 		tokens = match[0] = match[0].slice( 0 );
 		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
-				support.getById && context.nodeType === 9 && documentIsHTML &&
-				Expr.relative[ tokens[1].type ] ) {
+				context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) {
 
 			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
 			if ( !context ) {
diff --git a/external/sizzle/dist/sizzle.min.js b/external/sizzle/dist/sizzle.min.js
index 7358ad9..be03278 100644
--- a/external/sizzle/dist/sizzle.min.js
+++ b/external/sizzle/dist/sizzle.min.js
@@ -1,3 +1,3 @@
-/*! Sizzle v2.3.0 | (c) jQuery Foundation, Inc. | jquery.org/license */
-!function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\x00-\\xa0])+",M="\\[" [...]
+/*! Sizzle v2.3.3 | (c) jQuery Foundation, Inc. | jquery.org/license */
+!function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K [...]
 //# sourceMappingURL=sizzle.min.map
\ No newline at end of file
diff --git a/external/sizzle/dist/sizzle.min.map b/external/sizzle/dist/sizzle.min.map
index d86726d..031c1b7 100644
--- a/external/sizzle/dist/sizzle.min.map
+++ b/external/sizzle/dist/sizzle.min.map
@@ -1 +1 @@
-{"version":3,"sources":["sizzle.js"],"names":["window","i","support","Expr","getText","isXML","tokenize","compile","select","outermostContext","sortInput","hasDuplicate","setDocument","document","docElem","documentIsHTML","rbuggyQSA","rbuggyMatches","matches","contains","expando","Date","preferredDoc","dirruns","done","classCache","createCache","tokenCache","compilerCache","sortOrder","a","b","hasOwn","hasOwnProperty","arr","pop","push_native","push","slice","indexOf","list","elem","len" [...]
\ No newline at end of file
+{"version":3,"sources":["sizzle.js"],"names":["window","i","support","Expr","getText","isXML","tokenize","compile","select","outermostContext","sortInput","hasDuplicate","setDocument","document","docElem","documentIsHTML","rbuggyQSA","rbuggyMatches","matches","contains","expando","Date","preferredDoc","dirruns","done","classCache","createCache","tokenCache","compilerCache","sortOrder","a","b","hasOwn","hasOwnProperty","arr","pop","push_native","push","slice","indexOf","list","elem","len" [...]
\ No newline at end of file
diff --git a/package.json b/package.json
index 7c41aed..b6dd0d3 100644
--- a/package.json
+++ b/package.json
@@ -2,12 +2,12 @@
   "name": "jquery",
   "title": "jQuery",
   "description": "JavaScript library for DOM operations",
-  "version": "3.0.0-beta1",
+  "version": "3.1.1",
   "main": "dist/jquery.js",
   "homepage": "https://jquery.com",
   "author": {
     "name": "jQuery Foundation and other contributors",
-    "url": "https://github.com/jquery/jquery/blob/3.0.0-beta1/AUTHORS.txt"
+    "url": "https://github.com/jquery/jquery/blob/3.1.1/AUTHORS.txt"
   },
   "repository": {
     "type": "git",
@@ -25,44 +25,52 @@
   "license": "MIT",
   "dependencies": {},
   "devDependencies": {
-    "commitplease": "2.0.0",
-    "core-js": "0.9.17",
-    "grunt": "0.4.5",
-    "grunt-babel": "5.0.1",
-    "grunt-cli": "0.1.13",
-    "grunt-compare-size": "0.4.0",
-    "grunt-contrib-jshint": "0.11.2",
-    "grunt-contrib-uglify": "0.9.2",
-    "grunt-contrib-watch": "0.6.1",
-    "grunt-git-authors": "2.0.1",
-    "grunt-jscs": "2.1.0",
-    "grunt-jsonlint": "1.0.4",
+    "babel-preset-es2015": "6.6.0",
+    "commitplease": "2.6.1",
+    "core-js": "2.2.2",
+    "cross-spawn": "2.2.3",
+    "eslint-config-jquery": "1.0.0",
+    "grunt": "1.0.1",
+    "grunt-babel": "6.0.0",
+    "grunt-cli": "1.2.0",
+    "grunt-compare-size": "0.4.2",
+    "grunt-contrib-uglify": "1.0.1",
+    "grunt-contrib-watch": "1.0.0",
+    "grunt-eslint": "19.0.0",
+    "grunt-git-authors": "3.2.0",
+    "grunt-jsonlint": "1.0.7",
+    "grunt-newer": "1.2.0",
     "grunt-npmcopy": "0.1.0",
     "gzip-js": "0.3.2",
+    "husky": "0.11.4",
+    "insight": "0.8.1",
     "jsdom": "5.6.1",
-    "load-grunt-tasks": "1.0.0",
-    "native-promise-only": "0.7.8-a",
-    "promises-aplus-tests": "2.1.0",
-    "q": "1.1.2",
-    "qunitjs": "1.17.1",
+    "load-grunt-tasks": "3.5.0",
+    "native-promise-only": "0.8.1",
+    "promises-aplus-tests": "2.1.2",
+    "q": "1.4.1",
     "qunit-assert-step": "1.0.3",
-    "requirejs": "2.1.17",
-    "sinon": "1.10.3",
-    "sizzle": "2.3.0",
-    "strip-json-comments": "1.0.3",
-    "testswarm": "1.1.0",
-    "win-spawn": "2.0.0"
+    "qunitjs": "1.23.1",
+    "requirejs": "2.2.0",
+    "sinon": "1.17.3",
+    "sizzle": "2.3.3",
+    "strip-json-comments": "2.0.1",
+    "testswarm": "1.1.0"
   },
   "scripts": {
     "build": "npm install && grunt",
     "start": "grunt watch",
-    "test": "grunt && grunt test"
+    "test": "grunt && grunt test:slow",
+    "precommit": "grunt lint:newer",
+    "commitmsg": "node node_modules/commitplease"
   },
   "commitplease": {
+    "nohook": true,
     "components": [
       "Docs",
       "Tests",
       "Build",
+      "Support",
       "Release",
       "Core",
       "Ajax",
@@ -82,6 +90,8 @@
       "Serialize",
       "Traversing",
       "Wrap"
-    ]
+    ],
+    "markerPattern": "^((clos|fix|resolv)(e[sd]|ing))|(refs?)",
+    "ticketPattern": "^((Closes|Fixes) ([a-zA-Z]{2,}-)[0-9]+)|(Refs? [^#])"
   }
 }
diff --git a/src/.eslintrc.json b/src/.eslintrc.json
new file mode 100644
index 0000000..dbf16c5
--- /dev/null
+++ b/src/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+	// Support: IE <=9 only, Android <=4.0 only
+	// The above browsers are failing a lot of tests in the ES5
+	// test suite at http://test262.ecmascript.org.
+	"parserOptions": {
+		"ecmaVersion": 3
+	},
+	"globals": {
+		"window": true,
+		"jQuery": true,
+		"define": true,
+		"module": true,
+		"noGlobal": true
+	},
+	"rules": {
+		"strict": ["error", "function"]
+	}
+}
diff --git a/src/.jshintrc b/src/.jshintrc
deleted file mode 100644
index ea3549d..0000000
--- a/src/.jshintrc
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-	"boss": true,
-	"curly": true,
-	"eqeqeq": true,
-	"eqnull": true,
-	"expr": true,
-	"immed": true,
-	"noarg": true,
-	"quotmark": "double",
-	"undef": true,
-	"unused": true,
-
-	"sub": 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,
-
-		"jQuery": true,
-		"define": true,
-		"module": true,
-		"noGlobal": true
-	}
-}
diff --git a/src/ajax.js b/src/ajax.js
index 195a30a..36f707d 100644
--- a/src/ajax.js
+++ b/src/ajax.js
@@ -1,22 +1,24 @@
 define( [
 	"./core",
 	"./var/document",
-	"./var/rnotwhite",
+	"./var/rnothtmlwhite",
 	"./ajax/var/location",
 	"./ajax/var/nonce",
 	"./ajax/var/rquery",
 
 	"./core/init",
-	"./ajax/parseJSON",
 	"./ajax/parseXML",
 	"./event/trigger",
-	"./deferred"
-], function( jQuery, document, rnotwhite, location, nonce, rquery ) {
+	"./deferred",
+	"./serialize" // jQuery.param
+], function( jQuery, document, rnothtmlwhite, location, nonce, rquery ) {
+
+"use strict";
 
 var
 	r20 = /%20/g,
 	rhash = /#.*$/,
-	rts = /([?&])_=[^&]*/,
+	rantiCache = /([?&])_=[^&]*/,
 	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
 
 	// #7653, #8125, #8152: local protocol detection
@@ -62,7 +64,7 @@ function addToPrefiltersOrTransports( structure ) {
 
 		var dataType,
 			i = 0,
-			dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
+			dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];
 
 		if ( jQuery.isFunction( func ) ) {
 
@@ -304,6 +306,7 @@ jQuery.extend( {
 		processData: true,
 		async: true,
 		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+
 		/*
 		timeout: 0,
 		data: null,
@@ -347,7 +350,7 @@ jQuery.extend( {
 			"text html": true,
 
 			// Evaluate text as a json expression
-			"text json": jQuery.parseJSON,
+			"text json": JSON.parse,
 
 			// Parse text as xml
 			"text xml": jQuery.parseXML
@@ -529,18 +532,19 @@ jQuery.extend( {
 		s.type = options.method || options.type || s.method || s.type;
 
 		// Extract dataTypes list
-		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
+		s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];
 
 		// A cross-domain request is in order when the origin doesn't match the current origin.
 		if ( s.crossDomain == null ) {
 			urlAnchor = document.createElement( "a" );
 
-			// Support: IE8-11+
-			// IE throws exception if url is malformed, e.g. http://example.com:80x/
+			// Support: IE <=8 - 11, Edge 12 - 13
+			// IE throws exception on accessing the href property if url is malformed,
+			// e.g. http://example.com:80x/
 			try {
 				urlAnchor.href = s.url;
 
-				// Support: IE8-11+
+				// Support: IE <=8 - 11 only
 				// Anchor's host property isn't correctly set when s.url is relative
 				urlAnchor.href = urlAnchor.href;
 				s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
@@ -600,9 +604,9 @@ jQuery.extend( {
 				delete s.data;
 			}
 
-			// Add anti-cache in uncached url if needed
+			// Add or update anti-cache param if needed
 			if ( s.cache === false ) {
-				cacheURL = cacheURL.replace( rts, "" );
+				cacheURL = cacheURL.replace( rantiCache, "$1" );
 				uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached;
 			}
 
diff --git a/src/ajax/jsonp.js b/src/ajax/jsonp.js
index 666e5d1..8c406e7 100644
--- a/src/ajax/jsonp.js
+++ b/src/ajax/jsonp.js
@@ -5,6 +5,8 @@ define( [
 	"../ajax"
 ], function( jQuery, nonce, rquery ) {
 
+"use strict";
+
 var oldCallbacks = [],
 	rjsonp = /(=)\?(?=&|$)|\?\?/;
 
diff --git a/src/ajax/load.js b/src/ajax/load.js
index 82f0cf3..3ce3a5a 100644
--- a/src/ajax/load.js
+++ b/src/ajax/load.js
@@ -1,11 +1,14 @@
 define( [
 	"../core",
+	"../core/stripAndCollapse",
 	"../core/parseHTML",
 	"../ajax",
 	"../traversing",
 	"../manipulation",
 	"../selector"
-], function( jQuery ) {
+], function( jQuery, stripAndCollapse ) {
+
+"use strict";
 
 /**
  * Load a url into a page
@@ -16,7 +19,7 @@ jQuery.fn.load = function( url, params, callback ) {
 		off = url.indexOf( " " );
 
 	if ( off > -1 ) {
-		selector = jQuery.trim( url.slice( off ) );
+		selector = stripAndCollapse( url.slice( off ) );
 		url = url.slice( 0, off );
 	}
 
@@ -62,7 +65,7 @@ jQuery.fn.load = function( url, params, callback ) {
 		// If it fails, this function gets "jqXHR", "status", "error"
 		} ).always( callback && function( jqXHR, status ) {
 			self.each( function() {
-				callback.apply( self, response || [ jqXHR.responseText, status, jqXHR ] );
+				callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
 			} );
 		} );
 	}
diff --git a/src/ajax/parseJSON.js b/src/ajax/parseJSON.js
deleted file mode 100644
index c2aeb6a..0000000
--- a/src/ajax/parseJSON.js
+++ /dev/null
@@ -1,9 +0,0 @@
-define( [
-	"../core"
-], function( jQuery ) {
-
-jQuery.parseJSON = JSON.parse;
-
-return jQuery.parseJSON;
-
-} );
diff --git a/src/ajax/parseXML.js b/src/ajax/parseXML.js
index 6599aaf..acf7ab2 100644
--- a/src/ajax/parseXML.js
+++ b/src/ajax/parseXML.js
@@ -2,6 +2,8 @@ define( [
 	"../core"
 ], function( jQuery ) {
 
+"use strict";
+
 // Cross-browser xml parsing
 jQuery.parseXML = function( data ) {
 	var xml;
@@ -9,7 +11,8 @@ jQuery.parseXML = function( data ) {
 		return null;
 	}
 
-	// Support: IE9
+	// Support: IE 9 - 11 only
+	// IE throws on parseFromString with invalid input.
 	try {
 		xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
 	} catch ( e ) {
diff --git a/src/ajax/script.js b/src/ajax/script.js
index 485ba39..6e0d21e 100644
--- a/src/ajax/script.js
+++ b/src/ajax/script.js
@@ -4,6 +4,8 @@ define( [
 	"../ajax"
 ], function( jQuery, document ) {
 
+"use strict";
+
 // Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
 jQuery.ajaxPrefilter( function( s ) {
 	if ( s.crossDomain ) {
diff --git a/src/ajax/var/location.js b/src/ajax/var/location.js
index ff9578e..4171d18 100644
--- a/src/ajax/var/location.js
+++ b/src/ajax/var/location.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return window.location;
 } );
diff --git a/src/ajax/var/nonce.js b/src/ajax/var/nonce.js
index 83fd557..6e23708 100644
--- a/src/ajax/var/nonce.js
+++ b/src/ajax/var/nonce.js
@@ -1,5 +1,7 @@
 define( [
 	"../../core"
 ], function( jQuery ) {
+	"use strict";
+
 	return jQuery.now();
 } );
diff --git a/src/ajax/var/rquery.js b/src/ajax/var/rquery.js
index 0502146..06fc374 100644
--- a/src/ajax/var/rquery.js
+++ b/src/ajax/var/rquery.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return ( /\?/ );
 } );
diff --git a/src/ajax/xhr.js b/src/ajax/xhr.js
index db670ff..33dafb0 100644
--- a/src/ajax/xhr.js
+++ b/src/ajax/xhr.js
@@ -4,6 +4,8 @@ define( [
 	"../ajax"
 ], function( jQuery, support ) {
 
+"use strict";
+
 jQuery.ajaxSettings.xhr = function() {
 	try {
 		return new window.XMLHttpRequest();
@@ -15,7 +17,7 @@ var xhrSuccessStatus = {
 		// File protocol always yields status code 0, assume 200
 		0: 200,
 
-		// Support: IE9
+		// Support: IE <=9 only
 		// #1450: sometimes IE returns 1223 when it should be 204
 		1223: 204
 	},
@@ -79,7 +81,7 @@ jQuery.ajaxTransport( function( options ) {
 								xhr.abort();
 							} else if ( type === "error" ) {
 
-								// Support: IE9
+								// Support: IE <=9 only
 								// On a manual native abort, IE9 throws
 								// errors on any property access that is not readyState
 								if ( typeof xhr.status !== "number" ) {
@@ -97,7 +99,7 @@ jQuery.ajaxTransport( function( options ) {
 									xhrSuccessStatus[ xhr.status ] || xhr.status,
 									xhr.statusText,
 
-									// Support: IE9 only
+									// Support: IE <=9 only
 									// IE9 has no XHR2 but throws on binary (trac-11426)
 									// For XHR2 non-text, let the caller handle it (gh-2498)
 									( xhr.responseType || "text" ) !== "text"  ||
@@ -115,7 +117,7 @@ jQuery.ajaxTransport( function( options ) {
 				xhr.onload = callback();
 				errorCallback = xhr.onerror = callback( "error" );
 
-				// Support: IE9
+				// Support: IE 9 only
 				// Use onreadystatechange to replace onabort
 				// to handle uncaught aborts
 				if ( xhr.onabort !== undefined ) {
diff --git a/src/attributes.js b/src/attributes.js
index 691e0c0..2d801e5 100644
--- a/src/attributes.js
+++ b/src/attributes.js
@@ -6,6 +6,8 @@ define( [
 	"./attributes/val"
 ], function( jQuery ) {
 
+"use strict";
+
 // Return jQuery for attributes-only inclusion
 return jQuery;
 } );
diff --git a/src/attributes/attr.js b/src/attributes/attr.js
index 00b0848..2d9c76f 100644
--- a/src/attributes/attr.js
+++ b/src/attributes/attr.js
@@ -2,19 +2,14 @@ define( [
 	"../core",
 	"../core/access",
 	"./support",
-	"../var/rnotwhite",
+	"../var/rnothtmlwhite",
 	"../selector"
-], function( jQuery, access, support, rnotwhite ) {
+], function( jQuery, access, support, rnothtmlwhite ) {
 
-var boolHook,
-	attrHandle = jQuery.expr.attrHandle,
+"use strict";
 
-	// Exclusively lowercase A-Z in attribute names (gh-2730)
-	// https://dom.spec.whatwg.org/#converted-to-ascii-lowercase
-	raz = /[A-Z]+/g,
-	lowercase = function( ch ) {
-		return ch.toLowerCase();
-	};
+var boolHook,
+	attrHandle = jQuery.expr.attrHandle;
 
 jQuery.fn.extend( {
 	attr: function( name, value ) {
@@ -43,11 +38,10 @@ jQuery.extend( {
 			return jQuery.prop( elem, name, value );
 		}
 
-		// All attributes are lowercase
+		// Attribute hooks are determined by the lowercase version
 		// Grab necessary hook if one is defined
 		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
-			name = name.replace( raz, lowercase );
-			hooks = jQuery.attrHooks[ name ] ||
+			hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
 				( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
 		}
 
@@ -95,7 +89,10 @@ jQuery.extend( {
 	removeAttr: function( elem, value ) {
 		var name,
 			i = 0,
-			attrNames = value && value.match( rnotwhite );
+
+			// Attribute names can contain non-HTML whitespace characters
+			// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
+			attrNames = value && value.match( rnothtmlwhite );
 
 		if ( attrNames && elem.nodeType === 1 ) {
 			while ( ( name = attrNames[ i++ ] ) ) {
@@ -123,16 +120,18 @@ jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name )
 	var getter = attrHandle[ name ] || jQuery.find.attr;
 
 	attrHandle[ name ] = function( elem, name, isXML ) {
-		var ret, handle;
+		var ret, handle,
+			lowercaseName = name.toLowerCase();
+
 		if ( !isXML ) {
 
 			// Avoid an infinite loop by temporarily removing this function from the getter
-			handle = attrHandle[ name ];
-			attrHandle[ name ] = ret;
+			handle = attrHandle[ lowercaseName ];
+			attrHandle[ lowercaseName ] = ret;
 			ret = getter( elem, name, isXML ) != null ?
-				name.toLowerCase() :
+				lowercaseName :
 				null;
-			attrHandle[ name ] = handle;
+			attrHandle[ lowercaseName ] = handle;
 		}
 		return ret;
 	};
diff --git a/src/attributes/classes.js b/src/attributes/classes.js
index 7933873..23b4cd6 100644
--- a/src/attributes/classes.js
+++ b/src/attributes/classes.js
@@ -1,11 +1,12 @@
 define( [
 	"../core",
-	"../var/rnotwhite",
+	"../core/stripAndCollapse",
+	"../var/rnothtmlwhite",
 	"../data/var/dataPriv",
 	"../core/init"
-], function( jQuery, rnotwhite, dataPriv ) {
+], function( jQuery, stripAndCollapse, rnothtmlwhite, dataPriv ) {
 
-var rclass = /[\t\r\n\f]/g;
+"use strict";
 
 function getClass( elem ) {
 	return elem.getAttribute && elem.getAttribute( "class" ) || "";
@@ -23,12 +24,11 @@ jQuery.fn.extend( {
 		}
 
 		if ( typeof value === "string" && value ) {
-			classes = value.match( rnotwhite ) || [];
+			classes = value.match( rnothtmlwhite ) || [];
 
 			while ( ( elem = this[ i++ ] ) ) {
 				curValue = getClass( elem );
-				cur = elem.nodeType === 1 &&
-					( " " + curValue + " " ).replace( rclass, " " );
+				cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
 
 				if ( cur ) {
 					j = 0;
@@ -39,7 +39,7 @@ jQuery.fn.extend( {
 					}
 
 					// Only assign if different to avoid unneeded rendering.
-					finalValue = jQuery.trim( cur );
+					finalValue = stripAndCollapse( cur );
 					if ( curValue !== finalValue ) {
 						elem.setAttribute( "class", finalValue );
 					}
@@ -65,14 +65,13 @@ jQuery.fn.extend( {
 		}
 
 		if ( typeof value === "string" && value ) {
-			classes = value.match( rnotwhite ) || [];
+			classes = value.match( rnothtmlwhite ) || [];
 
 			while ( ( elem = this[ i++ ] ) ) {
 				curValue = getClass( elem );
 
 				// This expression is here for better compressibility (see addClass)
-				cur = elem.nodeType === 1 &&
-					( " " + curValue + " " ).replace( rclass, " " );
+				cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
 
 				if ( cur ) {
 					j = 0;
@@ -85,7 +84,7 @@ jQuery.fn.extend( {
 					}
 
 					// Only assign if different to avoid unneeded rendering.
-					finalValue = jQuery.trim( cur );
+					finalValue = stripAndCollapse( cur );
 					if ( curValue !== finalValue ) {
 						elem.setAttribute( "class", finalValue );
 					}
@@ -120,7 +119,7 @@ jQuery.fn.extend( {
 				// Toggle individual class names
 				i = 0;
 				self = jQuery( this );
-				classNames = value.match( rnotwhite ) || [];
+				classNames = value.match( rnothtmlwhite ) || [];
 
 				while ( ( className = classNames[ i++ ] ) ) {
 
@@ -163,10 +162,8 @@ jQuery.fn.extend( {
 		className = " " + selector + " ";
 		while ( ( elem = this[ i++ ] ) ) {
 			if ( elem.nodeType === 1 &&
-				( " " + getClass( elem ) + " " ).replace( rclass, " " )
-					.indexOf( className ) > -1
-			) {
-				return true;
+				( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
+					return true;
 			}
 		}
 
diff --git a/src/attributes/prop.js b/src/attributes/prop.js
index da7bc1e..49ac244 100644
--- a/src/attributes/prop.js
+++ b/src/attributes/prop.js
@@ -5,6 +5,8 @@ define( [
 	"../selector"
 ], function( jQuery, access, support ) {
 
+"use strict";
+
 var rfocusable = /^(?:input|select|textarea|button)$/i,
 	rclickable = /^(?:a|area)$/i;
 
@@ -57,18 +59,26 @@ jQuery.extend( {
 		tabIndex: {
 			get: function( elem ) {
 
+				// Support: IE <=9 - 11 only
 				// 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/
+				// https://web.archive.org/web/20141116233347/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 ) :
+				if ( tabindex ) {
+					return parseInt( tabindex, 10 );
+				}
+
+				if (
 					rfocusable.test( elem.nodeName ) ||
-						rclickable.test( elem.nodeName ) && elem.href ?
-							0 :
-							-1;
+					rclickable.test( elem.nodeName ) &&
+					elem.href
+				) {
+					return 0;
+				}
+
+				return -1;
 			}
 		}
 	},
@@ -79,14 +89,38 @@ jQuery.extend( {
 	}
 } );
 
+// Support: IE <=11 only
+// 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
+// eslint rule "no-unused-expressions" is disabled for this code
+// since it considers such accessions noop
 if ( !support.optSelected ) {
 	jQuery.propHooks.selected = {
 		get: function( elem ) {
+
+			/* eslint no-unused-expressions: "off" */
+
 			var parent = elem.parentNode;
 			if ( parent && parent.parentNode ) {
 				parent.parentNode.selectedIndex;
 			}
 			return null;
+		},
+		set: function( elem ) {
+
+			/* eslint no-unused-expressions: "off" */
+
+			var parent = elem.parentNode;
+			if ( parent ) {
+				parent.selectedIndex;
+
+				if ( parent.parentNode ) {
+					parent.parentNode.selectedIndex;
+				}
+			}
 		}
 	};
 }
diff --git a/src/attributes/support.js b/src/attributes/support.js
index f93ba01..af60e96 100644
--- a/src/attributes/support.js
+++ b/src/attributes/support.js
@@ -3,6 +3,8 @@ define( [
 	"../var/support"
 ], function( document, support ) {
 
+"use strict";
+
 ( function() {
 	var input = document.createElement( "input" ),
 		select = document.createElement( "select" ),
@@ -10,15 +12,15 @@ define( [
 
 	input.type = "checkbox";
 
-	// Support: Android<4.4
+	// Support: Android <=4.3 only
 	// Default value for a checkbox should be "on"
 	support.checkOn = input.value !== "";
 
-	// Support: IE<=11+
+	// Support: IE <=11 only
 	// Must access selectedIndex to make default options select
 	support.optSelected = opt.selected;
 
-	// Support: IE<=11+
+	// Support: IE <=11 only
 	// An input loses its value after becoming a radio
 	input = document.createElement( "input" );
 	input.value = "t";
diff --git a/src/attributes/val.js b/src/attributes/val.js
index 5f0b73e..fbf4069 100644
--- a/src/attributes/val.js
+++ b/src/attributes/val.js
@@ -1,8 +1,11 @@
 define( [
 	"../core",
+	"../core/stripAndCollapse",
 	"./support",
 	"../core/init"
-], function( jQuery, support ) {
+], function( jQuery, stripAndCollapse, support ) {
+
+"use strict";
 
 var rreturn = /\r/g;
 
@@ -25,13 +28,13 @@ jQuery.fn.extend( {
 
 				ret = elem.value;
 
-				return typeof ret === "string" ?
-
-					// Handle most common string cases
-					ret.replace( rreturn, "" ) :
+				// Handle most common string cases
+				if ( typeof ret === "string" ) {
+					return ret.replace( rreturn, "" );
+				}
 
-					// Handle cases where value is null/undef or number
-					ret == null ? "" : ret;
+				// Handle cases where value is null/undef or number
+				return ret == null ? "" : ret;
 			}
 
 			return;
@@ -80,27 +83,38 @@ jQuery.extend( {
 		option: {
 			get: function( elem ) {
 
-				// Support: IE<11
-				// option.value not trimmed (#14858)
-				return jQuery.trim( elem.value );
+				var val = jQuery.find.attr( elem, "value" );
+				return val != null ?
+					val :
+
+					// Support: IE <=10 - 11 only
+					// option.text throws exceptions (#14686, #14858)
+					// Strip and collapse whitespace
+					// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
+					stripAndCollapse( jQuery.text( elem ) );
 			}
 		},
 		select: {
 			get: function( elem ) {
-				var value, option,
+				var value, option, i,
 					options = elem.options,
 					index = elem.selectedIndex,
 					one = elem.type === "select-one",
 					values = one ? null : [],
-					max = one ? index + 1 : options.length,
-					i = index < 0 ?
-						max :
-						one ? index : 0;
+					max = one ? index + 1 : options.length;
+
+				if ( index < 0 ) {
+					i = max;
+
+				} else {
+					i = one ? index : 0;
+				}
 
 				// Loop through all the selected options
 				for ( ; i < max; i++ ) {
 					option = options[ i ];
 
+					// Support: IE <=9 only
 					// IE8-9 doesn't update selected after form reset (#2551)
 					if ( ( option.selected || i === index ) &&
 
@@ -133,11 +147,16 @@ jQuery.extend( {
 
 				while ( i-- ) {
 					option = options[ i ];
+
+					/* eslint-disable no-cond-assign */
+
 					if ( option.selected =
-							jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
+						jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
 					) {
 						optionSet = true;
 					}
+
+					/* eslint-enable no-cond-assign */
 				}
 
 				// Force browsers to behave consistently when non-matching value is set
diff --git a/src/callbacks.js b/src/callbacks.js
index b032c8f..a6d4df0 100644
--- a/src/callbacks.js
+++ b/src/callbacks.js
@@ -1,12 +1,14 @@
 define( [
 	"./core",
-	"./var/rnotwhite"
-], function( jQuery, rnotwhite ) {
+	"./var/rnothtmlwhite"
+], function( jQuery, rnothtmlwhite ) {
+
+"use strict";
 
 // Convert String-formatted options into Object-formatted ones
 function createOptions( options ) {
 	var object = {};
-	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
+	jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
 		object[ flag ] = true;
 	} );
 	return object;
diff --git a/src/core.js b/src/core.js
index 3a54ffc..4c8a4ab 100644
--- a/src/core.js
+++ b/src/core.js
@@ -1,6 +1,11 @@
+/* global Symbol */
+// Defining this global in .eslintrc.json would create a danger of using the global
+// unguarded in another place, it seems safer to define global only for this module
+
 define( [
 	"./var/arr",
 	"./var/document",
+	"./var/getProto",
 	"./var/slice",
 	"./var/concat",
 	"./var/push",
@@ -8,13 +13,18 @@ define( [
 	"./var/class2type",
 	"./var/toString",
 	"./var/hasOwn",
+	"./var/fnToString",
+	"./var/ObjectFunctionString",
 	"./var/support",
 	"./core/DOMEval"
-], function( arr, document, slice, concat,
-	push, indexOf, class2type, toString, hasOwn, support, DOMEval ) {
+], function( arr, document, getProto, slice, concat, push, indexOf,
+	class2type, toString, hasOwn, fnToString, ObjectFunctionString,
+	support, DOMEval ) {
+
+"use strict";
 
 var
-	version = "@VERSION",
+	version = "3.1.1",
 
 	// Define a local copy of jQuery
 	jQuery = function( selector, context ) {
@@ -24,7 +34,7 @@ var
 		return new jQuery.fn.init( selector, context );
 	},
 
-	// Support: Android<4.1
+	// Support: Android <=4.0 only
 	// Make sure we trim BOM and NBSP
 	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
 
@@ -54,13 +64,14 @@ jQuery.fn = jQuery.prototype = {
 	// 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 just the one element from the set
-			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
+		// Return all the elements in a clean array
+		if ( num == null ) {
+			return slice.call( this );
+		}
 
-			// Return all the elements in a clean array
-			slice.call( this );
+		// Return just the one element from the set
+		return num < 0 ? this[ num + this.length ] : this[ num ];
 	},
 
 	// Take an array of elements and push it onto the stack
@@ -217,31 +228,40 @@ jQuery.extend( {
 		// that can be coerced to finite numbers (gh-2662)
 		var type = jQuery.type( obj );
 		return ( type === "number" || type === "string" ) &&
-			( obj - parseFloat( obj ) + 1 ) >= 0;
+
+			// parseFloat NaNs numeric-cast false positives ("")
+			// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+			// subtraction forces infinities to NaN
+			!isNaN( obj - parseFloat( obj ) );
 	},
 
 	isPlainObject: function( obj ) {
+		var proto, Ctor;
 
-		// Not plain objects:
-		// - Any object or value whose internal [[Class]] property is not "[object Object]"
-		// - DOM nodes
-		// - window
-		if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+		// Detect obvious negatives
+		// Use toString instead of jQuery.type to catch host objects
+		if ( !obj || toString.call( obj ) !== "[object Object]" ) {
 			return false;
 		}
 
-		if ( obj.constructor &&
-				!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
-			return false;
+		proto = getProto( obj );
+
+		// Objects with no prototype (e.g., `Object.create( null )`) are plain
+		if ( !proto ) {
+			return true;
 		}
 
-		// If the function hasn't returned already, we're confident that
-		// |obj| is a plain object, created by {} or constructed with new Object
-		return true;
+		// Objects with prototype are plain iff they were constructed by a global Object function
+		Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
+		return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
 	},
 
 	isEmptyObject: function( obj ) {
+
+		/* eslint-disable no-unused-vars */
+		// See https://github.com/eslint/eslint/issues/6125
 		var name;
+
 		for ( name in obj ) {
 			return false;
 		}
@@ -253,7 +273,7 @@ jQuery.extend( {
 			return obj + "";
 		}
 
-		// Support: Android<4.0 (functionish RegExp)
+		// Support: Android <=2.3 only (functionish RegExp)
 		return typeof obj === "object" || typeof obj === "function" ?
 			class2type[ toString.call( obj ) ] || "object" :
 			typeof obj;
@@ -265,7 +285,7 @@ jQuery.extend( {
 	},
 
 	// Convert dashed to camelCase; used by the css and data modules
-	// Support: IE9-11+
+	// Support: IE <=9 - 11, Edge 12 - 13
 	// Microsoft forgot to hump their vendor prefix (#9572)
 	camelCase: function( string ) {
 		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
@@ -296,7 +316,7 @@ jQuery.extend( {
 		return obj;
 	},
 
-	// Support: Android<4.1
+	// Support: Android <=4.0 only
 	trim: function( text ) {
 		return text == null ?
 			"" :
@@ -325,7 +345,7 @@ jQuery.extend( {
 		return arr == null ? -1 : indexOf.call( arr, elem, i );
 	},
 
-	// Support: Android<4.1, PhantomJS<2
+	// Support: Android <=4.0 only, PhantomJS 1 only
 	// push.apply(_, arraylike) throws on ancient WebKit
 	merge: function( first, second ) {
 		var len = +second.length,
@@ -431,15 +451,9 @@ jQuery.extend( {
 	support: support
 } );
 
-// 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 ] = arr[ Symbol.iterator ];
 }
-/* jshint ignore: end */
 
 // Populate the class2type map
 jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
@@ -449,7 +463,7 @@ function( i, name ) {
 
 function isArrayLike( obj ) {
 
-	// Support: iOS 8.2 (not reproducible in simulator)
+	// Support: real iOS 8.2 only (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
diff --git a/src/core/DOMEval.js b/src/core/DOMEval.js
index 222b0ca..c49c12e 100644
--- a/src/core/DOMEval.js
+++ b/src/core/DOMEval.js
@@ -1,6 +1,8 @@
 define( [
 	"../var/document"
 ], function( document ) {
+	"use strict";
+
 	function DOMEval( code, doc ) {
 		doc = doc || document;
 
diff --git a/src/core/access.js b/src/core/access.js
index 19f79ef..86cdbc7 100644
--- a/src/core/access.js
+++ b/src/core/access.js
@@ -2,6 +2,8 @@ define( [
 	"../core"
 ], function( jQuery ) {
 
+"use strict";
+
 // 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 ) {
@@ -51,13 +53,16 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
 		}
 	}
 
-	return chainable ?
-		elems :
+	if ( chainable ) {
+		return elems;
+	}
+
+	// Gets
+	if ( bulk ) {
+		return fn.call( elems );
+	}
 
-		// Gets
-		bulk ?
-			fn.call( elems ) :
-			len ? fn( elems[ 0 ], key ) : emptyGet;
+	return len ? fn( elems[ 0 ], key ) : emptyGet;
 };
 
 return access;
diff --git a/src/core/init.js b/src/core/init.js
index a00d587..19a3c7c 100644
--- a/src/core/init.js
+++ b/src/core/init.js
@@ -6,6 +6,8 @@ define( [
 	"../traversing/findFilter"
 ], function( jQuery, document, rsingleTag ) {
 
+"use strict";
+
 // A central reference to the root jQuery(document)
 var rootjQuery,
 
diff --git a/src/core/parseHTML.js b/src/core/parseHTML.js
index 3040536..21ff6bf 100644
--- a/src/core/parseHTML.js
+++ b/src/core/parseHTML.js
@@ -8,6 +8,8 @@ define( [
 	"./support"
 ], function( jQuery, document, rsingleTag, buildFragment, support ) {
 
+"use strict";
+
 // Argument "data" should be string of html
 // context (optional): If specified, the fragment will be created in this context,
 // defaults to document
@@ -21,14 +23,28 @@ jQuery.parseHTML = function( data, context, keepScripts ) {
 		context = false;
 	}
 
-	// Stop scripts or inline event handlers from being executed immediately
-	// by using document.implementation
-	context = context || ( support.createHTMLDocument ?
-		document.implementation.createHTMLDocument( "" ) :
-		document );
+	var base, parsed, scripts;
+
+	if ( !context ) {
+
+		// Stop scripts or inline event handlers from being executed immediately
+		// by using document.implementation
+		if ( support.createHTMLDocument ) {
+			context = document.implementation.createHTMLDocument( "" );
+
+			// Set the base href for the created document
+			// so any parsed elements with URLs
+			// are based on the document's URL (gh-2965)
+			base = context.createElement( "base" );
+			base.href = document.location.href;
+			context.head.appendChild( base );
+		} else {
+			context = document;
+		}
+	}
 
-	var parsed = rsingleTag.exec( data ),
-		scripts = !keepScripts && [];
+	parsed = rsingleTag.exec( data );
+	scripts = !keepScripts && [];
 
 	// Single tag
 	if ( parsed ) {
diff --git a/src/core/ready-no-deferred.js b/src/core/ready-no-deferred.js
new file mode 100644
index 0000000..02d6014
--- /dev/null
+++ b/src/core/ready-no-deferred.js
@@ -0,0 +1,105 @@
+define( [
+	"../core",
+	"../var/document"
+], function( jQuery, document ) {
+
+"use strict";
+
+var readyCallbacks = [],
+	whenReady = function( fn ) {
+		readyCallbacks.push( fn );
+	},
+	executeReady = function( fn ) {
+
+		// Prevent errors from freezing future callback execution (gh-1823)
+		// Not backwards-compatible as this does not execute sync
+		window.setTimeout( function() {
+			fn.call( document, jQuery );
+		} );
+	};
+
+jQuery.fn.ready = function( fn ) {
+	whenReady( 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 );
+		}
+	},
+
+	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;
+		}
+
+		whenReady = function( fn ) {
+			readyCallbacks.push( fn );
+
+			while ( readyCallbacks.length ) {
+				fn = readyCallbacks.shift();
+				if ( jQuery.isFunction( fn ) ) {
+					executeReady( fn );
+				}
+			}
+		};
+
+		whenReady();
+	}
+} );
+
+// Make jQuery.ready Promise consumable (gh-1778)
+jQuery.ready.then = jQuery.fn.ready;
+
+/**
+ * The ready event handler and self cleanup method
+ */
+function completed() {
+	document.removeEventListener( "DOMContentLoaded", completed );
+	window.removeEventListener( "load", completed );
+	jQuery.ready();
+}
+
+// Catch cases where $(document).ready() is called
+// after the browser event has already occurred.
+// Support: IE9-10 only
+// 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 );
+
+} else {
+
+	// Use the handy event callback
+	document.addEventListener( "DOMContentLoaded", completed );
+
+	// A fallback to window.onload, that will always work
+	window.addEventListener( "load", completed );
+}
+
+} );
diff --git a/src/core/ready.js b/src/core/ready.js
index 73c3d70..53b1b2d 100644
--- a/src/core/ready.js
+++ b/src/core/ready.js
@@ -1,16 +1,26 @@
 define( [
 	"../core",
 	"../var/document",
+	"../core/readyException",
 	"../deferred"
 ], function( jQuery, document ) {
 
+"use strict";
+
 // The deferred used on DOM ready
-var readyList;
+var readyList = jQuery.Deferred();
 
 jQuery.fn.ready = function( fn ) {
 
-	// Add the callback
-	jQuery.ready.promise().done( fn );
+	readyList
+		.then( fn )
+
+		// Wrap jQuery.readyException in a function so that the lookup
+		// happens at the time of error handling instead of callback
+		// registration.
+		.catch( function( error ) {
+			jQuery.readyException( error );
+		} );
 
 	return this;
 };
@@ -54,43 +64,32 @@ jQuery.extend( {
 	}
 } );
 
-/**
- * The ready event handler and self cleanup method
- */
+jQuery.ready.then = readyList.then;
+
+// The ready event handler and self cleanup method
 function completed() {
 	document.removeEventListener( "DOMContentLoaded", completed );
 	window.removeEventListener( "load", completed );
 	jQuery.ready();
 }
 
-jQuery.ready.promise = function( obj ) {
-	if ( !readyList ) {
+// Catch cases where $(document).ready() is called
+// after the browser event has already occurred.
+// Support: IE <=9 - 10 only
+// Older IE sometimes signals "interactive" too soon
+if ( document.readyState === "complete" ||
+	( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
 
-		readyList = jQuery.Deferred();
+	// Handle it asynchronously to allow scripts the opportunity to delay ready
+	window.setTimeout( jQuery.ready );
 
-		// Catch cases where $(document).ready() is called
-		// after the browser event has already occurred.
-		// Support: IE9-10 only
-		// 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 );
-
-		} else {
+} else {
 
-			// Use the handy event callback
-			document.addEventListener( "DOMContentLoaded", completed );
+	// Use the handy event callback
+	document.addEventListener( "DOMContentLoaded", completed );
 
-			// A fallback to window.onload, that will always work
-			window.addEventListener( "load", completed );
-		}
-	}
-	return readyList.promise( obj );
-};
-
-// Kick off the DOM ready check even if the user does not
-jQuery.ready.promise();
+	// A fallback to window.onload, that will always work
+	window.addEventListener( "load", completed );
+}
 
 } );
diff --git a/src/core/readyException.js b/src/core/readyException.js
new file mode 100644
index 0000000..72bdd90
--- /dev/null
+++ b/src/core/readyException.js
@@ -0,0 +1,13 @@
+define( [
+	"../core"
+], function( jQuery ) {
+
+"use strict";
+
+jQuery.readyException = function( error ) {
+	window.setTimeout( function() {
+		throw error;
+	} );
+};
+
+} );
diff --git a/src/core/stripAndCollapse.js b/src/core/stripAndCollapse.js
new file mode 100644
index 0000000..ccad660
--- /dev/null
+++ b/src/core/stripAndCollapse.js
@@ -0,0 +1,14 @@
+define( [
+	"../var/rnothtmlwhite"
+], function( rnothtmlwhite ) {
+	"use strict";
+
+	// Strip and collapse whitespace according to HTML spec
+	// https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace
+	function stripAndCollapse( value ) {
+		var tokens = value.match( rnothtmlwhite ) || [];
+		return tokens.join( " " );
+	}
+
+	return stripAndCollapse;
+} );
diff --git a/src/core/support.js b/src/core/support.js
index 0609a70..13ae02f 100644
--- a/src/core/support.js
+++ b/src/core/support.js
@@ -3,7 +3,9 @@ define( [
 	"../var/support"
 ], function( document, support ) {
 
-// Support: Safari 8+
+"use strict";
+
+// Support: Safari 8 only
 // In Safari 8 documents created via document.implementation.createHTMLDocument
 // collapse sibling forms: the second one becomes a child of the first one.
 // Because of that, this security measure has to be disabled in Safari 8.
diff --git a/src/core/var/rsingleTag.js b/src/core/var/rsingleTag.js
index 1ddf95e..4d6e8a0 100644
--- a/src/core/var/rsingleTag.js
+++ b/src/core/var/rsingleTag.js
@@ -1,4 +1,5 @@
 define( function() {
+	"use strict";
 
 	// Match a standalone tag
 	return ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
diff --git a/src/css.js b/src/css.js
index 5d3a0a6..5e44511 100644
--- a/src/css.js
+++ b/src/css.js
@@ -7,7 +7,6 @@ define( [
 	"./var/rcssNum",
 	"./css/var/rnumnonpx",
 	"./css/var/cssExpand",
-	"./css/var/isHidden",
 	"./css/var/getStyles",
 	"./css/var/swap",
 	"./css/curCSS",
@@ -19,7 +18,9 @@ define( [
 	"./core/ready",
 	"./selector" // contains
 ], function( jQuery, pnum, access, rmargin, document, rcssNum, rnumnonpx, cssExpand,
-	isHidden, getStyles, swap, curCSS, adjustCSS, addGetHookIf, support ) {
+	getStyles, swap, curCSS, adjustCSS, addGetHookIf, support ) {
+
+"use strict";
 
 var
 
@@ -69,15 +70,17 @@ function setPositiveNumber( elem, value, subtract ) {
 }
 
 function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
-	var i = extra === ( isBorderBox ? "border" : "content" ) ?
-
-		// If we already have the right measurement, avoid augmentation
-		4 :
+	var i,
+		val = 0;
 
-		// Otherwise initialize for horizontal or vertical properties
-		name === "width" ? 1 : 0,
+	// If we already have the right measurement, avoid augmentation
+	if ( extra === ( isBorderBox ? "border" : "content" ) ) {
+		i = 4;
 
-		val = 0;
+	// Otherwise initialize for horizontal or vertical properties
+	} else {
+		i = name === "width" ? 1 : 0;
+	}
 
 	for ( ; i < 4; i += 2 ) {
 
@@ -120,20 +123,13 @@ function getWidthOrHeight( elem, name, extra ) {
 		styles = getStyles( elem ),
 		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
 
-	// Support: IE <= 11 only
+	// Support: IE <=11 only
 	// Running getBoundingClientRect on a disconnected node
 	// in IE throws an error.
 	if ( elem.getClientRects().length ) {
 		val = elem.getBoundingClientRect()[ name ];
 	}
 
-	// Support: IE11 only
-	// In IE 11 fullscreen elements inside of an iframe have
-	// 100x too small dimensions (gh-1764).
-	if ( document.msFullscreenElement && window.top !== window ) {
-		val *= 100;
-	}
-
 	// 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
@@ -252,7 +248,6 @@ jQuery.extend( {
 				value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
 			}
 
-			// Support: IE9-11+
 			// background-* props affect original clone's values
 			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
 				style[ name ] = "inherit";
@@ -326,7 +321,7 @@ jQuery.each( [ "height", "width" ], function( i, name ) {
 					// Support: Safari 8+
 					// Table columns in Safari have non-zero offsetWidth & zero
 					// getBoundingClientRect().width unless display is changed.
-					// Support: IE <= 11 only
+					// Support: IE <=11 only
 					// Running getBoundingClientRect on a disconnected node
 					// in IE throws an error.
 					( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
diff --git a/src/css/addGetHookIf.js b/src/css/addGetHookIf.js
index 9cd21f6..e4bb49a 100644
--- a/src/css/addGetHookIf.js
+++ b/src/css/addGetHookIf.js
@@ -1,5 +1,7 @@
 define( function() {
 
+"use strict";
+
 function addGetHookIf( conditionFn, hookFn ) {
 
 	// Define the hook, we'll check on the first run if it's really needed.
diff --git a/src/css/adjustCSS.js b/src/css/adjustCSS.js
index 48fcfec..ded3a16 100644
--- a/src/css/adjustCSS.js
+++ b/src/css/adjustCSS.js
@@ -3,13 +3,19 @@ define( [
 	"../var/rcssNum"
 ], function( jQuery, rcssNum ) {
 
+"use strict";
+
 function adjustCSS( elem, prop, valueParts, tween ) {
 	var adjusted,
 		scale = 1,
 		maxIterations = 20,
 		currentValue = tween ?
-			function() { return tween.cur(); } :
-			function() { return jQuery.css( elem, prop, "" ); },
+			function() {
+				return tween.cur();
+			} :
+			function() {
+				return jQuery.css( elem, prop, "" );
+			},
 		initial = currentValue(),
 		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
 
diff --git a/src/css/curCSS.js b/src/css/curCSS.js
index be643ab..313da42 100644
--- a/src/css/curCSS.js
+++ b/src/css/curCSS.js
@@ -7,13 +7,15 @@ define( [
 	"../selector" // Get jQuery.contains
 ], function( jQuery, rnumnonpx, rmargin, getStyles, support ) {
 
+"use strict";
+
 function curCSS( elem, name, computed ) {
 	var width, minWidth, maxWidth, ret,
 		style = elem.style;
 
 	computed = computed || getStyles( elem );
 
-	// Support: IE9
+	// Support: IE <=9 only
 	// getPropertyValue is only needed for .css('filter') (#12537)
 	if ( computed ) {
 		ret = computed.getPropertyValue( name ) || computed[ name ];
@@ -26,7 +28,7 @@ function curCSS( elem, name, computed ) {
 		// Android Browser returns percentage for some values,
 		// but width seems to be reliably pixels.
 		// This is against the CSSOM draft spec:
-		// http://dev.w3.org/csswg/cssom/#resolved-values
+		// https://drafts.csswg.org/cssom/#resolved-values
 		if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {
 
 			// Remember the original values
@@ -47,7 +49,7 @@ function curCSS( elem, name, computed ) {
 
 	return ret !== undefined ?
 
-		// Support: IE9-11+
+		// Support: IE <=9 - 11 only
 		// IE returns zIndex value as an integer.
 		ret + "" :
 		ret;
diff --git a/src/css/hiddenVisibleSelectors.js b/src/css/hiddenVisibleSelectors.js
index 9a8a28c..d7a9339 100644
--- a/src/css/hiddenVisibleSelectors.js
+++ b/src/css/hiddenVisibleSelectors.js
@@ -3,10 +3,12 @@ define( [
 	"../selector"
 ], function( jQuery ) {
 
-jQuery.expr.filters.hidden = function( elem ) {
-	return !jQuery.expr.filters.visible( elem );
+"use strict";
+
+jQuery.expr.pseudos.hidden = function( elem ) {
+	return !jQuery.expr.pseudos.visible( elem );
 };
-jQuery.expr.filters.visible = function( elem ) {
+jQuery.expr.pseudos.visible = function( elem ) {
 	return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
 };
 
diff --git a/src/css/showHide.js b/src/css/showHide.js
index 9e2cb1e..3eeafef 100644
--- a/src/css/showHide.js
+++ b/src/css/showHide.js
@@ -1,8 +1,10 @@
 define( [
 	"../core",
 	"../data/var/dataPriv",
-	"../css/var/isHidden"
-], function( jQuery, dataPriv, isHidden ) {
+	"../css/var/isHiddenWithinTree"
+], function( jQuery, dataPriv, isHiddenWithinTree ) {
+
+"use strict";
 
 var defaultDisplayMap = {};
 
@@ -16,7 +18,7 @@ function getDefaultDisplay( elem ) {
 		return display;
 	}
 
-	temp = doc.body.appendChild( doc.createElement( nodeName ) ),
+	temp = doc.body.appendChild( doc.createElement( nodeName ) );
 	display = jQuery.css( temp, "display" );
 
 	temp.parentNode.removeChild( temp );
@@ -54,12 +56,7 @@ function showHide( elements, show ) {
 					elem.style.display = "";
 				}
 			}
-			if ( elem.style.display === "" && jQuery.css( elem, "display" ) === "none" &&
-
-					// Support: Firefox <=42 - 43
-					// Don't set inline display on disconnected elements with computed display: none
-					jQuery.contains( elem.ownerDocument, elem ) ) {
-
+			if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
 				values[ index ] = getDefaultDisplay( elem );
 			}
 		} else {
@@ -95,7 +92,7 @@ jQuery.fn.extend( {
 		}
 
 		return this.each( function() {
-			if ( isHidden( this ) ) {
+			if ( isHiddenWithinTree( this ) ) {
 				jQuery( this ).show();
 			} else {
 				jQuery( this ).hide();
diff --git a/src/css/support.js b/src/css/support.js
index f8e02d0..883d0e5 100644
--- a/src/css/support.js
+++ b/src/css/support.js
@@ -5,29 +5,19 @@ define( [
 	"../var/support"
 ], function( jQuery, document, documentElement, support ) {
 
-( function() {
-	var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
-		container = document.createElement( "div" ),
-		div = document.createElement( "div" );
-
-	// Finish early in limited (non-browser) environments
-	if ( !div.style ) {
-		return;
-	}
-
-	// Support: IE9-11+
-	// Style of cloned element affects source element cloned (#8908)
-	div.style.backgroundClip = "content-box";
-	div.cloneNode( true ).style.backgroundClip = "";
-	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+"use strict";
 
-	container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
-		"padding:0;margin-top:1px;position:absolute";
-	container.appendChild( div );
+( function() {
 
 	// Executing both pixelPosition & boxSizingReliable tests require only one layout
 	// so they're executed at the same time to save the second computation.
 	function computeStyleTests() {
+
+		// This is a singleton, we need to execute it only once
+		if ( !div ) {
+			return;
+		}
+
 		div.style.cssText =
 			"box-sizing:border-box;" +
 			"position:relative;display:block;" +
@@ -38,6 +28,8 @@ define( [
 
 		var divStyle = window.getComputedStyle( div );
 		pixelPositionVal = divStyle.top !== "1%";
+
+		// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
 		reliableMarginLeftVal = divStyle.marginLeft === "2px";
 		boxSizingReliableVal = divStyle.width === "4px";
 
@@ -47,39 +39,46 @@ define( [
 		pixelMarginRightVal = divStyle.marginRight === "4px";
 
 		documentElement.removeChild( container );
+
+		// Nullify the div so it wouldn't be stored in the memory and
+		// it will also be a sign that checks already performed
+		div = null;
 	}
 
+	var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
+		container = document.createElement( "div" ),
+		div = document.createElement( "div" );
+
+	// Finish early in limited (non-browser) environments
+	if ( !div.style ) {
+		return;
+	}
+
+	// Support: IE <=9 - 11 only
+	// Style of cloned element affects source element cloned (#8908)
+	div.style.backgroundClip = "content-box";
+	div.cloneNode( true ).style.backgroundClip = "";
+	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+	container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
+		"padding:0;margin-top:1px;position:absolute";
+	container.appendChild( div );
+
 	jQuery.extend( support, {
 		pixelPosition: function() {
-
-			// This test is executed only once but we still do memoizing
-			// since we can use the boxSizingReliable pre-computing.
-			// No need to check if the test was already performed, though.
 			computeStyleTests();
 			return pixelPositionVal;
 		},
 		boxSizingReliable: function() {
-			if ( boxSizingReliableVal == null ) {
-				computeStyleTests();
-			}
+			computeStyleTests();
 			return boxSizingReliableVal;
 		},
 		pixelMarginRight: function() {
-
-			// Support: Android 4.0-4.3
-			// We're checking for boxSizingReliableVal here instead of pixelMarginRightVal
-			// since that compresses better and they're computed together anyway.
-			if ( boxSizingReliableVal == null ) {
-				computeStyleTests();
-			}
+			computeStyleTests();
 			return pixelMarginRightVal;
 		},
 		reliableMarginLeft: function() {
-
-			// Support: IE <=8 only, Android 4.0 - 4.3 only, Firefox <=3 - 37
-			if ( boxSizingReliableVal == null ) {
-				computeStyleTests();
-			}
+			computeStyleTests();
 			return reliableMarginLeftVal;
 		}
 	} );
diff --git a/src/css/var/cssExpand.js b/src/css/var/cssExpand.js
index 9f8194d..dd2007c 100644
--- a/src/css/var/cssExpand.js
+++ b/src/css/var/cssExpand.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return [ "Top", "Right", "Bottom", "Left" ];
 } );
diff --git a/src/css/var/getStyles.js b/src/css/var/getStyles.js
index 1fa915d..0b893ac 100644
--- a/src/css/var/getStyles.js
+++ b/src/css/var/getStyles.js
@@ -1,12 +1,14 @@
 define( function() {
+	"use strict";
+
 	return function( elem ) {
 
-		// Support: IE<=11+, Firefox<=30+ (#15098, #14150)
+		// Support: IE <=11 only, Firefox <=30 (#15098, #14150)
 		// IE throws on elements created in popups
 		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
 		var view = elem.ownerDocument.defaultView;
 
-		if ( !view.opener ) {
+		if ( !view || !view.opener ) {
 			view = window;
 		}
 
diff --git a/src/css/var/isHidden.js b/src/css/var/isHidden.js
deleted file mode 100644
index 7997eff..0000000
--- a/src/css/var/isHidden.js
+++ /dev/null
@@ -1,16 +0,0 @@
-define( [
-	"../../core",
-	"../../selector"
-
-	// css is assumed
-], function( jQuery ) {
-
-	return function( 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 );
-	};
-} );
diff --git a/src/css/var/isHiddenWithinTree.js b/src/css/var/isHiddenWithinTree.js
new file mode 100644
index 0000000..3cfb93e
--- /dev/null
+++ b/src/css/var/isHiddenWithinTree.js
@@ -0,0 +1,34 @@
+define( [
+	"../../core",
+	"../../selector"
+
+	// css is assumed
+], function( jQuery ) {
+	"use strict";
+
+	// isHiddenWithinTree reports if an element has a non-"none" display style (inline and/or
+	// through the CSS cascade), which is useful in deciding whether or not to make it visible.
+	// It differs from the :hidden selector (jQuery.expr.pseudos.hidden) in two important ways:
+	// * A hidden ancestor does not force an element to be classified as hidden.
+	// * Being disconnected from the document does not force an element to be classified as hidden.
+	// These differences improve the behavior of .toggle() et al. when applied to elements that are
+	// detached or contained within hidden ancestors (gh-2404, gh-2863).
+	return function( elem, el ) {
+
+		// isHiddenWithinTree might be called from jQuery#filter function;
+		// in that case, element will be second argument
+		elem = el || elem;
+
+		// Inline style trumps all
+		return elem.style.display === "none" ||
+			elem.style.display === "" &&
+
+			// Otherwise, check computed style
+			// Support: Firefox <=43 - 45
+			// Disconnected elements can have computed display: none, so first confirm that elem is
+			// in the document.
+			jQuery.contains( elem.ownerDocument, elem ) &&
+
+			jQuery.css( elem, "display" ) === "none";
+	};
+} );
diff --git a/src/css/var/rmargin.js b/src/css/var/rmargin.js
index 9be2212..0fbfbd8 100644
--- a/src/css/var/rmargin.js
+++ b/src/css/var/rmargin.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return ( /^margin/ );
 } );
diff --git a/src/css/var/rnumnonpx.js b/src/css/var/rnumnonpx.js
index ed13f0b..056cda7 100644
--- a/src/css/var/rnumnonpx.js
+++ b/src/css/var/rnumnonpx.js
@@ -1,5 +1,7 @@
 define( [
 	"../../var/pnum"
 ], function( pnum ) {
+	"use strict";
+
 	return new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
 } );
diff --git a/src/css/var/swap.js b/src/css/var/swap.js
index b6d3b67..1a9556b 100644
--- a/src/css/var/swap.js
+++ b/src/css/var/swap.js
@@ -1,5 +1,7 @@
 define( function() {
 
+"use strict";
+
 // A method for quickly swapping in/out CSS properties to get correct calculations.
 return function( elem, options, callback, args ) {
 	var ret, name,
diff --git a/src/data.js b/src/data.js
index d2855dd..087ce4e 100644
--- a/src/data.js
+++ b/src/data.js
@@ -5,6 +5,8 @@ define( [
 	"./data/var/dataUser"
 ], function( jQuery, access, dataPriv, dataUser ) {
 
+"use strict";
+
 //	Implementation Summary
 //
 //	1. Enforce API surface and semantic compatibility with 1.9.x branch
@@ -18,6 +20,31 @@ define( [
 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
 	rmultiDash = /[A-Z]/g;
 
+function getData( data ) {
+	if ( data === "true" ) {
+		return true;
+	}
+
+	if ( data === "false" ) {
+		return false;
+	}
+
+	if ( data === "null" ) {
+		return null;
+	}
+
+	// Only convert to a number if it doesn't change the string
+	if ( data === +data + "" ) {
+		return +data;
+	}
+
+	if ( rbrace.test( data ) ) {
+		return JSON.parse( data );
+	}
+
+	return data;
+}
+
 function dataAttr( elem, key, data ) {
 	var name;
 
@@ -29,14 +56,7 @@ function dataAttr( elem, key, data ) {
 
 		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;
+				data = getData( data );
 			} catch ( e ) {}
 
 			// Make sure we set the data so it isn't changed later
@@ -87,7 +107,7 @@ jQuery.fn.extend( {
 					i = attrs.length;
 					while ( i-- ) {
 
-						// Support: IE11+
+						// Support: IE 11 only
 						// The attrs elements can be null (#14894)
 						if ( attrs[ i ] ) {
 							name = attrs[ i ].name;
diff --git a/src/data/Data.js b/src/data/Data.js
index e214282..43ae016 100644
--- a/src/data/Data.js
+++ b/src/data/Data.js
@@ -1,8 +1,10 @@
 define( [
 	"../core",
-	"../var/rnotwhite",
+	"../var/rnothtmlwhite",
 	"./var/acceptData"
-], function( jQuery, rnotwhite, acceptData ) {
+], function( jQuery, rnothtmlwhite, acceptData ) {
+
+"use strict";
 
 function Data() {
 	this.expando = jQuery.expando + Data.uid++;
@@ -125,7 +127,7 @@ Data.prototype = {
 				// Otherwise, create an array by matching non-whitespace
 				key = key in cache ?
 					[ key ] :
-					( key.match( rnotwhite ) || [] );
+					( key.match( rnothtmlwhite ) || [] );
 			}
 
 			i = key.length;
@@ -138,10 +140,10 @@ Data.prototype = {
 		// Remove the expando if there's no more data
 		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
 
-			// Support: Chrome <= 35-45+
+			// Support: Chrome <=35 - 45
 			// Webkit & Blink performance suffers when deleting properties
 			// from DOM nodes, so set to undefined instead
-			// https://code.google.com/p/chromium/issues/detail?id=378607
+			// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
 			if ( owner.nodeType ) {
 				owner[ this.expando ] = undefined;
 			} else {
diff --git a/src/data/var/acceptData.js b/src/data/var/acceptData.js
index 6e15af1..e00f753 100644
--- a/src/data/var/acceptData.js
+++ b/src/data/var/acceptData.js
@@ -1,5 +1,7 @@
 define( function() {
 
+"use strict";
+
 /**
  * Determines whether an object can have data
  */
@@ -11,7 +13,6 @@ return function( owner ) {
 	//    - Node.DOCUMENT_NODE
 	//  - Object
 	//    - Any
-	/* jshint -W018 */
 	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
 };
 
diff --git a/src/data/var/dataPriv.js b/src/data/var/dataPriv.js
index 9a1343c..72713c8 100644
--- a/src/data/var/dataPriv.js
+++ b/src/data/var/dataPriv.js
@@ -1,5 +1,7 @@
 define( [
 	"../Data"
 ], function( Data ) {
+	"use strict";
+
 	return new Data();
 } );
diff --git a/src/data/var/dataUser.js b/src/data/var/dataUser.js
index 9a1343c..72713c8 100644
--- a/src/data/var/dataUser.js
+++ b/src/data/var/dataUser.js
@@ -1,5 +1,7 @@
 define( [
 	"../Data"
 ], function( Data ) {
+	"use strict";
+
 	return new Data();
 } );
diff --git a/src/deferred.js b/src/deferred.js
index bc2ea86..8139515 100644
--- a/src/deferred.js
+++ b/src/deferred.js
@@ -4,6 +4,8 @@ define( [
 	"./callbacks"
 ], function( jQuery, slice ) {
 
+"use strict";
+
 function Identity( v ) {
 	return v;
 }
@@ -11,6 +13,38 @@ function Thrower( ex ) {
 	throw ex;
 }
 
+function adoptValue( value, resolve, reject ) {
+	var method;
+
+	try {
+
+		// Check for promise aspect first to privilege synchronous behavior
+		if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
+			method.call( value ).done( resolve ).fail( reject );
+
+		// Other thenables
+		} else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
+			method.call( value, resolve, reject );
+
+		// Other non-thenables
+		} else {
+
+			// Support: Android 4.0 only
+			// Strict mode functions invoked without .call/.apply get global-object context
+			resolve.call( undefined, value );
+		}
+
+	// For Promises/A+, convert exceptions into rejections
+	// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
+	// Deferred#then to conditionally suppress rejection.
+	} catch ( value ) {
+
+		// Support: Android 4.0 only
+		// Strict mode functions invoked without .call/.apply get global-object context
+		reject.call( undefined, value );
+	}
+}
+
 jQuery.extend( {
 
 	Deferred: function( func ) {
@@ -60,7 +94,7 @@ jQuery.extend( {
 										.fail( newDefer.reject );
 								} else {
 									newDefer[ tuple[ 0 ] + "With" ](
-										this === promise ? newDefer.promise() : this,
+										this,
 										fn ? [ returned ] : arguments
 									);
 								}
@@ -73,7 +107,7 @@ jQuery.extend( {
 					var maxDepth = 0;
 					function resolve( depth, deferred, handler, special ) {
 						return function() {
-							var that = this === promise ? undefined : this,
+							var that = this,
 								args = arguments,
 								mightThrow = function() {
 									var returned, then;
@@ -128,7 +162,7 @@ jQuery.extend( {
 												resolve( maxDepth, deferred, Identity, special ),
 												resolve( maxDepth, deferred, Thrower, special ),
 												resolve( maxDepth, deferred, Identity,
-													deferred.notify )
+													deferred.notifyWith )
 											);
 										}
 
@@ -144,8 +178,7 @@ jQuery.extend( {
 
 										// Process the value(s)
 										// Default process is resolve
-										( special || deferred.resolveWith )(
-											that || deferred.promise(), args );
+										( special || deferred.resolveWith )( that, args );
 									}
 								},
 
@@ -174,8 +207,7 @@ jQuery.extend( {
 													args = [ e ];
 												}
 
-												deferred.rejectWith( that || deferred.promise(),
-													args );
+												deferred.rejectWith( that, args );
 											}
 										}
 									};
@@ -282,7 +314,7 @@ jQuery.extend( {
 			// deferred.resolve = function() { deferred.resolveWith(...) }
 			// deferred.reject = function() { deferred.rejectWith(...) }
 			deferred[ tuple[ 0 ] ] = function() {
-				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
+				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
 				return this;
 			};
 
@@ -305,62 +337,48 @@ jQuery.extend( {
 	},
 
 	// Deferred helper
-	when: function() {
-		var method, resolveContexts,
-			i = 0,
-			resolveValues = slice.call( arguments ),
-			length = resolveValues.length,
+	when: function( singleValue ) {
+		var
+
+			// count of uncompleted subordinates
+			remaining = arguments.length,
 
-			// the count of uncompleted subordinates
-			remaining = length,
+			// count of unprocessed arguments
+			i = remaining,
 
-			// the master Deferred.
+			// subordinate fulfillment data
+			resolveContexts = Array( i ),
+			resolveValues = slice.call( arguments ),
+
+			// the master Deferred
 			master = jQuery.Deferred(),
 
-			// Update function for both resolving subordinates
+			// subordinate callback factory
 			updateFunc = function( i ) {
 				return function( value ) {
 					resolveContexts[ i ] = this;
 					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
 					if ( !( --remaining ) ) {
-						master.resolveWith(
-							resolveContexts.length === 1 ? resolveContexts[ 0 ] : resolveContexts,
-							resolveValues
-						);
+						master.resolveWith( resolveContexts, resolveValues );
 					}
 				};
 			};
 
-		// Add listeners to promise-like subordinates; treat others as resolved
-		if ( length > 0 ) {
-			resolveContexts = new Array( length );
-			for ( ; i < length; i++ ) {
-
-				// jQuery.Deferred - treated specially to get resolve-sync behavior
-				if ( resolveValues[ i ] &&
-					jQuery.isFunction( ( method = resolveValues[ i ].promise ) ) ) {
-
-					method.call( resolveValues[ i ] )
-						.done( updateFunc( i ) )
-						.fail( master.reject );
-
-				// Other thenables
-				} else if ( resolveValues[ i ] &&
-					jQuery.isFunction( ( method = resolveValues[ i ].then ) ) ) {
-
-					method.call(
-						resolveValues[ i ],
-						updateFunc( i ),
-						master.reject
-					);
-				} else {
-					updateFunc( i )( resolveValues[ i ] );
-				}
+		// Single- and empty arguments are adopted like Promise.resolve
+		if ( remaining <= 1 ) {
+			adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject );
+
+			// Use .then() to unwrap secondary thenables (cf. gh-3000)
+			if ( master.state() === "pending" ||
+				jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
+
+				return master.then();
 			}
+		}
 
-		// If we're not waiting on anything, resolve the master
-		} else {
-			master.resolveWith();
+		// Multiple arguments are aggregated like Promise.all array elements
+		while ( i-- ) {
+			adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
 		}
 
 		return master.promise();
diff --git a/src/deferred/exceptionHook.js b/src/deferred/exceptionHook.js
index b995506..6dbdc85 100644
--- a/src/deferred/exceptionHook.js
+++ b/src/deferred/exceptionHook.js
@@ -3,16 +3,18 @@ define( [
 	"../deferred"
 ], function( jQuery ) {
 
+"use strict";
+
 // These usually indicate a programmer mistake during development,
 // warn about them ASAP rather than swallowing them by default.
 var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
 
 jQuery.Deferred.exceptionHook = function( error, stack ) {
 
-	// Support: IE9
+	// Support: IE 8 - 9 only
 	// Console exists when dev tools are open, which can happen at any time
 	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
-		window.console.warn( "jQuery.Deferred exception: " + error.message, stack );
+		window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
 	}
 };
 
diff --git a/src/deprecated.js b/src/deprecated.js
index 7888526..9fcc6b7 100644
--- a/src/deprecated.js
+++ b/src/deprecated.js
@@ -2,6 +2,8 @@ define( [
 	"./core"
 ], function( jQuery ) {
 
+"use strict";
+
 jQuery.fn.extend( {
 
 	bind: function( types, data, fn ) {
@@ -23,4 +25,6 @@ jQuery.fn.extend( {
 	}
 } );
 
+jQuery.parseJSON = JSON.parse;
+
 } );
diff --git a/src/dimensions.js b/src/dimensions.js
index 3d4dbff..46e7b1c 100644
--- a/src/dimensions.js
+++ b/src/dimensions.js
@@ -4,6 +4,8 @@ define( [
 	"./css"
 ], function( jQuery, access ) {
 
+"use strict";
+
 // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
 jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
 	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
diff --git a/src/effects.js b/src/effects.js
index 43162e1..68af96c 100644
--- a/src/effects.js
+++ b/src/effects.js
@@ -2,9 +2,9 @@ define( [
 	"./core",
 	"./var/document",
 	"./var/rcssNum",
-	"./var/rnotwhite",
+	"./var/rnothtmlwhite",
 	"./css/var/cssExpand",
-	"./css/var/isHidden",
+	"./css/var/isHiddenWithinTree",
 	"./css/var/swap",
 	"./css/adjustCSS",
 	"./data/var/dataPriv",
@@ -17,9 +17,11 @@ define( [
 	"./manipulation",
 	"./css",
 	"./effects/Tween"
-], function( jQuery, document, rcssNum, rnotwhite, cssExpand, isHidden, swap,
+], function( jQuery, document, rcssNum, rnothtmlwhite, cssExpand, isHiddenWithinTree, swap,
 	adjustCSS, dataPriv, showHide ) {
 
+"use strict";
+
 var
 	fxNow, timerId,
 	rfxtypes = /^(?:toggle|show|hide)$/,
@@ -49,7 +51,7 @@ function genFx( type, includeWidth ) {
 	// If we include width, step value is 1 to do all cssExpand values,
 	// otherwise step value is 2 to skip over Left and Right
 	includeWidth = includeWidth ? 1 : 0;
-	for ( ; i < 4 ; i += 2 - includeWidth ) {
+	for ( ; i < 4; i += 2 - includeWidth ) {
 		which = cssExpand[ i ];
 		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
 	}
@@ -76,13 +78,12 @@ function createTween( value, prop, animation ) {
 }
 
 function defaultPrefilter( elem, props, opts ) {
-	/* jshint validthis: true */
 	var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
 		isBox = "width" in props || "height" in props,
 		anim = this,
 		orig = {},
 		style = elem.style,
-		hidden = elem.nodeType && isHidden( elem ),
+		hidden = elem.nodeType && isHiddenWithinTree( elem ),
 		dataShow = dataPriv.get( elem, "fxshow" );
 
 	// Queue-skipping animations hijack the fx hooks
@@ -142,7 +143,7 @@ function defaultPrefilter( elem, props, opts ) {
 	// Restrict "overflow" and "display" styles during box animations
 	if ( isBox && elem.nodeType === 1 ) {
 
-		// Support: IE 9 - 11
+		// Support: IE <=9 - 11, Edge 12 - 13
 		// Record all 3 overflow attributes because IE does not infer the shorthand
 		// from identically-valued overflowX and overflowY
 		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
@@ -218,9 +219,12 @@ function defaultPrefilter( elem, props, opts ) {
 				showHide( [ elem ], true );
 			}
 
-			/* jshint -W083 */
+			/* eslint-disable no-loop-func */
+
 			anim.done( function() {
 
+			/* eslint-enable no-loop-func */
+
 				// The final step of a "hide" animation is actually hiding the element
 				if ( !hidden ) {
 					showHide( [ elem ] );
@@ -238,7 +242,7 @@ function defaultPrefilter( elem, props, opts ) {
 			dataShow[ prop ] = propTween.start;
 			if ( hidden ) {
 				propTween.end = propTween.start;
-				propTween.start = prop === "width" || prop === "height" ? 1 : 0;
+				propTween.start = 0;
 			}
 		}
 	}
@@ -298,14 +302,14 @@ function Animation( elem, properties, options ) {
 			var currentTime = fxNow || createFxNow(),
 				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
 
-				// Support: Android 2.3
+				// Support: Android 2.3 only
 				// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
 				temp = remaining / animation.duration || 0,
 				percent = 1 - temp,
 				index = 0,
 				length = animation.tweens.length;
 
-			for ( ; index < length ; index++ ) {
+			for ( ; index < length; index++ ) {
 				animation.tweens[ index ].run( percent );
 			}
 
@@ -346,7 +350,7 @@ function Animation( elem, properties, options ) {
 					return this;
 				}
 				stopped = true;
-				for ( ; index < length ; index++ ) {
+				for ( ; index < length; index++ ) {
 					animation.tweens[ index ].run( 1 );
 				}
 
@@ -364,7 +368,7 @@ function Animation( elem, properties, options ) {
 
 	propFilter( props, animation.opts.specialEasing );
 
-	for ( ; index < length ; index++ ) {
+	for ( ; index < length; index++ ) {
 		result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
 		if ( result ) {
 			if ( jQuery.isFunction( result.stop ) ) {
@@ -411,14 +415,14 @@ jQuery.Animation = jQuery.extend( Animation, {
 			callback = props;
 			props = [ "*" ];
 		} else {
-			props = props.match( rnotwhite );
+			props = props.match( rnothtmlwhite );
 		}
 
 		var prop,
 			index = 0,
 			length = props.length;
 
-		for ( ; index < length ; index++ ) {
+		for ( ; index < length; index++ ) {
 			prop = props[ index ];
 			Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
 			Animation.tweeners[ prop ].unshift( callback );
@@ -449,9 +453,14 @@ jQuery.speed = function( speed, easing, fn ) {
 		opt.duration = 0;
 
 	} else {
-		opt.duration = typeof opt.duration === "number" ?
-			opt.duration : opt.duration in jQuery.fx.speeds ?
-				jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+		if ( typeof opt.duration !== "number" ) {
+			if ( opt.duration in jQuery.fx.speeds ) {
+				opt.duration = jQuery.fx.speeds[ opt.duration ];
+
+			} else {
+				opt.duration = jQuery.fx.speeds._default;
+			}
+		}
 	}
 
 	// Normalize opt.queue - true/undefined/null -> "fx"
@@ -479,7 +488,7 @@ jQuery.fn.extend( {
 	fadeTo: function( speed, to, easing, callback ) {
 
 		// Show any hidden elements after setting opacity to 0
-		return this.filter( isHidden ).css( "opacity", 0 ).show()
+		return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
 
 			// Animate to the value specified
 			.end().animate( { opacity: to }, speed, easing, callback );
diff --git a/src/effects/Tween.js b/src/effects/Tween.js
index 47f15a9..43eb8fa 100644
--- a/src/effects/Tween.js
+++ b/src/effects/Tween.js
@@ -3,6 +3,8 @@ define( [
 	"../css"
 ], function( jQuery ) {
 
+"use strict";
+
 function Tween( elem, options, prop, end, easing ) {
 	return new Tween.prototype.init( elem, options, prop, end, easing );
 }
@@ -27,12 +29,17 @@ Tween.prototype = {
 			Tween.propHooks._default.get( this );
 	},
 	run: function( percent ) {
-		var hooks = Tween.propHooks[ this.prop ];
+		var eased,
+			hooks = Tween.propHooks[ this.prop ];
 
-		this.pos = this.options.duration ?
-			jQuery.easing[ this.easing ]( percent ) :
-			percent;
-		this.now = ( this.end - this.start ) * this.pos + this.start;
+		if ( this.options.duration ) {
+			this.pos = eased = jQuery.easing[ this.easing ](
+				percent, this.options.duration * percent, 0, 1, this.options.duration
+			);
+		} else {
+			this.pos = eased = percent;
+		}
+		this.now = ( this.end - this.start ) * eased + this.start;
 
 		if ( this.options.step ) {
 			this.options.step.call( this.elem, this.now, this );
@@ -88,7 +95,7 @@ Tween.propHooks = {
 	}
 };
 
-// Support: IE9
+// Support: IE <=9 only
 // Panic based approach to setting things on disconnected nodes
 Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
 	set: function( tween ) {
@@ -110,7 +117,7 @@ jQuery.easing = {
 
 jQuery.fx = Tween.prototype.init;
 
-// Back Compat <1.8 extension point
+// Back compat <1.8 extension point
 jQuery.fx.step = {};
 
 } );
diff --git a/src/effects/animatedSelector.js b/src/effects/animatedSelector.js
index d84c9c7..24c1bfb 100644
--- a/src/effects/animatedSelector.js
+++ b/src/effects/animatedSelector.js
@@ -4,7 +4,9 @@ define( [
 	"../effects"
 ], function( jQuery ) {
 
-jQuery.expr.filters.animated = function( elem ) {
+"use strict";
+
+jQuery.expr.pseudos.animated = function( elem ) {
 	return jQuery.grep( jQuery.timers, function( fn ) {
 		return elem === fn.elem;
 	} ).length;
diff --git a/src/event.js b/src/event.js
index ddd92c5..ab2c63c 100644
--- a/src/event.js
+++ b/src/event.js
@@ -1,13 +1,16 @@
 define( [
 	"./core",
 	"./var/document",
-	"./var/rnotwhite",
+	"./var/documentElement",
+	"./var/rnothtmlwhite",
 	"./var/slice",
 	"./data/var/dataPriv",
 
 	"./core/init",
 	"./selector"
-], function( jQuery, document, rnotwhite, slice, dataPriv ) {
+], function( jQuery, document, documentElement, rnothtmlwhite, slice, dataPriv ) {
+
+"use strict";
 
 var
 	rkeyEvent = /^key/,
@@ -22,7 +25,7 @@ function returnFalse() {
 	return false;
 }
 
-// Support: IE9
+// Support: IE <=9 only
 // See #13393 for more info
 function safeActiveElement() {
 	try {
@@ -70,6 +73,8 @@ function on( elem, types, selector, data, fn, one ) {
 	}
 	if ( fn === false ) {
 		fn = returnFalse;
+	} else if ( !fn ) {
+		return elem;
 	}
 
 	if ( one === 1 ) {
@@ -116,6 +121,12 @@ jQuery.event = {
 			selector = handleObjIn.selector;
 		}
 
+		// Ensure that invalid selectors throw exceptions at attach time
+		// Evaluate against documentElement in case elem is a non-element node (e.g., document)
+		if ( selector ) {
+			jQuery.find.matchesSelector( documentElement, selector );
+		}
+
 		// Make sure that the handler has a unique ID, used to find/remove it later
 		if ( !handler.guid ) {
 			handler.guid = jQuery.guid++;
@@ -136,7 +147,7 @@ jQuery.event = {
 		}
 
 		// Handle multiple events separated by a space
-		types = ( types || "" ).match( rnotwhite ) || [ "" ];
+		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
 		t = types.length;
 		while ( t-- ) {
 			tmp = rtypenamespace.exec( types[ t ] ) || [];
@@ -218,7 +229,7 @@ jQuery.event = {
 		}
 
 		// Once for each type.namespace in types; type may be omitted
-		types = ( types || "" ).match( rnotwhite ) || [ "" ];
+		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
 		t = types.length;
 		while ( t-- ) {
 			tmp = rtypenamespace.exec( types[ t ] ) || [];
@@ -279,19 +290,23 @@ jQuery.event = {
 		}
 	},
 
-	dispatch: function( event ) {
+	dispatch: function( nativeEvent ) {
 
 		// Make a writable jQuery.Event from the native event object
-		event = jQuery.event.fix( event );
+		var event = jQuery.event.fix( nativeEvent );
 
-		var i, j, ret, matched, handleObj,
-			handlerQueue = [],
-			args = slice.call( arguments ),
+		var i, j, ret, matched, handleObj, handlerQueue,
+			args = new Array( arguments.length ),
 			handlers = ( dataPriv.get( 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;
+
+		for ( i = 1; i < arguments.length; i++ ) {
+			args[ i ] = arguments[ i ];
+		}
+
 		event.delegateTarget = this;
 
 		// Call the preDispatch hook for the mapped type, and let it bail if desired
@@ -340,140 +355,95 @@ jQuery.event = {
 	},
 
 	handlers: function( event, handlers ) {
-		var i, matches, sel, handleObj,
+		var i, handleObj, sel, matchedHandlers, matchedSelectors,
 			handlerQueue = [],
 			delegateCount = handlers.delegateCount,
 			cur = event.target;
 
-		// Support (at least): Chrome, IE9
 		// Find delegate handlers
-		// Black-hole SVG <use> instance trees (#13180)
-		//
-		// Support: Firefox<=42+
-		// Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
-		if ( delegateCount && cur.nodeType &&
-			( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {
+		if ( delegateCount &&
+
+			// Support: IE <=9
+			// Black-hole SVG <use> instance trees (trac-13180)
+			cur.nodeType &&
+
+			// Support: Firefox <=42
+			// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
+			// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
+			// Support: IE 11 only
+			// ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
+			!( event.type === "click" && event.button >= 1 ) ) {
 
 			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 = [];
+				if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
+					matchedHandlers = [];
+					matchedSelectors = {};
 					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 ?
+						if ( matchedSelectors[ sel ] === undefined ) {
+							matchedSelectors[ sel ] = handleObj.needsContext ?
 								jQuery( sel, this ).index( cur ) > -1 :
 								jQuery.find( sel, this, null, [ cur ] ).length;
 						}
-						if ( matches[ sel ] ) {
-							matches.push( handleObj );
+						if ( matchedSelectors[ sel ] ) {
+							matchedHandlers.push( handleObj );
 						}
 					}
-					if ( matches.length ) {
-						handlerQueue.push( { elem: cur, handlers: matches } );
+					if ( matchedHandlers.length ) {
+						handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
 					}
 				}
 			}
 		}
 
 		// Add the remaining (directly-bound) handlers
+		cur = this;
 		if ( delegateCount < handlers.length ) {
-			handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } );
+			handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
 		}
 
 		return handlerQueue;
 	},
 
-	// Includes some event props shared by KeyEvent and MouseEvent
-	props: ( "altKey bubbles cancelable ctrlKey currentTarget detail 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 offsetX offsetY pageX pageY " +
-			"screenX screenY toElement" ).split( " " ),
-		filter: function( event, original ) {
-			var eventDoc, doc, body,
-				button = original.button;
-
-			// 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 );
-			}
+	addProp: function( name, hook ) {
+		Object.defineProperty( jQuery.Event.prototype, name, {
+			enumerable: true,
+			configurable: true,
 
-			// 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 ) ) );
+			get: jQuery.isFunction( hook ) ?
+				function() {
+					if ( this.originalEvent ) {
+							return hook( this.originalEvent );
+					}
+				} :
+				function() {
+					if ( this.originalEvent ) {
+							return this.originalEvent[ name ];
+					}
+				},
+
+			set: function( value ) {
+				Object.defineProperty( this, name, {
+					enumerable: true,
+					configurable: true,
+					writable: true,
+					value: value
+				} );
 			}
-
-			return event;
-		}
+		} );
 	},
 
-	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: Safari 6-8+
-		// Target should not be a text node (#504, #13143)
-		if ( event.target.nodeType === 3 ) {
-			event.target = event.target.parentNode;
-		}
-
-		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+	fix: function( originalEvent ) {
+		return originalEvent[ jQuery.expando ] ?
+			originalEvent :
+			new jQuery.Event( originalEvent );
 	},
 
 	special: {
@@ -556,11 +526,21 @@ jQuery.Event = function( src, props ) {
 		this.isDefaultPrevented = src.defaultPrevented ||
 				src.defaultPrevented === undefined &&
 
-				// Support: Android<4.0
+				// Support: Android <=2.3 only
 				src.returnValue === false ?
 			returnTrue :
 			returnFalse;
 
+		// Create target properties
+		// Support: Safari <=6 - 7 only
+		// Target should not be a text node (#504, #13143)
+		this.target = ( src.target && src.target.nodeType === 3 ) ?
+			src.target.parentNode :
+			src.target;
+
+		this.currentTarget = src.currentTarget;
+		this.relatedTarget = src.relatedTarget;
+
 	// Event type
 	} else {
 		this.type = src;
@@ -579,19 +559,20 @@ jQuery.Event = function( src, props ) {
 };
 
 // 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
+// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
 jQuery.Event.prototype = {
 	constructor: jQuery.Event,
 	isDefaultPrevented: returnFalse,
 	isPropagationStopped: returnFalse,
 	isImmediatePropagationStopped: returnFalse,
+	isSimulated: false,
 
 	preventDefault: function() {
 		var e = this.originalEvent;
 
 		this.isDefaultPrevented = returnTrue;
 
-		if ( e ) {
+		if ( e && !this.isSimulated ) {
 			e.preventDefault();
 		}
 	},
@@ -600,7 +581,7 @@ jQuery.Event.prototype = {
 
 		this.isPropagationStopped = returnTrue;
 
-		if ( e ) {
+		if ( e && !this.isSimulated ) {
 			e.stopPropagation();
 		}
 	},
@@ -609,7 +590,7 @@ jQuery.Event.prototype = {
 
 		this.isImmediatePropagationStopped = returnTrue;
 
-		if ( e ) {
+		if ( e && !this.isSimulated ) {
 			e.stopImmediatePropagation();
 		}
 
@@ -617,13 +598,74 @@ jQuery.Event.prototype = {
 	}
 };
 
+// Includes all common event props including KeyEvent and MouseEvent specific props
+jQuery.each( {
+	altKey: true,
+	bubbles: true,
+	cancelable: true,
+	changedTouches: true,
+	ctrlKey: true,
+	detail: true,
+	eventPhase: true,
+	metaKey: true,
+	pageX: true,
+	pageY: true,
+	shiftKey: true,
+	view: true,
+	"char": true,
+	charCode: true,
+	key: true,
+	keyCode: true,
+	button: true,
+	buttons: true,
+	clientX: true,
+	clientY: true,
+	offsetX: true,
+	offsetY: true,
+	pointerId: true,
+	pointerType: true,
+	screenX: true,
+	screenY: true,
+	targetTouches: true,
+	toElement: true,
+	touches: true,
+
+	which: function( event ) {
+		var button = event.button;
+
+		// Add which for key events
+		if ( event.which == null && rkeyEvent.test( event.type ) ) {
+			return event.charCode != null ? event.charCode : event.keyCode;
+		}
+
+		// Add which for click: 1 === left; 2 === middle; 3 === right
+		if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
+			if ( button & 1 ) {
+				return 1;
+			}
+
+			if ( button & 2 ) {
+				return 3;
+			}
+
+			if ( button & 4 ) {
+				return 2;
+			}
+
+			return 0;
+		}
+
+		return event.which;
+	}
+}, jQuery.event.addProp );
+
 // Create mouseenter/leave events using mouseover/out and event-time checks
 // so that event delegation works in jQuery.
 // Do the same for pointerenter/pointerleave and pointerover/pointerout
 //
 // Support: Safari 7 only
 // Safari sends mouseenter too often; see:
-// https://code.google.com/p/chromium/issues/detail?id=470258
+// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
 // for the description of the bug (it existed in older Chrome versions as well).
 jQuery.each( {
 	mouseenter: "mouseover",
diff --git a/src/event/ajax.js b/src/event/ajax.js
index 98e194b..500b36c 100644
--- a/src/event/ajax.js
+++ b/src/event/ajax.js
@@ -3,6 +3,8 @@ define( [
 	"../event"
 ], function( jQuery ) {
 
+"use strict";
+
 // Attach a bunch of functions for handling common AJAX events
 jQuery.each( [
 	"ajaxStart",
diff --git a/src/event/alias.js b/src/event/alias.js
index b1b8f70..863c94a 100644
--- a/src/event/alias.js
+++ b/src/event/alias.js
@@ -5,6 +5,8 @@ define( [
 	"./trigger"
 ], function( jQuery ) {
 
+"use strict";
+
 jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
 	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
 	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
diff --git a/src/event/focusin.js b/src/event/focusin.js
index ae7f831..7faef29 100644
--- a/src/event/focusin.js
+++ b/src/event/focusin.js
@@ -7,14 +7,16 @@ define( [
 	"./trigger"
 ], function( jQuery, dataPriv, support ) {
 
-// Support: Firefox
+"use strict";
+
+// Support: Firefox <=44
 // Firefox doesn't have focus(in | out) events
 // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
 //
-// Support: Chrome, Safari
+// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
 // focus(in | out) events fire after focus & blur events,
 // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
-// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
+// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
 if ( !support.focusin ) {
 	jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
 
diff --git a/src/event/support.js b/src/event/support.js
index 1c4d416..e3db9ad 100644
--- a/src/event/support.js
+++ b/src/event/support.js
@@ -2,6 +2,8 @@ define( [
 	"../var/support"
 ], function( support ) {
 
+"use strict";
+
 support.focusin = "onfocusin" in window;
 
 return support;
diff --git a/src/event/trigger.js b/src/event/trigger.js
index 8f5f778..ef39137 100644
--- a/src/event/trigger.js
+++ b/src/event/trigger.js
@@ -8,6 +8,8 @@ define( [
 	"../event"
 ], function( jQuery, document, dataPriv, acceptData, hasOwn ) {
 
+"use strict";
+
 var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;
 
 jQuery.extend( jQuery.event, {
@@ -148,6 +150,7 @@ jQuery.extend( jQuery.event, {
 	},
 
 	// Piggyback on a donor event to simulate a different one
+	// Used only for `focus(in | out)` events
 	simulate: function( type, elem, event ) {
 		var e = jQuery.extend(
 			new jQuery.Event(),
@@ -155,27 +158,10 @@ jQuery.extend( jQuery.event, {
 			{
 				type: type,
 				isSimulated: true
-
-				// Previously, `originalEvent: {}` was set here, so stopPropagation call
-				// would not be triggered on donor event, since in our own
-				// jQuery.event.stopPropagation function we had a check for existence of
-				// originalEvent.stopPropagation method, so, consequently it would be a noop.
-				//
-				// But now, this "simulate" function is used only for events
-				// for which stopPropagation() is noop, so there is no need for that anymore.
-				//
-				// For the compat branch though, guard for "click" and "submit"
-				// events is still used, but was moved to jQuery.event.stopPropagation function
-				// because `originalEvent` should point to the original event for the constancy
-				// with other events and for more focused logic
 			}
 		);
 
 		jQuery.event.trigger( e, null, elem );
-
-		if ( e.isDefaultPrevented() ) {
-			event.preventDefault();
-		}
 	}
 
 } );
diff --git a/src/exports/amd.js b/src/exports/amd.js
index add6eb9..cbb1ef5 100644
--- a/src/exports/amd.js
+++ b/src/exports/amd.js
@@ -2,6 +2,8 @@ define( [
 	"../core"
 ], function( jQuery ) {
 
+"use strict";
+
 // Register as a named AMD module, since jQuery can be concatenated with other
 // files that may use define, but not via a proper concatenation script that
 // understands anonymous AMD modules. A named AMD is safest and most robust
diff --git a/src/exports/global.js b/src/exports/global.js
index be9cbfb..460b56e 100644
--- a/src/exports/global.js
+++ b/src/exports/global.js
@@ -1,3 +1,9 @@
+define( [
+	"../core"
+], function( jQuery, noGlobal ) {
+
+"use strict";
+
 var
 
 	// Map over jQuery in case of overwrite
@@ -24,3 +30,5 @@ jQuery.noConflict = function( deep ) {
 if ( !noGlobal ) {
 	window.jQuery = window.$ = jQuery;
 }
+
+} );
diff --git a/src/jquery.js b/src/jquery.js
index 4cc9c8a..52fc87d 100644
--- a/src/jquery.js
+++ b/src/jquery.js
@@ -30,9 +30,12 @@ define( [
 	"./offset",
 	"./dimensions",
 	"./deprecated",
-	"./exports/amd"
+	"./exports/amd",
+	"./exports/global"
 ], function( jQuery ) {
 
-return ( window.jQuery = window.$ = jQuery );
+"use strict";
+
+return jQuery;
 
 } );
diff --git a/src/manipulation.js b/src/manipulation.js
index 0d10219..9b4f5e4 100644
--- a/src/manipulation.js
+++ b/src/manipulation.js
@@ -26,10 +26,18 @@ define( [
 	wrapMap, getAll, setGlobalEval, buildFragment, support,
 	dataPriv, dataUser, acceptData, DOMEval ) {
 
+"use strict";
+
 var
+
+	/* eslint-disable max-len */
+
+	// See https://github.com/eslint/eslint/issues/3229
 	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
 
-	// Support: IE 10-11, Edge 10240+
+	/* eslint-enable */
+
+	// Support: IE <=10 - 11, Edge 12 - 13
 	// In IE/Edge using regex groups here causes severe slowdowns.
 	// See https://connect.microsoft.com/IE/feedback/details/1736512/
 	rnoInnerhtml = /<script|<style|<link/i,
@@ -164,7 +172,7 @@ function domManip( collection, args, callback, ignored ) {
 					// Keep references to cloned scripts for later restoration
 					if ( hasScripts ) {
 
-						// Support: Android<4.1, PhantomJS<2
+						// Support: Android <=4.0 only, PhantomJS 1 only
 						// push.apply(_, arraylike) throws on ancient WebKit
 						jQuery.merge( scripts, getAll( node, "script" ) );
 					}
@@ -292,13 +300,13 @@ jQuery.extend( {
 						}
 					}
 
-					// Support: Chrome <= 35-45+
+					// Support: Chrome <=35 - 45+
 					// Assign undefined instead of using delete, see Data#remove
 					elem[ dataPriv.expando ] = undefined;
 				}
 				if ( elem[ dataUser.expando ] ) {
 
-					// Support: Chrome <= 35-45+
+					// Support: Chrome <=35 - 45+
 					// Assign undefined instead of using delete, see Data#remove
 					elem[ dataUser.expando ] = undefined;
 				}
@@ -465,7 +473,7 @@ jQuery.each( {
 			elems = i === last ? this : this.clone( true );
 			jQuery( insert[ i ] )[ original ]( elems );
 
-			// Support: Android<4.1, PhantomJS<2
+			// Support: Android <=4.0 only, PhantomJS 1 only
 			// .get() because push.apply(_, arraylike) throws on ancient WebKit
 			push.apply( ret, elems.get() );
 		}
diff --git a/src/manipulation/_evalUrl.js b/src/manipulation/_evalUrl.js
index 572fe30..f9ec702 100644
--- a/src/manipulation/_evalUrl.js
+++ b/src/manipulation/_evalUrl.js
@@ -2,6 +2,8 @@ define( [
 	"../ajax"
 ], function( jQuery ) {
 
+"use strict";
+
 jQuery._evalUrl = function( url ) {
 	return jQuery.ajax( {
 		url: url,
diff --git a/src/manipulation/buildFragment.js b/src/manipulation/buildFragment.js
index cfdd1c0..bcb5085 100644
--- a/src/manipulation/buildFragment.js
+++ b/src/manipulation/buildFragment.js
@@ -7,6 +7,8 @@ define( [
 	"./setGlobalEval"
 ], function( jQuery, rtagName, rscriptType, wrapMap, getAll, setGlobalEval ) {
 
+"use strict";
+
 var rhtml = /<|&#?\w+;/;
 
 function buildFragment( elems, context, scripts, selection, ignored ) {
@@ -24,7 +26,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
 			// Add nodes directly
 			if ( jQuery.type( elem ) === "object" ) {
 
-				// Support: Android<4.1, PhantomJS<2
+				// Support: Android <=4.0 only, PhantomJS 1 only
 				// push.apply(_, arraylike) throws on ancient WebKit
 				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
 
@@ -47,7 +49,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
 					tmp = tmp.lastChild;
 				}
 
-				// Support: Android<4.1, PhantomJS<2
+				// Support: Android <=4.0 only, PhantomJS 1 only
 				// push.apply(_, arraylike) throws on ancient WebKit
 				jQuery.merge( nodes, tmp.childNodes );
 
diff --git a/src/manipulation/getAll.js b/src/manipulation/getAll.js
index cc913f2..f68e321 100644
--- a/src/manipulation/getAll.js
+++ b/src/manipulation/getAll.js
@@ -2,19 +2,29 @@ define( [
 	"../core"
 ], function( jQuery ) {
 
+"use strict";
+
 function getAll( context, tag ) {
 
-	// Support: IE9-11+
+	// Support: IE <=9 - 11 only
 	// Use typeof to avoid zero-argument method invocation on host objects (#15151)
-	var ret = typeof context.getElementsByTagName !== "undefined" ?
-			context.getElementsByTagName( tag || "*" ) :
-			typeof context.querySelectorAll !== "undefined" ?
-				context.querySelectorAll( tag || "*" ) :
-			[];
-
-	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
-		jQuery.merge( [ context ], ret ) :
-		ret;
+	var ret;
+
+	if ( typeof context.getElementsByTagName !== "undefined" ) {
+		ret = context.getElementsByTagName( tag || "*" );
+
+	} else if ( typeof context.querySelectorAll !== "undefined" ) {
+		ret = context.querySelectorAll( tag || "*" );
+
+	} else {
+		ret = [];
+	}
+
+	if ( tag === undefined || tag && jQuery.nodeName( context, tag ) ) {
+		return jQuery.merge( [ context ], ret );
+	}
+
+	return ret;
 }
 
 return getAll;
diff --git a/src/manipulation/setGlobalEval.js b/src/manipulation/setGlobalEval.js
index 8ca69a0..cf95240 100644
--- a/src/manipulation/setGlobalEval.js
+++ b/src/manipulation/setGlobalEval.js
@@ -2,6 +2,8 @@ define( [
 	"../data/var/dataPriv"
 ], function( dataPriv ) {
 
+"use strict";
+
 // Mark scripts as having already been evaluated
 function setGlobalEval( elems, refElements ) {
 	var i = 0,
diff --git a/src/manipulation/support.js b/src/manipulation/support.js
index 4f6b9de..4a5d9af 100644
--- a/src/manipulation/support.js
+++ b/src/manipulation/support.js
@@ -3,12 +3,14 @@ define( [
 	"../var/support"
 ], function( document, support ) {
 
+"use strict";
+
 ( function() {
 	var fragment = document.createDocumentFragment(),
 		div = fragment.appendChild( document.createElement( "div" ) ),
 		input = document.createElement( "input" );
 
-	// Support: Android 4.0-4.3
+	// Support: Android 4.0 - 4.3 only
 	// Check state lost if the name is set (#11217)
 	// Support: Windows Web Apps (WWA)
 	// `name` and `type` must use .setAttribute for WWA (#14901)
@@ -18,11 +20,11 @@ define( [
 
 	div.appendChild( input );
 
-	// Support: Android<4.2
+	// Support: Android <=4.1 only
 	// Older WebKit doesn't clone checked state correctly in fragments
 	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
 
-	// Support: IE<=11+
+	// Support: IE <=11 only
 	// Make sure textarea (and checkbox) defaultValue is properly cloned
 	div.innerHTML = "<textarea>x</textarea>";
 	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
diff --git a/src/manipulation/var/rcheckableType.js b/src/manipulation/var/rcheckableType.js
index 4c95394..25bbcb4 100644
--- a/src/manipulation/var/rcheckableType.js
+++ b/src/manipulation/var/rcheckableType.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return ( /^(?:checkbox|radio)$/i );
 } );
diff --git a/src/manipulation/var/rscriptType.js b/src/manipulation/var/rscriptType.js
index 0c77c8a..7237c8a 100644
--- a/src/manipulation/var/rscriptType.js
+++ b/src/manipulation/var/rscriptType.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return ( /^$|\/(?:java|ecma)script/i );
 } );
diff --git a/src/manipulation/var/rtagName.js b/src/manipulation/var/rtagName.js
index 1f8751e..d565dd3 100644
--- a/src/manipulation/var/rtagName.js
+++ b/src/manipulation/var/rtagName.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i );
 } );
diff --git a/src/manipulation/wrapMap.js b/src/manipulation/wrapMap.js
index fdb430a..1f446f7 100644
--- a/src/manipulation/wrapMap.js
+++ b/src/manipulation/wrapMap.js
@@ -1,9 +1,11 @@
 define( function() {
 
+"use strict";
+
 // We have to close these tags to support XHTML (#13200)
 var wrapMap = {
 
-	// Support: IE9
+	// Support: IE <=9 only
 	option: [ 1, "<select multiple='multiple'>", "</select>" ],
 
 	// XHTML parsers do not magically insert elements in the
@@ -17,7 +19,7 @@ var wrapMap = {
 	_default: [ 0, "", "" ]
 };
 
-// Support: IE9
+// Support: IE <=9 only
 wrapMap.optgroup = wrapMap.option;
 
 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
diff --git a/src/offset.js b/src/offset.js
index f7ef79c..54442eb 100644
--- a/src/offset.js
+++ b/src/offset.js
@@ -13,6 +13,8 @@ define( [
 	"./selector" // contains
 ], function( jQuery, access, document, documentElement, rnumnonpx, curCSS, addGetHookIf, support ) {
 
+"use strict";
+
 /**
  * Gets a window from an element
  */
@@ -91,7 +93,7 @@ jQuery.fn.extend( {
 			return;
 		}
 
-		// Support: IE<=11+
+		// Support: IE <=11 only
 		// Running getBoundingClientRect on a
 		// disconnected node in IE throws an error
 		if ( !elem.getClientRects().length ) {
@@ -144,11 +146,10 @@ jQuery.fn.extend( {
 			}
 
 			// Add offsetParent borders
-			// Subtract offsetParent scroll positions
-			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ) -
-				offsetParent.scrollTop();
-			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ) -
-				offsetParent.scrollLeft();
+			parentOffset = {
+				top: parentOffset.top + jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ),
+				left: parentOffset.left + jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true )
+			};
 		}
 
 		// Subtract parent offsets and element margins
@@ -206,10 +207,10 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function(
 	};
 } );
 
-// Support: Safari<7-8+, Chrome<37-44+
+// Support: Safari <=7 - 9.1, Chrome <=37 - 49
 // Add the top/left cssHooks using jQuery.fn.position
 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
-// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280
+// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
 // getComputedStyle returns percent when specified for top/left/bottom/right;
 // rather than make the css module depend on the offset module, just check for it here
 jQuery.each( [ "top", "left" ], function( i, prop ) {
diff --git a/src/outro.js b/src/outro.js
deleted file mode 100644
index d792ffb..0000000
--- a/src/outro.js
+++ /dev/null
@@ -1,2 +0,0 @@
-return jQuery;
-}));
diff --git a/src/queue.js b/src/queue.js
index 813c41c..3a626a2 100644
--- a/src/queue.js
+++ b/src/queue.js
@@ -5,6 +5,8 @@ define( [
 	"./callbacks"
 ], function( jQuery, dataPriv ) {
 
+"use strict";
+
 jQuery.extend( {
 	queue: function( elem, type, data ) {
 		var queue;
diff --git a/src/queue/delay.js b/src/queue/delay.js
index 4def286..d471eed 100644
--- a/src/queue/delay.js
+++ b/src/queue/delay.js
@@ -4,6 +4,8 @@ define( [
 	"../effects" // Delay is optional because of this dependency
 ], function( jQuery ) {
 
+"use strict";
+
 // Based off of the plugin by Clint Helfers, with permission.
 // https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
 jQuery.fn.delay = function( time, type ) {
diff --git a/src/selector-native.js b/src/selector-native.js
index ee81483..da837a0 100644
--- a/src/selector-native.js
+++ b/src/selector-native.js
@@ -6,6 +6,8 @@ define( [
 	"./var/indexOf"
 ], function( jQuery, document, documentElement, hasOwn, indexOf ) {
 
+"use strict";
+
 /*
  * Optional (non-Sizzle) selector module for custom builds.
  *
@@ -37,7 +39,26 @@ var hasDuplicate, sortInput,
 		documentElement.webkitMatchesSelector ||
 		documentElement.mozMatchesSelector ||
 		documentElement.oMatchesSelector ||
-		documentElement.msMatchesSelector;
+		documentElement.msMatchesSelector,
+
+	// CSS string/identifier serialization
+	// https://drafts.csswg.org/cssom/#common-serializing-idioms
+	rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,
+	fcssescape = function( ch, asCodePoint ) {
+		if ( asCodePoint ) {
+
+			// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
+			if ( ch === "\0" ) {
+				return "\uFFFD";
+			}
+
+			// Control characters and (dependent upon position) numbers get escaped as code points
+			return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
+		}
+
+		// Other potentially-special ASCII characters get backslash-escaped
+		return "\\" + ch;
+	};
 
 function sortOrder( a, b ) {
 
@@ -110,7 +131,14 @@ function uniqueSort( results ) {
 	return results;
 }
 
+function escape( sel ) {
+	return ( sel + "" ).replace( rcssescape, fcssescape );
+}
+
 jQuery.extend( {
+	uniqueSort: uniqueSort,
+	unique: uniqueSort,
+	escapeSelector: escape,
 	find: function( selector, context, results, seed ) {
 		var elem, nodeType,
 			i = 0;
@@ -140,8 +168,6 @@ jQuery.extend( {
 
 		return results;
 	},
-	uniqueSort: uniqueSort,
-	unique: uniqueSort,
 	text: function( elem ) {
 		var node,
 			ret = "",
diff --git a/src/selector-sizzle.js b/src/selector-sizzle.js
index dcee45f..ff7bc70 100644
--- a/src/selector-sizzle.js
+++ b/src/selector-sizzle.js
@@ -1,14 +1,19 @@
 define( [
 	"./core",
-	"sizzle"
+	"../external/sizzle/dist/sizzle"
 ], function( jQuery, Sizzle ) {
 
+"use strict";
+
 jQuery.find = Sizzle;
 jQuery.expr = Sizzle.selectors;
+
+// Deprecated
 jQuery.expr[ ":" ] = jQuery.expr.pseudos;
 jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
 jQuery.text = Sizzle.getText;
 jQuery.isXMLDoc = Sizzle.isXML;
 jQuery.contains = Sizzle.contains;
+jQuery.escapeSelector = Sizzle.escape;
 
 } );
diff --git a/src/selector.js b/src/selector.js
index e13f585..2e0c17e 100644
--- a/src/selector.js
+++ b/src/selector.js
@@ -1 +1,3 @@
-define( [ "./selector-sizzle" ], function() {} );
+define( [ "./selector-sizzle" ], function() {
+	"use strict";
+} );
diff --git a/src/serialize.js b/src/serialize.js
index a0b9484..35dcf04 100644
--- a/src/serialize.js
+++ b/src/serialize.js
@@ -6,6 +6,8 @@ define( [
 	"./attributes/prop"
 ], function( jQuery, rcheckableType ) {
 
+"use strict";
+
 var
 	rbracket = /\[\]$/,
 	rCRLF = /\r?\n/g,
@@ -55,17 +57,16 @@ function buildParams( prefix, obj, traditional, add ) {
 jQuery.param = function( a, traditional ) {
 	var prefix,
 		s = [],
-		add = function( key, value ) {
+		add = function( key, valueOrFunction ) {
 
-			// If value is a function, invoke it and return its value
-			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
-			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
-		};
+			// If value is a function, invoke it and use its return value
+			var value = jQuery.isFunction( valueOrFunction ) ?
+				valueOrFunction() :
+				valueOrFunction;
 
-	// Set traditional to true for jQuery <= 1.3.2 behavior.
-	if ( traditional === undefined ) {
-		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
-	}
+			s[ s.length ] = encodeURIComponent( key ) + "=" +
+				encodeURIComponent( value == null ? "" : value );
+		};
 
 	// If an array was passed in, assume that it is an array of form elements.
 	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
@@ -110,13 +111,17 @@ jQuery.fn.extend( {
 		.map( function( i, elem ) {
 			var val = jQuery( this ).val();
 
-			return val == null ?
-				null :
-				jQuery.isArray( val ) ?
-					jQuery.map( val, function( val ) {
-						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
-					} ) :
-					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+			if ( val == null ) {
+				return null;
+			}
+
+			if ( jQuery.isArray( val ) ) {
+				return jQuery.map( val, function( val ) {
+					return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+				} );
+			}
+
+			return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
 		} ).get();
 	}
 } );
diff --git a/src/traversing.js b/src/traversing.js
index 8525c0a..50cd2d6 100644
--- a/src/traversing.js
+++ b/src/traversing.js
@@ -9,6 +9,8 @@ define( [
 	"./selector"
 ], function( jQuery, indexOf, dir, siblings, rneedsContext ) {
 
+"use strict";
+
 var rparentsprev = /^(?:parents|prev(?:Until|All))/,
 
 	// Methods guaranteed to produce a unique set when starting from a unique set
diff --git a/src/traversing/findFilter.js b/src/traversing/findFilter.js
index f3a929e..3bd036a 100644
--- a/src/traversing/findFilter.js
+++ b/src/traversing/findFilter.js
@@ -5,33 +5,39 @@ define( [
 	"../selector"
 ], function( jQuery, indexOf, rneedsContext ) {
 
+"use strict";
+
 var risSimple = /^.[^:#\[\.,]*$/;
 
 // Implement the identical functionality for filter and not
 function winnow( elements, qualifier, not ) {
 	if ( jQuery.isFunction( qualifier ) ) {
 		return jQuery.grep( elements, function( elem, i ) {
-			/* jshint -W018 */
 			return !!qualifier.call( elem, i, elem ) !== not;
 		} );
-
 	}
 
+	// Single element
 	if ( qualifier.nodeType ) {
 		return jQuery.grep( elements, function( elem ) {
 			return ( elem === qualifier ) !== not;
 		} );
-
 	}
 
-	if ( typeof qualifier === "string" ) {
-		if ( risSimple.test( qualifier ) ) {
-			return jQuery.filter( qualifier, elements, not );
-		}
+	// Arraylike of elements (jQuery, arguments, Array)
+	if ( typeof qualifier !== "string" ) {
+		return jQuery.grep( elements, function( elem ) {
+			return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
+		} );
+	}
 
-		qualifier = jQuery.filter( qualifier, elements );
+	// Simple selector that can be filtered directly, removing non-Elements
+	if ( risSimple.test( qualifier ) ) {
+		return jQuery.filter( qualifier, elements, not );
 	}
 
+	// Complex selector, compare the two sets, removing non-Elements
+	qualifier = jQuery.filter( qualifier, elements );
 	return jQuery.grep( elements, function( elem ) {
 		return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1;
 	} );
@@ -44,18 +50,19 @@ jQuery.filter = function( expr, elems, not ) {
 		expr = ":not(" + expr + ")";
 	}
 
-	return elems.length === 1 && elem.nodeType === 1 ?
-		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
-		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
-			return elem.nodeType === 1;
-		} ) );
+	if ( elems.length === 1 && elem.nodeType === 1 ) {
+		return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
+	}
+
+	return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+		return elem.nodeType === 1;
+	} ) );
 };
 
 jQuery.fn.extend( {
 	find: function( selector ) {
-		var i,
+		var i, ret,
 			len = this.length,
-			ret = [],
 			self = this;
 
 		if ( typeof selector !== "string" ) {
@@ -68,11 +75,13 @@ jQuery.fn.extend( {
 			} ) );
 		}
 
+		ret = this.pushStack( [] );
+
 		for ( i = 0; i < len; i++ ) {
 			jQuery.find( selector, self[ i ], ret );
 		}
 
-		return this.pushStack( len > 1 ? jQuery.uniqueSort( ret ) : ret );
+		return len > 1 ? jQuery.uniqueSort( ret ) : ret;
 	},
 	filter: function( selector ) {
 		return this.pushStack( winnow( this, selector || [], false ) );
diff --git a/src/traversing/var/dir.js b/src/traversing/var/dir.js
index b98fdca..366a823 100644
--- a/src/traversing/var/dir.js
+++ b/src/traversing/var/dir.js
@@ -2,6 +2,8 @@ define( [
 	"../../core"
 ], function( jQuery ) {
 
+"use strict";
+
 return function( elem, dir, until ) {
 	var matched = [],
 		truncate = until !== undefined;
diff --git a/src/traversing/var/rneedsContext.js b/src/traversing/var/rneedsContext.js
index f57fd9d..d0663ce 100644
--- a/src/traversing/var/rneedsContext.js
+++ b/src/traversing/var/rneedsContext.js
@@ -2,5 +2,7 @@ define( [
 	"../../core",
 	"../../selector"
 ], function( jQuery ) {
+	"use strict";
+
 	return jQuery.expr.match.needsContext;
 } );
diff --git a/src/traversing/var/siblings.js b/src/traversing/var/siblings.js
index 8a8880b..952629d 100644
--- a/src/traversing/var/siblings.js
+++ b/src/traversing/var/siblings.js
@@ -1,5 +1,7 @@
 define( function() {
 
+"use strict";
+
 return function( n, elem ) {
 	var matched = [];
 
diff --git a/src/var/ObjectFunctionString.js b/src/var/ObjectFunctionString.js
new file mode 100644
index 0000000..f9e850f
--- /dev/null
+++ b/src/var/ObjectFunctionString.js
@@ -0,0 +1,7 @@
+define( [
+	"./fnToString"
+], function( fnToString ) {
+	"use strict";
+
+	return fnToString.call( Object );
+} );
diff --git a/src/var/arr.js b/src/var/arr.js
index 3fd3640..84713d8 100644
--- a/src/var/arr.js
+++ b/src/var/arr.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return [];
 } );
diff --git a/src/var/class2type.js b/src/var/class2type.js
index 3a6932e..4365d46 100644
--- a/src/var/class2type.js
+++ b/src/var/class2type.js
@@ -1,4 +1,5 @@
 define( function() {
+	"use strict";
 
 	// [[Class]] -> type pairs
 	return {};
diff --git a/src/var/concat.js b/src/var/concat.js
index 9787bea..e47c19d 100644
--- a/src/var/concat.js
+++ b/src/var/concat.js
@@ -1,5 +1,7 @@
 define( [
 	"./arr"
 ], function( arr ) {
+	"use strict";
+
 	return arr.concat;
 } );
diff --git a/src/var/document.js b/src/var/document.js
index bb94284..dd3939d 100644
--- a/src/var/document.js
+++ b/src/var/document.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return window.document;
 } );
diff --git a/src/var/documentElement.js b/src/var/documentElement.js
index e0c0aea..0e3f8b4 100644
--- a/src/var/documentElement.js
+++ b/src/var/documentElement.js
@@ -1,5 +1,7 @@
 define( [
 	"./document"
 ], function( document ) {
+	"use strict";
+
 	return document.documentElement;
 } );
diff --git a/src/var/fnToString.js b/src/var/fnToString.js
new file mode 100644
index 0000000..18c43ff
--- /dev/null
+++ b/src/var/fnToString.js
@@ -0,0 +1,7 @@
+define( [
+	"./hasOwn"
+], function( hasOwn ) {
+	"use strict";
+
+	return hasOwn.toString;
+} );
diff --git a/src/var/getProto.js b/src/var/getProto.js
new file mode 100644
index 0000000..965fab8
--- /dev/null
+++ b/src/var/getProto.js
@@ -0,0 +1,5 @@
+define( function() {
+	"use strict";
+
+	return Object.getPrototypeOf;
+} );
diff --git a/src/var/hasOwn.js b/src/var/hasOwn.js
index 92f323e..44ab680 100644
--- a/src/var/hasOwn.js
+++ b/src/var/hasOwn.js
@@ -1,5 +1,7 @@
 define( [
 	"./class2type"
 ], function( class2type ) {
+	"use strict";
+
 	return class2type.hasOwnProperty;
 } );
diff --git a/src/var/indexOf.js b/src/var/indexOf.js
index 5283841..8320b98 100644
--- a/src/var/indexOf.js
+++ b/src/var/indexOf.js
@@ -1,5 +1,7 @@
 define( [
 	"./arr"
 ], function( arr ) {
+	"use strict";
+
 	return arr.indexOf;
 } );
diff --git a/src/var/pnum.js b/src/var/pnum.js
index 7fd9f66..6f06d73 100644
--- a/src/var/pnum.js
+++ b/src/var/pnum.js
@@ -1,3 +1,5 @@
 define( function() {
+	"use strict";
+
 	return ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
 } );
diff --git a/src/var/push.js b/src/var/push.js
index bca1a58..9465620 100644
--- a/src/var/push.js
+++ b/src/var/push.js
@@ -1,5 +1,7 @@
 define( [
 	"./arr"
 ], function( arr ) {
+	"use strict";
+
 	return arr.push;
 } );
diff --git a/src/var/rcssNum.js b/src/var/rcssNum.js
index 408f5e2..4214b14 100644
--- a/src/var/rcssNum.js
+++ b/src/var/rcssNum.js
@@ -2,6 +2,8 @@ define( [
 	"../var/pnum"
 ], function( pnum ) {
 
+"use strict";
+
 return new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
 
 } );
diff --git a/src/var/rnothtmlwhite.js b/src/var/rnothtmlwhite.js
new file mode 100644
index 0000000..30604db
--- /dev/null
+++ b/src/var/rnothtmlwhite.js
@@ -0,0 +1,8 @@
+define( function() {
+	"use strict";
+
+	// Only count HTML whitespace
+	// Other whitespace should count in values
+	// https://html.spec.whatwg.org/multipage/infrastructure.html#space-character
+	return ( /[^\x20\t\r\n\f]+/g );
+} );
diff --git a/src/var/rnotwhite.js b/src/var/rnotwhite.js
deleted file mode 100644
index 6067728..0000000
--- a/src/var/rnotwhite.js
+++ /dev/null
@@ -1,3 +0,0 @@
-define( function() {
-	return ( /\S+/g );
-} );
diff --git a/src/var/slice.js b/src/var/slice.js
index d8206d3..915f837 100644
--- a/src/var/slice.js
+++ b/src/var/slice.js
@@ -1,5 +1,7 @@
 define( [
 	"./arr"
 ], function( arr ) {
+	"use strict";
+
 	return arr.slice;
 } );
diff --git a/src/var/support.js b/src/var/support.js
index 3db9b67..094d0ae 100644
--- a/src/var/support.js
+++ b/src/var/support.js
@@ -1,4 +1,5 @@
 define( function() {
+	"use strict";
 
 	// All support tests are defined in their respective modules.
 	return {};
diff --git a/src/var/toString.js b/src/var/toString.js
index 80ac7f1..ff4ecdc 100644
--- a/src/var/toString.js
+++ b/src/var/toString.js
@@ -1,5 +1,7 @@
 define( [
 	"./class2type"
 ], function( class2type ) {
+	"use strict";
+
 	return class2type.toString;
 } );
diff --git a/src/wrap.js b/src/wrap.js
index 69c7cbf..88b9bb5 100644
--- a/src/wrap.js
+++ b/src/wrap.js
@@ -5,6 +5,8 @@ define( [
 	"./traversing" // parent, contents
 ], function( jQuery ) {
 
+"use strict";
+
 jQuery.fn.extend( {
 	wrapAll: function( html ) {
 		var wrap;
diff --git a/src/intro.js b/src/wrapper.js
similarity index 64%
rename from src/intro.js
rename to src/wrapper.js
index 2918495..af90fb2 100644
--- a/src/intro.js
+++ b/src/wrapper.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-unused-vars*/
 /*!
  * jQuery JavaScript Library v at VERSION
  * https://jquery.com/
@@ -11,10 +12,12 @@
  *
  * Date: @DATE
  */
+( function( global, factory ) {
 
-(function( global, factory ) {
+	"use strict";
 
 	if ( typeof module === "object" && typeof module.exports === "object" ) {
+
 		// For CommonJS and CommonJS-like environments where a proper `window`
 		// is present, execute the factory and get jQuery.
 		// For environments that do not have a `window` with a `document`
@@ -35,10 +38,16 @@
 	}
 
 // Pass this if window is not defined yet
-}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
+// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
+// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
+// enough that all such attempts are guarded in a try block.
+"use strict";
+
+// @CODE
+// build.js inserts compiled jQuery here
 
-// Support: Firefox 18+
-// Can't be in strict mode, several libs including ASP.NET trace
-// the stack via arguments.caller.callee and Firefox dies if
-// you try to trace through "use strict" call chains. (#13335)
-//"use strict";
+return jQuery;
+} );
diff --git a/test/.jshintrc b/test/.eslintrc.json
similarity index 54%
rename from test/.jshintrc
rename to test/.eslintrc.json
index 5516f84..744de89 100644
--- a/test/.jshintrc
+++ b/test/.eslintrc.json
@@ -1,24 +1,13 @@
 {
-	"boss": true,
-	"curly": true,
-	"eqeqeq": true,
-	"eqnull": true,
-	"expr": true,
-	"immed": true,
-	"noarg": true,
-	"quotmark": "double",
-	"undef": true,
-	"unused": true,
-
-	"es3": true,
-
-	"evil": true,
-	"sub": true,
-
-	"browser": true,
-	"devel": true,
-	"wsh": true,
-
+	"env": {
+		"browser": true
+	},
+	// Support: IE <=9 only, Android <=4.0 only
+	// The above browsers are failing a lot of tests in the ES5
+	// test suite at http://test262.ecmascript.org.
+	"parserOptions": {
+		"ecmaVersion": 3
+	},
 	"globals": {
 		"require": false,
 		"define": false,
@@ -38,7 +27,6 @@
 		"url": false,
 		"t": false,
 		"q": false,
-
 		"jQuery": true,
 		"sinon": true,
 		"amdDefined": true,
@@ -52,5 +40,19 @@
 		"original$": true,
 		"baseURL": true,
 		"externalHost": true
+	},
+	"rules": {
+		// See https://github.com/eslint/eslint/issues/2342
+		"no-unused-vars": "off",
+
+		// Too much errors
+		"max-len": "off",
+		"brace-style": "off",
+		"key-spacing": "off",
+		"camelcase": "off",
+
+		// Not really too much - waiting autofix features for these rules
+		"lines-around-comment": "off",
+		"dot-notation": "off"
 	}
 }
diff --git a/test/data/ajax/onunload.html b/test/data/ajax/onunload.html
index eb1bca2..ec6e4b7 100644
--- a/test/data/ajax/onunload.html
+++ b/test/data/ajax/onunload.html
@@ -4,6 +4,7 @@
 	<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
 	<title>onunload ajax requests (#14379)</title>
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 	<form id="navigate" action="../iframe.html"></form>
@@ -17,7 +18,7 @@
 					ajaxStatus = status;
 				}
 			});
-			parent.iframeCallback( ajaxStatus );
+			startIframeTest( ajaxStatus );
 		});
 
 		jQuery(function() {
diff --git a/test/data/ajax/unreleasedXHR.html b/test/data/ajax/unreleasedXHR.html
index 8a41c49..3eedaab 100644
--- a/test/data/ajax/unreleasedXHR.html
+++ b/test/data/ajax/unreleasedXHR.html
@@ -4,13 +4,14 @@
 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 <title>Attempt to block tests because of dangling XHR requests (IE)</title>
 <script src="../../jquery.js"></script>
+<script src="../iframeTest.js"></script>
 <script type="text/javascript">
 window.onunload = function() {};
 jQuery(function() {
 	setTimeout(function() {
 		var parent = window.parent;
 		document.write("");
-		parent.iframeCallback();
+		startIframeTest();
 	}, 200 );
 	var number = 50;
 	while( number-- ) {
diff --git a/test/data/core/aliased.html b/test/data/core/aliased.html
index ed06606..87b5871 100644
--- a/test/data/core/aliased.html
+++ b/test/data/core/aliased.html
@@ -10,15 +10,16 @@
 		};
 	</script>
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 	<form>
 		<input type="text" id="nodeName"/>
 	</form>
 	<script>
-		jQuery(function() {
-			window.parent.iframeCallback( errors );
-		});
+		jQuery( function() {
+			startIframeTest( errors );
+		} );
 	</script>
 </body>
 </html>
diff --git a/test/data/core/cc_on.html b/test/data/core/cc_on.html
index 131e2e8..5db1891 100644
--- a/test/data/core/cc_on.html
+++ b/test/data/core/cc_on.html
@@ -13,10 +13,11 @@
 		};
 	</script>
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 	<script>
-		window.parent.iframeCallback( cc_on, errors, jQuery );
+		startIframeTest( cc_on, errors );
 	</script>
 </body>
 </html>
diff --git a/test/data/core/dynamic_ready.html b/test/data/core/dynamic_ready.html
index 3b29c11..1db068b 100644
--- a/test/data/core/dynamic_ready.html
+++ b/test/data/core/dynamic_ready.html
@@ -4,6 +4,7 @@
 	<meta charset="utf-8">
 	<script src="../../jquery.js"></script>
 	<script>var $j = jQuery.noConflict();</script>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 <iframe id="dont_return" src="dont_return.php"></iframe>
@@ -20,14 +21,14 @@
 			$(function () {
 				clearTimeout( timeoutId );
 				if ( !timeoutFired ) {
-					window.parent.iframeCallback( true );
+					startIframeTest( true );
 				}
 			});
 		});
 
 		timeoutId = setTimeout(function () {
 			timeoutFired = true;
-			window.parent.iframeCallback( false );
+			startIframeTest( false );
 		}, 10000);
 	});
 </script>
diff --git a/test/data/core/onready.html b/test/data/core/onready.html
index 1e8f127..88ba94a 100644
--- a/test/data/core/onready.html
+++ b/test/data/core/onready.html
@@ -8,6 +8,7 @@
 		window.onready = function() { error = "Called window.onready"; };
 	</script>
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 	<form>
@@ -16,7 +17,7 @@
 	<script>
 		jQuery(function() {
 			setTimeout( function() {
-				window.parent.iframeCallback( error );
+				startIframeTest( error );
 			});
 		});
 	</script>
diff --git a/test/data/css/cssWidthBeforeDocReady.html b/test/data/css/cssWidthBeforeDocReady.html
index 12e954d..4ebc2c9 100644
--- a/test/data/css/cssWidthBeforeDocReady.html
+++ b/test/data/css/cssWidthBeforeDocReady.html
@@ -4,7 +4,6 @@
 	<meta charset="utf-8">
 	<style>
 		#test {
-			-webkit-box-sizing: border-box;
 			box-sizing: border-box;
 			width: 100px;
 			height: 100px;
@@ -15,8 +14,9 @@
 <body>
 <div id="test"></div>
 <script src="../../jquery.js"></script>
+<script src="../iframeTest.js"></script>
 <script>
-	window.parent.iframeCallback( jQuery( "#test" ).css( 'width' ) );
+	startIframeTest( jQuery( "#test" ).css( 'width' ) );
 </script>
 </body>
 </html>
diff --git a/test/data/data/dataAttrs.html b/test/data/data/dataAttrs.html
index 5e6e442..785150e 100644
--- a/test/data/data/dataAttrs.html
+++ b/test/data/data/dataAttrs.html
@@ -4,9 +4,10 @@
 	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 	<title>IE11 onpageshow strangeness (#14894)</title>
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 	<script>
 		$(function(){
-			window.parent.iframeCallback( $( "body" ).data().result );
+			startIframeTest( $( "body" ).data().result );
 		});
 	</script>
 </head>
diff --git a/test/data/dimensions/documentLarge.html b/test/data/dimensions/documentLarge.html
index a6598fc..9d6a729 100644
--- a/test/data/dimensions/documentLarge.html
+++ b/test/data/dimensions/documentLarge.html
@@ -8,10 +8,14 @@
 			height: 1000px;
 		}
 	</style>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 	<div>
 		<script src="../../jquery.js"></script>
+		<script>
+			jQuery( startIframeTest );
+		</script>
 	</div>
 </body>
 </html>
diff --git a/test/data/event/focusElem.html b/test/data/event/focusElem.html
index a9a9765..1940e8b 100644
--- a/test/data/event/focusElem.html
+++ b/test/data/event/focusElem.html
@@ -5,12 +5,13 @@
 	<title>.focus() (activeElement access #13393)</title>
 
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 	<a href="#" id="frame-link"></a>
 	<script>
 		jQuery( "#frame-link" ).trigger( "focus" );
-		window.parent.iframeCallback( true );
+		startIframeTest( true );
 	</script>
 </body>
 </html>
diff --git a/test/data/event/focusinCrossFrame.html b/test/data/event/focusinCrossFrame.html
index 487f8de..6dd187e 100644
--- a/test/data/event/focusinCrossFrame.html
+++ b/test/data/event/focusinCrossFrame.html
@@ -5,13 +5,14 @@
 	<title>focusin event cross-frame (#14180)</title>
 
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 </head>
 <body>
 	<input type="text" id="frame-input" />
 	<script>
 		// Call parent when this frame is fully loaded, it will mess with #frame-input
 		jQuery( window ).one( "load", function() {
-			window.parent.iframeCallback( document );
+			startIframeTest();
 		});
 	</script>
 </body>
diff --git a/test/data/event/interactiveReady.html b/test/data/event/interactiveReady.html
index 77b3710..3fb25f9 100644
--- a/test/data/event/interactiveReady.html
+++ b/test/data/event/interactiveReady.html
@@ -4,13 +4,14 @@
 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 <title>Test case for gh-2100</title>
 <script src="../../jquery.js"></script>
+<script src="../iframeTest.js"></script>
 </head>
 <body>
 
 <script type="text/javascript">
-jQuery( document ).ready(function () {
-	window.parent.iframeCallback( jQuery("#container").length === 1 );
-});
+jQuery( function () {
+	startIframeTest( jQuery("#container").length === 1 );
+} );
 </script>
 
 <!-- external resources that come before elements trick
diff --git a/test/data/event/promiseReady.html b/test/data/event/promiseReady.html
index 17b6e7f..c6e0862 100644
--- a/test/data/event/promiseReady.html
+++ b/test/data/event/promiseReady.html
@@ -4,10 +4,11 @@
 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 <title>Test case for jQuery ticket #11470</title>
 <script src="../../jquery.js"></script>
+<script src="../iframeTest.js"></script>
 <script type="text/javascript">
 jQuery.when( jQuery.ready ).done(function() {
 	jQuery("body").append("<div>modifying DOM</div>");
-	window.parent.iframeCallback( $("div").text() === "modifying DOM" );
+	startIframeTest( $("div").text() === "modifying DOM" );
 });
 </script>
 </head>
diff --git a/test/data/event/syncReady.html b/test/data/event/syncReady.html
index dfa9ac3..61a50e4 100644
--- a/test/data/event/syncReady.html
+++ b/test/data/event/syncReady.html
@@ -4,12 +4,13 @@
 <meta http-equiv="content-type" content="text/html; charset=utf-8">
 <title>Test case for jQuery ticket #10067</title>
 <script src="../../jquery.js"></script>
+<script src="../iframeTest.js"></script>
 </head>
 <body>
 
 <script type="text/javascript">
 jQuery( document ).ready(function () {
-	window.parent.iframeCallback( jQuery('#container').length === 1 );
+	startIframeTest( jQuery('#container').length === 1 );
 });
 </script>
 
diff --git a/test/data/event/triggerunload.html b/test/data/event/triggerunload.html
index a7879c7..02be59d 100644
--- a/test/data/event/triggerunload.html
+++ b/test/data/event/triggerunload.html
@@ -1,6 +1,7 @@
 <!doctype html>
 <html>
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 	<script>
 		var called = false,
 			error = false;
@@ -12,7 +13,7 @@
 			return "maybe";
 		}).on( "load", function( event ) {
 			$( window ).triggerHandler( "beforeunload" );
-			window.parent.iframeCallback( called && !error );
+			startIframeTest( called && !error );
 		});
 	</script>
 </html>
diff --git a/test/data/iframeTest.js b/test/data/iframeTest.js
new file mode 100644
index 0000000..877e884
--- /dev/null
+++ b/test/data/iframeTest.js
@@ -0,0 +1,8 @@
+
+window.startIframeTest = function() {
+	var args = Array.prototype.slice.call( arguments );
+
+	// Note: jQuery may be undefined if page did not load it
+	args.unshift( window.jQuery, window, document );
+	window.parent.iframeCallback.apply( null, args );
+};
diff --git a/test/data/manipulation/iframe-denied.html b/test/data/manipulation/iframe-denied.html
index 14df26a..e74872a 100644
--- a/test/data/manipulation/iframe-denied.html
+++ b/test/data/manipulation/iframe-denied.html
@@ -7,6 +7,7 @@
 	<body>
 		<div id="qunit-fixture"></div>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script>
 			var script = document.getElementsByTagName( "script" )[ 0 ],
 				div = document.createElement( "div" ),
@@ -25,7 +26,7 @@
 					error = e;
 				}
 
-				window.parent.iframeCallback({
+				startIframeTest({
 					status: success,
 					description: "buildFragment sets the context without throwing an exception" +
 						( error ? ": " + error : "" )
diff --git a/test/data/manipulation/scripts-context.html b/test/data/manipulation/scripts-context.html
index 6958453..1b75e3a 100644
--- a/test/data/manipulation/scripts-context.html
+++ b/test/data/manipulation/scripts-context.html
@@ -7,12 +7,9 @@
 	<body>
 		<div id="qunit-fixture"></div>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script>
-			window.parent.iframeCallback(
-				window,
-				document.body,
-				"<script>window.scriptTest = true;<\x2fscript>"
-			);
+			startIframeTest();
 		</script>
 	</body>
 </html>
diff --git a/test/data/offset/absolute.html b/test/data/offset/absolute.html
index 58f0cbd..9d87004 100644
--- a/test/data/offset/absolute.html
+++ b/test/data/offset/absolute.html
@@ -16,6 +16,7 @@
 			#positionTest { position: absolute; }
 		</style>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script type="text/javascript" charset="utf-8">
 			jQuery(function($) {
 				$(".absolute").click(function() {
@@ -24,6 +25,7 @@
 					$(this).css({ top: pos.top, left: pos.left });
 					return false;
 				});
+				startIframeTest();
 			});
 		</script>
 	</head>
diff --git a/test/data/offset/body.html b/test/data/offset/body.html
index c0de297..75757d7 100644
--- a/test/data/offset/body.html
+++ b/test/data/offset/body.html
@@ -10,12 +10,14 @@
 			#firstElement { width: 50px; height: 50px; background: green; }
 		</style>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script type="text/javascript" charset="utf-8">
 			jQuery(function($) {
 				$("body").click(function() {
 					$("marker").css( $(this).offset() );
 					return false;
 				});
+				startIframeTest();
 			});
 		</script>
 	</head>
diff --git a/test/data/offset/fixed.html b/test/data/offset/fixed.html
index c695ba4..48a2c47 100644
--- a/test/data/offset/fixed.html
+++ b/test/data/offset/fixed.html
@@ -13,6 +13,7 @@
 			#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
 		</style>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script type="text/javascript" charset="utf-8">
 			jQuery(function($) {
 				window.scrollTo(1000,1000);
@@ -20,6 +21,7 @@
 					$("#marker").css( $(this).offset() );
 					return false;
 				});
+				startIframeTest();
 			});
 		</script>
 	</head>
diff --git a/test/data/offset/relative.html b/test/data/offset/relative.html
index f88c82a..288fd4e 100644
--- a/test/data/offset/relative.html
+++ b/test/data/offset/relative.html
@@ -12,6 +12,7 @@
 			#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
 		</style>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script type="text/javascript" charset="utf-8">
 			jQuery(function($) {
 				$(".relative").click(function() {
@@ -20,6 +21,7 @@
 					$(this).css({ position: 'absolute', top: pos.top, left: pos.left });
 					return false;
 				});
+				startIframeTest();
 			});
 		</script>
 	</head>
diff --git a/test/data/offset/scroll.html b/test/data/offset/scroll.html
index c8c0802..28cade1 100644
--- a/test/data/offset/scroll.html
+++ b/test/data/offset/scroll.html
@@ -15,6 +15,7 @@
 			#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
 		</style>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script type="text/javascript" charset="utf-8">
 			jQuery(function($) {
 				window.scrollTo(1000,1000);
@@ -24,6 +25,7 @@
 					$("#marker").css( $(this).offset() );
 					return false;
 				});
+				startIframeTest();
 			});
 		</script>
 	</head>
diff --git a/test/data/offset/static.html b/test/data/offset/static.html
index f8cafa8..1f4c3dc 100644
--- a/test/data/offset/static.html
+++ b/test/data/offset/static.html
@@ -11,6 +11,7 @@
 			#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
 		</style>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script type="text/javascript" charset="utf-8">
 			jQuery(function($) {
 				$(".static").click(function() {
@@ -19,6 +20,7 @@
 					$(this).css({ position: 'absolute', top: pos.top, left: pos.left });
 					return false;
 				});
+				startIframeTest();
 			});
 		</script>
 	</head>
diff --git a/test/data/offset/table.html b/test/data/offset/table.html
index ff2b857..eeac19b 100644
--- a/test/data/offset/table.html
+++ b/test/data/offset/table.html
@@ -11,12 +11,14 @@
 			#marker { position: absolute; border: 2px solid #000; width: 50px; height: 50px; background: #ccc; }
 		</style>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script type="text/javascript" charset="utf-8">
 			jQuery(function($) {
 				$("table, th, td").click(function() {
 					$("#marker").css( $(this).offset() );
 					return false;
 				});
+				startIframeTest();
 			});
 		</script>
 	</head>
diff --git a/test/data/selector/html5_selector.html b/test/data/selector/html5_selector.html
index 30f25c9..35c583c 100644
--- a/test/data/selector/html5_selector.html
+++ b/test/data/selector/html5_selector.html
@@ -5,7 +5,7 @@
 	<title>jQuery selector - attributes</title>
 
 	<script src="../../jquery.js"></script>
-
+	<script src="../iframeTest.js"></script>
 	<script id="script1"
 			defer
 			async></script>
@@ -15,6 +15,8 @@
 		document.createElement('audio');
 		document.createElement('article');
 		document.createElement('details');
+
+		jQuery( startIframeTest );
 	</script>
 </head>
 <body>
diff --git a/test/data/selector/sizzle_cache.html b/test/data/selector/sizzle_cache.html
index 1055c75..513390c 100644
--- a/test/data/selector/sizzle_cache.html
+++ b/test/data/selector/sizzle_cache.html
@@ -5,6 +5,7 @@
 	<title>jQuery selector - sizzle cache</title>
 
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 	<script>
 		document.write(
 			"<script>var $cached = jQuery.noConflict(true);<\x2Fscript>" +
@@ -17,5 +18,8 @@
 	<div class="test">
 		<a href="#" id="collision">Worlds collide</a>
 	</div>
+	<script>
+		jQuery( startIframeTest );
+	</script>
 </body>
 </html>
diff --git a/test/data/support/bodyBackground.html b/test/data/support/bodyBackground.html
index cac0998..d95f39d 100644
--- a/test/data/support/bodyBackground.html
+++ b/test/data/support/bodyBackground.html
@@ -18,10 +18,11 @@
 <body>
 	<div>
 		<script src="../../jquery.js"></script>
+		<script src="../iframeTest.js"></script>
 		<script src="getComputedSupport.js"></script>
 	</div>
 	<script>
-		window.parent.iframeCallback( jQuery( "body" ).css( "backgroundColor" ),
+		startIframeTest( jQuery( "body" ).css( "backgroundColor" ),
 				getComputedSupport( jQuery.support ) );
 	</script>
 </body>
diff --git a/test/data/support/csp.js b/test/data/support/csp.js
index 5ebdcea..4d55bda 100644
--- a/test/data/support/csp.js
+++ b/test/data/support/csp.js
@@ -1,3 +1,3 @@
 jQuery( function() {
-	parent.iframeCallback( getComputedSupport( jQuery.support ) );
+	startIframeTest( getComputedSupport( jQuery.support ) );
 } );
diff --git a/test/data/support/csp.php b/test/data/support/csp.php
index 0783e17..4460002 100644
--- a/test/data/support/csp.php
+++ b/test/data/support/csp.php
@@ -9,6 +9,7 @@
 	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 	<title>CSP Test Page</title>
 	<script src="../../jquery.js"></script>
+	<script src="../iframeTest.js"></script>
 	<script src="csp.js"></script>
 	<script src="getComputedSupport.js"></script>
 </head>
diff --git a/test/data/test3.html b/test/data/test3.html
index a7f862a..446d5c1 100644
--- a/test/data/test3.html
+++ b/test/data/test3.html
@@ -2,3 +2,4 @@
 <div class="user">This is a user</div>
 <div class="teacher">This is a teacher</div>
 <div id="superuser">This is a superuser</div>
+<div id="whitespace\xA0">This is a superuser with non-HTML whitespace</div>
diff --git a/test/data/testinit.js b/test/data/testinit.js
index ca8f433..ef210e7 100644
--- a/test/data/testinit.js
+++ b/test/data/testinit.js
@@ -1,7 +1,6 @@
-/*jshint multistr:true, quotmark:false */
+/* eslint no-multi-str: "off" */
 
-var fireNative, originaljQuery, original$,
-	baseURL = "",
+var baseURL = "",
 	supportjQuery = this.jQuery,
 
 	// see RFC 2606
@@ -12,8 +11,8 @@ this.isLocal = window.location.protocol === "file:";
 
 // Setup global variables before loading jQuery for testing .noConflict()
 supportjQuery.noConflict( true );
-originaljQuery = this.jQuery = undefined;
-original$ = this.$ = "replaced";
+window.originaljQuery = this.jQuery = undefined;
+window.original$ = this.$ = "replaced";
 
 /**
  * Returns an array of elements with the given IDs
@@ -32,57 +31,81 @@ this.q = function() {
 
 /**
  * Asserts that a select matches the given IDs
- * @param {String} a - Assertion name
- * @param {String} b - Sizzle selector
- * @param {String} c - Array of ids to construct what is expected
- * @example t("Check for something", "//[a]", ["foo", "bar"]);
- * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'bar'
+ * @param {String} message - Assertion name
+ * @param {String} selector - Sizzle selector
+ * @param {String} expectedIds - Array of ids to construct what is expected
+ * @param {(String|Node)=document} context - Selector context
+ * @example match("Check for something", "p", ["foo", "bar"]);
  */
-QUnit.assert.t = function( a, b, c ) {
-	var f = jQuery( b ).get(),
+function match( message, selector, expectedIds, context ) {
+	var f = jQuery( selector, context ).get(),
 		s = "",
 		i = 0;
 
 	for ( ; i < f.length; i++ ) {
-		s += ( s && "," ) + '"' + f[ i ].id + '"';
+		s += ( s && "," ) + "\"" + f[ i ].id + "\"";
 	}
 
-	this.deepEqual( f, q.apply( q, c ), a + " (" + b + ")" );
+	this.deepEqual( f, q.apply( q, expectedIds ), message + " (" + selector + ")" );
+}
+
+/**
+ * Asserts that a select matches the given IDs.
+ * The select is not bound by a context.
+ * @param {String} message - Assertion name
+ * @param {String} selector - Sizzle selector
+ * @param {String} expectedIds - Array of ids to construct what is expected
+ * @example t("Check for something", "p", ["foo", "bar"]);
+ */
+QUnit.assert.t = function( message, selector, expectedIds ) {
+	match( message, selector, expectedIds, undefined );
+};
+
+/**
+ * Asserts that a select matches the given IDs.
+ * The select is performed within the `#qunit-fixture` context.
+ * @param {String} message - Assertion name
+ * @param {String} selector - Sizzle selector
+ * @param {String} expectedIds - Array of ids to construct what is expected
+ * @example selectInFixture("Check for something", "p", ["foo", "bar"]);
+ */
+QUnit.assert.selectInFixture = function( message, selector, expectedIds ) {
+	match( message, selector, expectedIds, "#qunit-fixture" );
 };
 
 this.createDashboardXML = function() {
-	var string = '<?xml version="1.0" encoding="UTF-8"?> \
+	var string = "<?xml version='1.0' encoding='UTF-8'?> \
 	<dashboard> \
-		<locations class="foo"> \
-			<location for="bar" checked="different"> \
-				<infowindowtab normal="ab" mixedCase="yes"> \
-					<tab title="Location"><![CDATA[blabla]]></tab> \
-					<tab title="Users"><![CDATA[blublu]]></tab> \
+		<locations class='foo'> \
+			<location for='bar' checked='different'> \
+				<infowindowtab normal='ab' mixedCase='yes'> \
+					<tab title='Location'><![CDATA[blabla]]></tab> \
+					<tab title='Users'><![CDATA[blublu]]></tab> \
 				</infowindowtab> \
 			</location> \
 		</locations> \
-	</dashboard>';
+	</dashboard>";
 
 	return jQuery.parseXML( string );
 };
 
 this.createWithFriesXML = function() {
-	var string = '<?xml version="1.0" encoding="UTF-8"?> \
-	<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" \
-		xmlns:xsd="http://www.w3.org/2001/XMLSchema" \
-		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> \
+	var string = "<?xml version='1.0' encoding='UTF-8'?> \
+	<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' \
+		xmlns:xsd='http://www.w3.org/2001/XMLSchema' \
+		xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> \
 		<soap:Body> \
-			<jsconf xmlns="http://{{ externalHost }}/ns1"> \
-				<response xmlns:ab="http://{{ externalHost }}/ns2"> \
+			<jsconf xmlns='http://{{ externalHost }}/ns1'> \
+				<response xmlns:ab='http://{{ externalHost }}/ns2'> \
 					<meta> \
-						<component id="seite1" class="component"> \
-							<properties xmlns:cd="http://{{ externalHost }}/ns3"> \
-								<property name="prop1"> \
+						<component id='seite1' class='component'> \
+							<properties xmlns:cd='http://{{ externalHost }}/ns3'> \
+								<property name='prop1'> \
 									<thing /> \
 									<value>1</value> \
 								</property> \
-								<property name="prop2"> \
-									<thing att="something" /> \
+								<property name='prop2'> \
+									<thing att='something' /> \
 								</property> \
 								<foo_bar>foo</foo_bar> \
 							</properties> \
@@ -91,7 +114,7 @@ this.createWithFriesXML = function() {
 				</response> \
 			</jsconf> \
 		</soap:Body> \
-	</soap:Envelope>';
+	</soap:Envelope>";
 
 	return jQuery.parseXML( string.replace( /\{\{\s*externalHost\s*\}\}/g, externalHost ) );
 };
@@ -99,7 +122,7 @@ this.createWithFriesXML = function() {
 this.createXMLFragment = function() {
 	var xml, frag;
 	if ( window.ActiveXObject ) {
-		xml = new ActiveXObject( "msxml2.domdocument" );
+		xml = new window.ActiveXObject( "msxml2.domdocument" );
 	} else {
 		xml = document.implementation.createDocument( "", "", null );
 	}
@@ -111,7 +134,7 @@ this.createXMLFragment = function() {
 	return frag;
 };
 
-fireNative = document.createEvent ?
+window.fireNative = document.createEvent ?
 	function( node, type ) {
 		var event = document.createEvent( "HTMLEvents" );
 
@@ -208,46 +231,15 @@ this.ajaxTest = function( title, expect, options ) {
 	} );
 };
 
-this.testIframe = function( fileName, name, fn ) {
-	QUnit.test( name, function( assert ) {
-		var done = assert.async();
-
-		// load fixture in iframe
-		var iframe = loadFixture(),
-			win = iframe.contentWindow,
-			interval = setInterval( function() {
-				if ( win && win.jQuery && win.jQuery.isReady ) {
-					clearInterval( interval );
-
-					// call actual tests passing the correct jQuery instance to use
-					fn.call( this, win.jQuery, win, win.document, assert );
-					done();
-					document.body.removeChild( iframe );
-					iframe = null;
-				}
-			}, 15 );
-	} );
-
-	function loadFixture() {
-		var src = url( "./data/" + fileName + ".html" ),
-			iframe = jQuery( "<iframe />" ).appendTo( "body" )[ 0 ];
-			iframe.style.cssText = "width: 500px; height: 500px; position: absolute; " +
-				"top: -600px; left: -600px; visibility: hidden;";
-
-		iframe.contentWindow.location = src;
-		return iframe;
-	}
-};
-
-this.testIframeWithCallback = function( title, fileName, func ) {
-	QUnit.test( title, 1, function( assert ) {
+this.testIframe = function( title, fileName, func ) {
+	QUnit.test( title, function( assert ) {
 		var iframe;
 		var done = assert.async();
 
 		window.iframeCallback = function() {
 			var args = Array.prototype.slice.call( arguments );
 
-			args.push( assert );
+			args.unshift( assert );
 
 			setTimeout( function() {
 				this.iframeCallback = undefined;
@@ -255,7 +247,6 @@ this.testIframeWithCallback = function( title, fileName, func ) {
 				func.apply( this, args );
 				func = function() {};
 				iframe.remove();
-
 				done();
 			} );
 		};
@@ -269,9 +260,10 @@ this.iframeCallback = undefined;
 // Tests are always loaded async
 QUnit.config.autostart = false;
 this.loadTests = function() {
+
 	// Leverage QUnit URL parsing to detect testSwarm environment and "basic" testing mode
-	var loadSwarm = ( QUnit.urlParams[ "swarmURL" ] + "" ).indexOf( "http" ) === 0,
-		basicTests = ( QUnit.urlParams[ "module" ] + "" ) === "basic";
+	QUnit.isSwarm = ( QUnit.urlParams.swarmURL + "" ).indexOf( "http" ) === 0;
+	QUnit.basicTests = ( QUnit.urlParams.module + "" ) === "basic";
 
 	// Get testSubproject from testrunner first
 	require( [ "data/testrunner.js" ], function() {
@@ -311,7 +303,7 @@ this.loadTests = function() {
 			var dep = tests[ i++ ];
 
 			if ( dep ) {
-				if ( !basicTests || i === 1 ) {
+				if ( !QUnit.basicTests || i === 1 ) {
 					require( [ dep ], loadDep );
 
 				// Support: Android 2.3 only
@@ -328,12 +320,10 @@ this.loadTests = function() {
 				/**
 				 * Run in noConflict mode
 				 */
-				if ( jQuery.noConflict ) {
-					jQuery.noConflict();
-				}
+				jQuery.noConflict();
 
 				// Load the TestSwarm listener if swarmURL is in the address.
-				if ( loadSwarm ) {
+				if ( QUnit.isSwarm ) {
 					require( [ "http://swarm.jquery.org/js/inject.js?" + ( new Date() ).getTime() ],
 					function() {
 						QUnit.start();
diff --git a/test/data/testsuite.css b/test/data/testsuite.css
index 253bea9..f6225df 100644
--- a/test/data/testsuite.css
+++ b/test/data/testsuite.css
@@ -53,11 +53,11 @@ div.medopacity {
 }
 
 div.nowidth {
-	width: 0px;
+	width: 0;
 }
 
 div.noheight {
-	height: 0px;
+	height: 0;
 }
 
 div.noopacity {
@@ -130,3 +130,15 @@ section { background:#f0f; display:block; }
 .inline { display: inline; }
 .list-item { display: list-item; }
 .hidden, .none { display: none; }
+
+#div-gh-2836 {
+	position: relative;
+	overflow: auto;
+	height: 100px;
+}
+#div-gh-2836 div {
+	position: relative;
+	height: 100%;
+	padding: 0;
+	margin: 0;
+}
diff --git a/test/index.html b/test/index.html
index fa515c8..1e6343d 100644
--- a/test/index.html
+++ b/test/index.html
@@ -14,7 +14,7 @@
 
 	<script src="../external/qunit/qunit.js"></script>
 	<script src="../external/qunit-assert-step/qunit-assert-step.js"></script>
-	<script src="../external/sinon/sinon-1.14.1.js"></script>
+	<script src="../external/sinon/sinon.js"></script>
 	<script src="../external/npo/npo.js"></script>
 	<script src="../external/requirejs/require.js"></script>
 	<!-- See testinit for the list of tests -->
diff --git a/test/jquery.js b/test/jquery.js
index 233e696..8119d3f 100644
--- a/test/jquery.js
+++ b/test/jquery.js
@@ -18,10 +18,7 @@
 	// This detection allows AMD tests to be run in an iframe
 	if ( QUnit.urlParams.amd && window.QUnit ) {
 		require.config( {
-			baseUrl: path,
-			paths: {
-				sizzle: "external/sizzle/dist/sizzle"
-			}
+			baseUrl: path
 		} );
 		src = "src/jquery";
 
diff --git a/test/node_smoke_tests/.eslintrc.json b/test/node_smoke_tests/.eslintrc.json
new file mode 100644
index 0000000..0877d24
--- /dev/null
+++ b/test/node_smoke_tests/.eslintrc.json
@@ -0,0 +1,7 @@
+{
+	"env": {
+		"es6": true
+	},
+	"extends" : "../../.eslintrc.json",
+	"root": true
+}
diff --git a/test/node_smoke_tests/.jshintrc b/test/node_smoke_tests/.jshintrc
deleted file mode 100644
index 1445c7b..0000000
--- a/test/node_smoke_tests/.jshintrc
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-	"boss": true,
-	"curly": true,
-	"eqeqeq": true,
-	"eqnull": true,
-	"expr": true,
-	"immed": true,
-	"noarg": true,
-	"quotmark": "double",
-	"undef": true,
-	"unused": true,
-
-	"node": true
-}
diff --git a/test/node_smoke_tests/iterable_with_symbol_polyfill.js b/test/node_smoke_tests/iterable_with_symbol_polyfill.js
index dd377f1..34701d6 100644
--- a/test/node_smoke_tests/iterable_with_symbol_polyfill.js
+++ b/test/node_smoke_tests/iterable_with_symbol_polyfill.js
@@ -1,5 +1,3 @@
-/* jshint esnext: true */
-
 "use strict";
 
 var assert = require( "assert" );
diff --git a/test/node_smoke_tests/lib/ensure_iterability_es6.js b/test/node_smoke_tests/lib/ensure_iterability_es6.js
index ebe6853..8e2396d 100644
--- a/test/node_smoke_tests/lib/ensure_iterability_es6.js
+++ b/test/node_smoke_tests/lib/ensure_iterability_es6.js
@@ -1,5 +1,3 @@
-/* jshint esnext: true */
-
 "use strict";
 
 var assert = require( "assert" );
diff --git a/test/promises_aplus_adapters/.eslintrc.json b/test/promises_aplus_adapters/.eslintrc.json
new file mode 100644
index 0000000..d117757
--- /dev/null
+++ b/test/promises_aplus_adapters/.eslintrc.json
@@ -0,0 +1,4 @@
+{
+	"extends": "../../.eslintrc.json",
+	"root": true
+}
diff --git a/test/promises_aplus_adapter.js b/test/promises_aplus_adapters/deferred.js
similarity index 76%
rename from test/promises_aplus_adapter.js
rename to test/promises_aplus_adapters/deferred.js
index c7440b9..31af166 100644
--- a/test/promises_aplus_adapter.js
+++ b/test/promises_aplus_adapters/deferred.js
@@ -1,5 +1,3 @@
-/* jshint node: true */
-
 "use strict";
 
 require( "jsdom" ).env( "", function( errors, window ) {
@@ -8,9 +6,9 @@ require( "jsdom" ).env( "", function( errors, window ) {
 		return;
 	}
 
-	var jQuery = require( ".." )( window );
+	var jQuery = require( "../../" )( window );
 
-	exports.deferred = function() {
+	module.exports.deferred = function() {
 		var deferred = jQuery.Deferred();
 
 		return {
diff --git a/test/promises_aplus_adapters/when.js b/test/promises_aplus_adapters/when.js
new file mode 100644
index 0000000..adde1a1
--- /dev/null
+++ b/test/promises_aplus_adapters/when.js
@@ -0,0 +1,49 @@
+"use strict";
+
+require( "jsdom" ).env( "", function( errors, window ) {
+	if ( errors ) {
+		console.error( errors );
+		return;
+	}
+
+	var jQuery = require( "../../" )( window );
+
+	module.exports.deferred = function() {
+		var adopted, promised,
+			obj = {
+				resolve: function() {
+					if ( !adopted ) {
+						adopted = jQuery.when.apply( jQuery, arguments );
+						if ( promised ) {
+							adopted.then( promised.resolve, promised.reject );
+						}
+					}
+					return adopted;
+				},
+				reject: function( value ) {
+					if ( !adopted ) {
+						adopted = jQuery.when( jQuery.Deferred().reject( value ) );
+						if ( promised ) {
+							adopted.then( promised.resolve, promised.reject );
+						}
+					}
+					return adopted;
+				},
+
+				// A manually-constructed thenable that works even if calls precede resolve/reject
+				promise: {
+					then: function() {
+						if ( !adopted ) {
+							if ( !promised ) {
+								promised = jQuery.Deferred();
+							}
+							return promised.then.apply( promised, arguments );
+						}
+						return adopted.then.apply( adopted, arguments );
+					}
+				}
+			};
+
+		return obj;
+	};
+} );
diff --git a/test/unit/ajax.js b/test/unit/ajax.js
index 3e00afd..681aa46 100644
--- a/test/unit/ajax.js
+++ b/test/unit/ajax.js
@@ -28,7 +28,7 @@ QUnit.module( "ajax", {
 
 //----------- jQuery.ajax()
 
-	testIframeWithCallback(
+	testIframe(
 		"XMLHttpRequest - Attempt to block tests because of dangling XHR requests (IE)",
 		"ajax/unreleasedXHR.html",
 		function( assert ) {
@@ -99,7 +99,7 @@ QUnit.module( "ajax", {
 				assert.ok( true, "success" );
 			},
 			fail: function() {
-				if (jQuery.support.cors === false) {
+				if ( jQuery.support.cors === false ) {
 					assert.ok( true, "fail" );
 				}
 			},
@@ -249,9 +249,8 @@ QUnit.module( "ajax", {
 				"Nullable": null,
 				"undefined": undefined
 
-				// Support: Firefox
+				// Support: IE 9 - 11, Edge 12 - 13+
 				// Not all browsers allow empty-string headers
-				// https://bugzilla.mozilla.org/show_bug.cgi?id=815299
 				//"Empty": ""
 			},
 			success: function( data, _, xhr ) {
@@ -374,6 +373,64 @@ QUnit.module( "ajax", {
 		];
 	} );
 
+	ajaxTest( "jQuery.ajax() - traditional param encoding", 4, function( assert ) {
+		return [
+			{
+				url: "/",
+				traditional: true,
+				data: {
+					"devo": "hat",
+					"answer": 42,
+					"quux": "a space"
+				},
+				beforeSend: function( xhr, settings ) {
+					assert.equal( settings.url, "/?devo=hat&answer=42&quux=a%20space", "Simple case" );
+					return false;
+				},
+				error: true
+			},
+			{
+				url: "/",
+				traditional: true,
+				data: {
+					"a": [ 1, 2, 3 ],
+					"b[]": [ "b1", "b2" ]
+				},
+				beforeSend: function( xhr, settings ) {
+					assert.equal( settings.url, "/?a=1&a=2&a=3&b%5B%5D=b1&b%5B%5D=b2", "Arrays" );
+					return false;
+				},
+				error: true
+			},
+			{
+				url: "/",
+				traditional: true,
+				data: {
+					"a": [ [ 1, 2 ], [ 3, 4 ], 5 ]
+				},
+				beforeSend: function( xhr, settings ) {
+					assert.equal( settings.url, "/?a=1%2C2&a=3%2C4&a=5", "Nested arrays" );
+					return false;
+				},
+				error: true
+			},
+			{
+				url: "/",
+				traditional: true,
+				data: {
+					"a": [ "w", [ [ "x", "y" ], "z" ] ]
+				},
+				cache: false,
+				beforeSend: function( xhr, settings ) {
+					var url = settings.url.replace( /\d{3,}/, "" );
+					assert.equal( url, "/?a=w&a=x%2Cy%2Cz&_=", "Cache-buster" );
+					return false;
+				},
+				error: true
+			}
+		];
+	} );
+
 	ajaxTest( "jQuery.ajax() - cross-domain detection", 8, function( assert ) {
 		function request( url, title, crossDomainOrOptions ) {
 			return jQuery.extend( {
@@ -771,8 +828,9 @@ QUnit.module( "ajax", {
 		} ), "generic" );
 	} );
 
-	ajaxTest( "jQuery.ajax() - cache", 12, function( assert ) {
-		var re = /_=(.*?)(&|$)/g;
+	ajaxTest( "jQuery.ajax() - cache", 28, function( assert ) {
+		var re = /_=(.*?)(&|$)/g,
+			rootUrl = "data/text.php";
 
 		function request( url, title ) {
 			return {
@@ -780,6 +838,11 @@ QUnit.module( "ajax", {
 				cache: false,
 				beforeSend: function() {
 					var parameter, tmp;
+
+					// URL sanity check
+					assert.equal( this.url.indexOf( rootUrl ), 0, "root url not mangled: " + this.url );
+					assert.equal( /\&.*\?/.test( this.url ), false, "parameter delimiters in order" );
+
 					while ( ( tmp = re.exec( this.url ) ) ) {
 						assert.strictEqual( parameter, undefined, title + ": only one 'no-cache' parameter" );
 						parameter = tmp[ 1 ];
@@ -793,27 +856,31 @@ QUnit.module( "ajax", {
 
 		return [
 			request(
-				"data/text.php",
-				"no parameter"
+				rootUrl,
+				"no query"
 			),
 			request(
-				"data/text.php?pizza=true",
+				rootUrl + "?",
+				"empty query"
+			),
+			request(
+				rootUrl + "?pizza=true",
 				"1 parameter"
 			),
 			request(
-				"data/text.php?_=tobereplaced555",
+				rootUrl + "?_=tobereplaced555",
 				"_= parameter"
 			),
 			request(
-				"data/text.php?pizza=true&_=tobereplaced555",
+				rootUrl + "?pizza=true&_=tobereplaced555",
 				"1 parameter and _="
 			),
 			request(
-				"data/text.php?_=tobereplaced555&tv=false",
+				rootUrl + "?_=tobereplaced555&tv=false",
 				"_= and 1 parameter"
 			),
 			request(
-				"data/text.php?name=David&_=tobereplaced555&washere=true",
+				rootUrl + "?name=David&_=tobereplaced555&washere=true",
 				"2 parameters surrounding _="
 			)
 		];
@@ -1120,7 +1187,7 @@ QUnit.module( "ajax", {
 			},
 			success: function( text ) {
 				assert.strictEqual( typeof text, "string", "json wasn't auto-determined" );
-				var json = jQuery.parseJSON( text );
+				var json = JSON.parse( 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" );
@@ -1680,11 +1747,11 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
 } else {
 
 	// No built-in support for binary data, but it's easy to add via a prefilter
-	jQuery.ajaxPrefilter( "arraybuffer", function ( s ) {
+	jQuery.ajaxPrefilter( "arraybuffer", function( s ) {
 		s.xhrFields = { responseType: "arraybuffer" };
 		s.responseFields.arraybuffer = "response";
 		s.converters[ "binary arraybuffer" ] = true;
-	});
+	} );
 
 	ajaxTest( "gh-2498 - jQuery.ajax() - binary data shouldn't throw an exception", 2, function( assert ) {
 		return {
@@ -1699,16 +1766,6 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
 }
 
 	QUnit.asyncTest( "#11743 - jQuery.ajax() - script, throws exception", 1, function( assert ) {
-
-		// Support: Android 2.3 only
-		// Android 2.3 doesn't fire the window.onerror handler, just accept the reality there.
-		if ( /android 2\.3/i.test( navigator.userAgent ) ) {
-			assert.ok( true, "Test skipped, Android 2.3 doesn't fire window.onerror for " +
-				"errors in dynamically included scripts" );
-			QUnit.start();
-			return;
-		}
-
 		var onerror = window.onerror;
 		window.onerror = function() {
 			assert.ok( true, "Exception thrown" );
@@ -1768,13 +1825,6 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
 				var parsedXML = jQuery( jQuery.parseXML( "<tab title=\"Added\">blibli</tab>" ) ).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" );
@@ -1844,10 +1894,10 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
 		};
 	} );
 
-	testIframeWithCallback(
+	testIframe(
 		"#14379 - jQuery.ajax() on unload",
 		"ajax/onunload.html",
-		function( status, assert ) {
+		function( assert, jQuery, window, document, status ) {
 			assert.expect( 1 );
 			assert.strictEqual( status, "success", "Request completed" );
 		}
@@ -1921,7 +1971,7 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
 			url: url( "data/ajax/content-type.php" ),
 			data: {
 				"content-type": "test/jsontest",
-				"response": JSON.stringify({test: "test"})
+				"response": JSON.stringify( { test: "test" } )
 			},
 			success: function( result ) {
 				assert.strictEqual(
@@ -2103,6 +2153,25 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
 	);
 
 	QUnit.test(
+		"jQuery#load() - should resolve with correct context", 2,
+		function( assert ) {
+			var done = assert.async();
+			var ps = jQuery( "<p></p><p></p>" );
+			var i = 0;
+
+			ps.appendTo( "#qunit-fixture" );
+
+			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() );
@@ -2258,6 +2327,17 @@ if ( typeof window.ArrayBuffer === "undefined" || typeof new XMLHttpRequest().re
 		} );
 	} );
 
+	// Selector should be trimmed to avoid leading spaces (#14773)
+	// Selector should include any valid non-HTML whitespace (#3003)
+	QUnit.test( "jQuery.fn.load( URL_SELECTOR with non-HTML whitespace(#3003) )", function( assert ) {
+		assert.expect( 1 );
+		var done = assert.async();
+		jQuery( "#first" ).load( "data/test3.html   #whitespace\\\\xA0 ", function() {
+			assert.strictEqual( jQuery( this ).children( "div" ).length, 1, "Verify that specific elements were injected" );
+			done();
+		} );
+	} );
+
 	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" );
diff --git a/test/unit/attributes.js b/test/unit/attributes.js
index 7f92d35..1284ffd 100644
--- a/test/unit/attributes.js
+++ b/test/unit/attributes.js
@@ -128,8 +128,8 @@ QUnit.test( "attr(String)", function( assert ) {
 	assert.equal( jQuery( option ).prop( "selected" ), true, "Make sure that a single option is selected, even when in an optgroup." );
 
 	$img = jQuery( "<img style='display:none' width='215' height='53' src='data/1x1.jpg'/>" ).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." );
+	assert.equal( $img.attr( "width" ), "215", "Retrieve width attribute on an element with display:none." );
+	assert.equal( $img.attr( "height" ), "53", "Retrieve height attribute on an element with display:none." );
 
 	// Check for style support
 	styleElem = jQuery( "<div/>" ).appendTo( "#qunit-fixture" ).css( {
@@ -220,7 +220,8 @@ QUnit.test( "attr(String, Function)", function( assert ) {
 QUnit.test( "attr(Hash)", function( assert ) {
 	assert.expect( 3 );
 	var pass = true;
-	jQuery( "div" ).attr( {
+
+	jQuery( "#qunit-fixture div" ).attr( {
 		"foo": "baz",
 		"zoo": "ping"
 	} ).each( function() {
@@ -258,7 +259,7 @@ QUnit.test( "attr(String, Object)", function( assert ) {
 		attributeNode, commentNode, textNode, obj,
 		table, td, j, type,
 		check, thrown, button, $radio, $radios, $svg,
-		div = jQuery( "div" ).attr( "foo", "bar" ),
+		div = jQuery( "#qunit-fixture div" ).attr( "foo", "bar" ),
 		i = 0,
 		fail = false;
 
@@ -486,15 +487,24 @@ QUnit.test( "attr(non-ASCII)", function( assert ) {
 QUnit.test( "attr - extending the boolean attrHandle", function( assert ) {
 	assert.expect( 1 );
 	var called = false,
-		_handle = jQuery.expr.attrHandle.checked || $.noop;
+		origAttrHandleHadChecked = "checked" in jQuery.expr.attrHandle,
+		origAttrHandleChecked = jQuery.expr.attrHandle.checked,
+		_handle = origAttrHandleChecked || $.noop;
 	jQuery.expr.attrHandle.checked = function() {
 		called = true;
 		_handle.apply( this, arguments );
 	};
-	jQuery( "input" ).attr( "checked" );
+	jQuery( "#qunit-fixture input" ).attr( "checked" );
 	called = false;
-	jQuery( "input" ).attr( "checked" );
+	jQuery( "#qunit-fixture input" ).attr( "checked" );
 	assert.ok( called, "The boolean attrHandle does not drop custom attrHandles" );
+
+	if ( origAttrHandleHadChecked ) {
+		jQuery.expr.attrHandle.checked = origAttrHandleChecked;
+	} else {
+		delete jQuery.expr.attrHandle.checked;
+	}
+
 } );
 
 QUnit.test( "attr(String, Object) - Loaded via XML document", function( assert ) {
@@ -647,6 +657,28 @@ QUnit.test( "removeAttr(Multi String, variable space width)", function( assert )
 	} );
 } );
 
+QUnit.test( "removeAttr(Multi String, non-HTML whitespace is valid in attribute names (gh-3003)", function( assert ) {
+	assert.expect( 8 );
+
+	var div = jQuery( "<div id='a' data-\xA0='b' title='c' rel='d'></div>" );
+	var tests = {
+		id: "a",
+		"data-\xA0": "b",
+		title: "c",
+		rel: "d"
+	};
+
+	jQuery.each( tests, function( key, val ) {
+		assert.equal( div.attr( key ), val, "Attribute \"" + key + "\" exists, and has a value of \"" + val + "\"" );
+	} );
+
+	div.removeAttr( "id   data-\xA0 title  rel  " );
+
+	jQuery.each( tests, function( key ) {
+		assert.equal( div.attr( key ), undefined, "Attribute \"" + key + "\" was removed" );
+	} );
+} );
+
 QUnit.test( "prop(String, Object)", function( assert ) {
 
 	assert.expect( 17 );
@@ -748,9 +780,9 @@ QUnit.test( "prop('tabindex')", function( assert ) {
 
 QUnit.test( "image.prop( 'tabIndex' )", function( assert ) {
 	assert.expect( 1 );
-	var image = jQuery("<img src='data/1x1.jpg' />")
-		.appendTo("#qunit-fixture");
-	assert.equal( image.prop("tabIndex" ), -1, "tabIndex on image" );
+	var image = jQuery( "<img src='data/1x1.jpg' />" )
+		.appendTo( "#qunit-fixture" );
+	assert.equal( image.prop( "tabIndex" ), -1, "tabIndex on image" );
 } );
 
 QUnit.test( "prop('tabindex', value)", function( assert ) {
@@ -796,6 +828,37 @@ QUnit.test( "prop('tabindex', value)", function( assert ) {
 	assert.equal( clone[ 0 ].getAttribute( "tabindex" ), "1", "set tabindex on cloned element" );
 } );
 
+QUnit.test( "option.prop('selected', true) affects select.selectedIndex (gh-2732)", function( assert ) {
+	assert.expect( 2 );
+
+	function addOptions( $elem ) {
+		return $elem.append(
+			jQuery( "<option/>" ).val( "a" ).text( "One" ),
+			jQuery( "<option/>" ).val( "b" ).text( "Two" ),
+			jQuery( "<option/>" ).val( "c" ).text( "Three" )
+		)
+		.find( "[value=a]" ).prop( "selected", true ).end()
+		.find( "[value=c]" ).prop( "selected", true ).end();
+	}
+
+	var $optgroup,
+		$select = jQuery( "<select/>" );
+
+	// Check select with options
+	addOptions( $select ).appendTo( "#qunit-fixture" );
+	$select.find( "[value=b]" ).prop( "selected", true );
+	assert.equal( $select[ 0 ].selectedIndex, 1, "Setting option selected affects selectedIndex" );
+
+	$select.empty();
+
+	// Check select with optgroup
+	$optgroup = jQuery( "<optgroup/>" );
+	addOptions( $optgroup ).appendTo( $select );
+	$select.find( "[value=b]" ).prop( "selected", true );
+
+	assert.equal( $select[ 0 ].selectedIndex, 1, "Setting option in optgroup selected affects selectedIndex" );
+} );
+
 QUnit.test( "removeProp(String)", function( assert ) {
 	assert.expect( 6 );
 	var attributeNode = document.createAttribute( "irrelevant" ),
@@ -996,7 +1059,7 @@ QUnit.test( "val(Function)", function( assert ) {
 QUnit.test( "val(Array of Numbers) (Bug #7123)", function( assert ) {
 	assert.expect( 4 );
 	jQuery( "#form" ).append( "<input type='checkbox' name='arrayTest' value='1' /><input type='checkbox' name='arrayTest' value='2' /><input type='checkbox' name='arrayTest' value='3' checked='checked' /><input type='checkbox' name='arrayTest' value='4' />" );
-	var elements = jQuery( "input[name=arrayTest]" ).val( [ 1, 2 ] );
+	var elements = jQuery( "#form 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" );
@@ -1075,6 +1138,75 @@ QUnit.test( "val(select) after form.reset() (Bug #2551)", function( assert ) {
 	jQuery( "#kk" ).remove();
 } );
 
+QUnit.test( "select.val(space characters) (gh-2978)", function( assert ) {
+	assert.expect( 37 );
+
+	var $select = jQuery( "<select/>" ).appendTo( "#qunit-fixture" ),
+		spaces = {
+			"\\t": {
+				html: "	",
+				val: "\t"
+			},
+			"\\n": {
+				html: "
",
+				val: "\n"
+			},
+			"\\r": {
+				html: "
",
+				val: "\r"
+			},
+			"\\f": "\f",
+			"space": " ",
+			"\\u00a0": "\u00a0",
+			"\\u1680": "\u1680"
+		},
+		html = "";
+	jQuery.each( spaces, function( key, obj ) {
+		var value = obj.html || obj;
+		html += "<option value='attr" + value + "'></option>";
+		html += "<option value='at" + value + "tr'></option>";
+		html += "<option value='" + value + "attr'></option>";
+	} );
+	$select.html( html );
+
+	jQuery.each( spaces, function( key, obj ) {
+		var val = obj.val || obj;
+		$select.val( "attr" + val );
+		assert.equal( $select.val(), "attr" + val, "Value ending with space character (" + key + ") selected (attr)" );
+
+		$select.val( "at" + val + "tr" );
+		assert.equal( $select.val(), "at" + val + "tr", "Value with space character (" + key + ") in the middle selected (attr)" );
+
+		$select.val( val + "attr" );
+		assert.equal( $select.val(), val + "attr", "Value starting with space character (" + key + ") selected (attr)" );
+	} );
+
+	jQuery.each( spaces, function( key, obj ) {
+		var value = obj.html || obj,
+			val = obj.val || obj;
+		html = "";
+		html += "<option>text" + value + "</option>";
+		html += "<option>te" + value + "xt</option>";
+		html += "<option>" + value + "text</option>";
+		$select.html( html );
+
+
+		if ( /^\\u/.test( key ) ) {
+			$select.val( val + "text" );
+			assert.equal( $select.val(), val + "text", "Value with non-HTML space character at beginning is not stripped (" + key + ") selected (" + key + "text)" );
+			$select.val( "te" + val + "xt" );
+			assert.equal( $select.val(), "te" + val + "xt", "Value with non-space whitespace character (" + key + ") in the middle selected (text)" );
+			$select.val( "text" + val );
+			assert.equal( $select.val(), "text" + val, "Value with non-HTML space character at end is not stripped (" + key + ") selected (text" + key + ")" );
+		} else {
+			$select.val( "text" );
+			assert.equal( $select.val(), "text", "Value with HTML space character at beginning or end is stripped (" + key + ") selected (text)" );
+			$select.val( "te xt" );
+			assert.equal( $select.val(), "te xt", "Value with space character (" + key + ") in the middle selected (text)" );
+		}
+	} );
+} );
+
 var testAddClass = function( valueObj, assert ) {
 	assert.expect( 9 );
 
@@ -1435,6 +1567,24 @@ QUnit.test( "addClass, removeClass, hasClass on many elements", function( assert
 		"Did not find a class when not present" );
 } );
 
+QUnit.test( "addClass, removeClass, hasClass on elements with classes with non-HTML whitespace (gh-3072, gh-3003)", function( assert ) {
+	assert.expect( 9 );
+
+	var $elem = jQuery( "<div class='&#xA0;test'></div>" );
+
+	function testMatches() {
+		assert.ok( $elem.is( ".\\A0 test" ), "Element matches with collapsed space" );
+		assert.ok( $elem.is( ".\\A0test" ), "Element matches with non-breaking space" );
+		assert.ok( $elem.hasClass( "\xA0test" ), "Element has class with non-breaking space" );
+	}
+
+	testMatches();
+	$elem.addClass( "foo" );
+	testMatches();
+	$elem.removeClass( "foo" );
+	testMatches();
+} );
+
 QUnit.test( "contents().hasClass() returns correct values", function( assert ) {
 	assert.expect( 2 );
 
@@ -1491,17 +1641,22 @@ QUnit.test( "option value not trimmed when setting via parent select", function(
 	assert.equal( jQuery( "<select><option> 2</option></select>" ).val( "2" ).val(), "2" );
 } );
 
-QUnit.test( "Insignificant white space returned for $(option).val() (#14858)", function( assert ) {
-	assert.expect( 3 );
+QUnit.test( "Insignificant white space returned for $(option).val() (#14858, gh-2978)", function( assert ) {
+	assert.expect( 16 );
 
 	var val = jQuery( "<option></option>" ).val();
 	assert.equal( val.length, 0, "Empty option should have no value" );
 
-	val = jQuery( "<option>  </option>" ).val();
-	assert.equal( val.length, 0, "insignificant white-space returned for value" );
+	jQuery.each( [ " ", "\n", "\t", "\f", "\r" ], function( i, character ) {
+		var val = jQuery( "<option>" + character + "</option>" ).val();
+		assert.equal( val.length, 0, "insignificant white-space returned for value" );
+
+		val = jQuery( "<option>" + character + "test" + character + "</option>" ).val();
+		assert.equal( val.length, 4, "insignificant white-space returned for value" );
 
-	val = jQuery( "<option>  test  </option>" ).val();
-	assert.equal( val.length, 4, "insignificant white-space returned for value" );
+		val = jQuery( "<option>te" + character + "st</option>" ).val();
+		assert.equal( val, "te st", "Whitespace is collapsed in values" );
+	} );
 } );
 
 QUnit.test( "SVG class manipulation (gh-2199)", function( assert ) {
@@ -1531,3 +1686,22 @@ QUnit.test( "SVG class manipulation (gh-2199)", function( assert ) {
 		assert.ok( !elem.hasClass( "awesome" ), "SVG element (" + this + ") toggles the class off" );
 	} );
 } );
+
+QUnit.test( "non-lowercase boolean attribute getters should not crash", function( assert ) {
+	assert.expect( 3 );
+
+	var elem = jQuery( "<input checked required autofocus type='checkbox'>" );
+
+	jQuery.each( {
+		checked: "Checked",
+		required: "requiRed",
+		autofocus: "AUTOFOCUS"
+	}, function( lowercased, original ) {
+		try {
+			assert.strictEqual( elem.attr( original ), lowercased,
+				"The '" + this + "' attribute getter should return the lowercased name" );
+		} catch ( e ) {
+			assert.ok( false, "The '" + this + "' attribute getter threw" );
+		}
+	} );
+} );
diff --git a/test/unit/basic.js b/test/unit/basic.js
index 49b518d..5a2f5ab 100644
--- a/test/unit/basic.js
+++ b/test/unit/basic.js
@@ -76,7 +76,7 @@ QUnit.test( "show/hide", function( assert ) {
 }
 
 QUnit.test( "core", function( assert ) {
-	assert.expect( 28 );
+	assert.expect( 27 );
 
 	var elem = jQuery( "<div></div><span></span>" );
 
@@ -135,8 +135,6 @@ QUnit.test( "core", function( assert ) {
 
 	assert.strictEqual( jQuery.parseHTML( "<div></div><span></span>" ).length,
 		2, "jQuery.parseHTML" );
-
-	assert.deepEqual( jQuery.parseJSON( "{\"a\": 2}" ), { a: 2 }, "jQuery.parseJON" );
 } );
 
 QUnit.test( "data", function( assert ) {
@@ -189,10 +187,7 @@ QUnit.test( "manipulation", function( assert ) {
 	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( "<span/>" ).html().toLowerCase(),
+		elem1.html( "<span/>" ).html(),
 		"<span></span>",
 		".html getter/setter"
 	);
@@ -205,10 +200,7 @@ QUnit.test( "manipulation", function( assert ) {
 	child.before( "<b/>" );
 
 	assert.strictEqual(
-
-		// Support: IE 8 only
-		// IE 8 prints tag names in upper case.
-		elem1.html().toLowerCase(),
+		elem1.html(),
 		"<div></div><b></b><span></span><a></a>",
 		".after/.before"
 	);
@@ -277,10 +269,7 @@ QUnit.test( "wrap", function( assert ) {
 	elem.find( "b" ).wrap( "<span>" );
 
 	assert.strictEqual(
-
-		// Support: IE 8 only
-		// IE 8 prints tag names in upper case.
-		elem.html().toLowerCase(),
+		elem.html(),
 		"<a><span><b></b></span></a><a></a>",
 		".wrap"
 	);
@@ -288,10 +277,7 @@ QUnit.test( "wrap", function( assert ) {
 	elem.find( "span" ).wrapInner( "<em>" );
 
 	assert.strictEqual(
-
-		// Support: IE 8 only
-		// IE 8 prints tag names in upper case.
-		elem.html().toLowerCase(),
+		elem.html(),
 		"<a><span><em><b></b></em></span></a><a></a>",
 		".wrapInner"
 	);
@@ -299,10 +285,7 @@ QUnit.test( "wrap", function( assert ) {
 	elem.find( "a" ).wrapAll( "<i>" );
 
 	assert.strictEqual(
-
-		// Support: IE 8 only
-		// IE 8 prints tag names in upper case.
-		elem.html().toLowerCase(),
+		elem.html(),
 		"<i><a><span><em><b></b></em></span></a><a></a></i>",
 		".wrapAll"
 	);
diff --git a/test/unit/core.js b/test/unit/core.js
index 5f7d41d..f5083b4 100644
--- a/test/unit/core.js
+++ b/test/unit/core.js
@@ -1,4 +1,12 @@
-QUnit.module( "core", { teardown: moduleTeardown } );
+QUnit.module( "core", {
+	setup: function() {
+		this.sandbox = sinon.sandbox.create();
+	},
+	teardown: function() {
+		this.sandbox.restore();
+		return moduleTeardown.apply( this, arguments );
+	}
+} );
 
 QUnit.test( "Basic requirements", function( assert ) {
 	assert.expect( 7 );
@@ -95,7 +103,7 @@ QUnit.test( "jQuery()", function( assert ) {
 	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." );
 
-	assert.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" );
 
 	assert.equal( jQuery( document.body ).get( 0 ), jQuery( "body" ).get( 0 ), "Test passing an html node to the factory" );
 
@@ -189,27 +197,24 @@ QUnit.test( "globalEval execution after script injection (#7862)", function( ass
 	assert.ok( window.strictEvalTest - now < 500, "Code executed synchronously" );
 } );
 
-// This is not run in AMD mode
-if ( jQuery.noConflict ) {
-	QUnit.test( "noConflict", function( assert ) {
-		assert.expect( 7 );
+QUnit.test( "noConflict", function( assert ) {
+	assert.expect( 7 );
 
-		var $$ = jQuery;
+	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." );
+	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 = $ = $$;
+	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." );
+	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 = $$;
-	} );
-}
+	window[ "jQuery" ] = jQuery = $$;
+} );
 
 QUnit.test( "trim", function( assert ) {
 	assert.expect( 13 );
@@ -274,7 +279,7 @@ QUnit.test( "type", function( assert ) {
 
 QUnit.test( "type for `Symbol`", function( assert ) {
 	// Prevent reference errors
-	if( typeof Symbol !== "function" ) {
+	if ( typeof Symbol !== "function" ) {
 		assert.expect( 0 );
 		return;
 	}
@@ -283,16 +288,30 @@ QUnit.test( "type for `Symbol`", function( assert ) {
 
 	assert.equal( jQuery.type( Symbol() ), "symbol", "Symbol" );
 	assert.equal( jQuery.type( Object( Symbol() ) ), "symbol", "Symbol" );
-});
+} );
 
 QUnit.asyncTest( "isPlainObject", function( assert ) {
-	assert.expect( 15 );
 
-	var pass, iframe, doc,
+	assert.expect( 23 );
+
+	var pass, iframe, doc, parentObj, childObj, deep,
 		fn = function() {};
 
 	// The use case that we want to match
 	assert.ok( jQuery.isPlainObject( {} ), "{}" );
+	assert.ok( jQuery.isPlainObject( new window.Object() ), "new Object" );
+	assert.ok( jQuery.isPlainObject( { constructor: fn } ),
+		"plain object with constructor property" );
+	assert.ok( jQuery.isPlainObject( { constructor: "foo" } ),
+		"plain object with primitive constructor property" );
+
+	parentObj = {};
+	childObj = Object.create( parentObj );
+	assert.ok( !jQuery.isPlainObject( childObj ), "Object.create({})" );
+	parentObj.foo = "bar";
+	assert.ok( !jQuery.isPlainObject( childObj ), "Object.create({...})" );
+	childObj.bar = "foo";
+	assert.ok( !jQuery.isPlainObject( childObj ), "extend(Object.create({...}), ...)" );
 
 	// Not objects shouldn't be matched
 	assert.ok( !jQuery.isPlainObject( "" ), "string" );
@@ -320,6 +339,14 @@ QUnit.asyncTest( "isPlainObject", function( assert ) {
 	// Again, instantiated objects shouldn't be matched
 	assert.ok( !jQuery.isPlainObject( new fn() ), "new fn" );
 
+	// Instantiated objects with primitive constructors shouldn't be matched
+	fn.prototype.constructor = "foo";
+	assert.ok( !jQuery.isPlainObject( new fn() ), "new fn with primitive constructor" );
+
+	// Deep object
+	deep = { "foo": { "baz": true }, "foo2": document };
+	assert.ok( jQuery.isPlainObject( deep ), "Object with objects is still plain" );
+
 	// DOM Element
 	assert.ok( !jQuery.isPlainObject( document.createElement( "div" ) ), "DOM Element" );
 
@@ -353,7 +380,6 @@ QUnit.asyncTest( "isPlainObject", function( assert ) {
 	}
 } );
 
-//
 QUnit[ typeof Symbol === "function" ? "test" : "skip" ]( "isPlainObject(Symbol)", function( assert ) {
 	assert.expect( 2 );
 
@@ -361,6 +387,23 @@ QUnit[ typeof Symbol === "function" ? "test" : "skip" ]( "isPlainObject(Symbol)"
 	assert.equal( jQuery.isPlainObject( Object( Symbol() ) ), false, "Symbol inside an object" );
 } );
 
+QUnit.test( "isPlainObject(localStorage)", function( assert ) {
+	assert.expect( 1 );
+
+	assert.equal( jQuery.isPlainObject( localStorage ), false );
+} );
+
+QUnit[ "assign" in Object ? "test" : "skip" ]( "isPlainObject(Object.assign(...))",
+	function( assert ) {
+		assert.expect( 1 );
+
+		var parentObj = { foo: "bar" };
+		var childObj = Object.assign( Object.create( parentObj ), { bar: "foo" } );
+
+		assert.ok( !jQuery.isPlainObject( childObj ), "isPlainObject(Object.assign(...))" );
+	}
+);
+
 
 QUnit.test( "isFunction", function( assert ) {
 	assert.expect( 19 );
@@ -449,7 +492,7 @@ QUnit.test( "isFunction", function( assert ) {
 } );
 
 QUnit.test( "isNumeric", function( assert ) {
-	assert.expect( 38 );
+	assert.expect( 43 );
 
 	var t = jQuery.isNumeric,
 		ToString = function( value ) {
@@ -464,9 +507,6 @@ QUnit.test( "isNumeric", function( assert ) {
 	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" );
@@ -474,8 +514,28 @@ QUnit.test( "isNumeric", function( assert ) {
 	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( "040" ), "Legacy octal integer literal string" );
+	assert.ok( t( "0xFF" ), "Hexadecimal integer literal string (0x...)" );
+	assert.ok( t( "0Xba" ), "Hexadecimal integer literal string (0X...)" );
+	assert.ok( t( 0xFFF ), "Hexadecimal integer literal" );
+
+	if ( +"0b1" === 1 ) {
+		assert.ok( t( "0b111110" ), "Binary integer literal string (0b...)" );
+		assert.ok( t( "0B111110" ), "Binary integer literal string (0B...)" );
+	} else {
+		assert.ok( true, "Browser does not support binary integer literal (0b...)" );
+		assert.ok( true, "Browser does not support binary integer literal (0B...)" );
+	}
+
+	if ( +"0o1" === 1 ) {
+		assert.ok( t( "0o76" ), "Octal integer literal string (0o...)" );
+		assert.ok( t( "0O76" ), "Octal integer literal string (0O...)" );
+	} else {
+		assert.ok( true, "Browser does not support octal integer literal (0o...)" );
+		assert.ok( true, "Browser does not support octal integer literal (0O...)" );
+	}
 
-	assert.equal( t( new ToString( "42" ) ), false, "Custom .toString returning number" );
+	assert.equal( t( new ToString( "42" ) ), false, "Only limited to strings and numbers" );
 	assert.equal( t( "" ), false, "Empty string" );
 	assert.equal( t( "        " ), false, "Whitespace characters string" );
 	assert.equal( t( "\t\t" ), false, "Tab characters string" );
@@ -913,7 +973,7 @@ QUnit.test( "jQuery.map", function( assert ) {
 	assert.ok( !result, "empty NodeList treated like array" );
 
 	result = jQuery.map( Array( 4 ), function( v, k ) {
-		return k % 2 ? k : [ k,k,k ];
+		return k % 2 ? k : [ k, k, k ];
 	} );
 	assert.equal( result.join( "" ), "00012223", "Array results flattened (#2616)" );
 } );
@@ -1065,7 +1125,7 @@ QUnit.test( "jQuery.grep(Array-like)", function( assert ) {
 		[],
 		"Satisfying elements absent, Array-like object used, and grep explicitly uninverted"
 	);
-});
+} );
 
 QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
 	assert.expect( 28 );
@@ -1133,7 +1193,6 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
 	ret = jQuery.extend( true, { "foo": 4 }, { "foo": new MyNumber( 5 ) } );
 	assert.ok( parseInt( ret.foo, 10 ) === 5, "Wrapped numbers copy correctly" );
 
-	nullUndef;
 	nullUndef = jQuery.extend( {}, options, { "xnumber2": null } );
 	assert.ok( nullUndef[ "xnumber2" ] === null, "Check to make sure null values are copied" );
 
@@ -1183,19 +1242,19 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
 QUnit.test( "jQuery.extend(Object, Object {created with \"defineProperties\"})", function( assert ) {
 	assert.expect( 2 );
 
-	var definedObj = Object.defineProperties({}, {
+	var definedObj = Object.defineProperties( {}, {
         "enumerableProp": {
-          get: function () {
+          get: function() {
             return true;
           },
           enumerable: true
         },
         "nonenumerableProp": {
-          get: function () {
+          get: function() {
             return true;
           }
         }
-      }),
+      } ),
       accessorObj = {};
 
 	jQuery.extend( accessorObj, definedObj );
@@ -1214,7 +1273,7 @@ QUnit.test( "jQuery.extend(true,{},{a:[], o:{}}); deep copy with array, followed
 		// If "copyIsArray" doesn't get reset to false, the check
 		// will evaluate true and enter the array copy block
 		// instead of the object copy block. Since the ternary in the
-		// "copyIsArray" block will will evaluate to false
+		// "copyIsArray" block will evaluate to false
 		// (check if operating on an array with ), this will be
 		// replaced by an empty array.
 		object: {}
@@ -1253,7 +1312,7 @@ QUnit.test( "jQuery.each(Object,Function)", function( assert ) {
 	assert.deepEqual( seen, [ 1, 2 ], "Broken array iteration" );
 
 	seen = [];
-	jQuery.each( { "a": 1, "b": 2,"c": 3 }, function( k, v ) {
+	jQuery.each( { "a": 1, "b": 2, "c": 3 }, function( k, v ) {
 		seen.push( v );
 		return false;
 	} );
@@ -1373,7 +1432,7 @@ QUnit.test( "jQuery.makeArray", function( assert ) {
 
 	assert.equal( ( function() { return jQuery.makeArray( arguments ); } )( 1, 2 ).join( "" ), "12", "Pass makeArray an arguments array" );
 
-	assert.equal( jQuery.makeArray( [ 1,2,3 ] ).join( "" ), "123", "Pass makeArray a real array" );
+	assert.equal( jQuery.makeArray( [ 1, 2, 3 ] ).join( "" ), "123", "Pass makeArray a real array" );
 
 	assert.equal( jQuery.makeArray().length, 0, "Pass nothing to makeArray and expect an empty array" );
 
@@ -1515,6 +1574,15 @@ QUnit.test( "jQuery.parseHTML", function( assert ) {
 	assert.ok( jQuery.parseHTML( "<#if><tr><p>This is a test.</p></tr><#/if>" ) || true, "Garbage input should not cause error" );
 } );
 
+QUnit.test( "jQuery.parseHTML(<a href>) - gh-2965", function( assert ) {
+	assert.expect( 1 );
+
+	var html = "<a href='test.html'></a>",
+		href = jQuery.parseHTML( html )[ 0 ].href;
+
+	assert.ok( /\/test\.html$/.test( href ), "href is not lost after parsing anchor" );
+} );
+
 if ( jQuery.support.createHTMLDocument ) {
 	QUnit.asyncTest( "jQuery.parseHTML", function( assert ) {
 		assert.expect( 1 );
@@ -1531,93 +1599,6 @@ if ( jQuery.support.createHTMLDocument ) {
 	} );
 }
 
-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" );
-	assert.throws( function() {
-		jQuery.parseJSON( "" );
-	}, null, "Empty string raises an error" );
-	assert.throws( function() {
-		jQuery.parseJSON( "''" );
-	}, null, "Single-quoted string raises an error" );
-	/*
-
-	// 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" );
-	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 );
 
@@ -1671,14 +1652,14 @@ QUnit.test( "jQuery.camelCase()", function( assert ) {
 	} );
 } );
 
-testIframeWithCallback(
+testIframe(
 	"Conditional compilation compatibility (#13274)",
 	"core/cc_on.html",
-	function( cc_on, errors, $, assert ) {
+	function( assert, jQuery, window, document, cc_on, errors ) {
 		assert.expect( 3 );
 		assert.ok( true, "JScript conditional compilation " + ( cc_on ? "supported" : "not supported" ) );
 		assert.deepEqual( errors, [], "No errors" );
-		assert.ok( $(), "jQuery executes" );
+		assert.ok( jQuery(), "jQuery executes" );
 	}
 );
 
@@ -1686,36 +1667,35 @@ testIframeWithCallback(
 // 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(
+	testIframe(
 		"document ready when jQuery loaded asynchronously (#13655)",
 		"core/dynamic_ready.html",
-		function( ready, assert ) {
+		function( assert, jQuery, window, document, ready ) {
 			assert.expect( 1 );
 			assert.equal( true, ready, "document ready correctly fired when jQuery is loaded after DOMContentLoaded" );
 		}
 	);
 }
 
-testIframeWithCallback(
+testIframe(
 	"Tolerating alias-masked DOM properties (#14074)",
 	"core/aliased.html",
-	function( errors, assert ) {
+	function( assert, jQuery, window, document, errors ) {
 		assert.expect( 1 );
 		assert.deepEqual( errors, [], "jQuery loaded" );
 	}
 );
 
-testIframeWithCallback(
+testIframe(
 	"Don't call window.onready (#14802)",
 	"core/onready.html",
-	function( error, assert ) {
+	function( assert, jQuery, window, document, error ) {
 		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;
@@ -1733,3 +1713,45 @@ QUnit.test( "Iterability of jQuery objects (gh-1693)", function( assert ) {
 		assert.ok( true, "The browser doesn't support Symbols" );
 	}
 } );
+
+QUnit[ jQuery.Deferred ? "test" : "skip" ]( "jQuery.readyException (original)", function( assert ) {
+	assert.expect( 1 );
+
+	var message;
+
+	this.sandbox.stub( window, "setTimeout", function( fn ) {
+		try {
+			fn();
+		} catch ( error ) {
+			message = error.message;
+		}
+	} );
+
+	jQuery( function() {
+		throw new Error( "Error in jQuery ready" );
+	} );
+	assert.strictEqual(
+		message,
+		"Error in jQuery ready",
+		"The error should have been thrown in a timeout"
+	);
+} );
+
+QUnit[ jQuery.Deferred ? "test" : "skip" ]( "jQuery.readyException (custom)", function( assert ) {
+	assert.expect( 1 );
+
+	var done = assert.async();
+
+	this.sandbox.stub( jQuery, "readyException", function( error ) {
+		assert.strictEqual(
+			error.message,
+			"Error in jQuery ready",
+			"The custom jQuery.readyException should have been called"
+		);
+		done();
+	} );
+
+	jQuery( function() {
+		throw new Error( "Error in jQuery ready" );
+	} );
+} );
diff --git a/test/unit/css.js b/test/unit/css.js
index 4ecd08a..2f529b6 100644
--- a/test/unit/css.js
+++ b/test/unit/css.js
@@ -58,9 +58,9 @@ QUnit.test( "css(String|Hash)", function( assert ) {
 	jQuery( "#foo" ).css( { "opacity": "" } );
 	assert.equal( jQuery( "#foo" ).css( "opacity" ), "1", "Assert opacity is 1 when set to an empty String" );
 
-	assert.equal( jQuery( "#empty" ).css( "opacity" ), "0", "Assert opacity is accessible via filter property set in stylesheet in IE" );
+	assert.equal( jQuery( "#empty" ).css( "opacity" ), "0", "Assert opacity is accessible" );
 	jQuery( "#empty" ).css( { "opacity": "1" } );
-	assert.equal( jQuery( "#empty" ).css( "opacity" ), "1", "Assert opacity is taken from style attribute when set vs stylesheet in IE with filters" );
+	assert.equal( jQuery( "#empty" ).css( "opacity" ), "1", "Assert opacity is taken from style attribute when set" );
 
 	div = jQuery( "#nothiddendiv" );
 	child = jQuery( "#nothiddendivchild" );
@@ -141,7 +141,7 @@ QUnit.test( "css() explicit and relative values", function( assert ) {
 	$elem.css( "width", "+=9" );
 	assert.equal( $elem.css( "width" ), "10px", "'+=9' on width (params)" );
 
-	$elem.css( "width", "-=9" ) ;
+	$elem.css( "width", "-=9" );
 	assert.equal( $elem.css( "width" ), "1px", "'-=9' on width (params)" );
 
 	$elem.css( "width", "+=9px" );
@@ -354,8 +354,8 @@ QUnit.test( "css(String, Function)", function( assert ) {
 		sizes = [ "10px", "20px", "30px" ];
 
 	jQuery( "<div id='cssFunctionTest'><div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div></div>" )
+				"<div class='cssFunction'></div>" +
+				"<div class='cssFunction'></div></div>" )
 		.appendTo( "body" );
 
 	index = 0;
@@ -385,8 +385,8 @@ QUnit.test( "css(String, Function) with incoming value", function( assert ) {
 		sizes = [ "10px", "20px", "30px" ];
 
 	jQuery( "<div id='cssFunctionTest'><div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div></div>" )
+				"<div class='cssFunction'></div>" +
+				"<div class='cssFunction'></div></div>" )
 		.appendTo( "body" );
 
 	index = 0;
@@ -416,8 +416,8 @@ QUnit.test( "css(Object) where values are Functions", function( assert ) {
 		sizes = [ "10px", "20px", "30px" ];
 
 	jQuery( "<div id='cssFunctionTest'><div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div></div>" )
+				"<div class='cssFunction'></div>" +
+				"<div class='cssFunction'></div></div>" )
 		.appendTo( "body" );
 
 	index = 0;
@@ -447,8 +447,8 @@ QUnit.test( "css(Object) where values are Functions with incoming values", funct
 		sizes = [ "10px", "20px", "30px" ];
 
 	jQuery( "<div id='cssFunctionTest'><div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div>" +
-				 "<div class='cssFunction'></div></div>" )
+				"<div class='cssFunction'></div>" +
+				"<div class='cssFunction'></div></div>" )
 		.appendTo( "body" );
 
 	index = 0;
@@ -478,7 +478,7 @@ QUnit.test( "show()", function( assert ) {
 
 	assert.expect( 18 );
 
-	var hiddendiv, div, pass, old, test;
+	var hiddendiv, div, pass, test;
 		hiddendiv = jQuery( "div.hidden" );
 
 	assert.equal( jQuery.css( hiddendiv[ 0 ], "display" ), "none", "hiddendiv is display: none" );
@@ -504,13 +504,9 @@ QUnit.test( "show()", function( assert ) {
 		"<div id='show-tests'>" +
 		"<div><p><a href='#'></a></p><code></code><pre></pre><span></span></div>" +
 		"<table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody></table>" +
-		"<ul><li></li></ul></div>" +
-		"<table id='test-table'></table>"
+		"<ul><li></li></ul></div>"
 	).appendTo( "#qunit-fixture" ).find( "*" ).css( "display", "none" );
 
-	old = jQuery( "#test-table" ).show().css( "display" ) !== "table";
-	jQuery( "#test-table" ).remove();
-
 	test = {
 		"div": "block",
 		"p": "block",
@@ -518,14 +514,14 @@ QUnit.test( "show()", function( assert ) {
 		"code": "inline",
 		"pre": "block",
 		"span": "inline",
-		"table": old ? "block" : "table",
-		"thead": old ? "block" : "table-header-group",
-		"tbody": old ? "block" : "table-row-group",
-		"tr": old ? "block" : "table-row",
-		"th": old ? "block" : "table-cell",
-		"td": old ? "block" : "table-cell",
+		"table": "table",
+		"thead": "table-header-group",
+		"tbody": "table-row-group",
+		"tr": "table-row",
+		"th": "table-cell",
+		"td": "table-cell",
 		"ul": "block",
-		"li": old ? "block" : "list-item"
+		"li": "list-item"
 	};
 
 	jQuery.each( test, function( selector, expected ) {
@@ -930,6 +926,30 @@ QUnit[ jQuery.find.compile && jQuery.fn.toggle ? "test" : "skip" ]( "toggle()",
 	jQuery.fn.hide = oldHide;
 } );
 
+QUnit[ jQuery.find.compile && jQuery.fn.toggle ? "test" : "skip" ]( "detached toggle()", function( assert ) {
+	assert.expect( 6 );
+	var detached = jQuery( "<p><a/><p>" ).find( "*" ).addBack(),
+		hiddenDetached = jQuery( "<p><a/></p>" ).find( "*" ).addBack().css( "display", "none" ),
+		cascadeHiddenDetached = jQuery( "<p><a/></p>" ).find( "*" ).addBack().addClass( "hidden" );
+
+	detached.toggle();
+	detached.appendTo( "#qunit-fixture" );
+	assert.equal( detached[ 0 ].style.display, "none", "detached element" );
+	assert.equal( detached[ 1 ].style.display, "none", "element in detached tree" );
+
+	hiddenDetached.toggle();
+	hiddenDetached.appendTo( "#qunit-fixture" );
+	assert.equal( hiddenDetached[ 0 ].style.display, "", "detached, hidden element" );
+	assert.equal( hiddenDetached[ 1 ].style.display, "", "hidden element in detached tree" );
+
+	cascadeHiddenDetached.toggle();
+	cascadeHiddenDetached.appendTo( "#qunit-fixture" );
+	assert.equal( cascadeHiddenDetached[ 0 ].style.display, "none",
+		"detached, cascade-hidden element" );
+	assert.equal( cascadeHiddenDetached[ 1 ].style.display, "none",
+		"cascade-hidden element in detached tree" );
+} );
+
 QUnit.test( "jQuery.css(elem, 'height') doesn't clear radio buttons (bug #1095)", function( assert ) {
 	assert.expect( 4 );
 
@@ -968,18 +988,18 @@ QUnit.test( "computed margins (trac-3333; gh-2237)", function( assert ) {
 	assert.equal( $div.css( "marginRight" ), "0px",
 		"marginRight correctly calculated with a width and display block" );
 
-	$div.css({
+	$div.css( {
 		position: "absolute",
 		top: 0,
 		left: 0,
 		width: "100px"
-	});
-	$child.css({
+	} );
+	$child.css( {
 		width: "50px",
 		margin: "auto"
-	});
+	} );
 	assert.equal( $child.css( "marginLeft" ), "25px", "auto margins are computed to pixels" );
-});
+} );
 
 QUnit.test( "box model properties incorrectly returning % instead of px, see #10639 and #12088", function( assert ) {
 	assert.expect( 2 );
@@ -1037,6 +1057,16 @@ QUnit.test( "can't get css for disconnected in IE<9, see #10254 and #8388", func
 	assert.equal( div.css( "top" ), "10px", "can't get top in IE<9, see #8388" );
 } );
 
+QUnit.test( "Ensure styles are retrieving from parsed html on document fragments", function( assert ) {
+	assert.expect( 1 );
+
+	var $span = jQuery(
+		jQuery.parseHTML( "<span style=\"font-family: Cuprum,sans-serif; font-size: 14px; color: #999999;\">some text</span>" )
+	);
+
+	assert.equal( $span.css( "font-size" ), "14px", "Font-size retrievable on parsed HTML node" );
+} );
+
 QUnit.test( "can't get background-position in IE<9, see #10796", function( assert ) {
 	var div = jQuery( "<div/>" ).appendTo( "#qunit-fixture" ),
 		units = [
@@ -1078,12 +1108,7 @@ QUnit.test( "Do not append px (#9548, #12990, #2792)", function( assert ) {
 
 	$div.css( "fill-opacity", 1 );
 
-	// Support: Android 2.3 (no support for fill-opacity)
-	if ( $div.css( "fill-opacity" ) !== undefined ) {
-		assert.equal( $div.css( "fill-opacity" ), 1, "Do not append px to 'fill-opacity'" );
-	} else {
-		assert.ok( true, "No support for fill-opacity CSS property" );
-	}
+	assert.equal( $div.css( "fill-opacity" ), 1, "Do not append px to 'fill-opacity'" );
 
 	$div.css( "column-count", 1 );
 	if ( $div.css( "column-count" ) !== undefined ) {
@@ -1105,8 +1130,7 @@ QUnit.test( "Do not append px (#9548, #12990, #2792)", function( assert ) {
 QUnit.test( "css('width') and css('height') should respect box-sizing, see #11004", function( assert ) {
 	assert.expect( 4 );
 
-	// Support: Android 2.3 (-webkit-box-sizing).
-	var el_dis = jQuery( "<div style='width:300px;height:300px;margin:2px;padding:2px;-webkit-box-sizing:border-box;box-sizing:border-box;'>test</div>" ),
+	var el_dis = jQuery( "<div style='width:300px;height:300px;margin:2px;padding:2px;box-sizing:border-box;'>test</div>" ),
 		el = el_dis.clone().appendTo( "#qunit-fixture" );
 
 	assert.equal( el.css( "width" ), el.css( "width", el.css( "width" ) ).css( "width" ), "css('width') is not respecting box-sizing, see #11004" );
@@ -1115,10 +1139,10 @@ QUnit.test( "css('width') and css('height') should respect box-sizing, see #1100
 	assert.equal( el_dis.css( "height" ), el_dis.css( "height", el_dis.css( "height" ) ).css( "height" ), "css('height') is not respecting box-sizing for disconnected element, see #11004" );
 } );
 
-testIframeWithCallback(
+testIframe(
 	"css('width') should work correctly before document ready (#14084)",
 	"css/cssWidthBeforeDocReady.html",
-	function( cssWidthBeforeDocReady, assert ) {
+	function( assert, jQuery, window, document, cssWidthBeforeDocReady ) {
 		assert.expect( 1 );
 		assert.strictEqual( cssWidthBeforeDocReady, "100px", "elem.css('width') works correctly before document ready" );
 	}
@@ -1179,7 +1203,8 @@ QUnit.test( "certain css values of 'normal' should be convertable to a number, s
 	assert.equal( typeof el.css( "fontWeight" ), "string", ".css() returns a string" );
 } );
 
-// only run this test in IE9
+// Support: IE 9 only
+// Only run this test in IE9
 if ( document.documentMode === 9 ) {
 	QUnit.test( ".css('filter') returns a string in IE9, see #12537", function( assert ) {
 		assert.expect( 1 );
@@ -1229,23 +1254,21 @@ QUnit.test( "cssHooks - expand", function( assert ) {
 } );
 
 QUnit.test( "css opacity consistency across browsers (#12685)", function( assert ) {
-	assert.expect( 4 );
+	assert.expect( 3 );
 
 	var el,
 		fixture = jQuery( "#qunit-fixture" );
 
 	// Append style element
-	jQuery( "<style>.opacityWithSpaces_t12685 { opacity: 0.1; filter: alpha(opacity = 10); } .opacityNoSpaces_t12685 { opacity: 0.2; filter: alpha(opacity=20); }</style>" ).appendTo( fixture );
+	jQuery( "<style>.opacity_t12685 { opacity: 0.1; }</style>" ).appendTo( fixture );
 
-	el = jQuery( "<div class='opacityWithSpaces_t12685'></div>" ).appendTo( fixture );
+	el = jQuery( "<div class='opacity_t12685'></div>" ).appendTo( fixture );
 
-	assert.equal( Math.round( el.css( "opacity" ) * 100 ), 10, "opacity from style sheet (filter:alpha with spaces)" );
-	el.removeClass( "opacityWithSpaces_t12685" ).addClass( "opacityNoSpaces_t12685" );
-	assert.equal( Math.round( el.css( "opacity" ) * 100 ), 20, "opacity from style sheet (filter:alpha without spaces)" );
+	assert.equal( Math.round( el.css( "opacity" ) * 100 ), 10, "opacity from style sheet" );
 	el.css( "opacity", 0.3 );
 	assert.equal( Math.round( el.css( "opacity" ) * 100 ), 30, "override opacity" );
 	el.css( "opacity", "" );
-	assert.equal( Math.round( el.css( "opacity" ) * 100 ), 20, "remove opacity override" );
+	assert.equal( Math.round( el.css( "opacity" ) * 100 ), 10, "remove opacity override" );
 } );
 
 QUnit[ jQuery.find.compile ? "test" : "skip" ]( ":visible/:hidden selectors", function( assert ) {
@@ -1286,7 +1309,7 @@ QUnit[ jQuery.find.compile ? "test" : "skip" ]( ":visible/:hidden selectors", fu
 
 	assert.t( "Is Visible", "#qunit-fixture div:visible:lt(2)", [ "foo", "nothiddendiv" ] );
 	assert.t( "Is Not Hidden", "#qunit-fixture:hidden", [] );
-	assert.t( "Is Hidden", "#form input:hidden", [ "hidden1","hidden2" ] );
+	assert.t( "Is Hidden", "#form input:hidden", [ "hidden1", "hidden2" ] );
 
 	$a = jQuery( "<a href='#'><h1>Header</h1></a>" ).appendTo( "#qunit-fixture" );
 	assert.ok( $a.is( ":visible" ), "Anchor tag with flow content is visible (gh-2227)" );
@@ -1299,7 +1322,16 @@ QUnit.test( "Keep the last style if the new one isn't recognized by the browser
 	el = jQuery( "<div></div>" ).css( "position", "absolute" ).css( "position", "fake value" );
 	assert.equal( el.css( "position" ), "absolute", "The old style is kept when setting an unrecognized value" );
 	el = jQuery( "<div></div>" ).css( "position", "absolute" ).css( "position", " " );
-	assert.equal( el.css( "position" ), "absolute", "The old style is kept when setting to a space" );
+
+	// Support: Edge 14
+	// Edge collapses whitespace-only values when setting a style property and
+	// there is no easy way for us to work around it. Just skip the test there
+	// and hope for the better future.
+	if ( /edge\//i.test( navigator.userAgent ) ) {
+		assert.ok( true, "Skipped (Edge 14 handles whitespace-only values incorrectly)" );
+	} else {
+		assert.equal( el.css( "position" ), "absolute", "The old style is kept when setting to a space" );
+	}
 } );
 
 QUnit.test( "Reset the style if set to an empty string", function( assert ) {
@@ -1322,7 +1354,7 @@ QUnit.test(
 				name: "backgroundAttachment",
 				value: [ "fixed" ],
 				expected: [ "scroll" ]
-			},{
+			}, {
 				name: "backgroundColor",
 				value: [ "rgb(255, 0, 0)", "rgb(255,0,0)", "#ff0000" ],
 				expected: [ "transparent" ]
@@ -1399,7 +1431,7 @@ QUnit.test(
 	}
 );
 
-// Support: IE < 11
+// Support: IE <=10 only
 // We have to jump through the hoops here in order to test work with "order" CSS property,
 // that some browsers do not support. This test is not, strictly speaking, correct,
 // but it's the best that we can do.
diff --git a/test/unit/data.js b/test/unit/data.js
index 31c6a61..0eb1d51 100644
--- a/test/unit/data.js
+++ b/test/unit/data.js
@@ -277,7 +277,7 @@ QUnit.test( "data-* attributes", function( assert ) {
 
 	var prop, i, l, metadata, elem,
 		obj, obj2, check, num, num2,
-		parseJSON = jQuery.parseJSON,
+		parseJSON = JSON.parse,
 		div = jQuery( "<div>" ),
 		child = jQuery( "<div data-myobj='old data' data-ignored=\"DOM\" data-other='test' data-foo-42='boosh'></div>" ),
 		dummy = jQuery( "<div data-myobj='old data' data-ignored=\"DOM\" data-other='test' data-foo-42='boosh'></div>" );
@@ -336,7 +336,7 @@ QUnit.test( "data-* attributes", function( assert ) {
 
 	// attribute parsing
 	i = 0;
-	jQuery.parseJSON = function() {
+	JSON.parse = function() {
 		i++;
 		return parseJSON.apply( this, arguments );
 	};
@@ -389,7 +389,7 @@ QUnit.test( "data-* attributes", function( assert ) {
 	assert.strictEqual( child.data( "string" ), "test", "Typical string read from attribute" );
 	assert.equal( i, 2, "Correct number of JSON parse attempts when reading from attributes" );
 
-	jQuery.parseJSON = parseJSON;
+	JSON.parse = parseJSON;
 	child.remove();
 
 	// tests from metadata plugin
@@ -409,7 +409,7 @@ QUnit.test( "data-* attributes", function( assert ) {
 			break;
 		case 3:
 			assert.equal( jQuery( elem ).data( "number" ), true, "Check number property" );
-			assert.deepEqual( jQuery( elem ).data( "stuff" ), [ 2,8 ], "Check stuff property" );
+			assert.deepEqual( jQuery( elem ).data( "stuff" ), [ 2, 8 ], "Check stuff property" );
 			break;
 		default:
 			assert.ok( false, [ "Assertion failed on index ", index, ", with data" ].join( "" ) );
@@ -857,10 +857,10 @@ QUnit.test( "Check proper data removal of non-element descendants nodes (#8335)"
 	assert.ok( !text.data( "test" ), "Be sure data is not stored in non-element" );
 } );
 
-testIframeWithCallback(
+testIframe(
 	"enumerate data attrs on body (#14894)",
 	"data/dataAttrs.html",
-	function( result, assert ) {
+	function( assert, jQuery, window, document, result ) {
 		assert.expect( 1 );
 		assert.equal( result, "ok", "enumeration of data- attrs on body" );
 	}
@@ -881,13 +881,13 @@ QUnit.test( "Check that the expando is removed when there's no more data", funct
 			assert.strictEqual( div[ 0 ][ key ], undefined, "Expando was not removed when there was no more data" );
 		}
 	}
-});
+} );
 
 QUnit.test( "Check that the expando is removed when there's no more data on non-nodes", function( assert ) {
 	assert.expect( 1 );
 
 	var key,
-		obj = jQuery( {key: 42} );
+		obj = jQuery( { key: 42 } );
 	obj.data( "some", "data" );
 	assert.equal( obj.data( "some" ), "data", "Data is added" );
 	obj.removeData( "some" );
diff --git a/test/unit/deferred.js b/test/unit/deferred.js
index d65ce34..32f3256 100644
--- a/test/unit/deferred.js
+++ b/test/unit/deferred.js
@@ -16,20 +16,21 @@ jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
 
 		assert.ok( jQuery.isFunction( defer.pipe ), "defer.pipe is a function" );
 
-		createDeferred().resolve().done( function() {
+		defer.resolve().done( function() {
 			assert.ok( true, "Success on resolve" );
-			assert.strictEqual( this.state(), "resolved", "Deferred is resolved (state)" );
+			assert.strictEqual( defer.state(), "resolved", "Deferred is resolved (state)" );
 		} ).fail( function() {
 			assert.ok( false, "Error on resolve" );
 		} ).always( function() {
 			assert.ok( true, "Always callback on resolve" );
 		} );
 
-		createDeferred().reject().done( function() {
+		defer = createDeferred();
+		defer.reject().done( function() {
 			assert.ok( false, "Success on reject" );
 		} ).fail( function() {
 			assert.ok( true, "Error on reject" );
-			assert.strictEqual( this.state(), "rejected", "Deferred is rejected (state)" );
+			assert.strictEqual( defer.state(), "rejected", "Deferred is rejected (state)" );
 		} ).always( function() {
 			assert.ok( true, "Always callback on reject" );
 		} );
@@ -405,21 +406,31 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function(
 
 QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
 
-	assert.expect( 7 );
+	assert.expect( 11 );
 
 	var defer, piped, defer2, piped2,
-		context = {},
-		done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
+		context = { custom: true },
+		done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
 
 	jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
+		assert.strictEqual( this, context, "custom context received by .then handler" );
 		return value * 3;
 	} ).done( function( value ) {
-		assert.notStrictEqual( this, context, "custom context not propagated through .then" );
+		assert.notStrictEqual( this, context,
+			"custom context not propagated through .then handler" );
 		assert.strictEqual( value, 6, "proper value received" );
 		done.pop().call();
 	} );
 
+	jQuery.Deferred().resolveWith( context, [ 2 ] ).then().done( function( value ) {
+		assert.strictEqual( this, context,
+			"custom context propagated through .then without handler" );
+		assert.strictEqual( value, 2, "proper value received" );
+		done.pop().call();
+	} );
+
 	jQuery.Deferred().resolve().then( function() {
+		assert.strictEqual( this, window, "default context in .then handler" );
 		return jQuery.Deferred().resolveWith( context );
 	} ).done( function() {
 		assert.strictEqual( this, context,
@@ -435,8 +446,7 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
 	defer.resolve( 2 );
 
 	piped.done( function( value ) {
-		assert.strictEqual( this, piped,
-			"default context gets updated to latest promise in the chain" );
+		assert.strictEqual( this, window, ".then handler does not introduce context" );
 		assert.strictEqual( value, 6, "proper value received" );
 		done.pop().call();
 	} );
@@ -447,8 +457,7 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
 	defer2.resolve( 2 );
 
 	piped2.done( function( value ) {
-		assert.strictEqual( this, piped2,
-			"default context updated to latest promise in the chain (without passing function)" );
+		assert.strictEqual( this, window, ".then without handler does not introduce context" );
 		assert.strictEqual( value, 2, "proper value received (without passing function)" );
 		done.pop().call();
 	} );
@@ -456,21 +465,31 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
 
 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
 
-	assert.expect( 7 );
+	assert.expect( 11 );
 
 	var defer, piped, defer2, piped2,
-		context = {},
-		done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
+		context = { custom: true },
+		done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
 
 	jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
+		assert.strictEqual( this, context, "custom context received by .pipe handler" );
 		return value * 3;
 	} ).done( function( value ) {
-		assert.strictEqual( this, context, "[PIPE ONLY] custom context correctly propagated" );
+		assert.strictEqual( this, context,
+			"[PIPE ONLY] custom context propagated through .pipe handler" );
 		assert.strictEqual( value, 6, "proper value received" );
 		done.pop().call();
 	} );
 
+	jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe().done( function( value ) {
+		assert.strictEqual( this, context,
+			"[PIPE ONLY] custom context propagated through .pipe without handler" );
+		assert.strictEqual( value, 2, "proper value received" );
+		done.pop().call();
+	} );
+
 	jQuery.Deferred().resolve().pipe( function() {
+		assert.strictEqual( this, window, "default context in .pipe handler" );
 		return jQuery.Deferred().resolveWith( context );
 	} ).done( function() {
 		assert.strictEqual( this, context,
@@ -486,8 +505,7 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
 	defer.resolve( 2 );
 
 	piped.done( function( value ) {
-		assert.strictEqual( this, piped,
-			"default context gets updated to latest promise in the chain" );
+		assert.strictEqual( this, window, ".pipe handler does not introduce context" );
 		assert.strictEqual( value, 6, "proper value received" );
 		done.pop().call();
 	} );
@@ -498,8 +516,7 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
 	defer2.resolve( 2 );
 
 	piped2.done( function( value ) {
-		assert.strictEqual( this, piped2,
-			"default context updated to latest promise in the chain (without passing function)" );
+		assert.strictEqual( this, window, ".pipe without handler does not introduce context" );
 		assert.strictEqual( value, 2, "proper value received (without passing function)" );
 		done.pop().call();
 	} );
@@ -525,27 +542,49 @@ QUnit.test( "jQuery.Deferred.then - spec compatibility", function( assert ) {
 	} catch ( _ ) {}
 } );
 
+// Test fails in IE9 but is skipped there because console is not active
 QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook", function( assert ) {
 
-	assert.expect( 1 );
+	assert.expect( 2 );
 
 	var done = assert.async(),
 		defer = jQuery.Deferred(),
 		oldWarn = window.console.warn;
 
-	window.console.warn = function( msg ) {
-		assert.ok( /barf/.test( msg ), "Message: " + msg );
+	window.console.warn = function() {
+
+		// Support: Chrome <=41 only
+		// Some Chrome versions newer than 30 but older than 42 display the "undefined is
+		// not a function" error, not mentioning the function name. This has been fixed
+		// in Chrome 42. Relax this test there.
+		// This affects our Android 5.0 & Yandex.Browser testing.
+		var msg = Array.prototype.join.call( arguments, " " ),
+			oldChromium = false;
+		if ( /chrome/i.test( navigator.userAgent ) ) {
+			oldChromium = parseInt(
+					navigator.userAgent.match( /chrome\/(\d+)/i )[ 1 ], 10 ) < 42;
+		}
+		if ( oldChromium ) {
+			assert.ok( /(?:barf|undefined)/.test( msg ), "Message (weak assertion): " + msg );
+		} else {
+			assert.ok( /barf/.test( msg ), "Message: " + msg );
+		}
 	};
 	jQuery.when(
 		defer.then( function() {
+
 			// Should get an error
 			jQuery.barf();
 		} ).then( null, jQuery.noop ),
+
 		defer.then( function() {
+
 			// Should NOT get an error
 			throw new Error( "Make me a sandwich" );
 		} ).then( null, jQuery.noop )
-	).then( function( ) {
+	).then( function barf( ) {
+		jQuery.thisDiesToo();
+	} ).then( null, function( ) {
 		window.console.warn = oldWarn;
 		done();
 	} );
@@ -553,6 +592,7 @@ QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook", func
 	defer.resolve();
 } );
 
+// Test fails in IE9 but is skipped there because console is not active
 QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with stack hooks", function( assert ) {
 
 	assert.expect( 2 );
@@ -562,6 +602,7 @@ QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with s
 		oldWarn = window.console.warn;
 
 	jQuery.Deferred.getStackHook = function() {
+
 		// Default exceptionHook assumes the stack is in a form console.warn can log,
 		// but a custom getStackHook+exceptionHook pair could save a raw form and
 		// format it to a string only when an exception actually occurs.
@@ -569,9 +610,26 @@ QUnit[ window.console ? "test" : "skip" ]( "jQuery.Deferred.exceptionHook with s
 		return "NO STACK FOR YOU";
 	};
 
-	window.console.warn = function( msg, stack ) {
-		assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg );
-		assert.ok( /NO STACK FOR YOU/.test( stack ), "Stack trace included: " + stack );
+	window.console.warn = function() {
+
+		// Support: Chrome <=41 only
+		// Some Chrome versions newer than 30 but older than 42 display the "undefined is
+		// not a function" error, not mentioning the function name. This has been fixed
+		// in Chrome 42. Relax this test there.
+		// This affects our Android 5.0 & Yandex.Browser testing.
+		var msg = Array.prototype.join.call( arguments, " " ),
+			oldChromium = false;
+		if ( /chrome/i.test( navigator.userAgent ) ) {
+			oldChromium = parseInt(
+					navigator.userAgent.match( /chrome\/(\d+)/i )[ 1 ], 10 ) < 42;
+		}
+		if ( oldChromium ) {
+			assert.ok( /(?:cough_up_hairball|undefined)/.test( msg ),
+				"Function mentioned (weak assertion): " + msg );
+		} else {
+			assert.ok( /cough_up_hairball/.test( msg ), "Function mentioned: " + msg );
+		}
+		assert.ok( /NO STACK FOR YOU/.test( msg ), "Stack trace included: " + msg );
 	};
 	defer.then( function() {
 		jQuery.cough_up_hairball();
@@ -710,11 +768,60 @@ QUnit.test( "jQuery.Deferred - notify and resolve", function( assert ) {
 	} );
 } );
 
-QUnit.test( "jQuery.when", function( assert ) {
+QUnit.test( "jQuery.Deferred - resolved to a notifying deferred", function( assert ) {
+
+	assert.expect( 2 );
+
+    var deferred = jQuery.Deferred(),
+		done = assert.async( 2 );
+
+	deferred.resolve( jQuery.Deferred( function( notifyingDeferred ) {
+		notifyingDeferred.notify( "foo", "bar" );
+		notifyingDeferred.resolve( "baz", "quux" );
+	} ) );
+
+	// Apply an empty then to force thenable unwrapping.
+	// See https://github.com/jquery/jquery/issues/3000 for more info.
+	deferred.then().then( function() {
+		assert.deepEqual(
+			[].slice.call( arguments ),
+			[ "baz", "quux" ],
+			"The fulfilled handler receives proper params"
+		);
+		done();
+	}, null, function() {
+		assert.deepEqual(
+			[].slice.call( arguments ),
+			[ "foo", "bar" ],
+			"The progress handler receives proper params"
+		);
+		done();
+	} );
+} );
+
+QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert ) {
+	"use strict";
+
+	assert.expect( 44 );
+
+	var
 
-	assert.expect( 37 );
+		// Support: Android 4.0 only
+		// Strict mode functions invoked without .call/.apply get global-object context
+		defaultContext = ( function getDefaultContext() { return this; } ).call(),
+
+		done = assert.async( 20 );
+
+	jQuery.when()
+		.done( function( resolveValue ) {
+			assert.strictEqual( resolveValue, undefined, "Resolved .done with no arguments" );
+			assert.strictEqual( this, defaultContext, "Default .done context with no arguments" );
+		} )
+		.then( function( resolveValue ) {
+			assert.strictEqual( resolveValue, undefined, "Resolved .then with no arguments" );
+			assert.strictEqual( this, defaultContext, "Default .then context with no arguments" );
+		} );
 
-	// Some other objects
 	jQuery.each( {
 		"an empty string": "",
 		"a non-empty string": "some string",
@@ -727,51 +834,145 @@ QUnit.test( "jQuery.when", function( assert ) {
 		"a plain object": {},
 		"an array": [ 1, 2, 3 ]
 	}, function( message, value ) {
-		assert.ok(
-			jQuery.isFunction(
-				jQuery.when( value ).done( function( resolveValue ) {
-					assert.strictEqual( this, window, "Context is the global object with " + message );
-					assert.strictEqual( resolveValue, value, "Test the promise was resolved with " + message );
-				} ).promise
-			),
-			"Test " + message + " triggers the creation of a new Promise"
-		);
+		var code = "jQuery.when( " + message + " )",
+			onFulfilled = function( method ) {
+				var call = code + "." + method;
+				return function( resolveValue ) {
+					assert.strictEqual( resolveValue, value, call + " resolve" );
+					assert.strictEqual( this, defaultContext, call + " context" );
+					done();
+				};
+			},
+			onRejected = function( method ) {
+				var call = code + "." + method;
+				return function() {
+					assert.ok( false, call + " reject" );
+					done();
+				};
+			};
+
+		jQuery.when( value )
+			.done( onFulfilled( "done" ) )
+			.fail( onRejected( "done" ) )
+			.then( onFulfilled( "then" ), onRejected( "then" ) );
 	} );
+} );
 
-	assert.ok(
-		jQuery.isFunction(
-			jQuery.when().done( function( resolveValue ) {
-				assert.strictEqual( this, window, "Test the promise was resolved with window as its context" );
-				assert.strictEqual( resolveValue, undefined, "Test the promise was resolved with no parameter" );
-			} ).promise
-		),
-		"Test calling when with no parameter triggers the creation of a new Promise"
-	);
+QUnit.test( "jQuery.when(thenable) - like Promise.resolve", function( assert ) {
+	"use strict";
+
+	var CASES = 16,
+		slice = [].slice,
+		sentinel = { context: "explicit" },
+		eventuallyFulfilled = jQuery.Deferred().notify( true ),
+		eventuallyRejected = jQuery.Deferred().notify( true ),
+		secondaryFulfilled = jQuery.Deferred().resolve( eventuallyFulfilled ),
+		secondaryRejected = jQuery.Deferred().resolve( eventuallyRejected ),
+		inputs = {
+			promise: Promise.resolve( true ),
+			rejectedPromise: Promise.reject( false ),
+			deferred: jQuery.Deferred().resolve( true ),
+			eventuallyFulfilled: eventuallyFulfilled,
+			secondaryFulfilled: secondaryFulfilled,
+			eventuallySecondaryFulfilled: jQuery.Deferred().notify( true ),
+			multiDeferred: jQuery.Deferred().resolve( "foo", "bar" ),
+			deferredWith: jQuery.Deferred().resolveWith( sentinel, [ true ] ),
+			multiDeferredWith: jQuery.Deferred().resolveWith( sentinel, [ "foo", "bar" ] ),
+			rejectedDeferred: jQuery.Deferred().reject( false ),
+			eventuallyRejected: eventuallyRejected,
+			secondaryRejected: secondaryRejected,
+			eventuallySecondaryRejected: jQuery.Deferred().notify( true ),
+			multiRejectedDeferred: jQuery.Deferred().reject( "baz", "quux" ),
+			rejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ false ] ),
+			multiRejectedDeferredWith: jQuery.Deferred().rejectWith( sentinel, [ "baz", "quux" ] )
+		},
+		contexts = {
+			deferredWith: sentinel,
+			multiDeferredWith: sentinel,
+			rejectedDeferredWith: sentinel,
+			multiRejectedDeferredWith: sentinel
+		},
+		willSucceed = {
+			promise: [ true ],
+			deferred: [ true ],
+			eventuallyFulfilled: [ true ],
+			secondaryFulfilled: [ true ],
+			eventuallySecondaryFulfilled: [ true ],
+			multiDeferred: [ "foo", "bar" ],
+			deferredWith: [ true ],
+			multiDeferredWith: [ "foo", "bar" ]
+		},
+		willError = {
+			rejectedPromise: [ false ],
+			rejectedDeferred: [ false ],
+			eventuallyRejected: [ false ],
+			secondaryRejected: [ false ],
+			eventuallySecondaryRejected: [ false ],
+			multiRejectedDeferred: [ "baz", "quux" ],
+			rejectedDeferredWith: [ false ],
+			multiRejectedDeferredWith: [ "baz", "quux" ]
+		},
 
-	var cache,
-		context = {};
+		// Support: Android 4.0 only
+		// Strict mode functions invoked without .call/.apply get global-object context
+		defaultContext = ( function getDefaultContext() { return this; } ).call(),
+
+		done = assert.async( CASES * 2 );
+
+	assert.expect( CASES * 4 );
+
+	jQuery.each( inputs, function( message, value ) {
+		var code = "jQuery.when( " + message + " )",
+			shouldResolve = willSucceed[ message ],
+			shouldError = willError[ message ],
+			context = contexts[ message ] || defaultContext,
+			onFulfilled = function( method ) {
+				var call = code + "." + method;
+				return function() {
+					if ( shouldResolve ) {
+						assert.deepEqual( slice.call( arguments ), shouldResolve,
+							call + " resolve" );
+						assert.strictEqual( this, context, call + " context" );
+					} else {
+						assert.ok( false,  call + " resolve" );
+					}
+					done();
+				};
+			},
+			onRejected = function( method ) {
+				var call = code + "." + method;
+				return function() {
+					if ( shouldError ) {
+						assert.deepEqual( slice.call( arguments ), shouldError, call + " reject" );
+						assert.strictEqual( this, context, call + " context" );
+					} else {
+						assert.ok( false, call + " reject" );
+					}
+					done();
+				};
+			};
 
-	jQuery.when( jQuery.Deferred().resolveWith( context ) ).done( function() {
-		assert.strictEqual( this, context, "when( promise ) propagates context" );
+		jQuery.when( value )
+			.done( onFulfilled( "done" ) )
+			.fail( onRejected( "done" ) )
+			.then( onFulfilled( "then" ), onRejected( "then" ) );
 	} );
 
-	jQuery.each( [ 1, 2, 3 ], function( k, i ) {
-		jQuery.when( cache || jQuery.Deferred( function() {
-				this.resolve( i );
-			} )
-		).done( function( value ) {
-			assert.strictEqual( value, 1, "Function executed" + ( i > 1 ? " only once" : "" ) );
-			cache = value;
-		} );
-
-	} );
+	setTimeout( function() {
+		eventuallyFulfilled.resolve( true );
+		eventuallyRejected.reject( false );
+		inputs.eventuallySecondaryFulfilled.resolve( secondaryFulfilled );
+		inputs.eventuallySecondaryRejected.resolve( secondaryRejected );
+	}, 50 );
 } );
 
-QUnit.test( "jQuery.when - joined", function( assert ) {
+QUnit.test( "jQuery.when(a, b) - like Promise.all", function( assert ) {
+	"use strict";
 
-	assert.expect( 81 );
+	assert.expect( 196 );
 
-	var deferreds = {
+	var slice = [].slice,
+		deferreds = {
 			rawValue: 1,
 			fulfilled: jQuery.Deferred().resolve( 1 ),
 			rejected: jQuery.Deferred().reject( 0 ),
@@ -791,44 +992,91 @@ QUnit.test( "jQuery.when - joined", function( assert ) {
 			eventuallyRejected: true,
 			rejectedStandardPromise: true
 		},
-		counter = 49;
 
-	QUnit.stop();
+		// Support: Android 4.0 only
+		// Strict mode functions invoked without .call/.apply get global-object context
+		defaultContext = ( function getDefaultContext() { return this; } ).call(),
 
-	function restart() {
-		if ( !--counter ) {
-			QUnit.start();
-		}
-	}
+		done = assert.async( 98 );
 
-	jQuery.each( deferreds, function( id1, defer1 ) {
-		jQuery.each( deferreds, function( id2, defer2 ) {
-			var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
+	jQuery.each( deferreds, function( id1, v1 ) {
+		jQuery.each( deferreds, function( id2, v2 ) {
+			var code = "jQuery.when( " + id1 + ", " + id2 + " )",
+				shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
 				shouldError = willError[ id1 ] || willError[ id2 ],
-				expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
-				code = "jQuery.when( " + id1 + ", " + id2 + " )",
-				context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : window,
-				context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : window;
-
-			jQuery.when( defer1, defer2 ).done( function( a, b ) {
-				if ( shouldResolve ) {
-					assert.deepEqual( [ a, b ], expected, code + " => resolve" );
-					assert.strictEqual( this[ 0 ], context1, code + " => first context OK" );
-					assert.strictEqual( this[ 1 ], context2, code + " => second context OK" );
-				} else {
-					assert.ok( false,  code + " => resolve" );
-				}
-			} ).fail( function( a, b ) {
-				if ( shouldError ) {
-					assert.deepEqual( [ a, b ], expected, code + " => reject" );
-				} else {
-					assert.ok( false, code + " => reject" );
-				}
-			} ).always( restart );
+				expected = shouldResolve ? [ 1, 1 ] : [ 0 ],
+				context = shouldResolve ? [ defaultContext, defaultContext ] : defaultContext,
+				onFulfilled = function( method ) {
+					var call = code + "." + method;
+					return function() {
+						if ( shouldResolve ) {
+							assert.deepEqual( slice.call( arguments ), expected,
+								call + " resolve" );
+							assert.deepEqual( this, context, code + " context" );
+						} else {
+							assert.ok( false,  call + " resolve" );
+						}
+						done();
+					};
+				},
+				onRejected = function( method ) {
+					var call = code + "." + method;
+					return function() {
+						if ( shouldError ) {
+							assert.deepEqual( slice.call( arguments ), expected, call + " reject" );
+							assert.deepEqual( this, context, code + " context" );
+						} else {
+							assert.ok( false, call + " reject" );
+						}
+						done();
+					};
+				};
+
+			jQuery.when( v1, v2 )
+				.done( onFulfilled( "done" ) )
+				.fail( onRejected( "done" ) )
+				.then( onFulfilled( "then" ), onRejected( "then" ) );
+		} );
+	} );
+
+	setTimeout( function() {
+		deferreds.eventuallyFulfilled.resolve( 1 );
+		deferreds.eventuallyRejected.reject( 0 );
+	}, 50 );
+} );
+
+QUnit.test( "jQuery.when - always returns a new promise", function( assert ) {
+
+	assert.expect( 42 );
+
+	jQuery.each( {
+		"no arguments": [],
+		"non-thenable": [ "foo" ],
+		"promise": [ Promise.resolve( "bar" ) ],
+		"rejected promise": [ Promise.reject( "bar" ) ],
+		"deferred": [ jQuery.Deferred().resolve( "baz" ) ],
+		"rejected deferred": [ jQuery.Deferred().reject( "baz" ) ],
+		"multi-resolved deferred": [ jQuery.Deferred().resolve( "qux", "quux" ) ],
+		"multiple non-thenables": [ "corge", "grault" ],
+		"multiple deferreds": [
+			jQuery.Deferred().resolve( "garply" ),
+			jQuery.Deferred().resolve( "waldo" )
+		]
+	}, function( label, args ) {
+		var result = jQuery.when.apply( jQuery, args );
+
+		assert.ok( jQuery.isFunction( result.then ), "Thenable returned from " + label );
+		assert.strictEqual( result.resolve, undefined, "Non-deferred returned from " + label );
+		assert.strictEqual( result.promise(), result, "Promise returned from " + label );
+
+		jQuery.each( args, function( i, arg ) {
+			assert.notStrictEqual( result, arg, "Returns distinct from arg " + i + " of " + label );
+			if ( arg.promise ) {
+				assert.notStrictEqual( result, arg.promise(),
+					"Returns distinct from promise of arg " + i + " of " + label );
+			}
 		} );
 	} );
-	deferreds.eventuallyFulfilled.resolve( 1 );
-	deferreds.eventuallyRejected.reject( 0 );
 } );
 
 QUnit.test( "jQuery.when - notify does not affect resolved", function( assert ) {
@@ -848,106 +1096,27 @@ QUnit.test( "jQuery.when - notify does not affect resolved", function( assert )
 	} );
 } );
 
-QUnit.test( "jQuery.when - filtering", function( assert ) {
-
-	assert.expect( 2 );
-
-	function increment( x ) {
-		return x + 1;
-	}
-
-	QUnit.stop();
-
-	jQuery.when(
-		jQuery.Deferred().resolve( 3 ).then( increment ),
-		jQuery.Deferred().reject( 5 ).then( null, increment )
-	).done( function( four, six ) {
-		assert.strictEqual( four, 4, "resolved value incremented" );
-		assert.strictEqual( six, 6, "rejected value incremented" );
-		QUnit.start();
-	} );
-} );
-
-QUnit.test( "jQuery.when - exceptions", function( assert ) {
-
-	assert.expect( 2 );
-
-	function woops() {
-		throw "exception thrown";
-	}
-
-	QUnit.stop();
-
-	jQuery.Deferred().resolve().then( woops ).fail( function( doneException ) {
-		assert.strictEqual( doneException, "exception thrown", "throwing in done handler" );
-		jQuery.Deferred().reject().then( null, woops ).fail( function( failException ) {
-			assert.strictEqual( failException, "exception thrown", "throwing in fail handler" );
-			QUnit.start();
-		} );
-	} );
-} );
-
-QUnit.test( "jQuery.when - chaining", function( assert ) {
-
-	assert.expect( 4 );
-
-	var defer = jQuery.Deferred();
-
-	function chain() {
-		return defer;
-	}
-
-	function chainStandard() {
-		return Promise.resolve( "std deferred" );
-	}
-
-	QUnit.stop();
-
-	jQuery.when(
-		jQuery.Deferred().resolve( 3 ).then( chain ),
-		jQuery.Deferred().reject( 5 ).then( null, chain ),
-		jQuery.Deferred().resolve( 3 ).then( chainStandard ),
-		jQuery.Deferred().reject( 5 ).then( null, chainStandard )
-	).done( function( v1, v2, s1, s2 ) {
-		assert.strictEqual( v1, "other deferred", "chaining in done handler" );
-		assert.strictEqual( v2, "other deferred", "chaining in fail handler" );
-		assert.strictEqual( s1, "std deferred", "chaining thenable in done handler" );
-		assert.strictEqual( s2, "std deferred", "chaining thenable in fail handler" );
-		QUnit.start();
-	} );
-
-	defer.resolve( "other deferred" );
-} );
+QUnit.test( "jQuery.when(...) - opportunistically synchronous", function( assert ) {
 
-QUnit.test( "jQuery.when - solitary thenables", function( assert ) {
+	assert.expect( 5 );
 
-	assert.expect( 1 );
-
-	var done = assert.async(),
-		rejected = new Promise( function( resolve, reject ) {
-			setTimeout( function() {
-				reject( "rejected" );
-			}, 100 );
-		} );
-
-	jQuery.when( rejected ).then(
-		function() {
-			assert.ok( false, "Rejected, solitary, non-Deferred thenable should not resolve" );
-			done();
+	var when = "before",
+		resolved = jQuery.Deferred().resolve( true ),
+		rejected = jQuery.Deferred().reject( false ),
+		validate = function( label ) {
+			return function() {
+				assert.equal( when, "before", label );
+			};
 		},
-		function() {
-			assert.ok( true, "Rejected, solitary, non-Deferred thenable rejected properly" );
-			done();
-		}
-	);
-} );
-
-QUnit.test( "jQuery.when does not reuse a solitary jQuery Deferred (gh-2018)", function( assert ) {
+		done = assert.async( 5 );
 
-	assert.expect( 2 );
-	var defer = jQuery.Deferred().resolve(),
-		promise = jQuery.when( defer );
+	jQuery.when().done( validate( "jQuery.when()" ) ).always( done );
+	jQuery.when( when ).done( validate( "jQuery.when(nonThenable)" ) ).always( done );
+	jQuery.when( resolved ).done( validate( "jQuery.when(alreadyFulfilled)" ) ).always( done );
+	jQuery.when( rejected ).fail( validate( "jQuery.when(alreadyRejected)" ) ).always( done );
+	jQuery.when( resolved, rejected )
+		.always( validate( "jQuery.when(alreadyFulfilled, alreadyRejected)" ) )
+		.always( done );
 
-	assert.equal( promise.state(), "resolved", "Master Deferred is immediately resolved" );
-	assert.notStrictEqual( defer.promise(), promise, "jQuery.when returns the master deferred's promise" );
+	when = "after";
 } );
diff --git a/test/unit/deprecated.js b/test/unit/deprecated.js
index 797290f..27ba1cf 100644
--- a/test/unit/deprecated.js
+++ b/test/unit/deprecated.js
@@ -14,7 +14,7 @@ QUnit.test( "bind/unbind", function( assert ) {
 			assert.equal( e.type, "click", "correct event type" );
 			assert.equal( e.data.bindData, 19, "correct trigger data" );
 			assert.equal( trig, 42, "correct bind data" );
-			assert.equal( e.target.nodeName.toLowerCase(), "b" , "correct element" );
+			assert.equal( e.target.nodeName.toLowerCase(), "b", "correct element" );
 		} )
 		.trigger( "click", [ 42 ] )
 		.unbind( "click" )
@@ -32,11 +32,81 @@ QUnit.test( "delegate/undelegate", function( assert ) {
 	markup
 		.delegate( "b", "click", function( e ) {
 			assert.equal( e.type, "click", "correct event type" );
-			assert.equal( e.target.nodeName.toLowerCase(), "b" , "correct element" );
+			assert.equal( e.target.nodeName.toLowerCase(), "b", "correct element" );
 		} )
 		.find( "b" )
 			.trigger( "click" )
 			.end()
 		.undelegate( "b", "click" )
 		.remove();
-} );
\ No newline at end of file
+} );
+
+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" );
+	assert.throws( function() {
+		jQuery.parseJSON( "" );
+	}, null, "Empty string raises an error" );
+	assert.throws( function() {
+		jQuery.parseJSON( "''" );
+	}, null, "Single-quoted string raises an error" );
+
+	assert.throws( function() {
+		var result = jQuery.parseJSON( "0101" );
+
+		// Support: IE <=9 only
+		// 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" );
+	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" );
+} );
diff --git a/test/unit/dimensions.js b/test/unit/dimensions.js
index c681478..5741b2a 100644
--- a/test/unit/dimensions.js
+++ b/test/unit/dimensions.js
@@ -295,7 +295,7 @@ QUnit.test( "child of a hidden elem (or unconnected node) has accurate inner/out
 	assert.equal( $divChild.outerWidth(), $divNormal.outerWidth(), "child of a hidden element outerWidth() is wrong see #9441" );
 	assert.equal( $divChild.outerWidth( true ), $divNormal.outerWidth( true ), "child of a hidden element outerWidth( true ) is wrong see #9300" );
 
-	// Support: IE 10-11, Edge
+	// Support: IE 10 - 11, Edge 12 - 13+
 	// Child height is not always decimal
 	assert.equal( $divChild.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "child of a hidden element height() is wrong see #9441" );
 	assert.equal( $divChild.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "child of a hidden element innerHeight() is wrong see #9441" );
@@ -308,7 +308,7 @@ QUnit.test( "child of a hidden elem (or unconnected node) has accurate inner/out
 	assert.equal( $divUnconnected.outerWidth(), $divNormal.outerWidth(), "unconnected element outerWidth() is wrong see #9441" );
 	assert.equal( $divUnconnected.outerWidth( true ), $divNormal.outerWidth( true ), "unconnected element outerWidth( true ) is wrong see #9300" );
 
-	// Support: IE 10-11, Edge
+	// Support: IE 10 - 11, Edge 12 - 13+
 	// Child height is not always decimal
 	assert.equal( $divUnconnected.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "unconnected element height() is wrong see #9441" );
 	assert.equal( $divUnconnected.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "unconnected element innerHeight() is wrong see #9441" );
@@ -372,7 +372,7 @@ QUnit.test( "box-sizing:border-box child of a hidden elem (or unconnected node)
 	assert.equal( $divChild.outerWidth(), $divNormal.outerWidth(), "child of a hidden element outerWidth() is wrong see #10413" );
 	assert.equal( $divChild.outerWidth( true ), $divNormal.outerWidth( true ), "child of a hidden element outerWidth( true ) is wrong see #10413" );
 
-	// Support: IE 10-11, Edge
+	// Support: IE 10 - 11, Edge 12 - 13+
 	// Child height is not always decimal
 	assert.equal( $divChild.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "child of a hidden element height() is wrong see #10413" );
 	assert.equal( $divChild.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "child of a hidden element innerHeight() is wrong see #10413" );
@@ -385,7 +385,7 @@ QUnit.test( "box-sizing:border-box child of a hidden elem (or unconnected node)
 	assert.equal( $divUnconnected.outerWidth(), $divNormal.outerWidth(), "unconnected element outerWidth() is wrong see #10413" );
 	assert.equal( $divUnconnected.outerWidth( true ), $divNormal.outerWidth( true ), "unconnected element outerWidth( true ) is wrong see #10413" );
 
-	// Support: IE 10-11, Edge
+	// Support: IE 10 - 11, Edge 12 - 13+
 	// Child height is not always decimal
 	assert.equal( $divUnconnected.height().toFixed( 3 ), $divNormal.height().toFixed( 3 ), "unconnected element height() is wrong see #10413" );
 	assert.equal( $divUnconnected.innerHeight().toFixed( 3 ), $divNormal.innerHeight().toFixed( 3 ), "unconnected element innerHeight() is wrong see #10413" );
@@ -406,11 +406,13 @@ QUnit.test( "passing undefined is a setter #5571", function( assert ) {
 } );
 
 QUnit.test( "setters with and without box-sizing:border-box", function( assert ) {
-	assert.expect( 60 );
+	assert.expect( 120 );
 
-	var parent = jQuery( "#foo" ).css({ width: "200px", height: "200px", "font-size": "16px" }),
+	var parent = jQuery( "#foo" ).css( { width: "200px", height: "200px", "font-size": "16px" } ),
 		el_bb = jQuery( "<div style='margin:5px;padding:1px;border:2px solid black;box-sizing:border-box;'></div>" ).appendTo( parent ),
-		el = jQuery( "<div style='margin:5px;padding:1px;border:2px solid black;'></div>" ).appendTo( parent );
+		el = jQuery( "<div style='margin:5px;padding:1px;border:2px solid black;'></div>" ).appendTo( parent ),
+		el_bb_np = jQuery( "<div style='margin:5px; padding:0px; border:0px solid green;box-sizing:border-box;'></div>" ).appendTo( parent ),
+		el_np = jQuery( "<div style='margin:5px; padding:0px; border:0px solid green;'></div>" ).appendTo( parent );
 
 	jQuery.each( {
 		"number": { set: 100, expected: 100 },
@@ -421,32 +423,56 @@ QUnit.test( "setters with and without box-sizing:border-box", function( assert )
 		assert.equal( el_bb.innerWidth( values.set ).width(), values.expected - 2, "test border-box innerWidth(" + units + ") by roundtripping" );
 		assert.equal( el_bb.outerWidth( values.set ).width(), values.expected - 6, "test border-box outerWidth(" + units + ") by roundtripping" );
 		assert.equal( el_bb.outerWidth( values.set, false ).width(), values.expected - 6, "test border-box outerWidth(" + units + ", false) by roundtripping" );
-		assert.equal( el_bb.outerWidth( values.set, true ).width(), values.expected - 16, "test border-box innerWidth(" + units + ", true) by roundtripping" );
+		assert.equal( el_bb.outerWidth( values.set, true ).width(), values.expected - 16, "test border-box outerWidth(" + units + ", true) by roundtripping" );
 
 		assert.equal( el_bb.height( values.set ).height(), values.expected, "test border-box height(" + units + ") by roundtripping" );
 		assert.equal( el_bb.innerHeight( values.set ).height(), values.expected - 2, "test border-box innerHeight(" + units + ") by roundtripping" );
 		assert.equal( el_bb.outerHeight( values.set ).height(), values.expected - 6, "test border-box outerHeight(" + units + ") by roundtripping" );
 		assert.equal( el_bb.outerHeight( values.set, false ).height(), values.expected - 6, "test border-box outerHeight(" + units + ", false) by roundtripping" );
-		assert.equal( el_bb.outerHeight( values.set, true ).height(), values.expected - 16, "test border-box innerHeight(" + units + ", true) by roundtripping" );
+		assert.equal( el_bb.outerHeight( values.set, true ).height(), values.expected - 16, "test border-box outerHeight(" + units + ", true) by roundtripping" );
 
 		assert.equal( el.width( values.set ).width(), values.expected, "test non-border-box width(" + units + ") by roundtripping" );
 		assert.equal( el.innerWidth( values.set ).width(), values.expected - 2, "test non-border-box innerWidth(" + units + ") by roundtripping" );
 		assert.equal( el.outerWidth( values.set ).width(), values.expected - 6, "test non-border-box outerWidth(" + units + ") by roundtripping" );
 		assert.equal( el.outerWidth( values.set, false ).width(), values.expected - 6, "test non-border-box outerWidth(" + units + ", false) by roundtripping" );
-		assert.equal( el.outerWidth( values.set, true ).width(), values.expected - 16, "test non-border-box innerWidth(" + units + ", true) by roundtripping" );
+		assert.equal( el.outerWidth( values.set, true ).width(), values.expected - 16, "test non-border-box outerWidth(" + units + ", true) by roundtripping" );
 
 		assert.equal( el.height( values.set ).height(), values.expected, "test non-border-box height(" + units + ") by roundtripping" );
 		assert.equal( el.innerHeight( values.set ).height(), values.expected - 2, "test non-border-box innerHeight(" + units + ") by roundtripping" );
 		assert.equal( el.outerHeight( values.set ).height(), values.expected - 6, "test non-border-box outerHeight(" + units + ") by roundtripping" );
 		assert.equal( el.outerHeight( values.set, false ).height(), values.expected - 6, "test non-border-box outerHeight(" + units + ", false) by roundtripping" );
-		assert.equal( el.outerHeight( values.set, true ).height(), values.expected - 16, "test non-border-box innerHeight(" + units + ", true) by roundtripping" );
+		assert.equal( el.outerHeight( values.set, true ).height(), values.expected - 16, "test non-border-box outerHeight(" + units + ", true) by roundtripping" );
+
+		assert.equal( el_bb_np.width( values.set ).width(), values.expected, "test border-box width and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_bb_np.innerWidth( values.set ).width(), values.expected, "test border-box innerWidth and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_bb_np.outerWidth( values.set ).width(), values.expected, "test border-box outerWidth and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_bb_np.outerWidth( values.set, false ).width(), values.expected, "test border-box outerWidth and negative padding(" + units + ", false) by roundtripping" );
+		assert.equal( el_bb_np.outerWidth( values.set, true ).width(), values.expected - 10, "test border-box outerWidth and negative padding(" + units + ", true) by roundtripping" );
+
+		assert.equal( el_bb_np.height( values.set ).height(), values.expected, "test border-box height  and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_bb_np.innerHeight( values.set ).height(), values.expected, "test border-box innerHeight and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_bb_np.outerHeight( values.set ).height(), values.expected, "test border-box outerHeight and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_bb_np.outerHeight( values.set, false ).height(), values.expected, "test border-box outerHeight and negative padding(" + units + ", false) by roundtripping" );
+		assert.equal( el_bb_np.outerHeight( values.set, true ).height(), values.expected - 10, "test border-box outerHeight and negative padding(" + units + ", true) by roundtripping" );
+
+		assert.equal( el_np.width( values.set ).width(), values.expected, "test non-border-box width  and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_np.innerWidth( values.set ).width(), values.expected, "test non-border-box innerWidth and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_np.outerWidth( values.set ).width(), values.expected, "test non-border-box outerWidth and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_np.outerWidth( values.set, false ).width(), values.expected, "test non-border-box outerWidth and negative padding(" + units + ", false) by roundtripping" );
+		assert.equal( el_np.outerWidth( values.set, true ).width(), values.expected - 10, "test non-border-box outerWidth and negative padding(" + units + ", true) by roundtripping" );
+
+		assert.equal( el_np.height( values.set ).height(), values.expected, "test non-border-box height and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_np.innerHeight( values.set ).height(), values.expected, "test non-border-box innerHeight and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_np.outerHeight( values.set ).height(), values.expected, "test non-border-box outerHeight and negative padding(" + units + ") by roundtripping" );
+		assert.equal( el_np.outerHeight( values.set, false ).height(), values.expected, "test non-border-box outerHeight and negative padding(" + units + ", false) by roundtripping" );
+		assert.equal( el_np.outerHeight( values.set, true ).height(), values.expected - 10, "test non-border-box outerHeight and negative padding(" + units + ", true) by roundtripping" );
 	} );
 } );
 
 testIframe(
-	"dimensions/documentLarge",
 	"window vs. large document",
-	function( jQuery, window, document, assert ) {
+	"dimensions/documentLarge.html",
+	function( assert, jQuery, window, document ) {
 		assert.expect( 2 );
 
 		assert.ok( jQuery( document ).height() > jQuery( window ).height(), "document height is larger than window height" );
@@ -471,4 +497,34 @@ QUnit.test( "allow modification of coordinates argument (gh-1848)", function( as
 		"coordinates are modified (got offset.top: " +  offsetTop + ")" );
 } );
 
+QUnit.test( "outside view position (gh-2836)", function( assert ) {
+
+	// This test ported from gh-2836 example
+	assert.expect( 1 );
+
+	var parent,
+		html = [
+		"<div id=div-gh-2836>",
+			"<div></div>",
+			"<div></div>",
+			"<div></div>",
+			"<div></div>",
+			"<div></div>",
+		"</div>"
+	].join( "" ),
+	stop = assert.async();
+
+	parent = jQuery( html );
+	parent.appendTo( "#qunit-fixture" );
+
+	parent.one( "scroll", function() {
+		var pos = parent.find( "div" ).eq( 3 ).position();
+
+		assert.strictEqual( pos.top, -100 );
+		stop();
+	} );
+
+	parent.scrollTop( 400 );
+} );
+
 } )();
diff --git a/test/unit/effects.js b/test/unit/effects.js
index ae43674..eafe4b1 100644
--- a/test/unit/effects.js
+++ b/test/unit/effects.js
@@ -50,7 +50,7 @@ QUnit.test( "show() basic", function( assert ) {
 QUnit.test( "show()", function( assert ) {
 	assert.expect( 27 );
 
-	var div, speeds, old, test,
+	var div, speeds, test,
 		hiddendiv = jQuery( "div.hidden" );
 
 	assert.equal( jQuery.css( hiddendiv[ 0 ], "display" ), "none", "hiddendiv is display: none" );
@@ -99,13 +99,9 @@ QUnit.test( "show()", function( assert ) {
 		"<div id='show-tests'>" +
 		"<div><p><a href='#'></a></p><code></code><pre></pre><span></span></div>" +
 		"<table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody></table>" +
-		"<ul><li></li></ul></div>" +
-		"<table id='test-table'></table>"
+		"<ul><li></li></ul></div>"
 	).appendTo( "#qunit-fixture" ).find( "*" ).css( "display", "none" );
 
-	old = jQuery( "#test-table" ).show().css( "display" ) !== "table";
-	jQuery( "#test-table" ).remove();
-
 	test = {
 		"div": "block",
 		"p": "block",
@@ -113,14 +109,14 @@ QUnit.test( "show()", function( assert ) {
 		"code": "inline",
 		"pre": "block",
 		"span": "inline",
-		"table": old ? "block" : "table",
-		"thead": old ? "block" : "table-header-group",
-		"tbody": old ? "block" : "table-row-group",
-		"tr": old ? "block" : "table-row",
-		"th": old ? "block" : "table-cell",
-		"td": old ? "block" : "table-cell",
+		"table": "table",
+		"thead": "table-header-group",
+		"tbody": "table-row-group",
+		"tr": "table-row",
+		"th": "table-cell",
+		"td": "table-cell",
 		"ul": "block",
-		"li": old ? "block" : "list-item"
+		"li": "list-item"
 	};
 
 	jQuery.each( test, function( selector, expected ) {
@@ -144,34 +140,28 @@ supportjQuery.each( hideOptions, function( type, setup ) {
 			"<div><p><a href='#'></a></p><code></code><pre></pre><span></span></div>" +
 			"<table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody>" +
 				"</table>" +
-			"<ul><li></li></ul></div>" +
-			"<table id='test-table'></table>"
+			"<ul><li></li></ul></div>"
 		).appendTo( "#qunit-fixture" ).find( "*" ).each( setup );
 
-		var test,
-			old = jQuery( "#test-table" ).show().css( "display" ) !== "table";
-
-		jQuery( "#test-table" ).remove();
-
 		// Note: inline elements are expected to be inline-block
 		// because we're showing width/height
 		// Can't animate width/height inline
 		// See #14344
-		test = {
+		var test = {
 			"div": "block",
 			"p": "block",
 			"a": "inline",
 			"code": "inline",
 			"pre": "block",
 			"span": "inline",
-			"table": old ? "block" : "table",
-			"thead": old ? "block" : "table-header-group",
-			"tbody": old ? "block" : "table-row-group",
-			"tr": old ? "block" : "table-row",
-			"th": old ? "block" : "table-cell",
-			"td": old ? "block" : "table-cell",
+			"table": "table",
+			"thead": "table-header-group",
+			"tbody": "table-row-group",
+			"tr": "table-row",
+			"th": "table-cell",
+			"td": "table-cell",
 			"ul": "block",
-			"li": old ? "block" : "list-item"
+			"li": "list-item"
 		};
 
 		jQuery.each( test, function( selector ) {
@@ -369,10 +359,8 @@ QUnit.test( "animate block width/height", function( assert ) {
 QUnit.test( "animate table width/height", function( assert ) {
 	assert.expect( 1 );
 
-	var displayMode = jQuery( "#table" ).css( "display" ) !== "table" ? "block" : "table";
-
 	jQuery( "#table" ).animate( { width: 42, height: 42 }, 100, function() {
-		assert.equal( jQuery( this ).css( "display" ), displayMode, "display mode is correct" );
+		assert.equal( jQuery( this ).css( "display" ), "table", "display mode is correct" );
 	} );
 	this.clock.tick( 100 );
 } );
@@ -550,7 +538,7 @@ QUnit.test( "animate duration 0", function( assert ) {
 	assert.expect( 11 );
 
 	var $elem,
-		$elems = jQuery( [ { a:0 },{ a:0 } ] ),
+		$elems = jQuery( [ { a:0 }, { a:0 } ] ),
 		counter = 0;
 
 	assert.equal( jQuery.timers.length, 0, "Make sure no animation was running from another test" );
@@ -661,35 +649,44 @@ QUnit.test( "stop()", function( assert ) {
 	this.clock.tick( 100 );
 } );
 
-QUnit.test( "stop() - several in queue", function( assert ) {
-	assert.expect( 5 );
+// In IE9 inside testswarm this test doesn't work properly
+( function() {
+	var type = "test";
 
-	var nw, $foo = jQuery( "#foo" );
+	if ( QUnit.isSwarm && /msie 9\.0/i.test( window.navigator.userAgent ) ) {
+		type = "skip";
+	}
 
-	// default duration is 400ms, so 800px ensures we aren't 0 or 1 after 1ms
-	$foo.hide().css( "width", 800 );
+	QUnit[ type ]( "stop() - several in queue", function( assert ) {
+		assert.expect( 5 );
 
-	$foo.animate( { "width": "show" }, 400, "linear" );
-	$foo.animate( { "width": "hide" } );
-	$foo.animate( { "width": "show" } );
+		var nw, $foo = jQuery( "#foo" );
 
-	this.clock.tick( 1 );
+		// default duration is 400ms, so 800px ensures we aren't 0 or 1 after 1ms
+		$foo.hide().css( "width", 800 );
 
-	jQuery.fx.tick();
-	assert.equal( $foo.queue().length, 3, "3 in the queue" );
+		$foo.animate( { "width": "show" }, 400, "linear" );
+		$foo.animate( { "width": "hide" } );
+		$foo.animate( { "width": "show" } );
 
-	nw = $foo.css( "width" );
-	assert.notEqual( parseFloat( nw ), 1, "An animation occurred " + nw );
-	$foo.stop();
+		this.clock.tick( 1 );
 
-	assert.equal( $foo.queue().length, 2, "2 in the queue" );
-	nw = $foo.css( "width" );
-	assert.notEqual( parseFloat( nw ), 1, "Stop didn't reset the animation " + nw );
+		jQuery.fx.tick();
+		assert.equal( $foo.queue().length, 3, "3 in the queue" );
 
-	$foo.stop( true );
+		nw = $foo.css( "width" );
+		assert.notEqual( parseFloat( nw ), 1, "An animation occurred " + nw );
+		$foo.stop();
 
-	assert.equal( $foo.queue().length, 0, "0 in the queue" );
-} );
+		assert.equal( $foo.queue().length, 2, "2 in the queue" );
+		nw = $foo.css( "width" );
+		assert.notEqual( parseFloat( nw ), 1, "Stop didn't reset the animation " + nw );
+
+		$foo.stop( true );
+
+		assert.equal( $foo.queue().length, 0, "0 in the queue" );
+	} );
+} )();
 
 QUnit.test( "stop(clearQueue)", function( assert ) {
 	assert.expect( 4 );
@@ -1031,7 +1028,7 @@ jQuery.each( {
 				jQuery( elem ).remove();
 
 			} );
-			this.clock.tick( 50 );
+			this.clock.tick( 100 );
 		} );
 	} );
 } );
@@ -1389,13 +1386,7 @@ QUnit.test( "Do not append px to 'fill-opacity' #9548", function( assert ) {
 	var $div = jQuery( "<div>" ).appendTo( "#qunit-fixture" );
 
 	$div.css( "fill-opacity", 0 ).animate( { "fill-opacity": 1.0 }, 0, function() {
-
-		// Support: Android 2.3 (no support for fill-opacity)
-		if ( jQuery( this ).css( "fill-opacity" ) ) {
-			assert.equal( jQuery( this ).css( "fill-opacity" ), 1, "Do not append px to 'fill-opacity'" );
-		} else {
-			assert.ok( true, "No support for fill-opacity CSS property" );
-		}
+		assert.equal( jQuery( this ).css( "fill-opacity" ), 1, "Do not append px to 'fill-opacity'" );
 		$div.remove();
 	} );
 } );
@@ -1556,15 +1547,17 @@ QUnit.test( "animate should set display for disconnected nodes", function( asser
 	assert.expect( 20 );
 
 	var env = this,
-		methods = {
-			toggle: [ 1 ],
-			slideToggle: [],
+		showMethods = {
 			fadeIn: [],
 			fadeTo: [ "fast", 0.5 ],
 			slideDown: [ "fast" ],
 			show: [ 1 ],
 			animate: [ { width: "show" } ]
 		},
+		toggleMethods = {
+			toggle: [ 1 ],
+			slideToggle: []
+		},
 		$divEmpty = jQuery( "<div/>" ),
 		$divTest = jQuery( "<div>test</div>" ),
 		$divNone = jQuery( "<div style='display: none;'/>" ),
@@ -1587,7 +1580,7 @@ QUnit.test( "animate should set display for disconnected nodes", function( asser
 
 	assert.expectJqData( env, $divNone[ 0 ], "olddisplay" );
 
-	jQuery.each( methods, function( name, opt ) {
+	jQuery.each( showMethods, function( name, opt ) {
 		jQuery.fn[ name ].apply( jQuery( "<div/>" ), opt.concat( [ function() {
 			assert.strictEqual( jQuery( this ).css( "display" ), nullParentDisplay,
 				"." + name + " block with null parentNode" );
@@ -1598,6 +1591,17 @@ QUnit.test( "animate should set display for disconnected nodes", function( asser
 				"." + name + " block under fragment" );
 		} ] ) );
 	} );
+	jQuery.each( toggleMethods, function( name, opt ) {
+		jQuery.fn[ name ].apply( jQuery( "<div/>" ), opt.concat( [ function() {
+			assert.strictEqual( jQuery( this ).css( "display" ), "none",
+				"." + name + " block with null parentNode" );
+		} ] ) );
+
+		jQuery.fn[ name ].apply( jQuery( "<div>test</div>" ), opt.concat( [ function() {
+			assert.strictEqual( jQuery( this ).css( "display" ), "none",
+				"." + name + " block under fragment" );
+		} ] ) );
+	} );
 	clock.tick( 400 );
 } );
 
@@ -1841,7 +1845,7 @@ QUnit.test( "non-px animation handles non-numeric start (#11971)", function( ass
 	this.clock.tick( 10 );
 } );
 
-QUnit.test("Animation callbacks (#11797)", function( assert ) {
+QUnit.test( "Animation callbacks (#11797)", function( assert ) {
 	assert.expect( 16 );
 
 	var prog = 0,
@@ -1962,7 +1966,7 @@ QUnit.test( "Animation callbacks in order (#2292)", function( assert ) {
 		always: function() {
 			assert.step( 5 );
 		}
-	}).finish();
+	} ).finish();
 
 	this.clock.tick( dur + 10 );
 } );
diff --git a/test/unit/event.js b/test/unit/event.js
index 4a5611b..02aad5e 100644
--- a/test/unit/event.js
+++ b/test/unit/event.js
@@ -5,6 +5,28 @@ QUnit.module( "event", {
 	teardown: moduleTeardown
 } );
 
+QUnit.test( "null or undefined handler", function( assert ) {
+	assert.expect( 4 );
+
+	// Supports Fixes bug #7229
+	try {
+		jQuery( "#firstp" ).on( "click", null );
+		assert.ok( true, "Passing a null handler will not throw an exception" );
+	} catch ( e ) {}
+
+	try {
+		jQuery( "#firstp" ).on( "click", undefined );
+		assert.ok( true, "Passing an undefined handler will not throw an exception" );
+	} catch ( e ) {}
+
+	var expectedElem = jQuery( "#firstp" );
+	var actualElem = expectedElem.on( "click", null );
+	assert.equal( actualElem, expectedElem, "Passing a null handler should return the original element" );
+
+	actualElem = expectedElem.on( "click", undefined );
+	assert.equal( actualElem, expectedElem, "Passing a null handler should return the original element" );
+} );
+
 QUnit.test( "on() with non-null,defined data", function( assert ) {
 
 	assert.expect( 2 );
@@ -56,7 +78,7 @@ QUnit.test( "on(), with data", function( assert ) {
 
 	handler = function( event ) {
 		assert.ok( event.data, "on() with data, check passed data exists" );
-		assert.equal( event.data[ "foo" ], "bar", "on() with data, Check value of passed data" );
+		assert.equal( event.data.foo, "bar", "on() with data, Check value of passed data" );
 	};
 	jQuery( "#firstp" ).on( "click", { "foo": "bar" }, handler ).trigger( "click" ).off( "click", handler );
 
@@ -73,7 +95,7 @@ QUnit.test( "click(), with data", function( assert ) {
 	assert.expect( 3 );
 	var handler = function( event ) {
 		assert.ok( event.data, "on() with data, check passed data exists" );
-		assert.equal( event.data[ "foo" ], "bar", "on() with data, Check value of passed data" );
+		assert.equal( event.data.foo, "bar", "on() with data, Check value of passed data" );
 	};
 	jQuery( "#firstp" ).on( "click", { "foo": "bar" }, handler ).trigger( "click" ).off( "click", handler );
 
@@ -177,7 +199,7 @@ QUnit.test( "on(), namespace with special add", function( assert ) {
 			assert.ok( true, "Test event fired." );
 		} );
 
-	jQuery.event.special[ "test" ] = {
+	jQuery.event.special.test = {
 		_default: function( e, data ) {
 			assert.equal( e.type, "test", "Make sure we're dealing with a test event." );
 			assert.ok( data, "And that trigger data was passed." );
@@ -229,7 +251,7 @@ QUnit.test( "on(), namespace with special add", function( assert ) {
 	// Should trigger 2
 	div.appendTo( "#qunit-fixture" ).remove();
 
-	delete jQuery.event.special[ "test" ];
+	delete jQuery.event.special.test;
 } );
 
 QUnit.test( "on(), no data", function( assert ) {
@@ -409,20 +431,13 @@ QUnit.test( "on bubbling, isDefaultPrevented, stopImmediatePropagation", functio
 	$anchor2.off( "click" );
 	$main.off( "click", "**" );
 
-	// Android 2.3 doesn't support stopImmediatePropagation; jQuery fallbacks to stopPropagation
-	// in such a case.
-	// Support: Android 2.3
-	if ( /android 2\.3/i.test( navigator.userAgent ) ) {
-		assert.ok( true, "Android 2.3, skipping native stopImmediatePropagation check" );
-	} else {
-		$anchor2.on( "click", function( e ) {
-			e.stopImmediatePropagation();
-			assert.ok( true, "anchor was clicked and prop stopped" );
-		} );
-		$anchor2[ 0 ].addEventListener( "click", neverCallMe, false );
-		fakeClick( $anchor2 );
-		$anchor2[ 0 ].removeEventListener( "click", neverCallMe );
-	}
+	$anchor2.on( "click", function( e ) {
+		e.stopImmediatePropagation();
+		assert.ok( true, "anchor was clicked and prop stopped" );
+	} );
+	$anchor2[ 0 ].addEventListener( "click", neverCallMe, false );
+	fakeClick( $anchor2 );
+	$anchor2[ 0 ].removeEventListener( "click", neverCallMe );
 } );
 
 QUnit.test( "on(), iframes", function( assert ) {
@@ -711,8 +726,8 @@ QUnit.test( "on()/trigger()/off() on plain object", function( assert ) {
 	events = jQuery._data( obj, "events" );
 	assert.ok( events, "Object has events bound." );
 	assert.equal( obj[ "events" ], undefined, "Events object on plain objects is not events" );
-	assert.equal( obj[ "test" ], undefined, "Make sure that test event is not on the plain object." );
-	assert.equal( obj[ "handle" ], undefined, "Make sure that the event handler is not on the plain object." );
+	assert.equal( obj.test, undefined, "Make sure that test event is not on the plain object." );
+	assert.equal( obj.handle, undefined, "Make sure that the event handler is not on the plain object." );
 
 	// Should trigger 1
 	jQuery( obj ).trigger( "test" );
@@ -1177,7 +1192,7 @@ QUnit.test( "trigger(eventObject, [data], [fn])", function( assert ) {
 	//$child.on("foo", error );
 
 	event = new jQuery.Event( "foo" );
-	$child.trigger( event, [ 1,2,3 ] ).off();
+	$child.trigger( event, [ 1, 2, 3 ] ).off();
 	assert.equal( event.result, "result", "Check event.result attribute" );
 
 	// Will error if it bubbles
@@ -1274,6 +1289,21 @@ QUnit.test( "Delegated events in SVG (#10791; #13180)", function( assert ) {
 	jQuery( "#qunit-fixture" ).off( "click" );
 } );
 
+QUnit.test( "Delegated events with malformed selectors (gh-3071)", function( assert ) {
+	assert.expect( 3 );
+
+	assert.throws( function() {
+		jQuery( "#foo" ).on( "click", ":not", function() {} );
+	}, "malformed selector throws on attach" );
+
+	assert.throws( function() {
+		jQuery( "#foo" ).on( "click", "nonexistent:not", function() {} );
+	}, "short-circuitable malformed selector throws on attach" );
+
+	jQuery( "#foo > :first-child" ).click();
+	assert.ok( true, "malformed selector does not throw on event" );
+} );
+
 QUnit.test( "Delegated events in forms (#10844; #11145; #8165; #11382, #11764)", function( assert ) {
 	assert.expect( 5 );
 
@@ -1367,29 +1397,26 @@ QUnit.test( "Submit event can be stopped (#11049)", function( assert ) {
 	form.remove();
 } );
 
-// Test beforeunload event only if it supported.
-// Support: iOS 7+, Android<4.0
-// iOS & old Android have the window.onbeforeunload field but don't support the beforeunload
+// Support: iOS 7 - 9
+// iOS has the window.onbeforeunload field but doesn't support the beforeunload
 // handler making it impossible to feature-detect the support.
-if ( window.onbeforeunload === null &&
-	!/(ipad|iphone|ipod|android 2\.3)/i.test( navigator.userAgent ) ) {
-	QUnit.test( "on(beforeunload)", 1, function( assert ) {
-		var iframe = jQuery( jQuery.parseHTML( "<iframe src='data/event/onbeforeunload.html'><iframe>" ) );
-		var done = assert.async();
+QUnit[ /(ipad|iphone|ipod)/i.test( navigator.userAgent ) ? "skip" : "test" ](
+	"on(beforeunload)", 1, function( assert ) {
+	var iframe = jQuery( jQuery.parseHTML( "<iframe src='data/event/onbeforeunload.html'><iframe>" ) );
+	var done = assert.async();
 
-		window.onmessage = function( event ) {
-			var payload = JSON.parse( event.data );
+	window.onmessage = function( event ) {
+		var payload = JSON.parse( event.data );
 
-			assert.ok( payload.event, "beforeunload", "beforeunload event" );
+		assert.ok( payload.event, "beforeunload", "beforeunload event" );
 
-			iframe.remove();
-			window.onmessage = null;
-			done();
-		};
+		iframe.remove();
+		window.onmessage = null;
+		done();
+	};
 
-		iframe.appendTo( "#qunit-fixture" );
-	} );
-}
+	iframe.appendTo( "#qunit-fixture" );
+} );
 
 QUnit.test( "jQuery.Event( type, props )", function( assert ) {
 
@@ -1574,7 +1601,7 @@ QUnit.test( ".on()/.off()", function( assert ) {
 
 	// Test binding with different this object
 	jQuery( "#body" ).on( "click", "#foo", jQuery.proxy( function() {
-		assert.equal( this[ "foo" ], "bar", "on with event scope" ); }, { "foo": "bar" }
+		assert.equal( this.foo, "bar", "on with event scope" ); }, { "foo": "bar" }
 	) );
 	jQuery( "#foo" ).trigger( "click" );
 	jQuery( "#body" ).off( "click", "#foo" );
@@ -1833,7 +1860,7 @@ QUnit.test( "ignore comment nodes in event delegation (gh-2055)", function( asse
 			.appendTo( $foo.find( "#sap" ) );
 
 	if ( !test() ) {
-		fireNative( $comment[0], "DOMNodeInserted" );
+		fireNative( $comment[ 0 ], "DOMNodeInserted" );
 	}
 } );
 
@@ -2207,7 +2234,7 @@ QUnit.test( ".on and .off", function( assert ) {
 QUnit.test( "special on name mapping", function( assert ) {
 	assert.expect( 7 );
 
-	jQuery.event.special[ "slap" ] = {
+	jQuery.event.special.slap = {
 		bindType: "click",
 		delegateType: "swing",
 		handle: function( event ) {
@@ -2238,9 +2265,9 @@ QUnit.test( "special on name mapping", function( assert ) {
 			.trigger( "swing" )
 		.end()
 		.remove();
-	delete jQuery.event.special[ "slap" ];
+	delete jQuery.event.special.slap;
 
-	jQuery.event.special[ "gutfeeling" ] = {
+	jQuery.event.special.gutfeeling = {
 		bindType: "click",
 		delegateType: "click",
 		handle: function( event ) {
@@ -2329,9 +2356,9 @@ QUnit.test( "clone() delegated events (#11076)", function( assert ) {
 		clone = table.clone( true );
 
 	clone.find( "td" ).trigger( "click" );
-	assert.equal( counter[ "center" ], 1, "first child" );
-	assert.equal( counter[ "fold" ], 1, "last child" );
-	assert.equal( counter[ "centerfold" ], 2, "all children" );
+	assert.equal( counter.center, 1, "first child" );
+	assert.equal( counter.fold, 1, "last child" );
+	assert.equal( counter.centerfold, 2, "all children" );
 
 	table.remove();
 	clone.remove();
@@ -2384,7 +2411,8 @@ QUnit.test( "event object properties on natively-triggered event", function( ass
 		$link = jQuery( link ),
 		evt = document.createEvent( "MouseEvents" );
 
-	// IE9+ requires element to be in the body before it will dispatch
+	// Support: IE <=9 - 11 only
+	// IE requires element to be in the body before it will dispatch
 	$link.appendTo( "body" ).on( "click", function( e ) {
 
 		// Not trying to assert specific values here, just ensure the property exists
@@ -2397,36 +2425,28 @@ QUnit.test( "event object properties on natively-triggered event", function( ass
 	$link.off( "click" ).remove();
 } );
 
-QUnit.test( "fixHooks extensions", function( assert ) {
+QUnit.test( "addProp extensions", function( assert ) {
 	assert.expect( 2 );
 
-	// IE requires focusable elements to be visible, so append to body
-	var $fixture = jQuery( "<input type='text' id='hook-fixture' />" ).appendTo( "body" ),
-		saved = jQuery.event.fixHooks.click;
+	var $fixture = jQuery( "<div>" ).appendTo( "#qunit-fixture" );
 
 	// Ensure the property doesn't exist
 	$fixture.on( "click", function( event ) {
-		assert.ok( !( "blurrinessLevel" in event ), "event.blurrinessLevel does not exist" );
+		assert.ok( !( "testProperty" in event ), "event.testProperty does not exist" );
 	} );
 	fireNative( $fixture[ 0 ], "click" );
 	$fixture.off( "click" );
 
-	jQuery.event.fixHooks.click = {
-		filter: function( event ) {
-			event.blurrinessLevel = 42;
-			return event;
-		}
-	};
+	jQuery.event.addProp( "testProperty", function() { return 42; } );
 
 	// Trigger a native click and ensure the property is set
 	$fixture.on( "click", function( event ) {
-		assert.equal( event.blurrinessLevel, 42, "event.blurrinessLevel was set" );
+		assert.equal( event.testProperty, 42, "event.testProperty getter was invoked" );
 	} );
 	fireNative( $fixture[ 0 ], "click" );
+	$fixture.off( "click" );
 
-	delete jQuery.event.fixHooks.click;
-	$fixture.off( "click" ).remove();
-	jQuery.event.fixHooks.click = saved;
+	$fixture.remove();
 } );
 
 QUnit.test( "drag/drop events copy mouse-related event properties (gh-1925, gh-2009)", function( assert ) {
@@ -2463,10 +2483,10 @@ QUnit.test( "focusin using non-element targets", function( assert ) {
 
 } );
 
-testIframeWithCallback(
+testIframe(
 	"focusin from an iframe",
 	"event/focusinCrossFrame.html",
-	function( frameDoc, assert ) {
+	function( assert, framejQuery, frameWin, frameDoc ) {
 		assert.expect( 1 );
 
 		var input = jQuery( frameDoc ).find( "#frame-input" );
@@ -2494,10 +2514,10 @@ testIframeWithCallback(
 	}
 );
 
-testIframeWithCallback(
+testIframe(
 	"jQuery.ready promise",
 	"event/promiseReady.html",
-	function( isOk, assert ) {
+	function( assert, jQuery, window, document, isOk ) {
 		assert.expect( 1 );
 		assert.ok( isOk, "$.when( $.ready ) works" );
 	}
@@ -2505,29 +2525,29 @@ testIframeWithCallback(
 
 // need PHP here to make the incepted IFRAME hang
 if ( hasPHP ) {
-	testIframeWithCallback(
+	testIframe(
 		"jQuery.ready uses interactive",
 		"event/interactiveReady.html",
-		function( isOk, assert ) {
+	function( assert, jQuery, window, document, isOk ) {
 			assert.expect( 1 );
 			assert.ok( isOk, "jQuery fires ready when the DOM can truly be interacted with" );
 		}
 	);
 }
 
-testIframeWithCallback(
+testIframe(
 	"Focusing iframe element",
 	"event/focusElem.html",
-	function( isOk, assert ) {
+	function( assert, jQuery, window, document, isOk ) {
 		assert.expect( 1 );
 		assert.ok( isOk, "Focused an element in an iframe" );
 	}
 );
 
-testIframeWithCallback(
+testIframe(
 	"triggerHandler(onbeforeunload)",
 	"event/triggerunload.html",
-	function( isOk, assert ) {
+	function( assert, jQuery, window, document, isOk ) {
 		assert.expect( 1 );
 		assert.ok( isOk, "Triggered onbeforeunload without an error" );
 	}
@@ -2535,10 +2555,10 @@ testIframeWithCallback(
 
 // need PHP here to make the incepted IFRAME hang
 if ( hasPHP ) {
-	testIframeWithCallback(
+	testIframe(
 		"jQuery.ready synchronous load with long loading subresources",
 		"event/syncReady.html",
-		function( isOk, assert ) {
+		function( assert, jQuery, window, document, isOk ) {
 			assert.expect( 1 );
 			assert.ok( isOk, "jQuery loaded synchronously fires ready when the DOM can truly be interacted with" );
 		}
@@ -2556,7 +2576,7 @@ QUnit.test( "change handler should be detached from element", function( assert )
 			originRemoveEvent( elem, type, handle );
 		};
 
-	jQuery.removeEvent = wrapperRemoveEvent ;
+	jQuery.removeEvent = wrapperRemoveEvent;
 
 	$fixture.on( "change", function() {} );
 	$fixture.off( "change" );
@@ -2716,6 +2736,15 @@ QUnit.test( ".off() removes the expando when there's no more data", function( as
 	}
 } );
 
+QUnit.test( "jQuery.Event( src ) does not require a target property", function( assert ) {
+	assert.expect( 2 );
+
+	var event = jQuery.Event( { type: "offtarget" } );
+
+	assert.equal( event.type, "offtarget", "correct type" );
+	assert.equal( event.target, undefined, "no target" );
+} );
+
 QUnit.test( "preventDefault() on focusin does not throw exception", function( assert ) {
 	assert.expect( 1 );
 
@@ -2770,6 +2799,81 @@ QUnit.test( "Donor event interference", function( assert ) {
 	jQuery( "#donor-input" )[ 0 ].click();
 } );
 
+QUnit.test(
+	"native stop(Immediate)Propagation/preventDefault methods shouldn't be called",
+	function( assert ) {
+		var userAgent = window.navigator.userAgent;
+
+		if ( !( /firefox/i.test( userAgent ) || /safari/i.test( userAgent ) ) ) {
+			assert.expect( 1 );
+			assert.ok( true, "Assertions should run only in Chrome, Safari, Fx & Edge" );
+			return;
+		}
+
+		assert.expect( 3 );
+
+		var checker = {};
+
+		var html = "<div id='donor-outer'>" +
+			"<form id='donor-form'>" +
+				"<input id='donor-input' type='radio' />" +
+			"</form>" +
+		"</div>";
+
+		jQuery( "#qunit-fixture" ).append( html );
+		var outer = jQuery( "#donor-outer" );
+
+		outer
+			.on( "focusin", function( event ) {
+				checker.prevent = sinon.stub( event.originalEvent, "preventDefault" );
+				event.preventDefault();
+			} )
+			.on( "focusin", function( event ) {
+				checker.simple = sinon.stub( event.originalEvent, "stopPropagation" );
+				event.stopPropagation();
+			} )
+			.on( "focusin", function( event ) {
+				checker.immediate = sinon.stub( event.originalEvent, "stopImmediatePropagation" );
+				event.stopImmediatePropagation();
+			} );
+
+		jQuery( "#donor-input" ).trigger( "focus" );
+		assert.strictEqual( checker.simple.called, false );
+		assert.strictEqual( checker.immediate.called, false );
+		assert.strictEqual( checker.prevent.called, false );
+
+		// We need to "off" it, since yes QUnit always update the fixtures
+		// but "focus" event listener is attached to document for focus(in | out)
+		// event and document doesn't get cleared obviously :)
+		outer.off( "focusin" );
+	}
+);
+
+QUnit.test(
+	"isSimulated property always exist on event object",
+	function( assert ) {
+		var userAgent = window.navigator.userAgent;
+
+		if ( !( /firefox/i.test( userAgent ) || /safari/i.test( userAgent ) ) ) {
+			assert.expect( 1 );
+			assert.ok( true, "Assertions should run only in Chrome, Safari, Fx & Edge" );
+			return;
+		}
+
+		assert.expect( 1 );
+
+		var element = jQuery( "<input/>" );
+
+		jQuery( "#qunit-fixture" ).append( element );
+
+		element.on( "focus", function( event ) {
+			assert.notOk( event.isSimulated );
+		} );
+
+		element.trigger( "focus" );
+	}
+);
+
 QUnit.test( "originalEvent property for Chrome, Safari, Fx & Edge of simulated event", function( assert ) {
 	var userAgent = window.navigator.userAgent;
 
@@ -2780,6 +2884,7 @@ QUnit.test( "originalEvent property for Chrome, Safari, Fx & Edge of simulated e
 	}
 
 	assert.expect( 4 );
+	var done = assert.async();
 
 	var html = "<div id='donor-outer'>" +
 		"<form id='donor-form'>" +
@@ -2788,19 +2893,27 @@ QUnit.test( "originalEvent property for Chrome, Safari, Fx & Edge of simulated e
 	"</div>";
 
 	jQuery( "#qunit-fixture" ).append( html );
+	var outer = jQuery( "#donor-outer" );
+
+	outer
+		.on( "focusin", function( event ) {
+			assert.ok( true, "focusin bubbled to outer div" );
+			assert.equal( event.originalEvent.type, "focus",
+				"make sure originalEvent type is correct" );
+			assert.equal( event.type, "focusin", "make sure type is correct" );
+		} );
 
-	jQuery( "#donor-outer" ).on( "focusin", function( event ) {
-		assert.ok( true, "focusin bubbled to outer div" );
-		assert.equal( event.originalEvent.type, "focus",
-		             "make sure originalEvent type is correct" );
-		assert.equal( event.type, "focusin", "make sure type is correct" );
-	} );
 	jQuery( "#donor-input" ).on( "focus", function() {
 		assert.ok( true, "got a focus event from the input" );
+		done();
 	} );
 	jQuery( "#donor-input" ).trigger( "focus" );
-} );
 
+	// We need to "off" it, since yes QUnit always update the fixtures
+	// but "focus" event listener is attached to document for focus(in | out)
+	// event and document doesn't get cleared obviously :)
+	outer.off( "focusin" );
+} );
 
 QUnit[ jQuery.fn.click ? "test" : "skip" ]( "trigger() shortcuts", function( assert ) {
 	assert.expect( 5 );
@@ -2854,7 +2967,7 @@ QUnit[ jQuery.fn.click ? "test" : "skip" ]( "Event aliases", function( assert )
 	} );
 } );
 
-// Support: IE9 (remove when IE9 is no longer supported)
+// Support: IE <=9 only
 // https://msdn.microsoft.com/en-us/library/hh801223(v=vs.85).aspx
 QUnit.test( "VML with special event handlers (trac-7071)", function( assert ) {
 	assert.expect( 1 );
@@ -2907,6 +3020,7 @@ if ( !( /firefox/i.test( window.navigator.userAgent ) ) ) {
 			$text = jQuery( "#text1" ),
 			$radio = jQuery( "#radio1" ).trigger( "focus" );
 
+		// Support: IE <=10 only
 		// IE8-10 fire focus/blur events asynchronously; this is the resulting mess.
 		// IE's browser window must be topmost for this to work properly!!
 		QUnit.stop();
diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js
index 1346cc0..9118524 100644
--- a/test/unit/manipulation.js
+++ b/test/unit/manipulation.js
@@ -406,8 +406,7 @@ QUnit.test( "XML DOM manipulation (#9960)", function( assert ) {
 
 	assert.expect( 5 );
 
-	var scxml1Adopted,
-		xmlDoc1 = jQuery.parseXML( "<scxml xmlns='http://www.w3.org/2005/07/scxml' version='1.0'><state x='100' y='100' initial='actions' id='provisioning'></state><state x='100' y='100' id='error'></state><state x='100' y='100' id='finished' final='true'></state></scxml>" ),
+	var xmlDoc1 = jQuery.parseXML( "<scxml xmlns='http://www.w3.org/2005/07/scxml' version='1.0'><state x='100' y='100' initial='actions' id='provisioning'></state><state x='100' y='100' id='error'></state><state x='100' y='100' id='finished' final='true'></state></scxml>" ),
 		xmlDoc2 = jQuery.parseXML( "<scxml xmlns='http://www.w3.org/2005/07/scxml' version='1.0'><state id='provisioning3'></state></scxml>" ),
 		xml1 = jQuery( xmlDoc1 ),
 		xml2 = jQuery( xmlDoc2 ),
@@ -415,15 +414,6 @@ QUnit.test( "XML DOM manipulation (#9960)", function( assert ) {
 		scxml2 = jQuery( "scxml", xml2 ),
 		state = scxml2.find( "state" );
 
-	// Android 2.3 doesn't automatically adopt nodes from foreign documents.
-	// Although technically this is compliant behavior, no other browser
-	// (including newer Android Browsers) behave in this way so do the adopting
-	// just for Android 2.3.
-	// Support: Android 2.3
-	if ( /android 2\.3/i.test( navigator.userAgent ) ) {
-		state = jQuery( xmlDoc1.adoptNode( state[ 0 ] ) );
-	}
-
 	scxml1.append( state );
 	assert.strictEqual( scxml1[ 0 ].lastChild, state[ 0 ], "append" );
 
@@ -436,13 +426,7 @@ QUnit.test( "XML DOM manipulation (#9960)", function( assert ) {
 	scxml1.find( "#provisioning" ).before( state );
 	assert.strictEqual( scxml1[ 0 ].firstChild, state[ 0 ], "before" );
 
-	// Support: Android 2.3
-	if ( /android 2\.3/i.test( navigator.userAgent ) ) {
-		scxml1Adopted = jQuery( xmlDoc2.adoptNode( scxml1[ 0 ] ) );
-		scxml2.replaceWith( scxml1Adopted );
-	} else {
-		scxml2.replaceWith( scxml1 );
-	}
+	scxml2.replaceWith( scxml1 );
 	assert.deepEqual( jQuery( "state", xml2 ).get(), scxml1.find( "state" ).get(), "replaceWith" );
 } );
 
@@ -527,6 +511,18 @@ QUnit.test( "Tag name processing respects the HTML Standard (gh-2005)", function
 	}
 
 	function assertSpecialCharsSupport( method, characters ) {
+		// Support: Android 4.4 only
+		// Chromium < 35 incorrectly upper-cases µ; Android 4.4 uses such a version by default
+		// (and its WebView, being un-updatable, will use it for eternity) so we need to blacklist
+		// that one for the tests to pass.
+		if ( characters === "µ" && /chrome/i.test( navigator.userAgent ) &&
+			navigator.userAgent.match( /chrome\/(\d+)/i )[ 1 ] < 35 ) {
+			assert.ok( true, "This Chromium version upper-cases µ incorrectly; skip test" );
+			assert.ok( true, "This Chromium version upper-cases µ incorrectly; skip test" );
+			assert.ok( true, "This Chromium version upper-cases µ incorrectly; skip test" );
+			return;
+		}
+
 		var child,
 			codepoint = characters.charCodeAt( 0 ).toString( 16 ).toUpperCase(),
 			description = characters.length === 1 ?
@@ -613,7 +609,7 @@ QUnit.test( "append(xml)", function( assert ) {
 			// IE
 			for ( n = 0, len = aActiveX.length; n < len; n++ ) {
 				try {
-					elem = new ActiveXObject( aActiveX[ n ] );
+					elem = new window.ActiveXObject( aActiveX[ n ] );
 					return elem;
 				} catch ( _ ) {}
 			}
@@ -1179,7 +1175,7 @@ QUnit.test( ".after(disconnected node)", function( assert ) {
 
 QUnit.test( "insertAfter(String)", function( assert ) {
 
-	assert.expect( 1 ) ;
+	assert.expect( 1 );
 
 	var expected = "This is a normal link: Yahoobuga";
 	jQuery( "<b>buga</b>" ).insertAfter( "#yahoo" );
@@ -1215,7 +1211,7 @@ QUnit.test( "insertAfter(jQuery)", function( assert ) {
 
 function testReplaceWith( val, assert ) {
 
-	var tmp, y, child, child2, set, non_existent, $div,
+	var tmp, y, child, child2, set, nonExistent, $div,
 		expected = 29;
 
 	assert.expect( expected );
@@ -1299,8 +1295,8 @@ function testReplaceWith( val, assert ) {
 	assert.deepEqual( jQuery( ".pathological", "#qunit-fixture" ).get(), [],
 		"Replacement with following sibling (context removed)" );
 
-	non_existent = jQuery( "#does-not-exist" ).replaceWith( val( "<b>should not throw an error</b>" ) );
-	assert.equal( non_existent.length, 0, "Length of non existent element." );
+	nonExistent = jQuery( "#does-not-exist" ).replaceWith( val( "<b>should not throw an error</b>" ) );
+	assert.equal( nonExistent.length, 0, "Length of non existent element." );
 
 	$div = jQuery( "<div class='replacewith'></div>" ).appendTo( "#qunit-fixture" );
 	$div.replaceWith( val( "<div class='replacewith'></div><script>" +
@@ -1363,7 +1359,7 @@ QUnit.test( "Empty replaceWith (trac-13401; trac-13596; gh-2204)", function( ass
 		assert.strictEqual( $el.html(), "", "replaceWith(" + label + ")" );
 		$el.html( "<b/>" ).children().replaceWith( function() { return input; } );
 		assert.strictEqual( $el.html(), "", "replaceWith(function returning " + label + ")" );
-		$el.html( "<i/>" ).children().replaceWith( function( i ) { i; return input; } );
+		$el.html( "<i/>" ).children().replaceWith( function( i ) { return input; } );
 		assert.strictEqual( $el.html(), "", "replaceWith(other function returning " + label + ")" );
 		$el.html( "<p/>" ).children().replaceWith( function( i ) {
 			return i ?
@@ -1627,7 +1623,7 @@ QUnit.test( "clone(multiple selected options) (Bug #8129)", function( assert ) {
 	var element = jQuery( "<select><option>Foo</option><option selected>Bar</option><option selected>Baz</option></select>" );
 
 	function getSelectedOptions( collection ) {
-		return collection.find( "option" ).filter(function( option ) {
+		return collection.find( "option" ).filter( function( option ) {
 			return option.selected;
 		} );
 	}
@@ -2241,21 +2237,21 @@ QUnit.test( "domManip executes scripts containing html comments or CDATA (trac-9
 	].join( "\n" ) ).appendTo( "#qunit-fixture" );
 } );
 
-testIframeWithCallback(
+testIframe(
 	"domManip tolerates window-valued document[0] in IE9/10 (trac-12266)",
 	"manipulation/iframe-denied.html",
-	function( test, assert ) {
+	function( assert, jQuery, window, document, test ) {
 		assert.expect( 1 );
 		assert.ok( test.status, test.description );
 	}
 );
 
-testIframeWithCallback(
+testIframe(
 	"domManip executes scripts in iframes in the iframes' context",
 	"manipulation/scripts-context.html",
-	function( frameWindow, bodyElement, html, assert ) {
+	function( assert, framejQuery, frameWindow, frameDocument ) {
 		assert.expect( 2 );
-		jQuery( bodyElement ).append( html );
+		jQuery( frameDocument.body ).append( "<script>window.scriptTest = true;<\x2fscript>" );
 		assert.ok( !window.scriptTest, "script executed in iframe context" );
 		assert.ok( frameWindow.scriptTest, "script executed in iframe context" );
 	}
@@ -2365,18 +2361,6 @@ QUnit.test( "Ensure oldIE creates a new set on appendTo (#8894)", function( asse
 } );
 
 QUnit.asyncTest( "html() - script exceptions bubble (#11743)", 2, function( assert ) {
-
-	// Support: Android 2.3 only
-	// Android 2.3 doesn't fire the window.onerror handler, just accept the reality there.
-	if ( /android 2\.3/i.test( navigator.userAgent ) ) {
-		assert.ok( true, "Test skipped, Android 2.3 doesn't fire window.onerror for " +
-			"errors in dynamically included scripts" );
-		assert.ok( true, "Test skipped, Android 2.3 doesn't fire window.onerror for " +
-			"errors in dynamically included scripts" );
-		QUnit.start();
-		return;
-	}
-
 	var onerror = window.onerror;
 
 	setTimeout( function() {
@@ -2480,7 +2464,7 @@ QUnit.test( "script evaluation (#11795)", function( assert ) {
 	if ( jQuery.ajax ) {
 		Globals.register( "testBar" );
 		jQuery( "#qunit-fixture" ).append( "<script src='" + url( "data/testbar.php" ) + "'/>" );
-		assert.strictEqual( window[ "testBar" ], "bar", "Global script evaluation" );
+		assert.strictEqual( window.testBar, "bar", "Global script evaluation" );
 	} else {
 		assert.ok( true, "No jQuery.ajax" );
 		assert.ok( true, "No jQuery.ajax" );
@@ -2761,8 +2745,8 @@ QUnit.test( "Insert script with data-URI (gh-1887)", 1, function( assert ) {
 	jQuery( fixture ).append( "<script src=\"data:text/javascript,testFoo = 'foo';\"></script>" );
 
 	setTimeout( function() {
-		if ( window[ "testSrcFoo" ] === "foo" ) {
-			assert.strictEqual( window[ "testFoo" ], window[ "testSrcFoo" ], "data-URI script executed" );
+		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" );
diff --git a/test/unit/offset.js b/test/unit/offset.js
index a0f253b..c0df5f1 100644
--- a/test/unit/offset.js
+++ b/test/unit/offset.js
@@ -35,13 +35,6 @@ QUnit.module( "offset", { setup: function() {
 	forceScroll.detach();
 }, teardown: moduleTeardown } );
 
-/*
-	Closure-compiler will roll static methods off of the jQuery object and so they will
-	not be passed with the jQuery object across the windows. To differentiate this, the
-	testIframe callbacks use the "$" symbol to refer to the jQuery object passed from
-	the iframe window and the "jQuery" symbol is used to access any static methods.
-*/
-
 QUnit.test( "empty set", function( assert ) {
 	assert.expect( 2 );
 	assert.strictEqual( jQuery().offset(), undefined, "offset() returns undefined for empty set (#11962)" );
@@ -75,7 +68,7 @@ QUnit.test( "hidden (display: none) element", function( assert ) {
 	assert.equal( result.left, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" );
 } );
 
-testIframe( "offset/absolute", "absolute", function( $, iframe, document, assert ) {
+testIframe( "absolute", "offset/absolute.html", function( assert, $, iframe ) {
 	assert.expect( 4 );
 
 	var doc = iframe.document,
@@ -86,8 +79,8 @@ testIframe( "offset/absolute", "absolute", function( $, iframe, document, assert
 		{ "id": "#absolute-1", "top": 1, "left": 1 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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
@@ -95,12 +88,12 @@ testIframe( "offset/absolute", "absolute", function( $, iframe, document, assert
 		{ "id": "#absolute-1", "top": 0, "left": 0 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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 ) {
+testIframe( "absolute", "offset/absolute.html", function( assert, $ ) {
 	assert.expect( 178 );
 
 	var tests, offset;
@@ -113,8 +106,8 @@ testIframe( "offset/absolute", "absolute", function( $, window, document, assert
 		{ "id": "#absolute-2",     "top": 20, "left": 20 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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
@@ -125,8 +118,8 @@ testIframe( "offset/absolute", "absolute", function( $, window, document, assert
 		{ "id": "#absolute-2",     "top": 19, "left": 19 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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
@@ -154,38 +147,38 @@ testIframe( "offset/absolute", "absolute", function( $, window, document, assert
 		{ "id": "#absolute-1",     "top":  1, "left":  1 }
 	];
 	jQuery.each( tests, function() {
-		$( 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 } );
+		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 ) {
+		$( 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 };
 		} );
-		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 ) + " })" );
+		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( { "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.id ).offset( { "top": this.top, "left": this.left, "using": function( props ) {
 			$( this ).css( {
 				"top":  props.top  + 1,
 				"left": props.left + 1
 			} );
 		} } );
-		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 })" );
+		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 ) {
+testIframe( "relative", "offset/relative.html", function( assert, $ ) {
 	assert.expect( 64 );
 
 	// get offset
@@ -196,8 +189,8 @@ testIframe( "offset/relative", "relative", function( $, window, document, assert
 		{ "id": "#relative-2-1",   "top": 149, "left": 52 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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
@@ -208,8 +201,8 @@ testIframe( "offset/relative", "relative", function( $, window, document, assert
 		{ "id": "#relative-2-1",   "top": 5, "left": 5 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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
@@ -228,22 +221,22 @@ testIframe( "offset/relative", "relative", function( $, window, document, assert
 		{ "id": "#relative-1",   "top":   7, "left":   7 }
 	];
 	jQuery.each( tests, function() {
-		$( 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 } );
+		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.id ).offset( { "top": this.top, "left": this.left, "using": function( props ) {
 			$( this ).css( {
 				"top":  props.top  + 1,
 				"left": props.left + 1
 			} );
 		} } );
-		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 })" );
+		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( $, window, document, assert ) {
+testIframe( "static", "offset/static.html", function( assert, $ ) {
 	assert.expect( 80 );
 
 	// get offset
@@ -254,8 +247,8 @@ testIframe( "offset/static", "static", function( $, window, document, assert ) {
 		{ "id": "#static-2",     "top": 122, left: 7 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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
@@ -266,8 +259,8 @@ testIframe( "offset/static", "static", function( $, window, document, assert ) {
 		{ "id": "#static-2",     "top": 121, "left": 6 }
 	];
 	jQuery.each( tests, function() {
-		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" );
+		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
@@ -290,22 +283,22 @@ testIframe( "offset/static", "static", function( $, window, document, assert ) {
 		{ "id": "#static-1",     "top":   7, "left":   7 }
 	];
 	jQuery.each( tests, function() {
-		$( 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 } );
+		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.id ).offset( { "top": this.top, "left": this.left, "using": function( props ) {
 			$( this ).css( {
 				"top":  props.top  + 1,
 				"left": props.left + 1
 			} );
 		} } );
-		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 })" );
+		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 ) {
+testIframe( "fixed", "offset/fixed.html", function( assert, $, window ) {
 	assert.expect( 34 );
 
 	var tests, $noTopLeft;
@@ -335,10 +328,10 @@ testIframe( "offset/fixed", "fixed", function( $, window, document, assert ) {
 			assert.ok( true, "Browser doesn't support scroll position." );
 
 		} else if ( window.supportsFixedPosition ) {
-			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" );
+			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
@@ -360,18 +353,18 @@ testIframe( "offset/fixed", "fixed", function( $, window, document, assert ) {
 
 	jQuery.each( tests, function() {
 		if ( window.supportsFixedPosition ) {
-			$( 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 } );
+			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.id ).offset( { "top": this.top, "left": this.left, "using": function( props ) {
 				$( this ).css( {
 					"top":  props.top  + 1,
 					"left": props.left + 1
 				} );
 			} } );
-			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 })" );
+			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
@@ -395,7 +388,7 @@ testIframe( "offset/fixed", "fixed", function( $, window, document, assert ) {
 	}
 } );
 
-testIframe( "offset/table", "table", function( $, window, document, assert ) {
+testIframe( "table", "offset/table.html", function( assert, $ ) {
 	assert.expect( 4 );
 
 	assert.equal( $( "#table-1" ).offset().top, 6, "jQuery('#table-1').offset().top" );
@@ -405,8 +398,8 @@ testIframe( "offset/table", "table", function( $, window, document, assert ) {
 	assert.equal( $( "#th-1" ).offset().left, 10, "jQuery('#th-1').offset().left" );
 } );
 
-testIframe( "offset/scroll", "scroll", function( $, win, doc, assert ) {
-	assert.expect( 30 );
+testIframe( "scroll", "offset/scroll.html", function( assert, $, win ) {
+	assert.expect( 26 );
 
 	assert.equal( $( "#scroll-1" ).offset().top, 7, "jQuery('#scroll-1').offset().top" );
 	assert.equal( $( "#scroll-1" ).offset().left, 7, "jQuery('#scroll-1').offset().left" );
@@ -462,20 +455,9 @@ testIframe( "offset/scroll", "scroll", function( $, win, doc, assert ) {
 	assert.notEqual( $().scrollLeft( null ), null, "jQuery().scrollLeft(null) testing setter on empty jquery object" );
 	assert.strictEqual( $().scrollTop(), undefined, "jQuery().scrollTop() testing getter on empty jquery object" );
 	assert.strictEqual( $().scrollLeft(), undefined, "jQuery().scrollLeft() testing getter on empty jquery object" );
-
-	// Tests position after parent scrolling (#15239)
-	$( "#scroll-1" ).scrollTop( 0 );
-	$( "#scroll-1" ).scrollLeft( 0 );
-	assert.equal( $( "#scroll-1-1" ).position().top, 6, "jQuery('#scroll-1-1').position().top unaffected by parent scrolling" );
-	assert.equal( $( "#scroll-1-1" ).position().left, 6, "jQuery('#scroll-1-1').position().left unaffected by parent scrolling" );
-
-	$( "#scroll-1" ).scrollTop( 5 );
-	$( "#scroll-1" ).scrollLeft( 5 );
-	assert.equal( $( "#scroll-1-1" ).position().top, 6, "jQuery('#scroll-1-1').position().top unaffected by parent scrolling" );
-	assert.equal( $( "#scroll-1-1" ).position().left, 6, "jQuery('#scroll-1-1').position().left unaffected by parent scrolling" );
 } );
 
-testIframe( "offset/body", "body", function( $, window, document, assert ) {
+testIframe( "body", "offset/body.html", function( assert, $ ) {
 	assert.expect( 4 );
 
 	assert.equal( $( "body" ).offset().top, 1, "jQuery('#body').offset().top" );
@@ -486,6 +468,7 @@ testIframe( "offset/body", "body", function( $, window, document, assert ) {
 
 QUnit.test( "chaining", function( assert ) {
 	assert.expect( 3 );
+
 	var coords = { "top":  1, "left":  1 };
 	assert.equal( jQuery( "#absolute-1" ).offset( coords ).jquery, jQuery.fn.jquery, "offset(coords) returns jQuery object" );
 	assert.equal( jQuery( "#non-existent" ).offset( coords ).jquery, jQuery.fn.jquery, "offset(coords) with empty jQuery set returns jQuery object" );
@@ -550,7 +533,7 @@ QUnit.test( "fractions (see #7730 and #7885)", function( assert ) {
 
 	result = div.offset();
 
-	// Support: Chrome 45-46+
+	// 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" );
@@ -563,12 +546,13 @@ QUnit.test( "iframe scrollTop/Left (see gh-1945)", function( assert ) {
 
 	var ifDoc = jQuery( "#iframe" )[ 0 ].contentDocument;
 
-	// 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.
+	// Mobile Safari resize the iframe by its content meaning it's not possible to scroll
+	// the iframe but 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 ) ) {
+	if (
+		/iphone os/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" );
 
diff --git a/test/unit/queue.js b/test/unit/queue.js
index fa8b671..2248c68 100644
--- a/test/unit/queue.js
+++ b/test/unit/queue.js
@@ -232,7 +232,7 @@ QUnit.asyncTest( "fn.promise( \"queue\" ) - called whenever last queue function
 } );
 
 if ( jQuery.fn.animate ) {
-	
+
 QUnit.asyncTest( "fn.promise( \"queue\" ) - waits for animation to complete before resolving", 2, function( assert ) {
 	var foo = jQuery( "#foo" ),
 		test = 1;
diff --git a/test/unit/ready.js b/test/unit/ready.js
index 6272dbd..775b604 100644
--- a/test/unit/ready.js
+++ b/test/unit/ready.js
@@ -2,6 +2,8 @@ QUnit.module( "ready" );
 
 ( function() {
 	var notYetReady, noEarlyExecution,
+		whenified = jQuery.when( jQuery.ready ),
+		promisified = Promise.resolve( jQuery.ready ),
 		order = [],
 		args = {};
 
@@ -26,26 +28,47 @@ QUnit.module( "ready" );
 		};
 	}
 
+	function throwError( num ) {
+
+		// Not a global QUnit failure
+		var onerror = window.onerror;
+		window.onerror = function() {
+			window.onerror = onerror;
+		};
+
+		throw new Error( "Ready error " + num );
+	}
+
 	// Bind to the ready event in every possible way.
 	jQuery( makeHandler( "a" ) );
 	jQuery( document ).ready( makeHandler( "b" ) );
+	jQuery.ready.then( makeHandler( "c" ) );
+
+	// Throw in some errors
+	jQuery( function() {
+		throwError( 1 );
+	} );
+	jQuery( function() {
+		throwError( 2 );
+	} );
 
-	// Do it twice, just to be sure.
-	jQuery( makeHandler( "c" ) );
-	jQuery( document ).ready( makeHandler( "d" ) );
+	// Bind again to ensure that the errors didn't lock everything up
+	jQuery( makeHandler( "d" ) );
+	jQuery( document ).ready( makeHandler( "e" ) );
+	jQuery.ready.then( makeHandler( "f" ) );
 
 	noEarlyExecution = order.length === 0;
 
 	// This assumes that QUnit tests are run on DOM ready!
 	QUnit.test( "jQuery ready", function( assert ) {
-		assert.expect( 8 );
+		assert.expect( 10 );
 
 		assert.ok( noEarlyExecution,
 			"Handlers bound to DOM ready should not execute before DOM ready" );
 
 		// Ensure execution order.
-		assert.deepEqual( order, [ "a", "b", "c", "d" ],
-			"Bound DOM ready handlers should execute in on-order" );
+		assert.deepEqual( order, [ "a", "b", "c", "d", "e", "f" ],
+			"Bound DOM ready handlers should execute in bind order" );
 
 		// Ensure handler argument is correct.
 		assert.equal( args.a, jQuery,
@@ -55,16 +78,73 @@ QUnit.module( "ready" );
 
 		order = [];
 
-		// Now that the ready event has fired, again bind to the ready event
-		// in every possible way. These event handlers should execute immediately.
+		// Now that the ready event has fired, again bind to the ready event.
+		// These ready handlers should execute asynchronously.
+		var done = assert.async();
 		jQuery( makeHandler( "g" ) );
-		assert.equal( order.pop(), "g", "Event handler should execute immediately" );
-		assert.equal( args.g, jQuery, "Argument passed to fn in jQuery( fn ) should be jQuery" );
-
 		jQuery( document ).ready( makeHandler( "h" ) );
-		assert.equal( order.pop(), "h", "Event handler should execute immediately" );
-		assert.equal( args.h, jQuery,
-			"Argument passed to fn in jQuery(document).ready( fn ) should be jQuery" );
+		jQuery.ready.then( makeHandler( "i" ) );
+		window.setTimeout( function() {
+			assert.equal( order.shift(), "g",
+				"Event handler should execute immediately, but async" );
+			assert.equal( args.g, jQuery,
+				"Argument passed to fn in jQuery( fn ) should be jQuery" );
+
+			assert.equal( order.shift(), "h",
+				"Event handler should execute immediately, but async" );
+			assert.equal( args.h, jQuery,
+				"Argument passed to fn in jQuery(document).ready( fn ) should be jQuery" );
+
+			assert.equal( order.shift(), "i",
+				"Event handler should execute immediately, but async" );
+			assert.equal( args.h, jQuery,
+				"Argument passed to fn in jQuery.ready.then( fn ) should be jQuery" );
+
+			done();
+		} );
 	} );
 
+	QUnit.test( "jQuery.when(jQuery.ready)", function( assert ) {
+		assert.expect( 2 );
+		var done = jQuery.map( new Array( 2 ), function() { return assert.async(); } );
+
+		whenified.then( function() {
+			assert.ok( jQuery.isReady, "jQuery.when Deferred resolved" );
+			done.pop()();
+		} );
+
+		jQuery.when( jQuery.ready ).then( function() {
+			assert.ok( jQuery.isReady, "jQuery.when Deferred resolved" );
+			done.pop()();
+		} );
+	} );
+
+	QUnit.test( "Promise.resolve(jQuery.ready)", function( assert ) {
+		assert.expect( 2 );
+		var done = jQuery.map( new Array( 2 ), function() { return assert.async(); } );
+
+		promisified.then( function() {
+			assert.ok( jQuery.isReady, "Native promised resolved" );
+			done.pop()();
+		} );
+
+		Promise.resolve( jQuery.ready ).then( function() {
+			assert.ok( jQuery.isReady, "Native promised resolved" );
+			done.pop()();
+		} );
+	} );
+
+	QUnit.test( "Error in ready callback does not halt all future executions (gh-1823)", function( assert ) {
+		assert.expect( 1 );
+		var done = assert.async();
+
+		jQuery( function() {
+			throwError( 3 );
+		} );
+
+		jQuery( function() {
+			assert.ok( true, "Subsequent handler called" );
+			done();
+		} );
+	} );
 } )();
diff --git a/test/unit/selector.js b/test/unit/selector.js
index 2f6e9af..0177f4b 100644
--- a/test/unit/selector.js
+++ b/test/unit/selector.js
@@ -32,7 +32,7 @@ QUnit.test( "id", function( assert ) {
 	assert.t( "ID selector with existing ID descendant", "#firstp #simon1", [ "simon1" ] );
 	assert.t( "ID selector with non-existent 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( "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 > #台北", [ "台北" ] );
 
@@ -103,14 +103,14 @@ QUnit.test( "selectors with comma", function( assert ) {
 QUnit.test( "child and adjacent", function( assert ) {
 	assert.expect( 27 );
 
-	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( "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.selectInFixture( "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" ] );
@@ -163,7 +163,7 @@ QUnit.test( "attributes", function( assert ) {
 	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 option[value='1']", [ "option1b", "option2b", "option3b", "option4b", "option5c" ] );
 	assert.t( "Attribute Equals Number", "#qunit-fixture li[tabIndex='-1']", [ "foodWithNegativeTabIndex" ] );
 
 	document.getElementById( "anchor2" ).href = "#2";
@@ -187,13 +187,13 @@ QUnit.test( "attributes", function( assert ) {
 
 	assert.t( "Attribute selector using UTF8", "span[lang=中文]", [ "台北" ] );
 
-	assert.t( "Attribute Begins With", "a[href ^= 'http://www']", [ "google","yahoo" ] );
+	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 Contains", "a[href *= 'google']", [ "google", "groups" ] );
 
 	if ( jQuery.find.compile ) {
-		assert.t( "Empty values", "#select1 option[value!='']", [ "option1b","option1c","option1d" ] );
-		assert.t( "Attribute Is Not Equal", "#ap a[hreflang!='en']", [ "google","groups","anchor1" ] );
+		assert.t( "Empty values", "#select1 option[value!='']", [ "option1b", "option1c", "option1d" ] );
+		assert.t( "Attribute Is Not Equal", "#ap a[hreflang!='en']", [ "google", "groups", "anchor1" ] );
 		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" ] );
@@ -290,9 +290,9 @@ QUnit[ jQuery.find.compile ? "test" : "skip" ]( "disconnected nodes", function(
 } );
 
 testIframe(
-	"selector/html5_selector",
 	"attributes - jQuery.attr",
-	function( jQuery, window, document, assert ) {
+	"selector/html5_selector.html",
+	function( assert, jQuery, window, document ) {
 		assert.expect( 38 );
 
 		/**
@@ -490,10 +490,10 @@ QUnit.test( "jQuery.uniqueSort", function( assert ) {
 } );
 
 testIframe(
-	"selector/sizzle_cache",
 	"Sizzle cache collides with multiple Sizzles on a page",
-	function( jQuery, window, document, assert ) {
-		var $cached = window[ "$cached" ];
+	"selector/sizzle_cache.html",
+	function( assert, jQuery, window, document ) {
+		var $cached = window.$cached;
 
 		assert.expect( 4 );
 		assert.notStrictEqual( jQuery, $cached, "Loaded two engines" );
@@ -536,3 +536,9 @@ QUnit.asyncTest( "Iframe dispatch should not affect jQuery (#13936)", 1, functio
 	iframeDoc.write( "<body><form id='navigate' action='?'></form></body>" );
 	iframeDoc.close();
 } );
+
+QUnit.test( "Ensure escapeSelector exists (escape tests in Sizzle)", function( assert ) {
+	assert.expect( 1 );
+
+	assert.equal( jQuery.escapeSelector( "#foo.bar" ), "\\#foo\\.bar", "escapeSelector present" );
+} );
diff --git a/test/unit/serialize.js b/test/unit/serialize.js
index 65d9417..9d0afd7 100644
--- a/test/unit/serialize.js
+++ b/test/unit/serialize.js
@@ -3,14 +3,12 @@ QUnit.module( "serialize", { teardown: moduleTeardown } );
 QUnit.test( "jQuery.param()", function( assert ) {
 	assert.expect( 23 );
 
-	var params, settings;
-
-	assert.equal( !( jQuery.ajaxSettings && jQuery.ajaxSettings.traditional ), true, "traditional flag, falsy by default" );
+	var params;
 
 	params = { "foo":"bar", "baz":42, "quux":"All your base are belong to us" };
 	assert.equal( jQuery.param( params ), "foo=bar&baz=42&quux=All%20your%20base%20are%20belong%20to%20us", "simple" );
 
-	params = { "string":"foo","null":null,"undefined":undefined };
+	params = { "string":"foo", "null":null, "undefined":undefined };
 	assert.equal( jQuery.param( params ), "string=foo&null=&undefined=", "handle nulls and undefineds properly" );
 
 	params = { "someName": [ 1, 2, 3 ], "regularThing": "blah" };
@@ -25,66 +23,68 @@ QUnit.test( "jQuery.param()", function( assert ) {
 	params = { "foo": { "bar": "baz", "beep": 42, "quux": "All your base are belong to us" } };
 	assert.equal( jQuery.param( params ), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All%20your%20base%20are%20belong%20to%20us", "even more arrays" );
 
-	params = { a:[ 1,2 ], b:{ c:3, d:[ 4,5 ], e:{ x:[ 6 ], y:7, z:[ 8,9 ] }, f:true, g:false, h:undefined }, i:[ 10,11 ], j:true, k:false, l:[ undefined,0 ], m:"cowboy hat?" };
+	params = { a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" };
 	assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=&i[]=10&i[]=11&j=true&k=false&l[]=&l[]=0&m=cowboy hat?", "huge structure" );
 
 	params = { "a": [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { "b": [ 7, [ 8, 9 ], [ { "c": 10, "d": 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { "e": { "f": { "g": [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] };
 	assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=0&a[1][]=1&a[1][]=2&a[2][]=3&a[2][1][]=4&a[2][1][]=5&a[2][2][]=6&a[3][b][]=7&a[3][b][1][]=8&a[3][b][1][]=9&a[3][b][2][0][c]=10&a[3][b][2][0][d]=11&a[3][b][3][0][]=12&a[3][b][4][0][0][]=13&a[3][b][5][e][f][g][]=14&a[3][b][5][e][f][g][1][]=15&a[3][b][]=16&a[]=17", "nested arrays" );
 
-	params = { "a":[ 1,2 ], "b":{ "c":3, "d":[ 4,5 ], "e":{ "x":[ 6 ], "y":7, "z":[ 8,9 ] }, "f":true, "g":false, "h":undefined }, "i":[ 10,11 ], "j":true, "k":false, "l":[ undefined,0 ], "m":"cowboy hat?" };
+	params = { "a":[ 1, 2 ], "b":{ "c":3, "d":[ 4, 5 ], "e":{ "x":[ 6 ], "y":7, "z":[ 8, 9 ] }, "f":true, "g":false, "h":undefined }, "i":[ 10, 11 ], "j":true, "k":false, "l":[ undefined, 0 ], "m":"cowboy hat?" };
 	assert.equal( jQuery.param( params, true ), "a=1&a=2&b=%5Bobject%20Object%5D&i=10&i=11&j=true&k=false&l=&l=0&m=cowboy%20hat%3F", "huge structure, forced traditional" );
 
-	assert.equal( decodeURIComponent( jQuery.param( { "a": [ 1,2,3 ], "b[]": [ 4,5,6 ], "c[d]": [ 7,8,9 ], "e": { "f": [ 10 ], "g": [ 11,12 ], "h": 13 } } ) ), "a[]=1&a[]=2&a[]=3&b[]=4&b[]=5&b[]=6&c[d][]=7&c[d][]=8&c[d][]=9&e[f][]=10&e[g][]=11&e[g][]=12&e[h]=13", "Make sure params are not double-encoded." );
+	assert.equal( decodeURIComponent( jQuery.param( { "a": [ 1, 2, 3 ], "b[]": [ 4, 5, 6 ], "c[d]": [ 7, 8, 9 ], "e": { "f": [ 10 ], "g": [ 11, 12 ], "h": 13 } } ) ), "a[]=1&a[]=2&a[]=3&b[]=4&b[]=5&b[]=6&c[d][]=7&c[d][]=8&c[d][]=9&e[f][]=10&e[g][]=11&e[g][]=12&e[h]=13", "Make sure params are not double-encoded." );
 
 	// #7945
 	assert.equal( jQuery.param( { "jquery": "1.4.2" } ), "jquery=1.4.2", "Check that object with a jQuery property get serialized correctly" );
 
-	settings = { traditional: true };
-
-	if ( jQuery.ajaxSettings ) {
-		jQuery.ajaxSetup( settings );
-	} else {
-		jQuery.ajaxSettings = settings;
-	}
-
 	params = { "foo":"bar", "baz":42, "quux":"All your base are belong to us" };
-	assert.equal( jQuery.param( params ), "foo=bar&baz=42&quux=All%20your%20base%20are%20belong%20to%20us", "simple" );
+	assert.equal( jQuery.param( params, true ), "foo=bar&baz=42&quux=All%20your%20base%20are%20belong%20to%20us", "simple" );
 
 	params = { "someName": [ 1, 2, 3 ], "regularThing": "blah" };
-	assert.equal( jQuery.param( params ), "someName=1&someName=2&someName=3&regularThing=blah", "with array" );
+	assert.equal( jQuery.param( params, true ), "someName=1&someName=2&someName=3&regularThing=blah", "with array" );
 
 	params = { "foo": [ "a", "b", "c" ] };
-	assert.equal( jQuery.param( params ), "foo=a&foo=b&foo=c", "with array of strings" );
+	assert.equal( jQuery.param( params, true ), "foo=a&foo=b&foo=c", "with array of strings" );
 
 	params = { "foo[]":[ "baz", 42, "All your base are belong to us" ] };
-	assert.equal( jQuery.param( params ), "foo%5B%5D=baz&foo%5B%5D=42&foo%5B%5D=All%20your%20base%20are%20belong%20to%20us", "more array" );
+	assert.equal( jQuery.param( params, true ), "foo%5B%5D=baz&foo%5B%5D=42&foo%5B%5D=All%20your%20base%20are%20belong%20to%20us", "more array" );
 
 	params = { "foo[bar]":"baz", "foo[beep]":42, "foo[quux]":"All your base are belong to us" };
-	assert.equal( jQuery.param( params ), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All%20your%20base%20are%20belong%20to%20us", "even more arrays" );
+	assert.equal( jQuery.param( params, true ), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All%20your%20base%20are%20belong%20to%20us", "even more arrays" );
 
-	params = { a:[ 1,2 ], b:{ c:3, d:[ 4,5 ], e:{ x:[ 6 ], y:7, z:[ 8,9 ] }, f:true, g:false, h:undefined }, i:[ 10,11 ], j:true, k:false, l:[ undefined,0 ], m:"cowboy hat?" };
-	assert.equal( jQuery.param( params ), "a=1&a=2&b=%5Bobject%20Object%5D&i=10&i=11&j=true&k=false&l=&l=0&m=cowboy%20hat%3F", "huge structure" );
+	params = { a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" };
+	assert.equal( jQuery.param( params, true ), "a=1&a=2&b=%5Bobject%20Object%5D&i=10&i=11&j=true&k=false&l=&l=0&m=cowboy%20hat%3F", "huge structure" );
 
 	params = { "a": [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { "b": [ 7, [ 8, 9 ], [ { "c": 10, d: 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { "e": { "f": { "g": [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] };
-	assert.equal( jQuery.param( params ), "a=0&a=1%2C2&a=3%2C4%2C5%2C6&a=%5Bobject%20Object%5D&a=17", "nested arrays (not possible when jQuery.param.traditional == true)" );
+	assert.equal( jQuery.param( params, true ), "a=0&a=1%2C2&a=3%2C4%2C5%2C6&a=%5Bobject%20Object%5D&a=17", "nested arrays (not possible when traditional == true)" );
 
-	params = { a:[ 1,2 ], b:{ c:3, d:[ 4,5 ], e:{ x:[ 6 ], y:7, z:[ 8,9 ] }, f:true, g:false, h:undefined }, i:[ 10,11 ], j:true, k:false, l:[ undefined,0 ], m:"cowboy hat?" };
-	assert.equal( decodeURIComponent( jQuery.param( params, false ) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=&i[]=10&i[]=11&j=true&k=false&l[]=&l[]=0&m=cowboy hat?", "huge structure, forced not traditional" );
+	params = { a:[ 1, 2 ], b:{ c:3, d:[ 4, 5 ], e:{ x:[ 6 ], y:7, z:[ 8, 9 ] }, f:true, g:false, h:undefined }, i:[ 10, 11 ], j:true, k:false, l:[ undefined, 0 ], m:"cowboy hat?" };
+	assert.equal( decodeURIComponent( jQuery.param( params ) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=&i[]=10&i[]=11&j=true&k=false&l[]=&l[]=0&m=cowboy hat?", "huge structure, forced not traditional" );
 
 	params = { "param1": null };
-	assert.equal( jQuery.param( params, false ), "param1=", "Make sure that null params aren't traversed." );
+	assert.equal( jQuery.param( params ), "param1=", "Make sure that null params aren't traversed." );
+
+	params = { "param1": function() {}, "param2": function() { return null; } };
+	assert.equal( jQuery.param( params, false ), "param1=&param2=", "object with function property that returns null value" );
 
 	params = { "test": { "length": 3, "foo": "bar" } };
-	assert.equal( jQuery.param( params, false ), "test%5Blength%5D=3&test%5Bfoo%5D=bar", "Sub-object with a length property" );
+	assert.equal( jQuery.param( params ), "test%5Blength%5D=3&test%5Bfoo%5D=bar", "Sub-object with a length property" );
 
 	params = { "test": [ 1, 2, null ] };
-	assert.equal( jQuery.param( params, false ), "test%5B%5D=1&test%5B%5D=2&test%5B%5D=", "object with array property with null value" );
+	assert.equal( jQuery.param( params ), "test%5B%5D=1&test%5B%5D=2&test%5B%5D=", "object with array property with null value" );
+} );
 
-	if ( jQuery.ajaxSettings === settings ) {
-		delete jQuery.ajaxSettings;
-	} else {
-		jQuery.ajaxSetup( { traditional: false } );
-	}
+QUnit.test( "jQuery.param() not affected by ajaxSettings", function( assert ) {
+	assert.expect( 1 );
+
+	var oldTraditional = jQuery.ajaxSettings.traditional;
+	jQuery.ajaxSettings.traditional = true;
+	assert.equal(
+		jQuery.param( { "foo": [ "a", "b", "c" ] } ),
+		"foo%5B%5D=a&foo%5B%5D=b&foo%5B%5D=c",
+		"ajaxSettings.traditional is ignored"
+	);
+	jQuery.ajaxSettings.traditional = oldTraditional;
 } );
 
 QUnit.test( "jQuery.param() Constructed prop values", function( assert ) {
@@ -92,7 +92,7 @@ QUnit.test( "jQuery.param() Constructed prop values", function( assert ) {
 
 	/** @constructor */
 	function Record() {
-		this[ "prop" ] = "val";
+		this.prop = "val";
 	}
 
 	var MyString = String,
diff --git a/test/unit/support.js b/test/unit/support.js
index 6a51ccb..5be7819 100644
--- a/test/unit/support.js
+++ b/test/unit/support.js
@@ -18,10 +18,10 @@ function getComputedSupport( support ) {
 }
 
 if ( jQuery.css ) {
-	testIframeWithCallback(
+	testIframe(
 		"body background is not lost if set prior to loading jQuery (#9239)",
 		"support/bodyBackground.html",
-		function( color, support, assert ) {
+		function( assert, jQuery, window, document, color, support ) {
 			assert.expect( 2 );
 			var okValue = {
 				"#000000": true,
@@ -29,21 +29,23 @@ if ( jQuery.css ) {
 			};
 			assert.ok( okValue[ color ], "color was not reset (" + color + ")" );
 
-			assert.deepEqual( jQuery.extend( {}, support ), computedSupport, "Same support properties" );
+			assert.deepEqual( jQuery.extend( {}, support ), computedSupport,
+				"Same support properties" );
 		}
 	);
 }
 
 // This test checks CSP only for browsers with "Content-Security-Policy" header support
 // i.e. no old WebKit or old Firefox
-testIframeWithCallback(
+testIframe(
 	"Check CSP (https://developer.mozilla.org/en-US/docs/Security/CSP) restrictions",
 	"support/csp.php",
-	function( support, assert ) {
+	function( assert, jQuery, window, document, support ) {
 		var done = assert.async();
 
 		assert.expect( 2 );
-		assert.deepEqual( jQuery.extend( {}, support ), computedSupport, "No violations of CSP polices" );
+		assert.deepEqual( jQuery.extend( {}, support ), computedSupport,
+			"No violations of CSP polices" );
 
 		supportjQuery.get( "data/support/csp.log" ).done( function( data ) {
 			assert.equal( data, "", "No log request should be sent" );
@@ -53,16 +55,17 @@ testIframeWithCallback(
 );
 
 ( function() {
-	var expected,
+	var expected, version,
 		userAgent = window.navigator.userAgent;
 
 	if ( /edge\//i.test( userAgent ) ) {
+		version = userAgent.match( /edge\/(\d+)/i )[ 1 ];
 		expected = {
 			"ajax": true,
 			"boxSizingReliable": true,
 			"checkClone": true,
 			"checkOn": true,
-			"clearCloneStyle": false,
+			"clearCloneStyle": version >= 13,
 			"cors": true,
 			"createHTMLDocument": true,
 			"focusin": false,
@@ -127,7 +130,7 @@ testIframeWithCallback(
 			"radioValue": true,
 			"reliableMarginLeft": true
 		};
-	} else if ( /9\.0(\.\d+|) safari/i.test( userAgent ) ) {
+	} else if ( /\b(?:9|10)\.\d(\.\d+)* safari/i.test( userAgent ) ) {
 		expected = {
 			"ajax": true,
 			"boxSizingReliable": true,
@@ -144,23 +147,6 @@ testIframeWithCallback(
 			"radioValue": true,
 			"reliableMarginLeft": true
 		};
-	} else if ( /8\.0(\.\d+|) safari/i.test( userAgent ) ) {
-		expected = {
-			"ajax": true,
-			"boxSizingReliable": true,
-			"checkClone": true,
-			"checkOn": true,
-			"clearCloneStyle": true,
-			"cors": true,
-			"createHTMLDocument": false,
-			"focusin": false,
-			"noCloneChecked": true,
-			"optSelected": true,
-			"pixelMarginRight": true,
-			"pixelPosition": false,
-			"radioValue": true,
-			"reliableMarginLeft": true
-		};
 	} else if ( /firefox/i.test( userAgent ) ) {
 		expected = {
 			"ajax": true,
@@ -178,7 +164,7 @@ testIframeWithCallback(
 			"radioValue": true,
 			"reliableMarginLeft": false
 		};
-	} else if ( /iphone os 9_/i.test( userAgent ) ) {
+	} else if ( /iphone os (?:9|10)_/i.test( userAgent ) ) {
 		expected = {
 			"ajax": true,
 			"boxSizingReliable": true,
@@ -272,6 +258,6 @@ testIframeWithCallback(
 				assert.ok( true, "no ajax; skipping jQuery.support['" + i + "']" );
 			}
 		}
-	});
+	} );
 
 } )();
diff --git a/test/unit/traversing.js b/test/unit/traversing.js
index 3e0c376..c7ed014 100644
--- a/test/unit/traversing.js
+++ b/test/unit/traversing.js
@@ -393,8 +393,31 @@ QUnit.test( "closest(jQuery)", function( assert ) {
 QUnit[ jQuery.find.compile ? "test" : "skip" ]( "not(Selector)", function( assert ) {
 	assert.expect( 7 );
 	assert.equal( jQuery( "#qunit-fixture > p#ap > a" ).not( "#google" ).length, 2, "not('selector')" );
-	assert.deepEqual( jQuery( "p" ).not( ".result" ).get(), q( "firstp", "ap", "sndp", "en", "sap", "first" ), "not('.class')" );
-	assert.deepEqual( jQuery( "p" ).not( "#ap, #sndp, .result" ).get(), q( "firstp", "en", "sap", "first" ), "not('selector, selector')" );
+
+	assert.deepEqual(
+		jQuery( "#qunit-fixture p" ).not( ".result" ).get(),
+		q(
+			"firstp",
+			"ap",
+			"sndp",
+			"en",
+			"sap",
+			"first"
+		),
+		"not('.class')"
+	);
+
+
+	assert.deepEqual(
+		jQuery( "#qunit-fixture p" ).not( "#ap, #sndp, .result" ).get(),
+		q(
+			"firstp",
+			"en",
+			"sap",
+			"first"
+		),
+		"not('selector, selector')"
+	);
 
 	assert.deepEqual( jQuery( "#ap *" ).not( "code" ).get(), q( "google", "groups", "anchor1", "mark" ), "not('tag selector')" );
 	assert.deepEqual( jQuery( "#ap *" ).not( "code, #mark" ).get(), q( "google", "groups", "anchor1" ), "not('tag, ID selector')" );
@@ -440,7 +463,11 @@ QUnit.test( "not(Array)", function( assert ) {
 QUnit.test( "not(jQuery)", function( assert ) {
 	assert.expect( 1 );
 
-	assert.deepEqual( jQuery( "p" ).not( jQuery( "#ap, #sndp, .result" ) ).get(), q( "firstp", "en", "sap", "first" ), "not(jQuery)" );
+	assert.deepEqual(
+		jQuery( "#qunit-fixture p" ).not( jQuery( "#ap, #sndp, .result" ) ).get(),
+		q( "firstp", "en", "sap", "first" ),
+		"not(jQuery)"
+	);
 } );
 
 QUnit.test( "not(Selector) excludes non-element nodes (gh-2808)", function( assert ) {
@@ -454,6 +481,20 @@ QUnit.test( "not(Selector) excludes non-element nodes (gh-2808)", function( asse
 	assert.deepEqual( mixedContents.not( "[id=a],*,[id=b]" ).get(), [], "not [id=a],*,[id=b]" );
 } );
 
+QUnit.test( "not(arraylike) passes non-element nodes (gh-3226)", function( assert ) {
+	assert.expect( 5 );
+
+	var mixedContents = jQuery( "<span id='nonnodesElement'>hi</span> there <!-- mon ami -->" ),
+		mixedLength = mixedContents.length,
+		firstElement = mixedContents.first();
+
+	assert.deepEqual( mixedContents.not( mixedContents ).get(), [], "not everything" );
+	assert.deepEqual( mixedContents.not( firstElement ).length, mixedLength - 1, "not firstElement" );
+	assert.deepEqual( mixedContents.not( [ firstElement[ 0 ].nextSibling ] ).length, mixedLength - 1, "not textnode array" );
+	assert.deepEqual( mixedContents.not( firstElement[ 0 ].nextSibling ).length, mixedLength - 1, "not textnode" );
+	assert.deepEqual( mixedContents.not( document.body ).get(), mixedContents.get(), "not with unmatched element" );
+} );
+
 QUnit.test( "has(Element)", function( assert ) {
 	assert.expect( 3 );
 	var obj, detached, multipleParent;
diff --git a/test/unit/tween.js b/test/unit/tween.js
index f66aa37..28fcfad 100644
--- a/test/unit/tween.js
+++ b/test/unit/tween.js
@@ -160,7 +160,8 @@ QUnit.test( "jQuery.Tween - Plain Object", function( assert ) {
 
 	assert.equal( tween.now, 90, "Calculated tween" );
 
-	assert.ok( easingSpy.calledWith( 0.1 ), "...using jQuery.easing.linear" );
+	assert.ok( easingSpy.calledWith( 0.1, 0.1 * testOptions.duration, 0, 1, testOptions.duration ),
+		"...using jQuery.easing.linear with back-compat arguments" );
 	assert.equal( testObject.test, 90, "Set value" );
 
 	tween.run( 1 );
@@ -199,7 +200,10 @@ QUnit.test( "jQuery.Tween - Element", function( assert ) {
 	eased = 100 - ( jQuery.easing.swing( 0.1 ) * 100 );
 	assert.equal( tween.now, eased, "Calculated tween" );
 
-	assert.ok( easingSpy.calledWith( 0.1 ), "...using jQuery.easing.linear" );
+	assert.ok(
+		easingSpy.calledWith( 0.1, 0.1 * testOptions.duration, 0, 1, testOptions.duration ),
+		"...using jQuery.easing.linear with back-compat arguments"
+	);
 	assert.equal(
 		parseFloat( testElement.style.height ).toFixed( 2 ),
 		eased.toFixed( 2 ), "Set value"
diff --git a/test/unit/wrap.js b/test/unit/wrap.js
index 80abd9e..65af2b4 100644
--- a/test/unit/wrap.js
+++ b/test/unit/wrap.js
@@ -60,7 +60,7 @@ function testWrap( val, assert ) {
 	j.wrap( val( "<i></i>" ) );
 
 	assert.equal(
-		jQuery( "#nonnodes > i" ).length, jQuery( "#nonnodes" )[ 0 ].childNodes.length,
+		jQuery( "#nonnodes > i" ).length, 3,
 		"Check node,textnode,comment wraps ok"
 	);
 	assert.equal(
@@ -472,7 +472,7 @@ QUnit.test( "unwrap( selector )", function( assert ) {
 		jQuery( "#unwrap1" ).length, 1, "still wrapped"
 	);
 
-	 // Shouldn't unwrap, no match
+	// Shouldn't unwrap, no match
 	jQuery( "#unwrap1 span" ) .unwrap( "span" );
 	assert.equal(
 		jQuery( "#unwrap1" ).length, 1, "still wrapped"

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/jquery.git



More information about the Pkg-javascript-commits mailing list