From 5f5aee3b582cff72823efe9ed2785c1d17eadfd3 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Thu, 12 Nov 2015 13:23:53 -0500 Subject: [PATCH 001/465] Release: use chalk instead of colors for terminal colors --- build/release.js | 3 ++- build/release/ensure-sizzle.js | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/release.js b/build/release.js index 7fd00bd8c2..5feddfa240 100644 --- a/build/release.js +++ b/build/release.js @@ -56,5 +56,6 @@ module.exports = function( Release ) { module.exports.dependencies = [ "archiver@0.14.2", "shelljs@0.2.6", - "npm@2.3.0" + "npm@2.3.0", + "chalk@1.1.1" ]; diff --git a/build/release/ensure-sizzle.js b/build/release/ensure-sizzle.js index 7957606431..f9c5c70dd1 100644 --- a/build/release/ensure-sizzle.js +++ b/build/release/ensure-sizzle.js @@ -1,5 +1,6 @@ var fs = require( "fs" ), npm = require( "npm" ), + chalk = require( "chalk" ), sizzleLoc = __dirname + "/../../external/sizzle/dist/sizzle.js", rversion = /Engine v(\d+\.\d+\.\d+(?:-[-\.\d\w]+)?)/; @@ -37,12 +38,12 @@ function ensureSizzle( Release, callback ) { // colors is inherited from jquery-release console.log( - "The Sizzle version in the src folder (" + version.red + - ") is not the latest tag (" + latest.green + ")." + "The Sizzle version in the src folder (" + chalk.red( version ) + + ") is not the latest tag (" + chalk.green( latest ) + ")." ); Release.confirm( callback ); } else { - console.log( "Sizzle is latest (" + latest.green + ")" ); + console.log( "Sizzle is latest (" + chalk.green( latest ) + ")" ); callback(); } } ); From 8b65446a60c421cfc3411fe273edcc506783f628 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Thu, 12 Nov 2015 13:25:42 -0500 Subject: [PATCH 002/465] Release: update authors --- AUTHORS.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 58c73afbc9..35565fa20e 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -259,3 +259,16 @@ Chris Rebert Gabriel Schulhof Gilad Peleg Martin Naumann +Marek Lewandowski +Bruno Pérel +Reed Loden +Daniel Nill +Yongwoo Jeon +Sean Henderson +Richard Kraaijenhagen +Connor Atherton +Gary Ye +Christian Grete +Liza Ramo +Julian Alexander Murillo +Joelle Fleurantin From 78b9eac1198627eb4dad0cc35334c7704449f310 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Wed, 11 Nov 2015 10:35:37 -0500 Subject: [PATCH 003/465] Deferred: syncronize single and multiple target handling in $.when Fixes gh-2546 Fixes gh-2018 Close gh-2707 --- src/deferred.js | 22 +++++++++++----------- test/unit/deferred.js | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/deferred.js b/src/deferred.js index e1af425d48..846528e795 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -294,19 +294,17 @@ jQuery.extend( { }, // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { + when: function() { var method, i = 0, resolveValues = slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates - remaining = length !== 1 || - ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + remaining = length, // the master Deferred. - // If resolveValues consist of only a single Deferred, just use that. - master = remaining === 1 ? subordinate : jQuery.Deferred(), + master = jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { @@ -316,14 +314,17 @@ jQuery.extend( { if ( values === progressValues ) { master.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { - master.resolveWith( contexts, values ); + master.resolveWith( + contexts.length === 1 ? contexts[ 0 ] : contexts, + values + ); } }; }, progressValues, progressContexts, resolveContexts; // Add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { + if ( length > 0 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); @@ -345,14 +346,13 @@ jQuery.extend( { updateFunc( i, progressContexts, progressValues ) ); } else { - --remaining; + updateFunc( i, resolveContexts, resolveValues )( resolveValues[ i ] ); } } - } // If we're not waiting on anything, resolve the master - if ( !remaining ) { - master.resolveWith( resolveContexts, resolveValues ); + } else { + master.resolveWith(); } return master.promise(); diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 2277df1903..1a721d1ee0 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -667,7 +667,6 @@ QUnit.test( "jQuery.when", function( assert ) { "undefined": undefined, "a plain object": {}, "an array": [ 1, 2, 3 ] - }, function( message, value ) { assert.ok( jQuery.isFunction( @@ -698,12 +697,10 @@ QUnit.test( "jQuery.when", function( assert ) { } ); 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; } ); @@ -759,10 +756,8 @@ QUnit.test( "jQuery.when - joined", function( assert ) { expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ], expectedNotify = shouldNotify && [ willNotify[ id1 ], willNotify[ id2 ] ], code = "jQuery.when( " + id1 + ", " + id2 + " )", - context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : - ( defer1.then ? window : undefined ), - context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : - ( defer2.then ? window : undefined ); + 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 ) { @@ -880,3 +875,36 @@ QUnit.test( "jQuery.when - chaining", function( assert ) { defer.resolve( "other deferred" ); } ); + +QUnit.test( "jQuery.when - solitary thenables", function( assert ) { + + 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(); + }, + 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 ) { + + assert.expect( 2 ); + var defer = jQuery.Deferred().resolve(), + promise = jQuery.when( defer ); + + assert.equal( promise.state(), "resolved", "Master Deferred is immediately resolved" ); + assert.notStrictEqual( defer.promise(), promise, "jQuery.when returns the master deferred's promise" ); +} ); From cf7102c3f1bcbd135f24947a3a3216cff272bdc2 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Thu, 12 Nov 2015 13:18:59 -0500 Subject: [PATCH 004/465] Release: push a custom slim build to the CDN Fixes gh-2653 Close gh-2711 --- Gruntfile.js | 10 ++++++++-- build/release.js | 16 ++++++++++++++-- build/release/cdn.js | 19 ++++++++++--------- build/release/dist.js | 21 ++++++++++----------- build/tasks/build.js | 8 ++++++-- build/tasks/dist.js | 9 +++++---- build/tasks/sourcemap.js | 3 ++- 7 files changed, 55 insertions(+), 31 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index dd4b07d308..626531b293 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -31,6 +31,10 @@ module.exports = function( grunt ) { // But our modules can delete srcHintOptions.onevar; + if ( !grunt.option( "filename" ) ) { + grunt.option( "filename", "jquery.js" ); + } + grunt.initConfig( { pkg: grunt.file.readJSON( "package.json" ), dst: readOptionalJSON( "dist/.destination.json" ), @@ -173,12 +177,14 @@ module.exports = function( grunt ) { uglify: { all: { files: { - "dist/jquery.min.js": [ "dist/jquery.js" ] + "dist/<%= grunt.option('filename').replace('.js', '.min.js') %>": + "dist/<%= grunt.option('filename') %>" }, options: { preserveComments: false, sourceMap: true, - sourceMapName: "dist/jquery.min.map", + sourceMapName: + "dist/<%= grunt.option('filename').replace('.js', '.min.map') %>", report: "min", beautify: { "ascii_only": true diff --git a/build/release.js b/build/release.js index 5feddfa240..a333d05b44 100644 --- a/build/release.js +++ b/build/release.js @@ -2,7 +2,14 @@ module.exports = function( Release ) { var - files = [ "dist/jquery.js", "dist/jquery.min.js", "dist/jquery.min.map" ], + files = [ + "dist/jquery.js", + "dist/jquery.min.js", + "dist/jquery.min.map", + "dist/jquery.slim.js", + "dist/jquery.slim.min.js", + "dist/jquery.slim.min.map" + ], cdn = require( "./release/cdn" ), dist = require( "./release/dist" ), ensureSizzle = require( "./release/ensure-sizzle" ), @@ -27,6 +34,11 @@ 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 remove_map_comment --filename=jquery.slim.js", + "Grunt custom failed" + ); cdn.makeReleaseCopies( Release ); callback( files ); }, @@ -47,7 +59,7 @@ module.exports = function( Release ) { */ dist: function( callback ) { cdn.makeArchives( Release, function() { - dist( Release, callback ); + dist( Release, files, callback ); } ); } } ); diff --git a/build/release/cdn.js b/build/release/cdn.js index 07285a56d6..3b485112e9 100644 --- a/build/release/cdn.js +++ b/build/release/cdn.js @@ -5,22 +5,23 @@ var cdnFolder = "dist/cdn", - devFile = "dist/jquery.js", - minFile = "dist/jquery.min.js", - mapFile = "dist/jquery.min.map", - releaseFiles = { - "jquery-VER.js": devFile, - "jquery-VER.min.js": minFile, - "jquery-VER.min.map": mapFile + "jquery-VER.js": "dist/jquery.js", + "jquery-VER.min.js": "dist/jquery.min.js", + "jquery-VER.min.map": "dist/jquery.min.map", + "jquery-VER.slim.js": "dist/jquery.slim.js", + "jquery-VER.slim.min.js": "dist/jquery.slim.min.js", + "jquery-VER.slim.min.map": "dist/jquery.slim.min.map" }, googleFilesCDN = [ - "jquery.js", "jquery.min.js", "jquery.min.map" + "jquery.js", "jquery.min.js", "jquery.min.map", + "jquery.slim.js", "jquery.slim.min.js", "jquery.slim.min.map" ], msFilesCDN = [ - "jquery-VER.js", "jquery-VER.min.js", "jquery-VER.min.map" + "jquery-VER.js", "jquery-VER.min.js", "jquery-VER.min.map", + "jquery-VER.slim.js", "jquery-VER.slim.min.js", "jquery-VER.slim.min.map" ]; /** diff --git a/build/release/dist.js b/build/release/dist.js index 32053eafb0..514d407396 100644 --- a/build/release/dist.js +++ b/build/release/dist.js @@ -1,13 +1,16 @@ -module.exports = function( Release, complete ) { +module.exports = function( Release, files, complete ) { var fs = require( "fs" ), shell = require( "shelljs" ), pkg = require( Release.dir.repo + "/package.json" ), - distRemote = Release.remote.replace( "jquery.git", "jquery-dist.git" ), + distRemote = Release.remote + + // For local and github dists + .replace( /jquery(\.git|$)/, "jquery-dist$1" ), // These files are included with the distribution - files = [ + extras = [ "src", "LICENSE.txt", "AUTHORS.txt", @@ -54,17 +57,13 @@ module.exports = function( Release, complete ) { // Copy dist files var distFolder = Release.dir.dist + "/dist"; shell.mkdir( "-p", distFolder ); - [ - "dist/jquery.js", - "dist/jquery.min.js", - "dist/jquery.min.map" - ].forEach( function( file ) { - shell.cp( Release.dir.repo + "/" + file, distFolder ); + files.forEach( function( file ) { + shell.cp( "-f", Release.dir.repo + "/" + file, distFolder ); } ); // Copy other files - files.forEach( function( file ) { - shell.cp( "-r", Release.dir.repo + "/" + file, Release.dir.dist ); + extras.forEach( function( file ) { + shell.cp( "-rf", Release.dir.repo + "/" + file, Release.dir.dist ); } ); // Write generated bower file diff --git a/build/tasks/build.js b/build/tasks/build.js index 74fa47fde4..c9f1daeb8f 100644 --- a/build/tasks/build.js +++ b/build/tasks/build.js @@ -15,7 +15,6 @@ module.exports = function( grunt ) { config = { baseUrl: "src", name: "jquery", - out: "dist/jquery.js", // We have multiple minify steps optimize: "none", @@ -115,7 +114,7 @@ module.exports = function( grunt ) { done = this.async(), flags = this.flags, optIn = flags[ "*" ], - name = this.data.dest, + name = grunt.option( "filename" ), minimum = this.data.minimum, removeWith = this.data.removeWith, excluded = [], @@ -205,6 +204,11 @@ module.exports = function( grunt ) { } }; + // Filename can be passed to the command line using + // command line options + // e.g. grunt build --filename=jquery-custom.js + name = name ? ( "dist/" + name ) : this.data.dest; + // append commit id to version if ( process.env.COMMIT ) { version += " " + process.env.COMMIT; diff --git a/build/tasks/dist.js b/build/tasks/dist.js index 78ce2f254e..fa6920c889 100644 --- a/build/tasks/dist.js +++ b/build/tasks/dist.js @@ -2,11 +2,12 @@ module.exports = function( grunt ) { "use strict"; - var fs = require( "fs" ), + var fs = require( "fs" ), + filename = grunt.option( "filename" ), distpaths = [ - "dist/jquery.js", - "dist/jquery.min.map", - "dist/jquery.min.js" + "dist/" + filename, + "dist/" + filename.replace( ".js", ".min.map" ), + "dist/" + filename.replace( ".js", ".min.js" ) ]; // Process files for distribution diff --git a/build/tasks/sourcemap.js b/build/tasks/sourcemap.js index 3e4144de01..3f21b2afd0 100644 --- a/build/tasks/sourcemap.js +++ b/build/tasks/sourcemap.js @@ -1,8 +1,9 @@ var fs = require( "fs" ); module.exports = function( grunt ) { - var minLoc = Object.keys( grunt.config( "uglify.all.files" ) )[ 0 ]; + var config = grunt.config( "uglify.all.files" ); grunt.registerTask( "remove_map_comment", function() { + var minLoc = grunt.config.process( Object.keys( config )[ 0 ] ); // Remove the source map comment; it causes way too many problems. // The map file is still generated for manual associations From cb80b42b91bc7d0e75fb842f733878b848a8b9c1 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 16 Nov 2015 15:36:48 -0500 Subject: [PATCH 005/465] Effects: add tests for using jQuery.speed directly Fixes gh-2716 Close gh-2724 --- test/unit/effects.js | 85 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/test/unit/effects.js b/test/unit/effects.js index 0f38f81a5c..33357deda4 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -2495,4 +2495,89 @@ QUnit.test( "Show/hide/toggle and display: inline", function( assert ) { } ); } ); +function testEasing( assert, speed, easing, complete ) { + assert.expect( 4 ); + var options = jQuery.speed( speed, easing, complete ); + + assert.equal( options.duration, 10, "Duration set properly" ); + assert.equal( + jQuery.isFunction( options.easing ) ? options.easing() : options.easing, + "linear", + "Easing set properly" + ); + assert.equal( options.queue, "fx", "Queue defaults to fx" ); + options.complete(); +} + +QUnit.test( "jQuery.speed( speed, easing, complete )", function( assert ) { + testEasing( assert, 10, "linear", function() { + assert.ok( true, "Complete called" ); + } ); +} ); + +QUnit.test( "jQuery.speed( speed, easing, complete ) - with easing function", function( assert ) { + testEasing( + assert, + 10, + function() { + return "linear"; + }, + function() { + assert.ok( true, "Complete called" ); + } + ); +} ); + +QUnit.test( "jQuery.speed( options )", function( assert ) { + testEasing( assert, { + duration: 10, + easing: "linear", + complete: function() { + assert.ok( true, "Complete called" ); + } + } ); +} ); + +QUnit.test( "jQuery.speed( options ) - with easing function", function( assert ) { + testEasing( assert, { + duration: 10, + easing: function() { + return "linear"; + }, + complete: function() { + assert.ok( true, "Complete called" ); + } + } ); +} ); + +QUnit.test( "jQuery.speed( options ) - queue values", function( assert ) { + assert.expect( 5 ); + + var get = function( queue ) { + return jQuery.speed( { queue: queue } ).queue; + }; + + assert.equal( get( null ), "fx", "null defaults to 'fx'" ); + assert.equal( get( undefined ), "fx", "undefined defaults to 'fx'" ); + assert.equal( get( true ), "fx", "true defaults to 'fx'" ); + assert.equal( get( "fx" ), "fx", "'fx' passed through" ); + assert.equal( get( "custom" ), "custom", "'custom' passed through" ); +} ); + +QUnit.test( "jQuery.speed() - durations", function( assert ) { + assert.expect( 5 ); + + var get = function( duration ) { + return jQuery.speed( duration ).duration; + }; + + assert.equal( get( 100 ), 100, "jQuery.speed sets number duration" ); + assert.equal( get(), jQuery.fx.speeds._default, "jQuery.speed falls back default duration" ); + assert.equal( get( "slow" ), jQuery.fx.speeds.slow, "jQuery.speed uses preset speeds" ); + assert.equal( get( "fast" ), jQuery.fx.speeds.fast, "jQuery.speed uses preset speeds" ); + jQuery.fx.off = true; + assert.equal( get( 100 ), 0, "jQuery.speed defaults duration to zero if fx is off" ); + jQuery.fx.off = false; +} ); + } )(); From e0c25abb435db6e210d00407af2ba40e5f0b56ad Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 16 Nov 2015 15:49:59 -0500 Subject: [PATCH 006/465] Docs: add a note about loading source with AMD - Moves the note about the watch task and the note about loading with AMD to their own section under "Test Suite Tips" Fixes gh-2714 Close gh-2725 --- CONTRIBUTING.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f6547c568..cd6ab6fc4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,12 +101,6 @@ Run the build script $ npm run build ``` -Run the Grunt tools: - -```bash -$ grunt && grunt watch -``` - Now open the jQuery test suite in a browser at http://localhost/test. If there is a port, be sure to include it. Success! You just built and tested jQuery! @@ -118,13 +112,26 @@ During the process of writing your patch, you will run the test suite MANY times Example: -http://localhost/test/?filter=css +http://localhost/test/?module=css This will only run the "css" module tests. This will significantly speed up your development and debugging. **ALWAYS RUN THE FULL SUITE BEFORE COMMITTING AND PUSHING A PATCH!** +#### Loading changes on the test page + +Rather than rebuilding jQuery with `grunt` every time you make a change, you can use the included `grunt watch` task to rebuild distribution files whenever a file is saved. + +```bash +$ grunt watch +``` + +Alternatively, you can **load tests in AMD** to avoid the need for rebuilding altogether. + +Click "Load with AMD" after loading the test page. + + ### Browser support Remember that jQuery supports multiple browsers and their versions; any contributed code must work in all of them. You can refer to the [browser support page](http://jquery.com/browser-support/) for the current list of supported browsers. From eaa3e9f0cfc68083556cf61195821d90e369f646 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Sun, 8 Nov 2015 21:49:47 -0500 Subject: [PATCH 007/465] Ajax: Golf away 21 bytes Close gh-2699 --- src/ajax.js | 58 ++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/src/ajax.js b/src/ajax.js index ca2b910dd0..27abe375e1 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -406,6 +406,9 @@ jQuery.extend( { // Url cleanup var urlAnchor, + // Request state (becomes false upon send and true upon completion) + completed, + // To know if global events are to be dispatched fireGlobals, @@ -435,9 +438,6 @@ jQuery.extend( { requestHeaders = {}, requestHeadersNames = {}, - // The jqXHR state - state = 0, - // Default abort message strAbort = "canceled", @@ -448,7 +448,7 @@ jQuery.extend( { // Builds headers hashtable if needed getResponseHeader: function( key ) { var match; - if ( state === 2 ) { + if ( completed ) { if ( !responseHeaders ) { responseHeaders = {}; while ( ( match = rheaders.exec( responseHeadersString ) ) ) { @@ -462,14 +462,14 @@ jQuery.extend( { // Raw string getAllResponseHeaders: function() { - return state === 2 ? responseHeadersString : null; + return completed ? responseHeadersString : null; }, // Caches the header setRequestHeader: function( name, value ) { - var lname = name.toLowerCase(); - if ( !state ) { - name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; requestHeaders[ name ] = value; } return this; @@ -477,7 +477,7 @@ jQuery.extend( { // Overrides response content-type header overrideMimeType: function( type ) { - if ( !state ) { + if ( completed == null ) { s.mimeType = type; } return this; @@ -487,16 +487,16 @@ jQuery.extend( { statusCode: function( map ) { var code; if ( map ) { - if ( state < 2 ) { - for ( code in map ) { - - // Lazy-add the new callback in a way that preserves old ones - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } else { + if ( completed ) { // Execute the appropriate callbacks jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } } } return this; @@ -560,7 +560,7 @@ jQuery.extend( { inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); // If request was aborted inside a prefilter, stop there - if ( state === 2 ) { + if ( completed ) { return jqXHR; } @@ -642,7 +642,7 @@ jQuery.extend( { // Allow custom headers/mimetypes and early abort if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { // Abort if not done already and return return jqXHR.abort(); @@ -671,7 +671,7 @@ jQuery.extend( { } // If request was aborted inside ajaxSend, stop there - if ( state === 2 ) { + if ( completed ) { return jqXHR; } @@ -683,18 +683,17 @@ jQuery.extend( { } try { - state = 1; + completed = false; transport.send( requestHeaders, done ); } catch ( e ) { - // Propagate exception as error if not done - if ( state < 2 ) { - done( -1, e ); - - // Simply rethrow otherwise - } else { + // Rethrow post-completion exceptions + if ( completed ) { throw e; } + + // Propagate others as results + done( -1, e ); } } @@ -703,13 +702,12 @@ jQuery.extend( { var isSuccess, success, error, response, modified, statusText = nativeStatusText; - // Called once - if ( state === 2 ) { + // Ignore repeat invocations + if ( completed ) { return; } - // State is "done" now - state = 2; + completed = true; // Clear timeout if it exists if ( timeoutTimer ) { From e077ffb083743f4a4b990f586c9d25d787e7b417 Mon Sep 17 00:00:00 2001 From: Dave Methvin Date: Sun, 15 Nov 2015 21:51:18 -0500 Subject: [PATCH 008/465] Ajax: Preserve URL hash on requests Fixes gh-1732 Closes gh-2721 --- src/ajax.js | 28 ++++++++++++++++------------ test/unit/ajax.js | 22 ++++++++++++++++++---- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/ajax.js b/src/ajax.js index 27abe375e1..195a30a20b 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -415,6 +415,9 @@ jQuery.extend( { // Loop variable i, + // uncached part of the url + uncached, + // Create the final options object s = jQuery.ajaxSetup( {}, options ), @@ -516,11 +519,10 @@ jQuery.extend( { // Attach deferreds deferred.promise( jqXHR ); - // Remove hash character (#7531: and string promotion) // Add protocol if not provided (prefilters might expect it) // Handle falsy url in the settings object (#10093: consistency with old signature) // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ).replace( rhash, "" ) + s.url = ( ( url || s.url || location.href ) + "" ) .replace( rprotocol, location.protocol + "//" ); // Alias method option to type as per ticket #12004 @@ -581,30 +583,32 @@ jQuery.extend( { // Save the URL in case we're toying with the If-Modified-Since // and/or If-None-Match header later on - cacheURL = s.url; + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); // More options handling for requests with no content if ( !s.hasContent ) { + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + // If data is available, append data to url if ( s.data ) { - cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data ); + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; // #9682: remove data so that it's not used in an eventual retry delete s.data; } - // Add anti-cache in url if needed + // Add anti-cache in uncached url if needed if ( s.cache === false ) { - s.url = rts.test( cacheURL ) ? - - // If there is already a '_' parameter, set its value - cacheURL.replace( rts, "$1_=" + nonce++ ) : - - // Otherwise add one to the end - cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++; + cacheURL = cacheURL.replace( rts, "" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; } + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + // Change '%20' to '+' if this is encoded form body content (gh-2658) } else if ( s.data && s.processData && ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { diff --git a/test/unit/ajax.js b/test/unit/ajax.js index 12bf2ab31f..f468f44a8e 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -328,12 +328,12 @@ QUnit.module( "ajax", { }; } ); - ajaxTest( "jQuery.ajax() - hash", 3, function( assert ) { + ajaxTest( "jQuery.ajax() - hash", 4, function( assert ) { return [ { url: "data/name.html#foo", beforeSend: function( xhr, settings ) { - assert.equal( settings.url, "data/name.html", "Make sure that the URL is trimmed." ); + assert.equal( settings.url, "data/name.html#foo", "Make sure that the URL has its hash." ); return false; }, error: true @@ -341,7 +341,7 @@ QUnit.module( "ajax", { { url: "data/name.html?abc#foo", beforeSend: function( xhr, settings ) { - assert.equal( settings.url, "data/name.html?abc", "Make sure that the URL is trimmed." ); + assert.equal( settings.url, "data/name.html?abc#foo", "Make sure that the URL has its hash." ); return false; }, error: true @@ -352,7 +352,21 @@ QUnit.module( "ajax", { "test": 123 }, beforeSend: function( xhr, settings ) { - assert.equal( settings.url, "data/name.html?abc&test=123", "Make sure that the URL is trimmed." ); + assert.equal( settings.url, "data/name.html?abc&test=123#foo", "Make sure that the URL has its hash." ); + return false; + }, + error: true + }, + { + url: "data/name.html?abc#brownies", + data: { + "devo": "hat" + }, + cache: false, + beforeSend: function( xhr, settings ) { + // Remove the random number, but ensure the cashe-buster param is there + var url = settings.url.replace( /\d+/, "" ); + assert.equal( url, "data/name.html?abc&devo=hat&_=#brownies", "Make sure that the URL has its hash." ); return false; }, error: true From 6680c1b29ea79bf33ac6bd31578755c7c514ed3e Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Sat, 14 Nov 2015 01:28:16 +0300 Subject: [PATCH 009/465] Core: do not expose second argument of the `jQuery.globalEval` Closes jquery/api.jquery.com#831 Closes gh-2718 --- src/core.js | 14 ++++++-------- src/core/DOMEval.js | 14 ++++++++++++++ src/manipulation.js | 5 +++-- 3 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 src/core/DOMEval.js diff --git a/src/core.js b/src/core.js index 9095213eb3..3a54ffc0e6 100644 --- a/src/core.js +++ b/src/core.js @@ -8,8 +8,10 @@ define( [ "./var/class2type", "./var/toString", "./var/hasOwn", - "./var/support" -], function( arr, document, slice, concat, push, indexOf, class2type, toString, hasOwn, support ) { + "./var/support", + "./core/DOMEval" +], function( arr, document, slice, concat, + push, indexOf, class2type, toString, hasOwn, support, DOMEval ) { var version = "@VERSION", @@ -258,12 +260,8 @@ jQuery.extend( { }, // Evaluates a script in a global context - globalEval: function( code, context ) { - context = context || document; - var script = context.createElement( "script" ); - - script.text = code; - context.head.appendChild( script ).parentNode.removeChild( script ); + globalEval: function( code ) { + DOMEval( code ); }, // Convert dashed to camelCase; used by the css and data modules diff --git a/src/core/DOMEval.js b/src/core/DOMEval.js new file mode 100644 index 0000000000..222b0ca2a2 --- /dev/null +++ b/src/core/DOMEval.js @@ -0,0 +1,14 @@ +define( [ + "../var/document" +], function( document ) { + function DOMEval( code, doc ) { + doc = doc || document; + + var script = doc.createElement( "script" ); + + script.text = code; + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + return DOMEval; +} ); diff --git a/src/manipulation.js b/src/manipulation.js index eaf2e09983..cc4fd16cd7 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -15,6 +15,7 @@ define( [ "./data/var/dataPriv", "./data/var/dataUser", "./data/var/acceptData", + "./core/DOMEval", "./core/init", "./traversing", @@ -23,7 +24,7 @@ define( [ ], function( jQuery, concat, push, access, rcheckableType, rtagName, rscriptType, wrapMap, getAll, setGlobalEval, buildFragment, support, - dataPriv, dataUser, acceptData ) { + dataPriv, dataUser, acceptData, DOMEval ) { var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, @@ -192,7 +193,7 @@ function domManip( collection, args, callback, ignored ) { jQuery._evalUrl( node.src ); } } else { - jQuery.globalEval( node.textContent.replace( rcleanScript, "" ), doc ); + DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); } } } From fbf829b7245f7d76ea02a44ab0a62427214b7575 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 30 Nov 2015 11:30:31 -0500 Subject: [PATCH 010/465] Attributes: exclusively lowercase A-Z in attribute names Fixes gh-2730 Close gh-2749 --- src/attributes/attr.js | 11 +++++++++-- test/unit/attributes.js | 13 ++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/attributes/attr.js b/src/attributes/attr.js index ae48676d1f..00b08489db 100644 --- a/src/attributes/attr.js +++ b/src/attributes/attr.js @@ -7,7 +7,14 @@ define( [ ], function( jQuery, access, support, rnotwhite ) { var boolHook, - attrHandle = jQuery.expr.attrHandle; + attrHandle = jQuery.expr.attrHandle, + + // 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(); + }; jQuery.fn.extend( { attr: function( name, value ) { @@ -39,7 +46,7 @@ jQuery.extend( { // All attributes are lowercase // Grab necessary hook if one is defined if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - name = name.toLowerCase(); + name = name.replace( raz, lowercase ); hooks = jQuery.attrHooks[ name ] || ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); } diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 9bf2876881..cbf83d0ec4 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -451,7 +451,9 @@ QUnit.test( "attr(String, Object)", function( assert ) { $radio = jQuery( "", { "value": "sup", - "type": "radio" + // Use uppercase here to ensure the type + // attrHook is still used + "TYPE": "radio" } ).appendTo( "#testForm" ); assert.equal( $radio.val(), "sup", "Value is not reset when type is set after value on a radio" ); @@ -472,6 +474,15 @@ QUnit.test( "attr(String, Object)", function( assert ) { assert.equal( jQuery( "#name" ).attr( "nonexisting", undefined ).attr( "nonexisting" ), undefined, ".attr('attribute', undefined) does not create attribute (#5571)" ); } ); +QUnit.test( "attr(non-ASCII)", function( assert ) { + assert.expect( 2 ); + + var $div = jQuery( "
" ).appendTo( "#qunit-fixture" ); + + assert.equal( $div.attr( "Ω" ), "omega", ".attr() exclusively lowercases characters in the range A-Z (gh-2730)" ); + assert.equal( $div.attr( "AØC" ), "alpha", ".attr() exclusively lowercases characters in the range A-Z (gh-2730)" ); +} ); + QUnit.test( "attr - extending the boolean attrHandle", function( assert ) { assert.expect( 1 ); var called = false, From 1823a715660a5f31dd7e05672e9ad020066256a9 Mon Sep 17 00:00:00 2001 From: Jae Sung Park Date: Thu, 3 Dec 2015 15:34:03 +0900 Subject: [PATCH 011/465] Event: Remove duplicated word text on comment Closes #2751 --- src/event/trigger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event/trigger.js b/src/event/trigger.js index 448f90878d..8f5f778457 100644 --- a/src/event/trigger.js +++ b/src/event/trigger.js @@ -121,7 +121,7 @@ jQuery.extend( jQuery.event, { special._default.apply( eventPath.pop(), data ) === false ) && acceptData( elem ) ) { - // Call a native DOM method on the target with the same name name as the event. + // Call a native DOM method on the target with the same name as the event. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { From a4d16a26abe3ea36337f7bfc4d1bfe13b893cd17 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Thu, 7 Jan 2016 14:06:41 -0500 Subject: [PATCH 012/465] Revert "Attributes: Remove undocumented .toggleClass( boolean ) signature" This reverts commit 53f798cf4d783bb813b4d1ba97411bc752b275f3. - Turns out this is documented, even if not fully. Need to deprecate before removal. --- src/attributes/classes.js | 69 +++++++++++++++++++++++++++++---------- test/unit/attributes.js | 25 +++++++++++++- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/attributes/classes.js b/src/attributes/classes.js index 6ab6f6efa5..7933873c4f 100644 --- a/src/attributes/classes.js +++ b/src/attributes/classes.js @@ -1,8 +1,9 @@ define( [ "../core", "../var/rnotwhite", + "../data/var/dataPriv", "../core/init" -], function( jQuery, rnotwhite ) { +], function( jQuery, rnotwhite, dataPriv ) { var rclass = /[\t\r\n\f]/g; @@ -96,26 +97,60 @@ jQuery.fn.extend( { }, toggleClass: function( value, stateVal ) { - var type = typeof value, - classNames = type === "string" ? value.match( rnotwhite ) : []; + var type = typeof value; - return this.each( function( i ) { - var className, - self = jQuery( this ), - c = 0; + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } - if ( type === "function" ) { - classNames = value.call( this, i, getClass( this ), stateVal ) - .match( rnotwhite ) || []; - } + if ( jQuery.isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( type === "string" ) { - // Toggle individual class names based on presence or stateVal - while ( ( className = classNames[ c++ ] ) ) { + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = value.match( rnotwhite ) || []; + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } - if ( stateVal === false || stateVal !== true && self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); } } } ); diff --git a/test/unit/attributes.js b/test/unit/attributes.js index cbf83d0ec4..7f92d3505b 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -1239,7 +1239,7 @@ QUnit.test( "removeClass(undefined) is a no-op", function( assert ) { } ); var testToggleClass = function( valueObj, assert ) { - assert.expect( 11 ); + assert.expect( 19 ); var e = jQuery( "#firstp" ); assert.ok( !e.is( ".test" ), "Assert class not present" ); @@ -1267,6 +1267,29 @@ var testToggleClass = function( valueObj, assert ) { assert.ok( ( e.is( ".testA.testC" ) && !e.is( ".testB" ) ), "Assert 1 class added, 1 class removed, and 1 class kept" ); e.toggleClass( valueObj( "testA testC" ) ); assert.ok( ( !e.is( ".testA" ) && !e.is( ".testB" ) && !e.is( ".testC" ) ), "Assert no class present" ); + + // toggleClass storage + e.toggleClass( true ); + assert.ok( e[ 0 ].className === "", "Assert class is empty (data was empty)" ); + e.addClass( "testD testE" ); + assert.ok( e.is( ".testD.testE" ), "Assert class present" ); + e.toggleClass(); + assert.ok( !e.is( ".testD.testE" ), "Assert class not present" ); + assert.ok( jQuery._data( e[ 0 ], "__className__" ) === "testD testE", "Assert data was stored" ); + e.toggleClass(); + assert.ok( e.is( ".testD.testE" ), "Assert class present (restored from data)" ); + e.toggleClass( false ); + assert.ok( !e.is( ".testD.testE" ), "Assert class not present" ); + e.toggleClass( true ); + assert.ok( e.is( ".testD.testE" ), "Assert class present (restored from data)" ); + e.toggleClass(); + e.toggleClass( false ); + e.toggleClass(); + assert.ok( e.is( ".testD.testE" ), "Assert class present (restored from data)" ); + + // Cleanup + e.removeClass( "testD" ); + assert.expectJqData( this, e[ 0 ], "__className__" ); }; QUnit.test( "toggleClass(String|boolean|undefined[, boolean])", function( assert ) { From df822caff079177d1840d67e03d6b24a93ea99a5 Mon Sep 17 00:00:00 2001 From: Jun Sun Date: Wed, 30 Dec 2015 15:40:08 +0800 Subject: [PATCH 013/465] CSS: Add animation-iteration-count to cssNumber, fix tests Fixes gh-2792 Closes gh-2793 --- src/css.js | 1 + test/unit/css.js | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/css.js b/src/css.js index acd4bb3eb4..5d3a0a6b59 100644 --- a/src/css.js +++ b/src/css.js @@ -190,6 +190,7 @@ jQuery.extend( { // Don't automatically add "px" to these possibly-unitless properties cssNumber: { + "animationIterationCount": true, "columnCount": true, "fillOpacity": true, "flexGrow": true, diff --git a/test/unit/css.js b/test/unit/css.js index 8715c56d5a..4b4017ab87 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -840,26 +840,35 @@ if ( jQuery.fn.offset ) { } ); } -QUnit.test( "Do not append px (#9548, #12990)", function( assert ) { - assert.expect( 2 ); +QUnit.test( "Do not append px (#9548, #12990, #2792)", function( assert ) { + assert.expect( 3 ); var $div = jQuery( "
" ).appendTo( "#qunit-fixture" ); $div.css( "fill-opacity", 1 ); // Support: Android 2.3 (no support for fill-opacity) - if ( $div.css( "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" ); } $div.css( "column-count", 1 ); - if ( $div.css( "column-count" ) ) { + if ( $div.css( "column-count" ) !== undefined ) { assert.equal( $div.css( "column-count" ), 1, "Do not append px to 'column-count'" ); } else { assert.ok( true, "No support for column-count CSS property" ); } + + $div.css( "animation-iteration-count", 2 ); + if ( $div.css( "animation-iteration-count" ) !== undefined ) { + // if $div.css( "animation-iteration-count" ) return "1", + // it actually return the default value of animation-iteration-count + assert.equal( $div.css( "animation-iteration-count" ), 2, "Do not append px to 'animation-iteration-count'" ); + } else { + assert.ok( true, "No support for animation-iteration-count CSS property" ); + } } ); QUnit.test( "css('width') and css('height') should respect box-sizing, see #11004", function( assert ) { From fb9472c7fbf9979f48ef49aff76903ac130d0959 Mon Sep 17 00:00:00 2001 From: Leonardo Braga Date: Wed, 7 Oct 2015 00:51:23 -0400 Subject: [PATCH 014/465] Manipulation: Bring tagname regexes up to spec Fixes gh-2005 Closes gh-2634 --- src/core/var/rsingleTag.js | 2 +- src/manipulation.js | 2 +- src/manipulation/var/rtagName.js | 2 +- test/unit/manipulation.js | 67 ++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/core/var/rsingleTag.js b/src/core/var/rsingleTag.js index 1a55ee39d2..1ddf95ed4d 100644 --- a/src/core/var/rsingleTag.js +++ b/src/core/var/rsingleTag.js @@ -1,5 +1,5 @@ define( function() { // Match a standalone tag - return ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); + return ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); } ); diff --git a/src/manipulation.js b/src/manipulation.js index cc4fd16cd7..6c4b3ede87 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -27,7 +27,7 @@ define( [ dataPriv, dataUser, acceptData, DOMEval ) { var - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, + 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+ // In IE/Edge using regex groups here causes severe slowdowns. diff --git a/src/manipulation/var/rtagName.js b/src/manipulation/var/rtagName.js index 9e542694a7..1f8751ed8a 100644 --- a/src/manipulation/var/rtagName.js +++ b/src/manipulation/var/rtagName.js @@ -1,3 +1,3 @@ define( function() { - return ( /<([\w:-]+)/ ); + return ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); } ); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 4d8eed7d9a..f72000fc4d 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -500,6 +500,73 @@ QUnit.test( "html(String) tag-hyphenated elements (Bug #1987)", function( assert assert.equal( j.children().text(), "text", "Tags with multiple hypens behave normally" ); } ); +QUnit.test( "Tag name processing respects the HTML Standard (gh-2005)", function( assert ) { + + assert.expect( 240 ); + + var wrapper = jQuery( "
" ), + nameTerminatingChars = "\x20\t\r\n\f".split( "" ), + specialChars = "[ ] { } _ - = + \\ ( ) * & ^ % $ # @ ! ~ ` ' ; ? ¥ « µ λ ⊕ ≈ ξ ℜ ♣ €" + .split( " " ); + + specialChars.push( specialChars.join( "" ) ); + + jQuery.each( specialChars, function( i, characters ) { + assertSpecialCharsSupport( "html", characters ); + assertSpecialCharsSupport( "append", characters ); + } ); + + jQuery.each( nameTerminatingChars, function( i, character ) { + assertNameTerminatingCharsHandling( "html", character ); + assertNameTerminatingCharsHandling( "append", character ); + } ); + + function buildChild( method, html ) { + wrapper[ method ]( html ); + return wrapper.children()[ 0 ]; + } + + function assertSpecialCharsSupport( method, characters ) { + var child, + codepoint = characters.charCodeAt( 0 ).toString( 16 ).toUpperCase(), + description = characters.length === 1 ? + "U+" + ( "000" + codepoint ).slice( -4 ) + " " + characters : + "all special characters", + nodeName = "valid" + characters + "tagname"; + + child = buildChild( method, "<" + nodeName + ">" ); + assert.equal( child.nodeName.toUpperCase(), nodeName.toUpperCase(), + method + "(): Paired tag name includes " + description ); + + child = buildChild( method, "<" + nodeName + ">" ); + assert.equal( child.nodeName.toUpperCase(), nodeName.toUpperCase(), + method + "(): Unpaired tag name includes " + description ); + + child = buildChild( method, "<" + nodeName + "/>" ); + assert.equal( child.nodeName.toUpperCase(), nodeName.toUpperCase(), + method + "(): Self-closing tag name includes " + description ); + } + + function assertNameTerminatingCharsHandling( method, character ) { + var child, + codepoint = character.charCodeAt( 0 ).toString( 16 ).toUpperCase(), + description = "U+" + ( "000" + codepoint ).slice( -4 ) + " " + character, + nodeName = "div" + character + "this-will-be-discarded"; + + child = buildChild( method, "<" + nodeName + ">" ); + assert.equal( child.nodeName.toUpperCase(), "DIV", + method + "(): Paired tag name terminated by " + description ); + + child = buildChild( method, "<" + nodeName + ">" ); + assert.equal( child.nodeName.toUpperCase(), "DIV", + method + "(): Unpaired open tag name terminated by " + description ); + + child = buildChild( method, "<" + nodeName + "/>" ); + assert.equal( child.nodeName.toUpperCase(), "DIV", + method + "(): Self-closing tag name terminated by " + description ); + } +} ); + QUnit.test( "IE8 serialization bug", function( assert ) { assert.expect( 2 ); From 5c4be05d3b32456553dc944853b77fa96ae8b2b8 Mon Sep 17 00:00:00 2001 From: Timmy Willison Date: Mon, 11 Jan 2016 11:25:36 -0500 Subject: [PATCH 015/465] Selector: update Sizzle to 2.3.0 --- external/sizzle/dist/sizzle.js | 234 ++++++++++++++++++---------- external/sizzle/dist/sizzle.min.js | 4 +- external/sizzle/dist/sizzle.min.map | 2 +- package.json | 2 +- 4 files changed, 158 insertions(+), 84 deletions(-) diff --git a/external/sizzle/dist/sizzle.js b/external/sizzle/dist/sizzle.js index 9b1e5ba62c..cb93a5be54 100644 --- a/external/sizzle/dist/sizzle.js +++ b/external/sizzle/dist/sizzle.js @@ -1,12 +1,12 @@ /*! - * Sizzle CSS Selector Engine v2.2.1 - * http://sizzlejs.com/ + * Sizzle CSS Selector Engine v2.3.0 + * https://sizzlejs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2015-10-17 + * Date: 2016-01-04 */ (function( window ) { @@ -47,9 +47,6 @@ var i, return 0; }, - // General-purpose constants - MAX_NEGATIVE = 1 << 31, - // Instance methods hasOwn = ({}).hasOwnProperty, arr = [], @@ -58,7 +55,7 @@ var i, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf as it's faster than native - // http://jsperf.com/thor-indexof-vs-for/5 + // https://jsperf.com/thor-indexof-vs-for/5 indexOf = function( list, elem ) { var i = 0, len = list.length; @@ -78,7 +75,7 @@ var i, whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + @@ -135,9 +132,9 @@ var i, rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, - rescape = /'|\\/g, - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; @@ -153,13 +150,39 @@ var i, String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, + // 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; + }, + // Used for iframes // See setDocument() // Removing the function wrapper causes a "Permission Denied" // error in IE unloadHandler = function() { setDocument(); - }; + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true; + }, + { dir: "parentNode", next: "legend" } + ); // Optimize for push.apply( _, NodeList ) try { @@ -191,7 +214,7 @@ try { } function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, nidselect, match, groups, newSelector, + var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, // nodeType defaults to 9, since context defaults to document @@ -284,7 +307,7 @@ function Sizzle( selector, context, results, seed ) { // Capture the context ID, setting it first if necessary if ( (nid = context.getAttribute( "id" )) ) { - nid = nid.replace( rescape, "\\$&" ); + nid = nid.replace( rcssescape, fcssescape ); } else { context.setAttribute( "id", (nid = expando) ); } @@ -292,9 +315,8 @@ function Sizzle( selector, context, results, seed ) { // Prefix every selector in the list groups = tokenize( selector ); i = groups.length; - nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; while ( i-- ) { - groups[i] = nidselect + " " + toSelector( groups[i] ); + groups[i] = "#" + nid + " " + toSelector( groups[i] ); } newSelector = groups.join( "," ); @@ -355,22 +377,22 @@ function markFunction( fn ) { /** * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result + * @param {Function} fn Passed the created element and returns a boolean result */ function assert( fn ) { - var div = document.createElement("div"); + var el = document.createElement("fieldset"); try { - return !!fn( div ); + return !!fn( el ); } catch (e) { return false; } finally { // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); + if ( el.parentNode ) { + el.parentNode.removeChild( el ); } // release memory in IE - div = null; + el = null; } } @@ -397,8 +419,7 @@ function addHandle( attrs, handler ) { function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); + a.sourceIndex - b.sourceIndex; // Use IE sourceIndex if available on both nodes if ( diff ) { @@ -439,6 +460,34 @@ function createButtonPseudo( type ) { }; } +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @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 + 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 || + + // 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 || + + // Otherwise, assume any non-