[Pkg-javascript-commits] [jquery] 01/05: New upstream version 3.2.1

Marcelo Jorge Vieira metal at moszumanska.debian.org
Sun Sep 3 00:17:05 UTC 2017


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

metal pushed a commit to branch master
in repository jquery.

commit 4cbb91b2818cb1ea6148a030ab1310b33f0c5131
Author: Marcelo Jorge Vieira <metal at alucinados.com>
Date:   Sat Sep 2 20:36:35 2017 -0300

    New upstream version 3.2.1
---
 .eslintignore                                |  2 +
 src/.eslintrc.json => .eslintrc-browser.json | 13 ++++
 .eslintrc-node.json                          | 13 ++++
 .eslintrc.json                               |  6 +-
 .github/ISSUE_TEMPLATE.md                    |  4 +-
 .github/PULL_REQUEST_TEMPLATE.md             |  6 +-
 .npmignore                                   |  1 -
 .travis.yml                                  |  3 +-
 AUTHORS.txt                                  |  6 ++
 CONTRIBUTING.md                              |  4 +-
 Gruntfile.js                                 | 46 ++++++---------
 LICENSE.txt                                  |  2 +-
 build/release.js                             | 10 ++--
 build/release/cdn.js                         |  2 +-
 build/release/dist.js                        | 25 +++++++-
 build/tasks/build.js                         |  6 +-
 package.json                                 | 10 ++--
 src/.eslintrc.json                           | 19 +-----
 src/attributes/attr.js                       |  5 +-
 src/attributes/val.js                        | 10 ++--
 src/callbacks.js                             |  2 +-
 src/core.js                                  | 12 +---
 src/core/init.js                             |  1 +
 src/core/nodeName.js                         | 13 ++++
 src/core/ready-no-deferred.js                |  9 ---
 src/core/ready.js                            |  9 ---
 src/css.js                                   | 88 ++++++++++++++++------------
 src/css/curCSS.js                            | 10 +++-
 src/data/Data.js                             |  2 +-
 src/deferred.js                              | 14 +++--
 src/deprecated.js                            | 14 ++++-
 src/effects.js                               | 70 ++++++++++++----------
 src/event.js                                 |  7 ++-
 src/manipulation.js                          | 10 ++--
 src/manipulation/getAll.js                   |  7 ++-
 src/offset.js                                | 45 +++++++-------
 src/queue.js                                 |  2 +-
 src/serialize.js                             |  6 +-
 src/traversing.js                            | 17 +++++-
 src/wrapper.js                               |  2 +-
 test/.eslintrc.json                          | 20 ++++---
 test/{ => data}/readywait.html               | 28 +++++----
 test/data/readywaitasset.js                  |  1 -
 test/data/readywaitloader.js                 | 25 --------
 test/data/testinit.js                        |  2 +-
 test/data/testrunner.js                      |  2 +-
 test/jquery.js                               | 67 ++++++++++-----------
 test/node_smoke_tests/.eslintrc.json         | 12 +++-
 test/promises_aplus_adapters/.eslintrc.json  |  5 +-
 test/unit/callbacks.js                       | 21 +++++++
 test/unit/core.js                            | 31 ++++++----
 test/unit/css.js                             | 77 ++++++++++++++++++++++++
 test/unit/deferred.js                        |  4 +-
 test/unit/deprecated.js                      | 54 +++++++++++++++++
 test/unit/dimensions.js                      | 25 ++++++++
 test/unit/effects.js                         | 28 ++++-----
 test/unit/event.js                           | 10 ++++
 test/unit/manipulation.js                    | 36 +++++++++---
 test/unit/offset.js                          | 32 +++++++++-
 test/unit/queue.js                           |  2 +-
 test/unit/ready.js                           | 13 ++++
 test/unit/traversing.js                      | 52 ++++++++++++++++
 62 files changed, 729 insertions(+), 351 deletions(-)

diff --git a/.eslintignore b/.eslintignore
index 484ceee..4154933 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,6 +1,8 @@
 external
 node_modules
 *.min.js
+dist/**
+!dist/jquery.js
 test/data/jquery-1.9.1.js
 test/data/badcall.js
 test/data/badjson.js
diff --git a/src/.eslintrc.json b/.eslintrc-browser.json
similarity index 52%
copy from src/.eslintrc.json
copy to .eslintrc-browser.json
index dbf16c5..c704209 100644
--- a/src/.eslintrc.json
+++ b/.eslintrc-browser.json
@@ -1,10 +1,22 @@
 {
+	"root": true,
+
+	"extends": "jquery",
+
 	// 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
 	},
+
+	// The browser env is not enabled on purpose so that code takes
+	// all browser-only globals from window instead of assuming
+	// they're available as globals. This makes it possible to use
+	// jQuery with tools like jsdom which provide a custom window
+	// implementation.
+	"env": {},
+
 	"globals": {
 		"window": true,
 		"jQuery": true,
@@ -12,6 +24,7 @@
 		"module": true,
 		"noGlobal": true
 	},
+
 	"rules": {
 		"strict": ["error", "function"]
 	}
diff --git a/.eslintrc-node.json b/.eslintrc-node.json
new file mode 100644
index 0000000..904e7ba
--- /dev/null
+++ b/.eslintrc-node.json
@@ -0,0 +1,13 @@
+{
+	"root": true,
+
+	"extends": "jquery",
+
+	"parserOptions": {
+		"ecmaVersion": 5
+	},
+
+	"env": {
+		"node": true
+	}
+}
diff --git a/.eslintrc.json b/.eslintrc.json
index 6dcb635..d2c977c 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,7 +1,5 @@
 {
-	"extends": "eslint-config-jquery",
 	"root": true,
-	"env": {
-		"node": true
-	}
+
+	"extends": "./.eslintrc-node.json"
 }
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index a3c7d5b..1a95200 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -4,10 +4,10 @@ Feature Requests:
   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.
+  Note that we only can fix bugs in the latest version of jQuery.
   Briefly describe the issue you've encountered
   *  What do you expect to happen?
-  *  What acually happens?
+  *  What actually 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!
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0ec4516..98d2331 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,11 +6,15 @@ 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/
+* [ ] All authors have signed the CLA at https://cla.js.foundation/jquery/jquery
 * [ ] 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/.npmignore b/.npmignore
index 5af00bb..c95a77e 100644
--- a/.npmignore
+++ b/.npmignore
@@ -3,7 +3,6 @@
 
 /.editorconfig
 /.gitattributes
-/.jscs.json
 /.mailmap
 /.travis.yml
 
diff --git a/.travis.yml b/.travis.yml
index 7992702..0a57584 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
 language: node_js
 sudo: false
 node_js:
-- "0.10"
-- "0.12"
 - "4"
 - "6"
+- "7"
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 334363d..c32c25f 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -293,3 +293,9 @@ 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>
+Manoj Kumar <nithmanoj at gmail.com>
+David Broder-Rodgers <broder93 at gmail.com>
+Alex Louden <alex at louden.com>
+Alex Padilla <alexonezero at outlook.com>
+南漂一卒 <shiy007 at qq.com>
+karan-96 <karanbatra96 at gmail.com>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f7d5214..18c6bc8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,7 +8,7 @@
 Note: This is the code development repository for *jQuery Core* only. Before opening an issue or making a pull request, be sure you're in the right place.
 * jQuery plugin issues should be reported to the author of the plugin.
 * jQuery Core API documentation issues can be filed [at the API repo](https://github.com/jquery/api.jquery.com/issues).
-* Bugs or suggestions for other jQuery Foundation projects should be filed in [their respective repos](https://github.com/jquery/).
+* Bugs or suggestions for other jQuery organization projects should be filed in [their respective repos](https://github.com/jquery/).
 
 ## Getting Involved
 
@@ -16,7 +16,7 @@ Note: This is the code development repository for *jQuery Core* only. Before ope
 
 We're always looking for help [identifying bugs](#how-to-report-bugs), writing and reducing test cases, and improving documentation. And although new features are rare, anything passing our [guidelines](https://github.com/jquery/jquery/wiki/Adding-new-features) will be considered.
 
-More information on how to contribute to this and other jQuery Foundation projects is at [contribute.jquery.org](https://contribute.jquery.org), including a short guide with tips, tricks, and ideas on [getting started with open source](https://contribute.jquery.org/open-source/). Please review our [commit & pull request guide](https://contribute.jquery.org/commits-and-pull-requests/) and [style guides](https://contribute.jquery.org/style-guide/) for instructions on how to maintain a fork [...]
+More information on how to contribute to this and other jQuery organization projects is at [contribute.jquery.org](https://contribute.jquery.org), including a short guide with tips, tricks, and ideas on [getting started with open source](https://contribute.jquery.org/open-source/). Please review our [commit & pull request guide](https://contribute.jquery.org/commits-and-pull-requests/) and [style guides](https://contribute.jquery.org/style-guide/) for instructions on how to maintain a fo [...]
 
 
 ## Questions and Discussion
diff --git a/Gruntfile.js b/Gruntfile.js
index 7090a1a..12ae008 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -13,15 +13,7 @@ module.exports = function( grunt ) {
 	}
 
 	var fs = require( "fs" ),
-		gzip = require( "gzip-js" ),
-		oldNode = /^v0\./.test( process.version );
-
-	// 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;
-	}
+		gzip = require( "gzip-js" );
 
 	if ( !grunt.option( "filename" ) ) {
 		grunt.option( "filename", "jquery.js" );
@@ -175,7 +167,7 @@ module.exports = function( grunt ) {
 						"ascii_only": true
 					},
 					banner: "/*! jQuery v<%= pkg.version %> | " +
-						"(c) jQuery Foundation | jquery.org/license */",
+						"(c) JS Foundation and other contributors | jquery.org/license */",
 					compress: {
 						"hoist_funs": false,
 						loops: false,
@@ -187,32 +179,32 @@ module.exports = function( grunt ) {
 	} );
 
 	// Load grunt tasks from NPM packages
-	// 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-*" ]
-	} );
+	require( "load-grunt-tasks" )( grunt );
 
 	// Integrate jQuery specific tasks
 	grunt.loadTasks( "build/tasks" );
 
-	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( "lint", [
 		"jsonlint",
-		runIfNewNode( "eslint" )
+
+		// Running the full eslint task without breaking it down to targets
+		// would run the dist target first which would point to errors in the built
+		// file, making it harder to fix them. We want to check the built file only
+		// if we already know the source files pass the linter.
+		"eslint:dev",
+		"eslint:dist"
 	] );
 
 	grunt.registerTask( "lint:newer", [
 		"newer:jsonlint",
-		runIfNewNode( "newer:eslint" )
+
+		// Don't replace it with just the task; see the above comment.
+		"newer:eslint:dev",
+		"newer:eslint:dist"
 	] );
 
-	grunt.registerTask( "test:fast", runIfNewNode( "node_smoke_tests" ) );
-	grunt.registerTask( "test:slow", runIfNewNode( "promises_aplus_tests" ) );
+	grunt.registerTask( "test:fast", "node_smoke_tests" );
+	grunt.registerTask( "test:slow", "promises_aplus_tests" );
 
 	grunt.registerTask( "test", [
 		"test:fast",
@@ -221,7 +213,7 @@ module.exports = function( grunt ) {
 
 	grunt.registerTask( "dev", [
 		"build:*:*",
-		runIfNewNode( "newer:eslint:dev" ),
+		"newer:eslint:dev",
 		"newer:uglify",
 		"remove_map_comment",
 		"dist:*",
@@ -229,12 +221,12 @@ module.exports = function( grunt ) {
 	] );
 
 	grunt.registerTask( "default", [
-		runIfNewNode( "eslint:dev" ),
+		"eslint:dev",
 		"build:*:*",
 		"uglify",
 		"remove_map_comment",
 		"dist:*",
-		runIfNewNode( "eslint:dist" ),
+		"eslint:dist",
 		"test:fast",
 		"compare_size"
 	] );
diff --git a/LICENSE.txt b/LICENSE.txt
index 5312a4c..e4e5e00 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright jQuery Foundation and other contributors, https://jquery.org/
+Copyright JS Foundation and other contributors, https://js.foundation/
 
 This software consists of voluntary contributions made by many
 individuals. For exact contribution history, see the revision history
diff --git a/build/release.js b/build/release.js
index 22cd90c..de6ab0c 100644
--- a/build/release.js
+++ b/build/release.js
@@ -49,7 +49,7 @@ module.exports = function( Release ) {
 		generateArtifacts: function( callback ) {
 			Release.exec( "grunt", "Grunt command failed" );
 			Release.exec(
-				"grunt custom:-ajax,-effects,-deprecated --filename=jquery.slim.js && " +
+				"grunt custom:-ajax,-effects --filename=jquery.slim.js && " +
 					"grunt remove_map_comment --filename=jquery.slim.js",
 				"Grunt custom failed"
 			);
@@ -83,8 +83,8 @@ module.exports = function( Release ) {
 };
 
 module.exports.dependencies = [
-	"archiver at 0.14.2",
-	"shelljs at 0.7.0",
-	"npm at 2.3.0",
-	"chalk at 1.1.1"
+	"archiver at 1.3.0",
+	"shelljs at 0.7.7",
+	"npm at 4.4.1",
+	"chalk at 1.1.3"
 ];
diff --git a/build/release/cdn.js b/build/release/cdn.js
index 0506062..c65c53e 100644
--- a/build/release/cdn.js
+++ b/build/release/cdn.js
@@ -87,7 +87,7 @@ function makeArchives( Release, callback ) {
 				item.replace( rver, Release.newVersion );
 		} );
 
-		sum = Release.exec( "md5sum " + files.join( " " ), "Error retrieving md5sum" );
+		sum = Release.exec( "md5 -r " + files.join( " " ), "Error retrieving md5sum" );
 		fs.writeFileSync( md5file, sum );
 		files.push( md5file );
 
diff --git a/build/release/dist.js b/build/release/dist.js
index e0237fe..ca79226 100644
--- a/build/release/dist.js
+++ b/build/release/dist.js
@@ -50,6 +50,15 @@ module.exports = function( Release, files, complete ) {
 	}
 
 	/**
+	 * Replace the version in the README
+	 * @param {string} readme
+	 */
+	function editReadme( readme ) {
+		var rprev = new RegExp( Release.prevVersion, "g" );
+		return readme.replace( rprev, Release.newVersion );
+	}
+
+	/**
 	 * Copy necessary files over to the dist repo
 	 */
 	function copy() {
@@ -57,6 +66,7 @@ module.exports = function( Release, files, complete ) {
 		// Copy dist files
 		var distFolder = Release.dir.dist + "/dist",
 			externalFolder = Release.dir.dist + "/external",
+			readme = fs.readFileSync( Release.dir.dist + "/README.md", "utf8" ),
 			rmIgnore = files
 				.concat( [
 					"README.md",
@@ -93,8 +103,17 @@ module.exports = function( Release, files, complete ) {
 		// Write generated bower file
 		fs.writeFileSync( Release.dir.dist + "/bower.json", generateBower() );
 
-		console.log( "Adding files to dist..." );
+		fs.writeFileSync( Release.dir.dist + "/README.md", editReadme( readme ) );
+
+		console.log( "Files ready to add." );
+		console.log( "Edit the dist README.md to include the latest blog post link." );
+	}
 
+	/**
+	 * Add, commit, and tag the dist files
+	 */
+	function commit() {
+		console.log( "Adding files to dist..." );
 		Release.exec( "git add -A", "Error adding files." );
 		Release.exec(
 			"git commit -m \"Release " + Release.newVersion + "\"",
@@ -130,6 +149,10 @@ module.exports = function( Release, files, complete ) {
 		copy,
 		Release.confirmReview,
 
+		Release._section( "Add, commit, and tag files in distribution repo" ),
+		commit,
+		Release.confirmReview,
+
 		Release._section( "Pushing files to distribution repo" ),
 		push
 	], complete );
diff --git a/build/tasks/build.js b/build/tasks/build.js
index 1579691..69916bf 100644
--- a/build/tasks/build.js
+++ b/build/tasks/build.js
@@ -17,7 +17,11 @@ module.exports = function( grunt ) {
 		read = function( fileName ) {
 			return grunt.file.read( srcFolder + fileName );
 		},
-		wrapper = read( "wrapper.js" ).split( /\/\/ \@CODE\n\/\/[^\n]+/ ),
+
+		// Catch `// @CODE` and subsequent comment lines event if they don't start
+		// in the first column.
+		wrapper = read( "wrapper.js" ).split( /[\x20\t]*\/\/ @CODE\n(?:[\x20\t]*\/\/[^\n]+\n)*/ ),
+
 		config = {
 			baseUrl: "src",
 			name: "jquery",
diff --git a/package.json b/package.json
index b6dd0d3..fce3be6 100644
--- a/package.json
+++ b/package.json
@@ -2,12 +2,12 @@
   "name": "jquery",
   "title": "jQuery",
   "description": "JavaScript library for DOM operations",
-  "version": "3.1.1",
+  "version": "3.2.1",
   "main": "dist/jquery.js",
   "homepage": "https://jquery.com",
   "author": {
-    "name": "jQuery Foundation and other contributors",
-    "url": "https://github.com/jquery/jquery/blob/3.1.1/AUTHORS.txt"
+    "name": "JS Foundation and other contributors",
+    "url": "https://github.com/jquery/jquery/blob/3.2.1/AUTHORS.txt"
   },
   "repository": {
     "type": "git",
@@ -91,7 +91,7 @@
       "Traversing",
       "Wrap"
     ],
-    "markerPattern": "^((clos|fix|resolv)(e[sd]|ing))|(refs?)",
-    "ticketPattern": "^((Closes|Fixes) ([a-zA-Z]{2,}-)[0-9]+)|(Refs? [^#])"
+    "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
index dbf16c5..3a4a3d2 100644
--- a/src/.eslintrc.json
+++ b/src/.eslintrc.json
@@ -1,18 +1,5 @@
 {
-	// 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"]
-	}
+	"root": true,
+
+	"extends": "../.eslintrc-browser.json"
 }
diff --git a/src/attributes/attr.js b/src/attributes/attr.js
index 2d9c76f..6b5cbd2 100644
--- a/src/attributes/attr.js
+++ b/src/attributes/attr.js
@@ -1,10 +1,11 @@
 define( [
 	"../core",
 	"../core/access",
+	"../core/nodeName",
 	"./support",
 	"../var/rnothtmlwhite",
 	"../selector"
-], function( jQuery, access, support, rnothtmlwhite ) {
+], function( jQuery, access, nodeName, support, rnothtmlwhite ) {
 
 "use strict";
 
@@ -74,7 +75,7 @@ jQuery.extend( {
 		type: {
 			set: function( elem, value ) {
 				if ( !support.radioValue && value === "radio" &&
-					jQuery.nodeName( elem, "input" ) ) {
+					nodeName( elem, "input" ) ) {
 					var val = elem.value;
 					elem.setAttribute( "type", value );
 					if ( val ) {
diff --git a/src/attributes/val.js b/src/attributes/val.js
index fbf4069..d764ec6 100644
--- a/src/attributes/val.js
+++ b/src/attributes/val.js
@@ -2,8 +2,10 @@ define( [
 	"../core",
 	"../core/stripAndCollapse",
 	"./support",
+	"../core/nodeName",
+
 	"../core/init"
-], function( jQuery, stripAndCollapse, support ) {
+], function( jQuery, stripAndCollapse, support, nodeName ) {
 
 "use strict";
 
@@ -62,7 +64,7 @@ jQuery.fn.extend( {
 			} else if ( typeof val === "number" ) {
 				val += "";
 
-			} else if ( jQuery.isArray( val ) ) {
+			} else if ( Array.isArray( val ) ) {
 				val = jQuery.map( val, function( value ) {
 					return value == null ? "" : value + "";
 				} );
@@ -121,7 +123,7 @@ jQuery.extend( {
 							// Don't return options that are disabled or in a disabled optgroup
 							!option.disabled &&
 							( !option.parentNode.disabled ||
-								!jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+								!nodeName( option.parentNode, "optgroup" ) ) ) {
 
 						// Get the specific value for the option
 						value = jQuery( option ).val();
@@ -173,7 +175,7 @@ jQuery.extend( {
 jQuery.each( [ "radio", "checkbox" ], function() {
 	jQuery.valHooks[ this ] = {
 		set: function( elem, value ) {
-			if ( jQuery.isArray( value ) ) {
+			if ( Array.isArray( value ) ) {
 				return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
 			}
 		}
diff --git a/src/callbacks.js b/src/callbacks.js
index a6d4df0..8831e53 100644
--- a/src/callbacks.js
+++ b/src/callbacks.js
@@ -69,7 +69,7 @@ jQuery.Callbacks = function( options ) {
 		fire = function() {
 
 			// Enforce single-firing
-			locked = options.once;
+			locked = locked || options.once;
 
 			// Execute callbacks for all pending executions,
 			// respecting firingIndex overrides and runtime changes
diff --git a/src/core.js b/src/core.js
index 4c8a4ab..0e95274 100644
--- a/src/core.js
+++ b/src/core.js
@@ -24,7 +24,7 @@ define( [
 "use strict";
 
 var
-	version = "3.1.1",
+	version = "3.2.1",
 
 	// Define a local copy of jQuery
 	jQuery = function( selector, context ) {
@@ -172,11 +172,11 @@ jQuery.extend = jQuery.fn.extend = function() {
 
 				// Recurse if we're merging plain objects or arrays
 				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
-					( copyIsArray = jQuery.isArray( copy ) ) ) ) {
+					( copyIsArray = Array.isArray( copy ) ) ) ) {
 
 					if ( copyIsArray ) {
 						copyIsArray = false;
-						clone = src && jQuery.isArray( src ) ? src : [];
+						clone = src && Array.isArray( src ) ? src : [];
 
 					} else {
 						clone = src && jQuery.isPlainObject( src ) ? src : {};
@@ -215,8 +215,6 @@ jQuery.extend( {
 		return jQuery.type( obj ) === "function";
 	},
 
-	isArray: Array.isArray,
-
 	isWindow: function( obj ) {
 		return obj != null && obj === obj.window;
 	},
@@ -291,10 +289,6 @@ jQuery.extend( {
 		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
 	},
 
-	nodeName: function( elem, name ) {
-		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
-	},
-
 	each: function( obj, callback ) {
 		var length, i = 0;
 
diff --git a/src/core/init.js b/src/core/init.js
index 19a3c7c..8841264 100644
--- a/src/core/init.js
+++ b/src/core/init.js
@@ -3,6 +3,7 @@ define( [
 	"../core",
 	"../var/document",
 	"./var/rsingleTag",
+
 	"../traversing/findFilter"
 ], function( jQuery, document, rsingleTag ) {
 
diff --git a/src/core/nodeName.js b/src/core/nodeName.js
new file mode 100644
index 0000000..8a5f5f0
--- /dev/null
+++ b/src/core/nodeName.js
@@ -0,0 +1,13 @@
+define( function() {
+
+"use strict";
+
+function nodeName( elem, name ) {
+
+  return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+
+};
+
+return nodeName;
+
+} );
diff --git a/src/core/ready-no-deferred.js b/src/core/ready-no-deferred.js
index 02d6014..a7722ae 100644
--- a/src/core/ready-no-deferred.js
+++ b/src/core/ready-no-deferred.js
@@ -32,15 +32,6 @@ jQuery.extend( {
 	// 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
diff --git a/src/core/ready.js b/src/core/ready.js
index 53b1b2d..794feee 100644
--- a/src/core/ready.js
+++ b/src/core/ready.js
@@ -34,15 +34,6 @@ jQuery.extend( {
 	// the ready event fires. See #6781
 	readyWait: 1,
 
-	// Hold (or release) the ready event
-	holdReady: function( hold ) {
-		if ( hold ) {
-			jQuery.readyWait++;
-		} else {
-			jQuery.ready( true );
-		}
-	},
-
 	// Handle when the DOM is ready
 	ready: function( wait ) {
 
diff --git a/src/css.js b/src/css.js
index 5e44511..48a8340 100644
--- a/src/css.js
+++ b/src/css.js
@@ -28,6 +28,7 @@ var
 	// except "table", "table-cell", or "table-caption"
 	// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
 	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+	rcustomProp = /^--/,
 	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
 	cssNormalTransform = {
 		letterSpacing: "0",
@@ -57,6 +58,16 @@ function vendorPropName( name ) {
 	}
 }
 
+// Return a property mapped along what jQuery.cssProps suggests or to
+// a vendor prefixed property.
+function finalPropName( name ) {
+	var ret = jQuery.cssProps[ name ];
+	if ( !ret ) {
+		ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name;
+	}
+	return ret;
+}
+
 function setPositiveNumber( elem, value, subtract ) {
 
 	// Any relative (+/-) values have already been
@@ -117,44 +128,31 @@ function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
 
 function getWidthOrHeight( elem, name, extra ) {
 
-	// Start with offset property, which is equivalent to the border-box value
-	var val,
-		valueIsBorderBox = true,
+	// Start with computed style
+	var valueIsBorderBox,
 		styles = getStyles( elem ),
+		val = curCSS( elem, name, styles ),
 		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
 
-	// Support: IE <=11 only
-	// Running getBoundingClientRect on a disconnected node
-	// in IE throws an error.
-	if ( elem.getClientRects().length ) {
-		val = elem.getBoundingClientRect()[ name ];
+	// Computed unit is not pixels. Stop here and return.
+	if ( rnumnonpx.test( val ) ) {
+		return val;
 	}
 
-	// Some non-html elements return undefined for offsetWidth, so check for null/undefined
-	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
-	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
-	if ( val <= 0 || val == null ) {
-
-		// Fall back to computed then uncomputed css if necessary
-		val = curCSS( elem, name, styles );
-		if ( val < 0 || val == null ) {
-			val = elem.style[ name ];
-		}
-
-		// Computed unit is not pixels. Stop here and return.
-		if ( rnumnonpx.test( val ) ) {
-			return val;
-		}
-
-		// Check for style in case a browser which returns unreliable values
-		// for getComputedStyle silently falls back to the reliable elem.style
-		valueIsBorderBox = isBorderBox &&
-			( support.boxSizingReliable() || val === elem.style[ name ] );
+	// Check for style in case a browser which returns unreliable values
+	// for getComputedStyle silently falls back to the reliable elem.style
+	valueIsBorderBox = isBorderBox &&
+		( support.boxSizingReliable() || val === elem.style[ name ] );
 
-		// Normalize "", auto, and prepare for extra
-		val = parseFloat( val ) || 0;
+	// Fall back to offsetWidth/Height when value is "auto"
+	// This happens for inline elements with no explicit setting (gh-3571)
+	if ( val === "auto" ) {
+		val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ];
 	}
 
+	// Normalize "", auto, and prepare for extra
+	val = parseFloat( val ) || 0;
+
 	// Use the active box-sizing model to add/subtract irrelevant styles
 	return ( val +
 		augmentWidthOrHeight(
@@ -218,10 +216,15 @@ jQuery.extend( {
 		// Make sure that we're working with the right name
 		var ret, type, hooks,
 			origName = jQuery.camelCase( name ),
+			isCustomProp = rcustomProp.test( name ),
 			style = elem.style;
 
-		name = jQuery.cssProps[ origName ] ||
-			( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
+		// Make sure that we're working with the right name. We don't
+		// want to query the value if it is a CSS custom property
+		// since they are user-defined.
+		if ( !isCustomProp ) {
+			name = finalPropName( origName );
+		}
 
 		// Gets hook for the prefixed version, then unprefixed version
 		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
@@ -257,7 +260,11 @@ jQuery.extend( {
 			if ( !hooks || !( "set" in hooks ) ||
 				( value = hooks.set( elem, value, extra ) ) !== undefined ) {
 
-				style[ name ] = value;
+				if ( isCustomProp ) {
+					style.setProperty( name, value );
+				} else {
+					style[ name ] = value;
+				}
 			}
 
 		} else {
@@ -276,11 +283,15 @@ jQuery.extend( {
 
 	css: function( elem, name, extra, styles ) {
 		var val, num, hooks,
-			origName = jQuery.camelCase( name );
+			origName = jQuery.camelCase( name ),
+			isCustomProp = rcustomProp.test( name );
 
-		// Make sure that we're working with the right name
-		name = jQuery.cssProps[ origName ] ||
-			( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
+		// Make sure that we're working with the right name. We don't
+		// want to modify the value if it is a CSS custom property
+		// since they are user-defined.
+		if ( !isCustomProp ) {
+			name = finalPropName( origName );
+		}
 
 		// Try prefixed name followed by the unprefixed name
 		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
@@ -305,6 +316,7 @@ jQuery.extend( {
 			num = parseFloat( val );
 			return extra === true || isFinite( num ) ? num || 0 : val;
 		}
+
 		return val;
 	}
 } );
@@ -404,7 +416,7 @@ jQuery.fn.extend( {
 				map = {},
 				i = 0;
 
-			if ( jQuery.isArray( name ) ) {
+			if ( Array.isArray( name ) ) {
 				styles = getStyles( elem );
 				len = name.length;
 
diff --git a/src/css/curCSS.js b/src/css/curCSS.js
index 313da42..01ccad3 100644
--- a/src/css/curCSS.js
+++ b/src/css/curCSS.js
@@ -11,12 +11,18 @@ define( [
 
 function curCSS( elem, name, computed ) {
 	var width, minWidth, maxWidth, ret,
+
+		// Support: Firefox 51+
+		// Retrieving style before computed somehow
+		// fixes an issue with getting wrong values
+		// on detached elements
 		style = elem.style;
 
 	computed = computed || getStyles( elem );
 
-	// Support: IE <=9 only
-	// getPropertyValue is only needed for .css('filter') (#12537)
+	// getPropertyValue is needed for:
+	//   .css('filter') (IE 9 only, #12537)
+	//   .css('--customProperty) (#3144)
 	if ( computed ) {
 		ret = computed.getPropertyValue( name ) || computed[ name ];
 
diff --git a/src/data/Data.js b/src/data/Data.js
index 43ae016..8c856c0 100644
--- a/src/data/Data.js
+++ b/src/data/Data.js
@@ -115,7 +115,7 @@ Data.prototype = {
 		if ( key !== undefined ) {
 
 			// Support array or space separated string of keys
-			if ( jQuery.isArray( key ) ) {
+			if ( Array.isArray( key ) ) {
 
 				// If key is an array of keys...
 				// We always set camelCase keys, so remove that.
diff --git a/src/deferred.js b/src/deferred.js
index 8139515..7e2ced2 100644
--- a/src/deferred.js
+++ b/src/deferred.js
@@ -13,7 +13,7 @@ function Thrower( ex ) {
 	throw ex;
 }
 
-function adoptValue( value, resolve, reject ) {
+function adoptValue( value, resolve, reject, noValue ) {
 	var method;
 
 	try {
@@ -29,9 +29,10 @@ function adoptValue( 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 );
+			// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
+			// * false: [ value ].slice( 0 ) => resolve( value )
+			// * true: [ value ].slice( 1 ) => resolve()
+			resolve.apply( undefined, [ value ].slice( noValue ) );
 		}
 
 	// For Promises/A+, convert exceptions into rejections
@@ -41,7 +42,7 @@ function adoptValue( value, resolve, reject ) {
 
 		// Support: Android 4.0 only
 		// Strict mode functions invoked without .call/.apply get global-object context
-		reject.call( undefined, value );
+		reject.apply( undefined, [ value ] );
 	}
 }
 
@@ -366,7 +367,8 @@ jQuery.extend( {
 
 		// Single- and empty arguments are adopted like Promise.resolve
 		if ( remaining <= 1 ) {
-			adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject );
+			adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
+				!remaining );
 
 			// Use .then() to unwrap secondary thenables (cf. gh-3000)
 			if ( master.state() === "pending" ||
diff --git a/src/deprecated.js b/src/deprecated.js
index 9fcc6b7..9589ec8 100644
--- a/src/deprecated.js
+++ b/src/deprecated.js
@@ -1,6 +1,7 @@
 define( [
-	"./core"
-], function( jQuery ) {
+	"./core",
+	"./core/nodeName"
+], function( jQuery, nodeName ) {
 
 "use strict";
 
@@ -25,6 +26,15 @@ jQuery.fn.extend( {
 	}
 } );
 
+jQuery.holdReady = function( hold ) {
+	if ( hold ) {
+		jQuery.readyWait++;
+	} else {
+		jQuery.ready( true );
+	}
+};
+jQuery.isArray = Array.isArray;
 jQuery.parseJSON = JSON.parse;
+jQuery.nodeName = nodeName;
 
 } );
diff --git a/src/effects.js b/src/effects.js
index 68af96c..879b361 100644
--- a/src/effects.js
+++ b/src/effects.js
@@ -23,13 +23,18 @@ define( [
 "use strict";
 
 var
-	fxNow, timerId,
+	fxNow, inProgress,
 	rfxtypes = /^(?:toggle|show|hide)$/,
 	rrun = /queueHooks$/;
 
-function raf() {
-	if ( timerId ) {
-		window.requestAnimationFrame( raf );
+function schedule() {
+	if ( inProgress ) {
+		if ( document.hidden === false && window.requestAnimationFrame ) {
+			window.requestAnimationFrame( schedule );
+		} else {
+			window.setTimeout( schedule, jQuery.fx.interval );
+		}
+
 		jQuery.fx.tick();
 	}
 }
@@ -256,7 +261,7 @@ function propFilter( props, specialEasing ) {
 		name = jQuery.camelCase( index );
 		easing = specialEasing[ name ];
 		value = props[ index ];
-		if ( jQuery.isArray( value ) ) {
+		if ( Array.isArray( value ) ) {
 			easing = value[ 1 ];
 			value = props[ index ] = value[ 0 ];
 		}
@@ -315,12 +320,19 @@ function Animation( elem, properties, options ) {
 
 			deferred.notifyWith( elem, [ animation, percent, remaining ] );
 
+			// If there's more to do, yield
 			if ( percent < 1 && length ) {
 				return remaining;
-			} else {
-				deferred.resolveWith( elem, [ animation ] );
-				return false;
 			}
+
+			// If this was an empty animation, synthesize a final progress notification
+			if ( !length ) {
+				deferred.notifyWith( elem, [ animation, 1, 0 ] );
+			}
+
+			// Resolve the animation and report its conclusion
+			deferred.resolveWith( elem, [ animation ] );
+			return false;
 		},
 		animation = deferred.promise( {
 			elem: elem,
@@ -385,6 +397,13 @@ function Animation( elem, properties, options ) {
 		animation.opts.start.call( elem, animation );
 	}
 
+	// Attach callbacks from options
+	animation
+		.progress( animation.opts.progress )
+		.done( animation.opts.done, animation.opts.complete )
+		.fail( animation.opts.fail )
+		.always( animation.opts.always );
+
 	jQuery.fx.timer(
 		jQuery.extend( tick, {
 			elem: elem,
@@ -393,11 +412,7 @@ function Animation( elem, properties, options ) {
 		} )
 	);
 
-	// attach callbacks from options
-	return animation.progress( animation.opts.progress )
-		.done( animation.opts.done, animation.opts.complete )
-		.fail( animation.opts.fail )
-		.always( animation.opts.always );
+	return animation;
 }
 
 jQuery.Animation = jQuery.extend( Animation, {
@@ -448,8 +463,8 @@ jQuery.speed = function( speed, easing, fn ) {
 		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
 	};
 
-	// Go to the end state if fx are off or if document is hidden
-	if ( jQuery.fx.off || document.hidden ) {
+	// Go to the end state if fx are off
+	if ( jQuery.fx.off ) {
 		opt.duration = 0;
 
 	} else {
@@ -641,7 +656,7 @@ jQuery.fx.tick = function() {
 	for ( ; i < timers.length; i++ ) {
 		timer = timers[ i ];
 
-		// Checks the timer has not already been removed
+		// Run the timer and safely remove it when done (allowing for external removal)
 		if ( !timer() && timers[ i ] === timer ) {
 			timers.splice( i--, 1 );
 		}
@@ -655,30 +670,21 @@ jQuery.fx.tick = function() {
 
 jQuery.fx.timer = function( timer ) {
 	jQuery.timers.push( timer );
-	if ( timer() ) {
-		jQuery.fx.start();
-	} else {
-		jQuery.timers.pop();
-	}
+	jQuery.fx.start();
 };
 
 jQuery.fx.interval = 13;
 jQuery.fx.start = function() {
-	if ( !timerId ) {
-		timerId = window.requestAnimationFrame ?
-			window.requestAnimationFrame( raf ) :
-			window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
+	if ( inProgress ) {
+		return;
 	}
+
+	inProgress = true;
+	schedule();
 };
 
 jQuery.fx.stop = function() {
-	if ( window.cancelAnimationFrame ) {
-		window.cancelAnimationFrame( timerId );
-	} else {
-		window.clearInterval( timerId );
-	}
-
-	timerId = null;
+	inProgress = null;
 };
 
 jQuery.fx.speeds = {
diff --git a/src/event.js b/src/event.js
index ab2c63c..071deb6 100644
--- a/src/event.js
+++ b/src/event.js
@@ -5,10 +5,11 @@ define( [
 	"./var/rnothtmlwhite",
 	"./var/slice",
 	"./data/var/dataPriv",
+	"./core/nodeName",
 
 	"./core/init",
 	"./selector"
-], function( jQuery, document, documentElement, rnothtmlwhite, slice, dataPriv ) {
+], function( jQuery, document, documentElement, rnothtmlwhite, slice, dataPriv, nodeName ) {
 
 "use strict";
 
@@ -476,7 +477,7 @@ jQuery.event = {
 
 			// For checkbox, fire native event so checked state will be right
 			trigger: function() {
-				if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
+				if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) {
 					this.click();
 					return false;
 				}
@@ -484,7 +485,7 @@ jQuery.event = {
 
 			// For cross-browser consistency, don't fire native .click() on links
 			_default: function( event ) {
-				return jQuery.nodeName( event.target, "a" );
+				return nodeName( event.target, "a" );
 			}
 		},
 
diff --git a/src/manipulation.js b/src/manipulation.js
index 9b4f5e4..cd225a6 100644
--- a/src/manipulation.js
+++ b/src/manipulation.js
@@ -16,6 +16,7 @@ define( [
 	"./data/var/dataUser",
 	"./data/var/acceptData",
 	"./core/DOMEval",
+	"./core/nodeName",
 
 	"./core/init",
 	"./traversing",
@@ -24,7 +25,7 @@ define( [
 ], function( jQuery, concat, push, access,
 	rcheckableType, rtagName, rscriptType,
 	wrapMap, getAll, setGlobalEval, buildFragment, support,
-	dataPriv, dataUser, acceptData, DOMEval ) {
+	dataPriv, dataUser, acceptData, DOMEval, nodeName ) {
 
 "use strict";
 
@@ -47,11 +48,12 @@ var
 	rscriptTypeMasked = /^true\/(.*)/,
 	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
 
+// Prefer a tbody over its parent table for containing new rows
 function manipulationTarget( elem, content ) {
-	if ( jQuery.nodeName( elem, "table" ) &&
-		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
+	if ( nodeName( elem, "table" ) &&
+		nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
 
-		return elem.getElementsByTagName( "tbody" )[ 0 ] || elem;
+		return jQuery( ">tbody", elem )[ 0 ] || elem;
 	}
 
 	return elem;
diff --git a/src/manipulation/getAll.js b/src/manipulation/getAll.js
index f68e321..fede6c7 100644
--- a/src/manipulation/getAll.js
+++ b/src/manipulation/getAll.js
@@ -1,6 +1,7 @@
 define( [
-	"../core"
-], function( jQuery ) {
+	"../core",
+	"../core/nodeName"
+], function( jQuery, nodeName ) {
 
 "use strict";
 
@@ -20,7 +21,7 @@ function getAll( context, tag ) {
 		ret = [];
 	}
 
-	if ( tag === undefined || tag && jQuery.nodeName( context, tag ) ) {
+	if ( tag === undefined || tag && nodeName( context, tag ) ) {
 		return jQuery.merge( [ context ], ret );
 	}
 
diff --git a/src/offset.js b/src/offset.js
index 54442eb..c1ab857 100644
--- a/src/offset.js
+++ b/src/offset.js
@@ -7,21 +7,16 @@ define( [
 	"./css/curCSS",
 	"./css/addGetHookIf",
 	"./css/support",
+	"./core/nodeName",
 
 	"./core/init",
 	"./css",
 	"./selector" // contains
-], function( jQuery, access, document, documentElement, rnumnonpx, curCSS, addGetHookIf, support ) {
+], function( jQuery, access, document, documentElement, rnumnonpx,
+             curCSS, addGetHookIf, support, nodeName ) {
 
 "use strict";
 
-/**
- * Gets a window from an element
- */
-function getWindow( elem ) {
-	return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
-}
-
 jQuery.offset = {
 	setOffset: function( elem, options, i ) {
 		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
@@ -86,13 +81,14 @@ jQuery.fn.extend( {
 				} );
 		}
 
-		var docElem, win, rect, doc,
+		var doc, docElem, rect, win,
 			elem = this[ 0 ];
 
 		if ( !elem ) {
 			return;
 		}
 
+		// Return zeros for disconnected and hidden (display: none) elements (gh-2310)
 		// Support: IE <=11 only
 		// Running getBoundingClientRect on a
 		// disconnected node in IE throws an error
@@ -102,20 +98,14 @@ jQuery.fn.extend( {
 
 		rect = elem.getBoundingClientRect();
 
-		// Make sure element is not hidden (display: none)
-		if ( rect.width || rect.height ) {
-			doc = elem.ownerDocument;
-			win = getWindow( doc );
-			docElem = doc.documentElement;
-
-			return {
-				top: rect.top + win.pageYOffset - docElem.clientTop,
-				left: rect.left + win.pageXOffset - docElem.clientLeft
-			};
-		}
+		doc = elem.ownerDocument;
+		docElem = doc.documentElement;
+		win = doc.defaultView;
 
-		// Return zeros for disconnected and hidden elements (gh-2310)
-		return rect;
+		return {
+			top: rect.top + win.pageYOffset - docElem.clientTop,
+			left: rect.left + win.pageXOffset - docElem.clientLeft
+		};
 	},
 
 	position: function() {
@@ -141,7 +131,7 @@ jQuery.fn.extend( {
 
 			// Get correct offsets
 			offset = this.offset();
-			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+			if ( !nodeName( offsetParent[ 0 ], "html" ) ) {
 				parentOffset = offsetParent.offset();
 			}
 
@@ -188,7 +178,14 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function(
 
 	jQuery.fn[ method ] = function( val ) {
 		return access( this, function( elem, method, val ) {
-			var win = getWindow( elem );
+
+			// Coalesce documents and windows
+			var win;
+			if ( jQuery.isWindow( elem ) ) {
+				win = elem;
+			} else if ( elem.nodeType === 9 ) {
+				win = elem.defaultView;
+			}
 
 			if ( val === undefined ) {
 				return win ? win[ prop ] : elem[ method ];
diff --git a/src/queue.js b/src/queue.js
index 3a626a2..fbbbeab 100644
--- a/src/queue.js
+++ b/src/queue.js
@@ -17,7 +17,7 @@ jQuery.extend( {
 
 			// Speed up dequeue by getting out quickly if this is just a lookup
 			if ( data ) {
-				if ( !queue || jQuery.isArray( data ) ) {
+				if ( !queue || Array.isArray( data ) ) {
 					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
 				} else {
 					queue.push( data );
diff --git a/src/serialize.js b/src/serialize.js
index 35dcf04..2e28ce1 100644
--- a/src/serialize.js
+++ b/src/serialize.js
@@ -17,7 +17,7 @@ var
 function buildParams( prefix, obj, traditional, add ) {
 	var name;
 
-	if ( jQuery.isArray( obj ) ) {
+	if ( Array.isArray( obj ) ) {
 
 		// Serialize array item.
 		jQuery.each( obj, function( i, v ) {
@@ -69,7 +69,7 @@ jQuery.param = function( a, traditional ) {
 		};
 
 	// If an array was passed in, assume that it is an array of form elements.
-	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+	if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
 
 		// Serialize the form elements
 		jQuery.each( a, function() {
@@ -115,7 +115,7 @@ jQuery.fn.extend( {
 				return null;
 			}
 
-			if ( jQuery.isArray( val ) ) {
+			if ( Array.isArray( val ) ) {
 				return jQuery.map( val, function( val ) {
 					return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
 				} );
diff --git a/src/traversing.js b/src/traversing.js
index 50cd2d6..64c7252 100644
--- a/src/traversing.js
+++ b/src/traversing.js
@@ -4,10 +4,12 @@ define( [
 	"./traversing/var/dir",
 	"./traversing/var/siblings",
 	"./traversing/var/rneedsContext",
+	"./core/nodeName",
+
 	"./core/init",
 	"./traversing/findFilter",
 	"./selector"
-], function( jQuery, indexOf, dir, siblings, rneedsContext ) {
+], function( jQuery, indexOf, dir, siblings, rneedsContext, nodeName ) {
 
 "use strict";
 
@@ -143,7 +145,18 @@ jQuery.each( {
 		return siblings( elem.firstChild );
 	},
 	contents: function( elem ) {
-		return elem.contentDocument || jQuery.merge( [], elem.childNodes );
+        if ( nodeName( elem, "iframe" ) ) {
+            return elem.contentDocument;
+        }
+
+        // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
+        // Treat the template element as a regular one in browsers that
+        // don't support it.
+        if ( nodeName( elem, "template" ) ) {
+            elem = elem.content || elem;
+        }
+
+        return jQuery.merge( [], elem.childNodes );
 	}
 }, function( name, fn ) {
 	jQuery.fn[ name ] = function( until, selector ) {
diff --git a/src/wrapper.js b/src/wrapper.js
index af90fb2..0af1f49 100644
--- a/src/wrapper.js
+++ b/src/wrapper.js
@@ -6,7 +6,7 @@
  * Includes Sizzle.js
  * https://sizzlejs.com/
  *
- * Copyright jQuery Foundation and other contributors
+ * Copyright JS Foundation and other contributors
  * Released under the MIT license
  * https://jquery.org/license
  *
diff --git a/test/.eslintrc.json b/test/.eslintrc.json
index 744de89..8cc2adc 100644
--- a/test/.eslintrc.json
+++ b/test/.eslintrc.json
@@ -1,13 +1,15 @@
 {
+	"root": true,
+
+	"extends": "../.eslintrc-browser.json",
+
 	"env": {
+
+		// In source the browser env is not enabled but unit tests rely on them
+		// too much and we don't run them in non-browser environments anyway.
 		"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,
@@ -41,17 +43,19 @@
 		"baseURL": true,
 		"externalHost": true
 	},
+
 	"rules": {
 		// See https://github.com/eslint/eslint/issues/2342
 		"no-unused-vars": "off",
 
-		// Too much errors
+		// Too many errors
 		"max-len": "off",
 		"brace-style": "off",
 		"key-spacing": "off",
 		"camelcase": "off",
+		"strict": "off",
 
-		// Not really too much - waiting autofix features for these rules
+		// Not really too many - waiting for autofix features for these rules
 		"lines-around-comment": "off",
 		"dot-notation": "off"
 	}
diff --git a/test/readywait.html b/test/data/readywait.html
similarity index 77%
rename from test/readywait.html
rename to test/data/readywait.html
index e34a0d7..d7de0b0 100644
--- a/test/readywait.html
+++ b/test/data/readywait.html
@@ -14,22 +14,28 @@
 		#output { background-color: green }
 		#expectedOutput { background-color: green }
 	</style>
-	<script src="jquery.js"></script>
+	<script src="../jquery.js"></script>
+	<script src="./iframeTest.js"></script>
 
 	<!-- Load the script loader that uses
 		jQuery.readyWait -->
-	<script src="data/readywaitloader.js"></script>
 
 	<script type="text/javascript">
-	jQuery(function() {
-		// The delayedMessage is defined by
-		// the readywaitasset.js file, so the
-		// next line will only work if this DOM
-		// ready callback is called after readyWait
-		// has been decremented by readywaitloader.js
-		// If an error occurs.
-		jQuery("#output").append(delayedMessage);
-	});
+	(function() {
+		var released = false;
+		// Hold on jQuery!
+		jQuery.holdReady( true );
+
+		setTimeout( function() {
+			released = true;
+			jQuery.holdReady( false );
+		}, 300 );
+
+		jQuery(function() {
+			jQuery( "#output" ).text( "Ready called, holdReady released: " + released );
+			startIframeTest( released );
+		});
+	})();
 	</script>
 </head>
 <body>
diff --git a/test/data/readywaitasset.js b/test/data/readywaitasset.js
deleted file mode 100644
index 2308965..0000000
--- a/test/data/readywaitasset.js
+++ /dev/null
@@ -1 +0,0 @@
-var delayedMessage = "It worked!";
diff --git a/test/data/readywaitloader.js b/test/data/readywaitloader.js
deleted file mode 100644
index 8f4a345..0000000
--- a/test/data/readywaitloader.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// Simple script loader that uses jQuery.readyWait via jQuery.holdReady()
-
-//Hold on jQuery!
-jQuery.holdReady( true );
-
-var readyRegExp = /^(complete|loaded)$/;
-
-function assetLoaded( evt ) {
-	var node = evt.currentTarget || evt.srcElement;
-	if ( evt.type === "load" || readyRegExp.test( node.readyState ) ) {
-		jQuery.holdReady( false );
-	}
-}
-
-setTimeout( function() {
-	var script = document.createElement( "script" );
-	script.type = "text/javascript";
-	if ( script.addEventListener ) {
-		script.addEventListener( "load", assetLoaded, false );
-	} else {
-		script.attachEvent( "onreadystatechange", assetLoaded );
-	}
-	script.src = "data/readywaitasset.js";
-	document.getElementsByTagName( "head" )[ 0 ].appendChild( script );
-}, 2000 );
diff --git a/test/data/testinit.js b/test/data/testinit.js
index ef210e7..c37687f 100644
--- a/test/data/testinit.js
+++ b/test/data/testinit.js
@@ -169,7 +169,7 @@ this.ajaxTest = function( title, expect, options ) {
 		}
 		options = options || [];
 		requestOptions = options.requests || options.request || options;
-		if ( !jQuery.isArray( requestOptions ) ) {
+		if ( !Array.isArray( requestOptions ) ) {
 			requestOptions = [ requestOptions ];
 		}
 
diff --git a/test/data/testrunner.js b/test/data/testrunner.js
index e5ca013..0784ae0 100644
--- a/test/data/testrunner.js
+++ b/test/data/testrunner.js
@@ -35,7 +35,7 @@ QUnit.assert.expectJqData = function( env, elems, key ) {
 		if ( elems.jquery && elems.toArray ) {
 			elems = elems.toArray();
 		}
-		if ( !supportjQuery.isArray( elems ) ) {
+		if ( !Array.isArray( elems ) ) {
 			elems = [ elems ];
 		}
 
diff --git a/test/jquery.js b/test/jquery.js
index 8119d3f..e337a79 100644
--- a/test/jquery.js
+++ b/test/jquery.js
@@ -2,21 +2,35 @@
 ( function() {
 	/* global loadTests: false */
 
-	var src,
-		path = window.location.pathname.split( "test" )[ 0 ],
+	var path = window.location.pathname.split( "test" )[ 0 ],
 		QUnit = window.QUnit || parent.QUnit,
-		require = window.require || parent.require;
-
-	// iFrames won't load AMD (the iframe tests synchronously expect jQuery to be there)
-	QUnit.config.urlConfig.push( {
-		id: "amd",
-		label: "Load with AMD",
-		tooltip: "Load the AMD jQuery file (and its dependencies)"
-	} );
-
-	// If QUnit is on window, this is the main window
-	// This detection allows AMD tests to be run in an iframe
-	if ( QUnit.urlParams.amd && window.QUnit ) {
+		require = window.require || parent.require,
+
+		// Default to unminified jQuery for directly-opened iframes
+		urlParams = QUnit ?
+			QUnit.urlParams :
+			{ dev: true },
+		src = urlParams.dev ?
+			"dist/jquery.js" :
+			"dist/jquery.min.js";
+
+	// Define configuration parameters controlling how jQuery is loaded
+	if ( QUnit ) {
+		QUnit.config.urlConfig.push( {
+			id: "amd",
+			label: "Load with AMD",
+			tooltip: "Load the AMD jQuery file (and its dependencies)"
+		} );
+		QUnit.config.urlConfig.push( {
+			id: "dev",
+			label: "Load unminified",
+			tooltip: "Load the development (unminified) jQuery file"
+		} );
+	}
+
+	// Honor AMD loading on the main window (detected by seeing QUnit on it).
+	// This doesn't apply to iframes because they synchronously expect jQuery to be there.
+	if ( urlParams.amd && window.QUnit ) {
 		require.config( {
 			baseUrl: path
 		} );
@@ -28,28 +42,15 @@
 		} else {
 			require( [ src ] );
 		}
-		return;
-	}
 
-	// Config parameter to use minified jQuery
-	QUnit.config.urlConfig.push( {
-		id: "dev",
-		label: "Load unminified",
-		tooltip: "Load the development (unminified) jQuery file"
-	} );
-	if ( QUnit.urlParams.dev ) {
-		src = "dist/jquery.js";
+	// Otherwise, load synchronously
 	} else {
-		src = "dist/jquery.min.js";
-	}
-
-	// Load jQuery
-	document.write( "<script id='jquery-js' src='" + path + src + "'><\x2Fscript>" );
+		document.write( "<script id='jquery-js' src='" + path + src + "'><\x2Fscript>" );
 
-	// Synchronous-only tests
-	// Other tests are loaded from the test page
-	if ( typeof loadTests !== "undefined" ) {
-		document.write( "<script src='" + path + "test/unit/ready.js'><\x2Fscript>" );
+		// Synchronous-only tests (other tests are loaded from the test page)
+		if ( typeof loadTests !== "undefined" ) {
+			document.write( "<script src='" + path + "test/unit/ready.js'><\x2Fscript>" );
+		}
 	}
 
 } )();
diff --git a/test/node_smoke_tests/.eslintrc.json b/test/node_smoke_tests/.eslintrc.json
index 0877d24..a1bd6ec 100644
--- a/test/node_smoke_tests/.eslintrc.json
+++ b/test/node_smoke_tests/.eslintrc.json
@@ -1,7 +1,13 @@
 {
+	"root": true,
+
+	"extends": "../../.eslintrc-node.json",
+
+	"parserOptions": {
+		"ecmaVersion": 6
+	},
+
 	"env": {
 		"es6": true
-	},
-	"extends" : "../../.eslintrc.json",
-	"root": true
+	}
 }
diff --git a/test/promises_aplus_adapters/.eslintrc.json b/test/promises_aplus_adapters/.eslintrc.json
index d117757..f961645 100644
--- a/test/promises_aplus_adapters/.eslintrc.json
+++ b/test/promises_aplus_adapters/.eslintrc.json
@@ -1,4 +1,5 @@
 {
-	"extends": "../../.eslintrc.json",
-	"root": true
+	"root": true,
+
+	"extends": "../../.eslintrc-node.json"
 }
diff --git a/test/unit/callbacks.js b/test/unit/callbacks.js
index c6c379d..04d4444 100644
--- a/test/unit/callbacks.js
+++ b/test/unit/callbacks.js
@@ -366,3 +366,24 @@ QUnit.test( "jQuery.Callbacks() - disabled callback doesn't fire (gh-1790)", fun
 	cb.fire();
 	assert.ok( !fired, "Disabled callback function didn't fire" );
 } );
+
+QUnit.test( "jQuery.Callbacks() - list with memory stays locked (gh-3469)", function( assert ) {
+
+	assert.expect( 3 );
+
+	var cb = jQuery.Callbacks( "memory" ),
+		fired = 0,
+		count1 = function() { fired += 1; },
+		count2 = function() { fired += 10; };
+
+	cb.add( count1 );
+	cb.fire();
+	assert.equal( fired, 1, "Pre-lock() fire" );
+
+	cb.lock();
+	cb.add( count2 );
+	assert.equal( fired, 11, "Post-lock() add" );
+
+	cb.fire();
+	assert.equal( fired, 11, "Post-lock() fire ignored" );
+} );
diff --git a/test/unit/core.js b/test/unit/core.js
index f5083b4..adccfb5 100644
--- a/test/unit/core.js
+++ b/test/unit/core.js
@@ -687,17 +687,26 @@ QUnit.test( "jQuery('html')", function( assert ) {
 	assert.equal( jQuery( "\\<div\\>" ).length, 0, "Ignore escaped html characters" );
 } );
 
-QUnit.test( "jQuery(tag-hyphenated elements) gh-1987", function( assert ) {
-	assert.expect( 17 );
+QUnit.test( "jQuery(element with non-alphanumeric name)", function( assert ) {
+	assert.expect( 36 );
+
+	jQuery.each( [ "-", ":" ], function( i, symbol ) {
+		jQuery.each( [ "thead", "tbody", "tfoot", "colgroup", "caption", "tr", "th", "td" ],
+			function( j, tag ) {
+				var tagName = tag + symbol + "test";
+				var el = jQuery( "<" + tagName + "></" + tagName + ">" );
+				assert.ok( el[ 0 ], "Create a " + tagName + " element" );
+				assert.ok( el[ 0 ].nodeName === tagName.toUpperCase(),
+					tagName + " element has expected node name" );
+			}
+		);
 
-	jQuery.each( "thead tbody tfoot colgroup caption tr th td".split( " " ), function( i, name ) {
-		var j = jQuery( "<" + name + "-d></" + name + "-d>" );
-		assert.ok( j[ 0 ], "Create a tag-hyphenated elements" );
-		assert.ok( jQuery.nodeName( j[ 0 ], name.toUpperCase() + "-D" ), "Tag-hyphenated element has expected node name" );
+		var tagName = [ "tr", "multiple", "symbol" ].join( symbol );
+		var el = jQuery( "<" + tagName + "></" + tagName + ">" );
+		assert.ok( el[ 0 ], "Create a " + tagName + " element" );
+		assert.ok( el[ 0 ].nodeName === tagName.toUpperCase(),
+			tagName + " element has expected node name" );
 	} );
-
-	var j = jQuery( "<tr-multiple-hyphens></tr-multiple-hyphens>" );
-	assert.ok( jQuery.nodeName( j[ 0 ], "TR-MULTIPLE-HYPHENS" ), "Element with multiple hyphens in its tag has expected node name" );
 } );
 
 QUnit.test( "jQuery('massive html #7990')", function( assert ) {
@@ -1161,7 +1170,7 @@ QUnit.test( "jQuery.extend(Object, Object)", function( assert ) {
 	assert.ok( jQuery.extend( true, {}, nestedarray )[ "arr" ] !== arr, "Deep extend of object must clone child array" );
 
 	// #5991
-	assert.ok( jQuery.isArray( jQuery.extend( true, { "arr": {} }, nestedarray )[ "arr" ] ), "Cloned array have to be an Array" );
+	assert.ok( Array.isArray( jQuery.extend( true, { "arr": {} }, nestedarray )[ "arr" ] ), "Cloned array have to be an Array" );
 	assert.ok( jQuery.isPlainObject( jQuery.extend( true, { "arr": arr }, { "arr": {} } )[ "arr" ] ), "Cloned object have to be an plain object" );
 
 	empty = {};
@@ -1282,7 +1291,7 @@ QUnit.test( "jQuery.extend(true,{},{a:[], o:{}}); deep copy with array, followed
 	result = jQuery.extend( true, {}, initial );
 
 	assert.deepEqual( result, initial, "The [result] and [initial] have equal shape and values" );
-	assert.ok( !jQuery.isArray( result.object ), "result.object wasn't paved with an empty array" );
+	assert.ok( !Array.isArray( result.object ), "result.object wasn't paved with an empty array" );
 } );
 
 QUnit.test( "jQuery.each(Object,Function)", function( assert ) {
diff --git a/test/unit/css.js b/test/unit/css.js
index 2f529b6..b6bb955 100644
--- a/test/unit/css.js
+++ b/test/unit/css.js
@@ -1556,4 +1556,81 @@ QUnit.test( "Do not throw on frame elements from css method (#15098)", function(
 
 } )();
 
+( function() {
+	var supportsCssVars,
+		elem = jQuery( "<div>" ).appendTo( document.body ),
+		div = elem[ 0 ];
+
+	div.style.setProperty( "--prop", "value" );
+	supportsCssVars = !!getComputedStyle( div ).getPropertyValue( "--prop" );
+	elem.remove();
+
+	QUnit[ supportsCssVars ? "test" : "skip" ]( "css(--customProperty)", function( assert ) {
+		jQuery( "#qunit-fixture" ).append(
+			"<style>\n" +
+			"    .test__customProperties {\n" +
+			"        --prop1:val1;\n" +
+			"        --prop2: val2;\n" +
+			"        --prop3:val3 ;\n" +
+			"        --prop4:\"val4\";\n" +
+			"        --prop5:'val5';\n" +
+			"    }\n" +
+			"</style>"
+		);
+
+		var div = jQuery( "<div>" ).appendTo( "#qunit-fixture" ),
+			$elem = jQuery( "<div>" ).addClass( "test__customProperties" )
+				.appendTo( "#qunit-fixture" ),
+			webkit = /\bsafari\b/i.test( navigator.userAgent ) &&
+				!/\firefox\b/i.test( navigator.userAgent ) &&
+				!/\edge\b/i.test( navigator.userAgent ),
+			oldSafari = webkit && ( /\b9\.\d(\.\d+)* safari/i.test( navigator.userAgent ) ||
+				/\b10\.0(\.\d+)* safari/i.test( navigator.userAgent ) ||
+				/iphone os (?:9|10)_/i.test( navigator.userAgent ) ),
+			expected = 10;
+
+		if ( webkit ) {
+			expected -= 2;
+		}
+		if ( oldSafari ) {
+			expected -= 2;
+		}
+		assert.expect( expected );
+
+		div.css( "--color", "blue" );
+		assert.equal( div.css( "--color" ), "blue", "Modified CSS custom property using string" );
+
+		div.css( "--color", "yellow" );
+		assert.equal( div.css( "--color" ), "yellow", "Overwrite CSS custom property" );
+
+		div.css( { "--color": "red" } );
+		assert.equal( div.css( "--color" ), "red", "Modified CSS custom property using object" );
+
+		div.css( { "--mixedCase": "green" } );
+		assert.equal( div.css( "--mixedCase" ), "green",
+			"Modified CSS custom property with mixed case" );
+
+		div.css( { "--theme-dark": "purple" } );
+		assert.equal( div.css( "--theme-dark" ), "purple",
+			"Modified CSS custom property with dashed name" );
+
+		assert.equal( $elem.css( "--prop1" ), "val1", "Basic CSS custom property" );
+
+		// Support: Safari 9.1-10.0 only
+		// Safari collapses whitespaces & quotes. Ignore it.
+		if ( !oldSafari ) {
+			assert.equal( $elem.css( "--prop2" ), " val2", "Preceding whitespace maintained" );
+			assert.equal( $elem.css( "--prop3" ), "val3 ", "Following whitespace maintained" );
+		}
+
+		// Support: Chrome 49-55, Safari 9.1-10.0
+		// Chrome treats single quotes as double ones.
+		// Safari treats double quotes as single ones.
+		if ( !webkit ) {
+			assert.equal( $elem.css( "--prop4" ), "\"val4\"", "Works with double quotes" );
+			assert.equal( $elem.css( "--prop5" ), "'val5'", "Works with single quotes" );
+		}
+	} );
+} )();
+
 }
diff --git a/test/unit/deferred.js b/test/unit/deferred.js
index 32f3256..426af4b 100644
--- a/test/unit/deferred.js
+++ b/test/unit/deferred.js
@@ -814,11 +814,11 @@ QUnit.test( "jQuery.when(nonThenable) - like Promise.resolve", function( assert
 
 	jQuery.when()
 		.done( function( resolveValue ) {
-			assert.strictEqual( resolveValue, undefined, "Resolved .done with no arguments" );
+			assert.strictEqual( arguments.length, 0, "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( arguments.length, 0, "Resolved .then with no arguments" );
 			assert.strictEqual( this, defaultContext, "Default .then context with no arguments" );
 		} );
 
diff --git a/test/unit/deprecated.js b/test/unit/deprecated.js
index 27ba1cf..5c05d9b 100644
--- a/test/unit/deprecated.js
+++ b/test/unit/deprecated.js
@@ -110,3 +110,57 @@ QUnit.test( "jQuery.parseJSON", function( assert ) {
 
 	assert.strictEqual( jQuery.parseJSON( [ 0 ] ), 0, "Input cast to string" );
 } );
+
+QUnit.test( "jQuery.isArray", function( assert ) {
+	assert.expect( 1 );
+
+	assert.strictEqual( jQuery.isArray, Array.isArray, "Array.isArray equals jQuery.isArray" );
+} );
+
+QUnit.test( "jQuery.nodeName", function( assert ) {
+	assert.expect( 8 );
+
+	assert.strictEqual( typeof jQuery.nodeName, "function", "jQuery.nodeName is a function" );
+
+	assert.strictEqual(
+		jQuery.nodeName( document.createElement( "div" ), "div" ),
+		true,
+		"Basic usage (true)"
+	);
+
+	assert.strictEqual(
+		jQuery.nodeName( document.createElement( "div" ), "span" ),
+		false,
+		"Basic usage (false)"
+	);
+
+	assert.strictEqual(
+		jQuery.nodeName( document.createElement( "div" ), "DIV" ),
+		true,
+		"Ignores case in the name parameter"
+	);
+
+	assert.strictEqual(
+		jQuery.nodeName( document.createElement( "section" ), "section" ),
+		true,
+		"Works on HTML5 tags (true)"
+	);
+
+	assert.strictEqual(
+		jQuery.nodeName( document.createElement( "section" ), "article" ),
+		false,
+		"Works on HTML5 tags (false)"
+	);
+
+	assert.strictEqual(
+		jQuery.nodeName( document.createElement( "custom-element" ), "custom-element" ),
+		true,
+		"Works on custom elements (true)"
+	);
+
+	assert.strictEqual(
+		jQuery.nodeName( document.createElement( "custom-element" ), "my-element" ),
+		false,
+		"Works on custom elements (true)"
+	);
+} );
diff --git a/test/unit/dimensions.js b/test/unit/dimensions.js
index 5741b2a..cba8211 100644
--- a/test/unit/dimensions.js
+++ b/test/unit/dimensions.js
@@ -527,4 +527,29 @@ QUnit.test( "outside view position (gh-2836)", function( assert ) {
 	parent.scrollTop( 400 );
 } );
 
+QUnit.test( "width/height on element with transform (gh-3193)", function( assert ) {
+
+	assert.expect( 2 );
+
+	var $elem = jQuery( "<div style='width: 200px; height: 200px; transform: scale(2);' />" )
+		.appendTo( "#qunit-fixture" );
+
+	assert.equal( $elem.width(), 200, "Width ignores transforms" );
+	assert.equal( $elem.height(), 200, "Height ignores transforms" );
+} );
+
+QUnit.test( "width/height on an inline element with no explicitly-set dimensions (gh-3571)", function( assert ) {
+	assert.expect( 8 );
+
+	var $elem = jQuery( "<span style='border: 2px solid black;padding: 1px;margin: 3px;'>Hello, I'm some text.</span>" ).appendTo( "#qunit-fixture" );
+
+	jQuery.each( [ "Width", "Height" ], function( i, method ) {
+		var val = $elem[ method.toLowerCase() ]();
+		assert.notEqual( val, 0, method + " should not be zero on inline element." );
+		assert.equal( $elem[ "inner" + method ](), val + 2, "inner" + method + " should include padding" );
+		assert.equal( $elem[ "outer" + method ](), val + 6, "outer" + method + " should include padding and border" );
+		assert.equal( $elem[ "outer" + method ]( true ), val + 12, "outer" + method + "(true) should include padding, border, and margin" );
+	} );
+} );
+
 } )();
diff --git a/test/unit/effects.js b/test/unit/effects.js
index eafe4b1..54c7f79 100644
--- a/test/unit/effects.js
+++ b/test/unit/effects.js
@@ -1256,17 +1256,18 @@ QUnit.test( "animate with CSS shorthand properties", function( assert ) {
 } );
 
 QUnit.test( "hide hidden elements, with animation (bug #7141)", function( assert ) {
-	assert.expect( 3 );
+	assert.expect( 4 );
 
-	var div = jQuery( "<div style='display:none'></div>" ).appendTo( "#qunit-fixture" );
-	assert.equal( div.css( "display" ), "none", "Element is hidden by default" );
-	div.hide( 1, function() {
-		assert.ok( !jQuery._data( div, "olddisplay" ), "olddisplay is undefined after hiding an already-hidden element" );
-		div.show( 1, function() {
-			assert.equal( div.css( "display" ), "block", "Show a double-hidden element" );
+	var div = jQuery( "<div id='bug7141' style='display:none'/>" ).appendTo( "#qunit-fixture" );
+	assert.equal( div.css( "display" ), "none", "Element is initially hidden" );
+	div.hide( 10, function() {
+		assert.equal( div.css( "display" ), "none", "Element is hidden in .hide() callback" );
+		div.show( 11, function() {
+			assert.equal( div.css( "display" ), "block", "Element is visible in .show() callback" );
 		} );
 	} );
-	this.clock.tick( 10 );
+	this.clock.tick( 50 );
+	assert.equal( div.css( "display" ), "block", "Element is visible after animations" );
 } );
 
 QUnit.test( "animate unit-less properties (#4966)", function( assert ) {
@@ -1846,12 +1847,12 @@ QUnit.test( "non-px animation handles non-numeric start (#11971)", function( ass
 } );
 
 QUnit.test( "Animation callbacks (#11797)", function( assert ) {
-	assert.expect( 16 );
+	assert.expect( 15 );
 
 	var prog = 0,
 		targets = jQuery( "#foo" ).children(),
 		done = false,
-		expectedProgress = 0;
+		expectedProgress = 1;
 
 	targets.eq( 0 ).animate( {}, {
 		duration: 1,
@@ -1909,14 +1910,7 @@ QUnit.test( "Animation callbacks (#11797)", function( assert ) {
 			assert.ok( true, "async: start" );
 		},
 		progress: function( anim, percent ) {
-
-			// occasionally the progress handler is called twice in first frame.... *shrug*
-			if ( percent === 0 && expectedProgress === 1 ) {
-				return;
-			}
 			assert.equal( percent, expectedProgress, "async: progress " + expectedProgress );
-
-			// once at 0, once at 1
 			expectedProgress++;
 		},
 		done: function() {
diff --git a/test/unit/event.js b/test/unit/event.js
index 02aad5e..0c379d0 100644
--- a/test/unit/event.js
+++ b/test/unit/event.js
@@ -2915,6 +2915,16 @@ QUnit.test( "originalEvent property for Chrome, Safari, Fx & Edge of simulated e
 	outer.off( "focusin" );
 } );
 
+QUnit.test( "trigger('click') on radio passes extra params", function( assert ) {
+	assert.expect( 1 );
+	var $radio = jQuery( "<input type='radio' />" ).appendTo( "#qunit-fixture" )
+		.on( "click", function( e, data ) {
+			assert.ok( data, "Trigger data is passed to radio click handler" );
+		} );
+
+	$radio.trigger( "click", [ true ] );
+} );
+
 QUnit[ jQuery.fn.click ? "test" : "skip" ]( "trigger() shortcuts", function( assert ) {
 	assert.expect( 5 );
 
diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js
index 9118524..5959d32 100644
--- a/test/unit/manipulation.js
+++ b/test/unit/manipulation.js
@@ -474,13 +474,13 @@ QUnit.test( "html(String) tag-hyphenated elements (Bug #1987)", function( assert
 	jQuery.each( "thead tbody tfoot colgroup caption tr th td".split( " " ), function( i, name ) {
 		var j = jQuery( "<" + name + "-d></" + name + "-d><" + name + "-d></" + name + "-d>" );
 		assert.ok( j[ 0 ], "Create a tag-hyphenated element" );
-		assert.ok( jQuery.nodeName( j[ 0 ], name.toUpperCase() + "-D" ), "Hyphenated node name" );
-		assert.ok( jQuery.nodeName( j[ 1 ], name.toUpperCase() + "-D" ), "Hyphenated node name" );
+		assert.ok( j[ 0 ].nodeName === name.toUpperCase() + "-D", "Hyphenated node name" );
+		assert.ok( j[ 1 ].nodeName === name.toUpperCase() + "-D", "Hyphenated node name" );
 	} );
 
 	var j = jQuery( "<tr-multiple-hyphens><td-with-hyphen>text</td-with-hyphen></tr-multiple-hyphens>" );
-	assert.ok( jQuery.nodeName( j[ 0 ], "TR-MULTIPLE-HYPHENS" ), "Tags with multiple hyphens" );
-	assert.ok( jQuery.nodeName( j.children()[ 0 ], "TD-WITH-HYPHEN" ), "Tags with multiple hyphens" );
+	assert.ok( j[ 0 ].nodeName === "TR-MULTIPLE-HYPHENS", "Tags with multiple hyphens" );
+	assert.ok( j.children()[ 0 ].nodeName === "TD-WITH-HYPHEN", "Tags with multiple hyphens" );
 	assert.equal( j.children().text(), "text", "Tags with multiple hyphens behave normally" );
 } );
 
@@ -2616,14 +2616,14 @@ QUnit.test( "Make sure specific elements with content created correctly (#13232)
 
 	jQuery.each( elems, function( name, value ) {
 		var html = "<" + name + ">" + value + "</" + name + ">";
-		assert.ok( jQuery.nodeName( jQuery.parseHTML( "<" + name + ">" + value + "</" + name + ">" )[ 0 ], name ), name + " is created correctly" );
+		assert.ok( jQuery.parseHTML( "<" + name + ">" + value + "</" + name + ">" )[ 0 ].nodeName.toLowerCase() === name, name + " is created correctly" );
 
 		results.push( name );
 		args.push( html );
 	} );
 
 	jQuery.fn.append.apply( jQuery( "<div/>" ), args ).children().each( function( i ) {
-		assert.ok( jQuery.nodeName( this, results[ i ] ) );
+		assert.ok( this.nodeName.toLowerCase() === results[ i ] );
 	} );
 } );
 
@@ -2634,11 +2634,11 @@ QUnit.test( "Validate creation of multiple quantities of certain elements (#1381
 
 	jQuery.each( tags, function( index, tag ) {
 		jQuery( "<" + tag + "/><" + tag + "/>" ).each( function() {
-			assert.ok( jQuery.nodeName( this, tag ), tag + " empty elements created correctly" );
+			assert.ok( this.nodeName.toLowerCase() === tag, tag + " empty elements created correctly" );
 		} );
 
 		jQuery( "<" + this + "></" + tag + "><" + tag + "></" + tag + ">" ).each( function() {
-			assert.ok( jQuery.nodeName( this, tag ), tag + " elements with closing tag created correctly" );
+			assert.ok( this.nodeName.toLowerCase() === tag, tag + " elements with closing tag created correctly" );
 		} );
 	} );
 } );
@@ -2730,6 +2730,26 @@ QUnit.test( "Make sure col element is appended correctly", function( assert ) {
 	assert.strictEqual( table.find( "td" ).width(), 150 );
 } );
 
+QUnit.test( "Make sure tr is not appended to the wrong tbody (gh-3439)", function( assert ) {
+	assert.expect( 1 );
+
+	var htmlOut,
+		htmlIn =
+			"<thead><tr><td>" +
+				"<table><tbody><tr><td>nested</td></tr></tbody></table>" +
+			"</td></tr></thead>",
+		newRow = "<tr><td>added</td></tr>",
+		htmlExpected = htmlIn.replace( "</thead>", "</thead>" + newRow ),
+		table = supportjQuery( "<table/>" ).html( htmlIn ).appendTo( "#qunit-fixture" )[ 0 ];
+
+	jQuery( table ).append( newRow );
+
+	// Lowercase and replace spaces to remove possible browser inconsistencies
+	htmlOut = table.innerHTML.toLowerCase().replace( /\s/g, "" );
+
+	assert.strictEqual( htmlOut, htmlExpected );
+} );
+
 QUnit.test( "Insert script with data-URI (gh-1887)", 1, function( assert ) {
 	Globals.register( "testFoo" );
 	Globals.register( "testSrcFoo" );
diff --git a/test/unit/offset.js b/test/unit/offset.js
index c0df5f1..5b73ede 100644
--- a/test/unit/offset.js
+++ b/test/unit/offset.js
@@ -42,7 +42,7 @@ QUnit.test( "empty set", function( assert ) {
 } );
 
 QUnit.test( "disconnected element", function( assert ) {
-	assert.expect( 2 );
+	assert.expect( 3 );
 
 	var result = jQuery( document.createElement( "div" ) ).offset();
 
@@ -51,10 +51,11 @@ QUnit.test( "disconnected element", function( assert ) {
 	// valid input, but will return zeros for back-compat
 	assert.equal( result.top, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" );
 	assert.equal( result.left, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" );
+	assert.equal( Object.keys( result ).length, 2, "Retrieving offset on disconnected elements returns offset object (gh-3167)" );
 } );
 
 QUnit.test( "hidden (display: none) element", function( assert ) {
-	assert.expect( 2 );
+	assert.expect( 3 );
 
 	var node = jQuery( "<div style='display: none' />" ).appendTo( "#qunit-fixture" ),
 		result = node.offset();
@@ -66,6 +67,33 @@ QUnit.test( "hidden (display: none) element", function( assert ) {
 	// valid input, but will return zeros for back-compat
 	assert.equal( result.top, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" );
 	assert.equal( result.left, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" );
+	assert.equal( Object.keys( result ).length, 2, "Retrieving offset on hidden elements returns offset object (gh-3167)" );
+} );
+
+QUnit.test( "0 sized element", function( assert ) {
+	assert.expect( 3 );
+
+	var node = jQuery( "<div style='margin: 5px; width: 0; height: 0' />" ).appendTo( "#qunit-fixture" ),
+		result = node.offset();
+
+	node.remove();
+
+	assert.notEqual( result.top, 0, "Retrieving offset on 0 sized elements (gh-3167)" );
+	assert.notEqual( result.left, 0, "Retrieving offset on 0 sized elements (gh-3167)" );
+	assert.equal( Object.keys( result ).length, 2, "Retrieving offset on 0 sized elements returns offset object (gh-3167)" );
+} );
+
+QUnit.test( "hidden (visibility: hidden) element", function( assert ) {
+	assert.expect( 3 );
+
+	var node = jQuery( "<div style='margin: 5px; visibility: hidden' />" ).appendTo( "#qunit-fixture" ),
+		result = node.offset();
+
+	node.remove();
+
+	assert.notEqual( result.top, 0, "Retrieving offset on visibility:hidden elements (gh-3167)" );
+	assert.notEqual( result.left, 0, "Retrieving offset on visibility:hidden elements (gh-3167)" );
+	assert.equal( Object.keys( result ).length, 2, "Retrieving offset on visibility:hidden elements returns offset object (gh-3167)" );
 } );
 
 testIframe( "absolute", "offset/absolute.html", function( assert, $, iframe ) {
diff --git a/test/unit/queue.js b/test/unit/queue.js
index 2248c68..0a6862d 100644
--- a/test/unit/queue.js
+++ b/test/unit/queue.js
@@ -128,7 +128,7 @@ QUnit.test( "jQuery.queue should return array while manipulating the queue", fun
 
 	var div = document.createElement( "div" );
 
-	assert.ok( jQuery.isArray( jQuery.queue( div, "fx", jQuery.noop ) ), "jQuery.queue should return an array while manipulating the queue" );
+	assert.ok( Array.isArray( jQuery.queue( div, "fx", jQuery.noop ) ), "jQuery.queue should return an array while manipulating the queue" );
 } );
 
 QUnit.test( "delay()", function( assert ) {
diff --git a/test/unit/ready.js b/test/unit/ready.js
index 775b604..d3396b1 100644
--- a/test/unit/ready.js
+++ b/test/unit/ready.js
@@ -4,6 +4,7 @@ QUnit.module( "ready" );
 	var notYetReady, noEarlyExecution,
 		whenified = jQuery.when( jQuery.ready ),
 		promisified = Promise.resolve( jQuery.ready ),
+		start = new Date(),
 		order = [],
 		args = {};
 
@@ -147,4 +148,16 @@ QUnit.module( "ready" );
 			done();
 		} );
 	} );
+
+	testIframe(
+		"holdReady test needs to be a standalone test since it deals with DOM ready",
+		"readywait.html",
+		function( assert, jQuery, window, document, releaseCalled ) {
+			assert.expect( 2 );
+			var now = new Date();
+			assert.ok( now - start >= 300, "Needs to have waited at least half a second" );
+			assert.ok( releaseCalled, "The release function was called, which resulted in ready" );
+		}
+	);
+
 } )();
diff --git a/test/unit/traversing.js b/test/unit/traversing.js
index c7ed014..83c267a 100644
--- a/test/unit/traversing.js
+++ b/test/unit/traversing.js
@@ -743,6 +743,58 @@ QUnit.test( "contents()", function( assert ) {
 	assert.equal( c[ 0 ].nodeValue, "hi", "Check node,textnode,comment contents is just the one from span" );
 } );
 
+QUnit.test( "contents() for <template />", function( assert ) {
+    assert.expect( 4 );
+
+    jQuery( "#qunit-fixture" ).append(
+        "<template id='template'>" +
+        "    <div id='template-div0'>" +
+        "        <span>Hello, Web Component!</span>" +
+        "    </div>" +
+        "    <div id='template-div1'></div>" +
+        "    <div id='template-div2'></div>" +
+        "</template>"
+    );
+
+    var contents = jQuery( "#template" ).contents();
+    assert.equal( contents.length, 6, "Check template element contents" );
+
+    assert.equal( contents.find( "span" ).text(), "Hello, Web Component!", "Find span in template and check its text" );
+
+    jQuery( "<div id='templateTest' />" ).append(
+        jQuery( jQuery.map( contents, function( node ) {
+            return document.importNode( node, true );
+        } ) )
+    ).appendTo( "#qunit-fixture" );
+
+    contents = jQuery( "#templateTest" ).contents();
+    assert.equal( contents.length, 6, "Check cloned nodes of template element contents" );
+
+    assert.equal( contents.filter( "div" ).length, 3, "Count cloned elements from template" );
+} );
+
+QUnit[ "content" in document.createElement( "template" ) ? "test" : "skip" ](
+	"contents() for <template /> remains inert",
+	function( assert ) {
+        assert.expect( 2 );
+
+		Globals.register( "testScript" );
+		Globals.register( "testImgOnload" );
+
+        jQuery( "#qunit-fixture" ).append(
+            "<template id='template'>" +
+            "    <script>testScript = 1;</script>" +
+            "    <img src='data/1x1.jpg' onload='testImgOnload = 1' >" +
+            "</template>"
+        );
+
+        var content = jQuery( "#template" ).contents();
+
+        assert.strictEqual( window.testScript, true, "script in template isn't executed" );
+        assert.strictEqual( window.testImgOnload, true, "onload of image in template isn't executed" );
+	}
+);
+
 QUnit.test( "sort direction", function( assert ) {
 	assert.expect( 12 );
 

-- 
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