diff --git a/AUTHORS.txt b/AUTHORS.txt index 62df6f5a5f..cdd78792d0 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -157,3 +157,7 @@ Yiming He Devin Cooper Bennett Sorbo Sebastian Burkhard +Danil Somsikov +Jean Boussier +Adam Coulombe +Andrew Plummer diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 88a19420a3..6028db34d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ 2. [Discussion](#discussion) 3. [How To Report Bugs](#how-to-report-bugs) 4. [Core Style Guide](#jquery-core-style-guide) -5. [Tips For Bug Patching](#tips-for-jquery-bug-patching) +5. [Tips For Bug Patching](#tips-for-bug-patching) @@ -12,13 +12,13 @@ There are a number of ways to get involved with the development of jQuery core. Even if you've never contributed code to an Open Source project before, we're always looking for help identifying bugs, writing and reducing test cases and documentation. -This is the best way to contribute to jQuery core. Please read through the full guide detailing [How to Report Bugs](#How-to-Report-Bugs). +This is the best way to contribute to jQuery core. Please read through the full guide detailing [How to Report Bugs](#how-to-report-bugs). ## Discussion ### Forum and IRC -The jQuery core development team frequently tracks posts on the [jQuery Development Forum](http://forum.jquery.com/developing-jquery-core). If you have longer posts or questions please feel free to post them there. If you think you've found a bug please [file it in the bug tracker](#How-to-Report-Bugs). +The jQuery core development team frequently tracks posts on the [jQuery Development Forum](http://forum.jquery.com/developing-jquery-core). If you have longer posts or questions please feel free to post them there. If you think you've found a bug please [file it in the bug tracker](#how-to-report-bugs). Additionally most of the jQuery core development team can be found in the [#jquery-dev](http://webchat.freenode.net/?channels=jquery-dev) IRC channel on irc.freenode.net. diff --git a/Gruntfile.js b/Gruntfile.js index ea51d180a8..4058871d51 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -22,40 +22,43 @@ module.exports = function( grunt ) { files: distpaths }, selector: { - "src/selector.js": [ - "src/sizzle-jquery.js", - "src/sizzle/sizzle.js" - ] + destFile: "src/selector.js", + apiFile: "src/sizzle-jquery.js", + srcFile: "src/sizzle/sizzle.js" }, build: { - "dist/jquery.js": [ - "src/intro.js", - "src/core.js", - "src/callbacks.js", - "src/deferred.js", - "src/support.js", - "src/data.js", - "src/queue.js", - "src/attributes.js", - "src/event.js", - "src/selector.js", - "src/traversing.js", - "src/manipulation.js", - - { flag: "css", src: "src/css.js" }, - "src/serialize.js", - { flag: "ajax", src: "src/ajax.js" }, - { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"] }, - { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ] }, - { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"] }, - { flag: "effects", src: "src/effects.js", needs: ["css"] }, - { flag: "offset", src: "src/offset.js", needs: ["css"] }, - { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] }, - { flag: "deprecated", src: "src/deprecated.js" }, - - "src/exports.js", - "src/outro.js" - ] + all:{ + dest: "dist/jquery.js", + src: [ + "src/intro.js", + "src/core.js", + "src/callbacks.js", + "src/deferred.js", + "src/support.js", + "src/data.js", + "src/queue.js", + "src/attributes.js", + "src/event.js", + "src/selector.js", + "src/traversing.js", + "src/manipulation.js", + + { flag: "css", src: "src/css.js" }, + "src/serialize.js", + { flag: "event-alias", src: "src/event-alias.js" }, + { flag: "ajax", src: "src/ajax.js" }, + { flag: "ajax/script", src: "src/ajax/script.js", needs: ["ajax"] }, + { flag: "ajax/jsonp", src: "src/ajax/jsonp.js", needs: [ "ajax", "ajax/script" ] }, + { flag: "ajax/xhr", src: "src/ajax/xhr.js", needs: ["ajax"] }, + { flag: "effects", src: "src/effects.js", needs: ["css"] }, + { flag: "offset", src: "src/offset.js", needs: ["css"] }, + { flag: "dimensions", src: "src/dimensions.js", needs: ["css"] }, + { flag: "deprecated", src: "src/deprecated.js" }, + + "src/exports.js", + "src/outro.js" + ] + } }, jshint: { @@ -96,8 +99,14 @@ module.exports = function( grunt ) { "dist/jquery.min.js": [ "dist/jquery.js" ] }, options: { - banner: "/*! jQuery v<%= pkg.version %> jquery.com | jquery.org/license */", + banner: "/*! jQuery v<%= pkg.version %> | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license */", sourceMap: "dist/jquery.min.map", + compress: { + hoist_funs: false, + join_vars: false, + loops: false, + unused: false + }, beautify: { ascii_only: true } @@ -143,15 +152,15 @@ module.exports = function( grunt ) { }); // Build src/selector.js - grunt.registerMultiTask( "selector", "Build src/selector.js", function() { + grunt.registerTask( "selector", "Build src/selector.js", function() { - var name = this.file.dest, - files = this.file.src, - sizzle = { - api: grunt.file.read( files[0] ), - src: grunt.file.read( files[1] ) - }, - compiled, parts; + var cfg = grunt.config("selector"), + name = cfg.destFile, + sizzle = { + api: grunt.file.read( cfg.apiFile ), + src: grunt.file.read( cfg.srcFile ) + }, + compiled, parts; /** @@ -189,8 +198,8 @@ module.exports = function( grunt ) { grunt.verbose.write("Injected sizzle-jquery.js into sizzle.js"); - // Write concatenated source to file - grunt.file.write( name, compiled ); + // Write concatenated source to file, and ensure newline-only termination + grunt.file.write( name, compiled.replace( /\x0d\x0a/g, "\x0a" ) ); // Fail task if errors were logged. if ( this.errorCount ) { @@ -241,13 +250,14 @@ module.exports = function( grunt ) { "build", "Concatenate source (include/exclude modules with +/- flags), embed date/version", function() { + // Concat specified files. var compiled = "", modules = this.flags, optIn = !modules["*"], explicit = optIn || Object.keys(modules).length > 1, - name = this.file.dest, - src = this.file.srcRaw, + name = this.data.dest, + src = this.data.src, deps = {}, excluded = {}, version = grunt.config( "pkg.version" ), @@ -409,8 +419,16 @@ module.exports = function( grunt ) { nonascii = false; distpaths.forEach(function( filename ) { - var text = fs.readFileSync( filename, "utf8" ), - i, c; + var i, c, map, + text = fs.readFileSync( filename, "utf8" ); + + // Ensure files use only \n for line endings, not \r\n + if ( /\x0d\x0a/.test( text ) ) { + grunt.log.writeln( filename + ": Incorrect line endings (\\r\\n)" ); + nonascii = true; + } + + // Ensure only ASCII chars so script tags don't need a charset attribute if ( text.length !== Buffer.byteLength( text, "utf8" ) ) { grunt.log.writeln( filename + ": Non-ASCII characters detected:" ); for ( i = 0; i < text.length; i++ ) { @@ -418,17 +436,31 @@ module.exports = function( grunt ) { if ( c > 127 ) { grunt.log.writeln( "- position " + i + ": " + c ); grunt.log.writeln( "-- " + text.substring( i - 20, i + 20 ) ); - nonascii = true; break; } } + nonascii = true; } - // Modify map so that it points to files in the same folder; + // Modify map/min so that it points to files in the same folder; // see https://github.com/mishoo/UglifyJS2/issues/47 if ( /\.map$/.test( filename ) ) { text = text.replace( /"dist\//g, "\"" ); fs.writeFileSync( filename, text, "utf-8" ); + } else if ( /\.min\.js$/.test( filename ) ) { + // Wrap sourceMap directive in multiline comments (#13274) + text = text.replace( /\n?(\/\/@\s*sourceMappingURL=)(.*)/, + function( _, directive, path ) { + map = "\n" + directive + path.replace( /^dist\//, "" ); + return ""; + }); + if ( map ) { + text = text.replace( /(^\/\*[\w\W]*?)\s*\*\/|$/, + function( _, comment ) { + return ( comment || "\n/*" ) + map + "\n*/"; + }); + } + fs.writeFileSync( filename, text, "utf-8" ); } // Optionally copy dist files to other locations @@ -457,7 +489,7 @@ module.exports = function( grunt ) { grunt.loadNpmTasks("grunt-contrib-uglify"); // Default grunt - grunt.registerTask( "default", [ "update_submodules", "selector", "build:*:*", "jshint", "uglify", "dist:*", "compare_size" ] ); + grunt.registerTask( "default", [ "update_submodules", "selector", "build:*:*", "jshint", "uglify", "dist:*" ] ); // Short list as a high frequency watch task grunt.registerTask( "dev", [ "selector", "build:*:*", "jshint" ] ); diff --git a/README.md b/README.md index 6eba3ba522..97b6c220fa 100644 --- a/README.md +++ b/README.md @@ -14,25 +14,23 @@ In the spirit of open source software development, jQuery always encourages comm What you need to build your own jQuery -------------------------------------- -In order to build jQuery, you need to have GNU make 3.8 or later, Node.js/npm latest, and git 1.7 or later. +In order to build jQuery, you need to have Node.js/npm latest and git 1.7 or later. (Earlier versions might work OK, but are not tested.) Windows users have two options: -1. Install [msysgit](https://code.google.com/p/msysgit/) (Full installer for official Git), - [GNU make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm), and a - [binary version of Node.js](http://node-js.prcn.co.cc/). Make sure all three packages are installed to the same +1. Install [msysgit](https://code.google.com/p/msysgit/) (Full installer for official Git) and a + [binary version of Node.js](http://nodejs.org). Make sure all two packages are installed to the same location (by default, this is C:\Program Files\Git). -2. Install [Cygwin](http://cygwin.com/) (make sure you install the git, make, and which packages), then either follow - the [Node.js build instructions](https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-%28Windows%29) or install - the [binary version of Node.js](http://node-js.prcn.co.cc/). +2. Install [Cygwin](http://cygwin.com/) (make sure you install the git and which packages), and + a [binary version of Node.js](http://nodejs.org/). Mac OS users should install Xcode (comes on your Mac OS install DVD, or downloadable from [Apple's Xcode site](http://developer.apple.com/technologies/xcode.html)) and [Homebrew](http://mxcl.github.com/homebrew/). Once Homebrew is installed, run `brew install git` to install git, and `brew install node` to install Node.js. -Linux/BSD users should use their appropriate package managers to install make, git, and node, or build from source +Linux/BSD users should use their appropriate package managers to install git and Node.js, or build from source if you swing that way. Easy-peasy. diff --git a/build/release-notes.js b/build/release-notes.js index 87710815b0..dff2c11c62 100644 --- a/build/release-notes.js +++ b/build/release-notes.js @@ -5,21 +5,20 @@ var fs = require("fs"), http = require("http"), - tmpl = require("mustache"), - extract = /(.*?)<[^"]+"component">\s*(\S+)/g; + extract = /(.*?)<[^"]+"component">\s*(\S+)/g, + categories = [], + version = process.argv[2]; -var opts = { - version: "1.9 Beta 1", - short_version: "1.9b1", - final_version: "1.9", - categories: [] -}; +if ( !/^\d+\.\d+/.test( version ) ) { + console.error( "Invalid version number: " + version ); + process.exit( 1 ); +} http.request({ host: "bugs.jquery.com", port: 80, method: "GET", - path: "/query?status=closed&resolution=fixed&max=400&component=!web&order=component&milestone=" + opts.final_version + path: "/query?status=closed&resolution=fixed&max=400&component=!web&order=component&milestone=" + version }, function (res) { var data = []; @@ -36,19 +35,25 @@ http.request({ if ( "#" + match[1] !== match[2] ) { var cat = match[3]; - if ( !cur || cur.name !== cat ) { - cur = { name: match[3], niceName: match[3].replace(/^./, function(a){ return a.toUpperCase(); }), bugs: [] }; - opts.categories.push( cur ); + if ( !cur || cur !== cat ) { + if ( cur ) { + console.log(""); + } + cur = cat; + console.log( "

" + cat.charAt(0).toUpperCase() + cat.slice(1) + "

" ); + console.log("
"); + } - buildNotes(); }); }).end(); -function buildNotes() { - console.log( tmpl.to_html( fs.readFileSync("release-notes.txt", "utf8"), opts ) ); -} diff --git a/build/release-notes.txt b/build/release-notes.txt deleted file mode 100644 index 1d0ae7460f..0000000000 --- a/build/release-notes.txt +++ /dev/null @@ -1,27 +0,0 @@ -

jQuery {{version}} Released

- -

This is a preview release of jQuery. We're releasing it so that everyone can start testing the code in their applications, making sure that there are no major problems.

- -

You can get the code from the jQuery CDN:

- - - -

You can help us by dropping that code into your existing application and letting us know that if anything no longer works. Please file a bug and be sure to mention that you're testing against jQuery {{version}}.

- -

We want to encourage everyone from the community to try and get involved in contributing back to jQuery core. We've set up a full page of information dedicated towards becoming more involved with the team. The team is here and ready to help you help us!

- -

jQuery {{version}} Change Log

- -

The current change log of the {{version}} release.

- -{{#categories}} -

{{niceName}}

- - -{{/categories}} diff --git a/package.json b/package.json index 1fc7d8f46a..89e0ed51c7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "jquery", "title": "jQuery", "description": "JavaScript library for DOM operations", - "version": "1.9.0pre", + "version": "1.9.2pre", "homepage": "http://jquery.com", "author": { "name": "jQuery Foundation and other contributors", @@ -22,17 +22,16 @@ } ], "scripts": { - "test" : "./node_modules/.bin/grunt" + "test": "./node_modules/.bin/grunt" }, "dependencies": {}, "devDependencies": { "grunt-compare-size": "~0.3.0", "grunt-git-authors": "~1.1.0", "grunt-update-submodules": "~0.2.0", - "grunt-contrib-watch": "~0.1.0", - "grunt-contrib-jshint": "~0.1.0", - "grunt-contrib-uglify": "~0.1.0", - "grunt-cli": "~0.1.0", + "grunt-contrib-watch": "~0.1.1", + "grunt-contrib-jshint": "~0.1.1", + "grunt-contrib-uglify": "~0.1.1", "grunt": "~0.4.0", "testswarm": "0.2.2" }, diff --git a/src/.jshintrc b/src/.jshintrc index 72a58de2ea..240302db48 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -4,7 +4,6 @@ "newcap": false, "quotmark": "double", "regexdash": true, - "strict": true, "trailing": true, "undef": true, "unused": true, diff --git a/src/ajax.js b/src/ajax.js index 6dd34a7326..69b3d60336 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -2,7 +2,6 @@ var // Document location ajaxLocParts, ajaxLocation, - ajax_nonce = jQuery.now(), ajax_rquery = /\?/, @@ -115,7 +114,7 @@ function inspectPrefiltersOrTransports( structure, options, originalOptions, jqX // that takes "flat" options (not to be deep extended) // Fixes #9887 function ajaxExtend( target, src ) { - var key, deep, + var deep, key, flatOptions = jQuery.ajaxSettings.flatOptions || {}; for ( key in src ) { @@ -135,7 +134,7 @@ jQuery.fn.load = function( url, params, callback ) { return _load.apply( this, arguments ); } - var selector, type, response, + var selector, response, type, self = this, off = url.indexOf(" "); @@ -258,7 +257,8 @@ jQuery.extend({ responseFields: { xml: "responseXML", - text: "responseText" + text: "responseText", + json: "responseJSON" }, // Data converters @@ -316,20 +316,23 @@ jQuery.extend({ // Force options to be an object options = options || {}; - var transport, + var // Cross-domain detection vars + parts, + // Loop variable + i, // URL without anti-cache param cacheURL, - // Response headers + // Response headers as string responseHeadersString, - responseHeaders, // timeout handle timeoutTimer, - // Cross-domain detection vars - parts, + // To know if global events are to be dispatched fireGlobals, - // Loop variable - i, + + transport, + // Response headers + responseHeaders, // Create the final options object s = jQuery.ajaxSetup( {}, options ), // Callbacks context @@ -604,13 +607,19 @@ jQuery.extend({ // Set readyState jqXHR.readyState = status > 0 ? 4 : 0; + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + // Get response data if ( responses ) { response = ajaxHandleResponses( s, jqXHR, responses ); } + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + // If successful, handle type chaining - if ( status >= 200 && status < 300 || status === 304 ) { + if ( isSuccess ) { // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. if ( s.ifModified ) { @@ -624,17 +633,19 @@ jQuery.extend({ } } - // If not modified - if ( status === 304 ) { - isSuccess = true; + // if no content + if ( status === 204 ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { statusText = "notmodified"; - // If we have data + // If we have data, let's convert it } else { - isSuccess = ajaxConvert( s, response ); - statusText = isSuccess.state; - success = isSuccess.data; - error = isSuccess.error; + statusText = response.state; + success = response.data; + error = response.error; isSuccess = !error; } } else { @@ -694,23 +705,13 @@ jQuery.extend({ }); /* Handles responses to an ajax request: - * - sets all responseXXX fields accordingly * - finds the right dataType (mediates between content-type and expected dataType) * - returns the corresponding response */ function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, + var firstDataType, ct, finalDataType, type, contents = s.contents, - dataTypes = s.dataTypes, - responseFields = s.responseFields; - - // Fill responseXXX fields - for ( type in responseFields ) { - if ( type in responses ) { - jqXHR[ responseFields[type] ] = responses[ type ]; - } - } + dataTypes = s.dataTypes; // Remove auto dataType and get content-type in the process while( dataTypes[ 0 ] === "*" ) { @@ -759,20 +760,14 @@ function ajaxHandleResponses( s, jqXHR, responses ) { } } -// Chain conversions given the request and the original response -function ajaxConvert( s, response ) { - - var conv, conv2, current, tmp, +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, converters = {}, - i = 0, // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(), - prev = dataTypes[ 0 ]; - - // Apply the dataFilter if provided - if ( s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } + dataTypes = s.dataTypes.slice(); // Create converters map with lowercased keys if ( dataTypes[ 1 ] ) { @@ -781,14 +776,32 @@ function ajaxConvert( s, response ) { } } - // Convert to each sequential dataType, tolerating list modification - for ( ; (current = dataTypes[++i]); ) { + current = dataTypes.shift(); - // There's only work to do if current dataType is non-auto - if ( current !== "*" ) { + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; // Convert response if prev dataType is non-auto and differs from current - if ( prev !== "*" && prev !== current ) { + } else if ( prev !== "*" && prev !== current ) { // Seek a direct converter conv = converters[ prev + " " + current ] || converters[ "* " + current ]; @@ -798,7 +811,7 @@ function ajaxConvert( s, response ) { for ( conv2 in converters ) { // If conv2 outputs current - tmp = conv2.split(" "); + tmp = conv2.split( " " ); if ( tmp[ 1 ] === current ) { // If prev can be converted to accepted input @@ -812,9 +825,8 @@ function ajaxConvert( s, response ) { // Otherwise, insert the intermediate dataType } else if ( converters[ conv2 ] !== true ) { current = tmp[ 0 ]; - dataTypes.splice( i--, 0, current ); + dataTypes.unshift( tmp[ 1 ] ); } - break; } } @@ -825,7 +837,7 @@ function ajaxConvert( s, response ) { if ( conv !== true ) { // Unless errors are allowed to bubble, catch and return them - if ( conv && s["throws"] ) { + if ( conv && s[ "throws" ] ) { response = conv( response ); } else { try { @@ -836,9 +848,6 @@ function ajaxConvert( s, response ) { } } } - - // Update prev for next iteration - prev = current; } } diff --git a/src/ajax/xhr.js b/src/ajax/xhr.js index 3c1fde374c..3133dd4398 100644 --- a/src/ajax/xhr.js +++ b/src/ajax/xhr.js @@ -101,12 +101,7 @@ if ( xhrSupported ) { // Listener callback = function( _, isAbort ) { - - var status, - statusText, - responseHeaders, - responses, - xml; + var status, responseHeaders, statusText, responses; // Firefox throws exceptions when accessing properties // of an xhr when a network error occurred @@ -136,14 +131,8 @@ if ( xhrSupported ) { } else { responses = {}; status = xhr.status; - xml = xhr.responseXML; responseHeaders = xhr.getAllResponseHeaders(); - // Construct response list - if ( xml && xml.documentElement /* #4958 */ ) { - responses.xml = xml; - } - // When requesting binary data, IE6-9 will throw an exception // on any attempt to access responseText (#11426) if ( typeof xhr.responseText === "string" ) { diff --git a/src/attributes.js b/src/attributes.js index 4193bbffb2..6902244edf 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -137,7 +137,7 @@ jQuery.fn.extend({ } // Toggle whole class name - } else if ( type === "undefined" || type === "boolean" ) { + } else if ( type === core_strundefined || type === "boolean" ) { if ( this.className ) { // store className if set jQuery._data( this, "__className__", this.className ); @@ -166,7 +166,7 @@ jQuery.fn.extend({ }, val: function( value ) { - var hooks, ret, isFunction, + var ret, hooks, isFunction, elem = this[0]; if ( !arguments.length ) { @@ -290,7 +290,7 @@ jQuery.extend({ }, attr: function( elem, name, value ) { - var ret, hooks, notxml, + var hooks, notxml, ret, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes @@ -299,7 +299,7 @@ jQuery.extend({ } // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { + if ( typeof elem.getAttribute === core_strundefined ) { return jQuery.prop( elem, name, value ); } @@ -330,7 +330,11 @@ jQuery.extend({ } else { - ret = elem.getAttribute( name ); + // In IE9+, Flash objects don't have .getAttribute (#12945) + // Support: IE9+ + if ( typeof elem.getAttribute !== core_strundefined ) { + ret = elem.getAttribute( name ); + } // Non-existent attributes return null, we normalize to undefined return ret == null ? diff --git a/src/callbacks.js b/src/callbacks.js index 58f1402a84..d94f0b329e 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -40,18 +40,18 @@ jQuery.Callbacks = function( options ) { ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); - var // Last fire value (for non-forgettable lists) + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, // Actual callback list list = [], // Stack of fire calls for repeatable lists @@ -137,9 +137,10 @@ jQuery.Callbacks = function( options ) { } return this; }, - // Control if a given callback is in the list + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { - return jQuery.inArray( fn, list ) > -1; + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list empty: function() { diff --git a/src/core.js b/src/core.js index 656fc22f14..4115c5eae0 100644 --- a/src/core.js +++ b/src/core.js @@ -1,9 +1,13 @@ var + // The deferred used on DOM ready + readyList, + // A central reference to the root jQuery(document) rootjQuery, - // The deferred used on DOM ready - readyList, + // Support: IE<9 + // For `typeof node.method` instead of `node.method !== undefined` + core_strundefined = typeof undefined, // Use the correct document accordingly with window argument (sandbox) document = window.document, @@ -70,17 +74,25 @@ var return letter.toUpperCase(); }, - // The ready event handler and self cleanup method - DOMContentLoaded = function() { - if ( document.addEventListener ) { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - } else if ( document.readyState === "complete" ) { - // we're here because readyState === "complete" in oldIE - // which is good enough for us to call the dom ready! - document.detachEvent( "onreadystatechange", DOMContentLoaded ); + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); jQuery.ready(); } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } }; jQuery.fn = jQuery.prototype = { @@ -284,7 +296,7 @@ jQuery.fn = jQuery.prototype = { jQuery.fn.init.prototype = jQuery.fn; jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, + var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, @@ -496,8 +508,7 @@ jQuery.extend({ return [ context.createElement( parsed[1] ) ]; } - parsed = context.createDocumentFragment(); - jQuery.clean( [ data ], context, parsed, scripts ); + parsed = jQuery.buildFragment( [ data ], context, scripts ); if ( scripts ) { jQuery( scripts ).remove(); } @@ -767,7 +778,7 @@ jQuery.extend({ // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { - var tmp, args, proxy; + var args, proxy, tmp; if ( typeof context === "string" ) { tmp = fn[ context ]; @@ -866,18 +877,18 @@ jQuery.ready.promise = function( obj ) { // Standards-based browsers support DOMContentLoaded } else if ( document.addEventListener ) { // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + document.addEventListener( "DOMContentLoaded", completed, false ); // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); + window.addEventListener( "load", completed, false ); // If IE event model is used } else { // Ensure firing before onload, maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); + document.attachEvent( "onreadystatechange", completed ); // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); + window.attachEvent( "onload", completed ); // If IE and not a frame // continually check to see if the document is ready @@ -899,6 +910,9 @@ jQuery.ready.promise = function( obj ) { return setTimeout( doScrollCheck, 50 ); } + // detach all dom ready events + detach(); + // and execute any waiting functions jQuery.ready(); } diff --git a/src/css.js b/src/css.js index 210bed2e80..1d15314528 100644 --- a/src/css.js +++ b/src/css.js @@ -1,4 +1,4 @@ -var curCSS, getStyles, iframe, +var iframe, getStyles, curCSS, ralpha = /alpha\([^)]*\)/i, ropacity = /opacity\s*=\s*([^)]*)/, rposition = /^(top|right|bottom|left)$/, @@ -51,7 +51,7 @@ function isHidden( elem, el ) { } function showHide( elements, show ) { - var elem, + var display, elem, hidden, values = [], index = 0, length = elements.length; @@ -61,11 +61,13 @@ function showHide( elements, show ) { if ( !elem.style ) { continue; } + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; if ( show ) { // Reset the inline display of this element to learn if it is // being hidden by cascaded rules or not - if ( !values[ index ] && elem.style.display === "none" ) { + if ( !values[ index ] && display === "none" ) { elem.style.display = ""; } @@ -75,8 +77,15 @@ function showHide( elements, show ) { if ( elem.style.display === "" && isHidden( elem ) ) { values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); } - } else if ( !values[ index ] && !isHidden( elem ) ) { - jQuery._data( elem, "olddisplay", jQuery.css( elem, "display" ) ); + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } } } @@ -98,7 +107,7 @@ function showHide( elements, show ) { jQuery.fn.extend({ css: function( name, value ) { return jQuery.access( this, function( elem, name, value ) { - var styles, len, + var len, styles, map = {}, i = 0; @@ -213,8 +222,8 @@ jQuery.extend({ // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, // but it would mean to define eight (for every problematic property) identical functions - if ( value === "" && name.indexOf("background") === 0 ) { - value = " "; + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; } // If a hook was provided, use that value, otherwise just set the specified value @@ -239,7 +248,7 @@ jQuery.extend({ }, css: function( elem, name, extra, styles ) { - var val, num, hooks, + var num, val, hooks, origName = jQuery.camelCase( name ); // Make sure that we're working with the right name @@ -265,7 +274,7 @@ jQuery.extend({ } // Return, converting to number if forced or a qualifier was provided and val looks numeric - if ( extra ) { + if ( extra === "" || extra ) { num = parseFloat( val ); return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; } @@ -630,7 +639,10 @@ jQuery(function() { if ( jQuery.expr && jQuery.expr.filters ) { jQuery.expr.filters.hidden = function( elem ) { - return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); + // Support: Opera <= 12.12 + // Opera reports offsetWidths and offsetHeights less than zero on some elements + return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 || + (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); }; jQuery.expr.filters.visible = function( elem ) { diff --git a/src/data.js b/src/data.js index d5a25ff6c9..e471df4eaa 100644 --- a/src/data.js +++ b/src/data.js @@ -1,6 +1,6 @@ var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; - + function internalData( elem, name, data, pvt /* Internal Use Only */ ){ if ( !jQuery.acceptData( elem ) ) { return; @@ -95,13 +95,12 @@ function internalData( elem, name, data, pvt /* Internal Use Only */ ){ return ret; } -function internalRemoveData( elem, name, pvt /* For internal use only */ ){ +function internalRemoveData( elem, name, pvt ) { if ( !jQuery.acceptData( elem ) ) { return; } - var thisCache, i, l, - + var i, l, thisCache, isNode = elem.nodeType, // See jQuery.data for more information @@ -205,24 +204,29 @@ jQuery.extend({ }, data: function( elem, name, data ) { - return internalData( elem, name, data, false ); + return internalData( elem, name, data ); }, removeData: function( elem, name ) { - return internalRemoveData( elem, name, false ); + return internalRemoveData( elem, name ); }, // For internal use only. _data: function( elem, name, data ) { return internalData( elem, name, data, true ); }, - + _removeData: function( elem, name ) { return internalRemoveData( elem, name, true ); }, // A method for determining if a DOM node can handle the data expando acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; // nodes accept data unless otherwise specified; rejection can be conditional @@ -248,7 +252,7 @@ jQuery.fn.extend({ name = attrs[i].name; if ( !name.indexOf( "data-" ) ) { - name = jQuery.camelCase( name.substring(5) ); + name = jQuery.camelCase( name.slice(5) ); dataAttr( elem, name, data[ name ] ); } @@ -299,12 +303,12 @@ function dataAttr( elem, key, data ) { if ( typeof data === "string" ) { try { data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; } catch( e ) {} // Make sure we set the data so it isn't changed later diff --git a/src/deferred.js b/src/deferred.js index d842065704..0efc05dc3a 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -21,22 +21,19 @@ jQuery.extend({ return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], - fn = fns[ i ]; + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? - function() { - var returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, [ returned ] ); - } - } : - newDefer[ action ] - ); + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); }); fns = null; }).promise(); @@ -72,7 +69,7 @@ jQuery.extend({ // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( promise, arguments ); + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; diff --git a/src/effects.js b/src/effects.js index 3d9ae7f4ba..5cac7d2d8c 100644 --- a/src/effects.js +++ b/src/effects.js @@ -72,6 +72,7 @@ function createTweens( animation, props ) { function Animation( elem, properties, options ) { var result, + stopped, index = 0, length = animationPrefilters.length, deferred = jQuery.Deferred().always( function() { @@ -79,6 +80,9 @@ function Animation( elem, properties, options ) { delete tick.elem; }), tick = function() { + if ( stopped ) { + return false; + } var currentTime = fxNow || createFxNow(), remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) @@ -120,7 +124,10 @@ function Animation( elem, properties, options ) { // if we are going to the end, we want to run all the tweens // otherwise we skip this part length = gotoEnd ? animation.tweens.length : 0; - + if ( stopped ) { + return this; + } + stopped = true; for ( ; index < length ; index++ ) { animation.tweens[ index ].run( 1 ); } @@ -154,9 +161,9 @@ function Animation( elem, properties, options ) { jQuery.fx.timer( jQuery.extend( tick, { + elem: elem, anim: animation, - queue: animation.opts.queue, - elem: elem + queue: animation.opts.queue }) ); @@ -168,7 +175,7 @@ function Animation( elem, properties, options ) { } function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; + var value, name, index, easing, hooks; // camelCase, specialEasing and expand cssHook pass for ( index in props ) { @@ -236,7 +243,9 @@ jQuery.Animation = jQuery.extend( Animation, { function defaultPrefilter( elem, props, opts ) { /*jshint validthis:true */ - var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, + var prop, index, length, + value, dataShow, toggle, + tween, hooks, oldfire, anim = this, style = elem.style, orig = {}, @@ -296,7 +305,7 @@ function defaultPrefilter( elem, props, opts ) { if ( opts.overflow ) { style.overflow = "hidden"; if ( !jQuery.support.shrinkWrapBlocks ) { - anim.done(function() { + anim.always(function() { style.overflow = opts.overflow[ 0 ]; style.overflowX = opts.overflow[ 1 ]; style.overflowY = opts.overflow[ 2 ]; @@ -420,11 +429,11 @@ Tween.propHooks = { return tween.elem[ tween.prop ]; } - // passing a non empty string as a 3rd parameter to .css will automatically + // passing an empty string as a 3rd parameter to .css will automatically // attempt a parseFloat and fallback to a string if the parse fails // so, simple values such as "10px" are parsed to Float. // complex values such as "rotate(1rad)" are returned as is. - result = jQuery.css( tween.elem, tween.prop, "auto" ); + result = jQuery.css( tween.elem, tween.prop, "" ); // Empty strings, null, undefined and "auto" are converted to 0. return !result || result === "auto" ? 0 : result; }, @@ -477,12 +486,15 @@ jQuery.fn.extend({ doAnimation = function() { // Operate on a copy of prop so per-property easing won't be lost var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations resolve immediately - if ( empty ) { + doAnimation.finish = function() { + anim.stop( true ); + }; + // Empty animations, or finishing resolves immediately + if ( empty || jQuery._data( this, "finish" ) ) { anim.stop( true ); } }; + doAnimation.finish = doAnimation; return empty || optall.queue === false ? this.each( doAnimation ) : @@ -537,6 +549,47 @@ jQuery.fn.extend({ jQuery.dequeue( this, type ); } }); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each(function() { + var index, + data = jQuery._data( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // enable finishing flag on private data + data.finish = true; + + // empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.cur && hooks.cur.finish ) { + hooks.cur.finish.call( this ); + } + + // look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // turn off finishing flag + delete data.finish; + }); } }); diff --git a/src/event-alias.js b/src/event-alias.js new file mode 100644 index 0000000000..0a87c59655 --- /dev/null +++ b/src/event-alias.js @@ -0,0 +1,15 @@ +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +}); + +jQuery.fn.hover = function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); +}; diff --git a/src/event.js b/src/event.js index adaba76735..4681140be2 100644 --- a/src/event.js +++ b/src/event.js @@ -4,20 +4,30 @@ var rformElems = /^(?:input|select|textarea)$/i, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { + global: {}, + add: function( elem, types, handler, data, selector ) { - var elemData, eventHandle, events, - tns, type, namespaces, handleObj, - handleObjIn, handlers, special, - t = 0; + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); - // Don't attach events to noData or text/comment nodes (allow plain objects tho) - if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { return; } @@ -34,16 +44,14 @@ jQuery.event = { } // Init the element's event structure and main handler, if this is the first - events = elemData.events; - if ( !events ) { - elemData.events = events = {}; + if ( !(events = elemData.events) ) { + events = elemData.events = {}; } - eventHandle = elemData.handle; - if ( !eventHandle ) { - elemData.handle = eventHandle = function( e ) { + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; @@ -54,11 +62,11 @@ jQuery.event = { // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); types = ( types || "" ).match( core_rnotwhite ) || [""]; - for ( ; t < types.length; t++ ) { - - tns = rtypenamespace.exec( types[t] ) || []; - type = tns[1]; - namespaces = ( tns[2] || "" ).split( "." ).sort(); + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; @@ -72,7 +80,7 @@ jQuery.event = { // handleObj is passed to all event handlers handleObj = jQuery.extend({ type: type, - origType: tns[1], + origType: origType, data: data, handler: handler, guid: handler.guid, @@ -82,8 +90,7 @@ jQuery.event = { }, handleObjIn ); // Init the event handler queue if we're the first - handlers = events[ type ]; - if ( !handlers ) { + if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; @@ -122,14 +129,12 @@ jQuery.event = { elem = null; }, - global: {}, - // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { - - var tns, type, origType, namespaces, origCount, - j, events, special, eventType, handleObj, - t = 0, + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, elemData = jQuery.hasData( elem ) && jQuery._data( elem ); if ( !elemData || !(events = elemData.events) ) { @@ -138,10 +143,11 @@ jQuery.event = { // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( core_rnotwhite ) || [""]; - for ( ; t < types.length; t++ ) { - tns = rtypenamespace.exec( types[t] ) || []; - type = origType = tns[1]; - namespaces = tns[2]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { @@ -152,23 +158,23 @@ jQuery.event = { } special = jQuery.event.special[ type ] || {}; - type = ( selector? special.delegateType : special.bindType ) || type; - eventType = events[ type ] || []; - origCount = eventType.length; - namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); // Remove matching events - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && - ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - eventType.splice( j--, 1 ); + handlers.splice( j, 1 ); if ( handleObj.selector ) { - eventType.delegateCount--; + handlers.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); @@ -178,7 +184,7 @@ jQuery.event = { // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) - if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( origCount && !handlers.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } @@ -198,12 +204,13 @@ jQuery.event = { }, trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - var i, cur, old, ontype, special, handle, eventPath, bubbleType, - type = event.type || event, - namespaces = event.namespace ? event.namespace.split(".") : []; - - elem = elem || document; + cur = tmp = elem = elem || document; // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { @@ -221,21 +228,18 @@ jQuery.event = { type = namespaces.shift(); namespaces.sort(); } + ontype = type.indexOf(":") < 0 && "on" + type; - // Caller can pass in an Event, Object, or just an event type string - event = typeof event === "object" ? - // jQuery.Event object - event[ jQuery.expando ] ? event : - // Object literal - new jQuery.Event( type, event ) : - // Just the event type (string) - new jQuery.Event( type ); + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); - event.type = type; event.isTrigger = true; event.namespace = namespaces.join("."); - event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; - ontype = type.indexOf(":") < 0 ? "on" + type : ""; + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; // Clean up the event in case it is being reused event.result = undefined; @@ -244,8 +248,9 @@ jQuery.event = { } // Clone any incoming data and prepend the event, creating the handler arg list - data = data != null ? jQuery.makeArray( data ) : []; - data.unshift( event ); + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; @@ -255,33 +260,38 @@ jQuery.event = { // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - eventPath = [[ elem, special.bindType || type ]]; if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; - cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; - for ( old = elem; cur; cur = cur.parentNode ) { - eventPath.push([ cur, bubbleType ]); - old = cur; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( old === (elem.ownerDocument || document) ) { - eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path - for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - cur = eventPath[i][0]; - event.type = eventPath[i][1]; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + // jQuery handler handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } - // Note that this is a bare JS function and not a jQuery handler + + // Native handler handle = ontype && cur[ ontype ]; if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { event.preventDefault(); @@ -301,9 +311,9 @@ jQuery.event = { if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; + tmp = elem[ ontype ]; - if ( old ) { + if ( tmp ) { elem[ ontype ] = null; } @@ -317,8 +327,8 @@ jQuery.event = { } jQuery.event.triggered = undefined; - if ( old ) { - elem[ ontype ] = old; + if ( tmp ) { + elem[ ontype ] = tmp; } } } @@ -332,12 +342,11 @@ jQuery.event = { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event ); - var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, - handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), - delegateCount = handlers.delegateCount, + var i, ret, handleObj, matched, j, + handlerQueue = [], args = core_slice.call( arguments ), - special = jQuery.event.special[ event.type ] || {}, - handlerQueue = []; + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; @@ -348,76 +357,136 @@ jQuery.event = { return; } - // Determine handlers that should run if there are delegated events + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && !(event.button && event.type === "click") ) { + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + for ( ; cur != this; cur = cur.parentNode || this ) { - // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - selMatch = {}; + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; - sel = handleObj.selector; - if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = handleObj.needsContext ? + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) >= 0 : jQuery.find( sel, this, null, [ cur ] ).length; } - if ( selMatch[ sel ] ) { + if ( matches[ sel ] ) { matches.push( handleObj ); } } if ( matches.length ) { - handlerQueue.push({ elem: cur, matches: matches }); + handlerQueue.push({ elem: cur, handlers: matches }); } } } } // Add the remaining (directly-bound) handlers - if ( handlers.length > delegateCount ) { - handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } - // Run delegates first; they may want to stop propagation beneath us - for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { - matched = handlerQueue[ i ]; - event.currentTarget = matched.elem; + return handlerQueue; + }, - for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { - handleObj = matched.matches[ j ]; + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; - event.data = handleObj.data; - event.handleObj = handleObj; + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); + event = new jQuery.Event( originalEvent ); - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; } - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; } - return event.result; + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; }, // Includes some event props shared by KeyEvent and MouseEvent @@ -441,7 +510,7 @@ jQuery.event = { mouseHooks: { props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function( event, original ) { - var eventDoc, doc, body, + var body, eventDoc, doc, button = original.button, fromElement = original.fromElement; @@ -470,40 +539,6 @@ jQuery.event = { } }, - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, - originalEvent = event, - fixHook = jQuery.event.fixHooks[ event.type ] || {}, - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = jQuery.Event( originalEvent ); - - for ( i = copy.length; i; ) { - prop = copy[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Target should not be a text node (#504, Safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8) - event.metaKey = !!event.metaKey; - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - special: { load: { // Prevent triggered image.load events from bubbling to window.load @@ -526,8 +561,9 @@ jQuery.event = { this.focus(); return false; } catch ( e ) { - // IE<9 dies on focus to hidden element (#1486,#12518) - // If this happens, let .trigger() run the handlers + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers } } }, @@ -577,10 +613,6 @@ jQuery.event = { } }; -// Some plugins are using, but it's undocumented/deprecated and will be removed. -// The 1.7 special event interface should provide all the hooks needed now. -jQuery.event.handle = jQuery.event.dispatch; - jQuery.removeEvent = document.removeEventListener ? function( elem, type, handle ) { if ( elem.removeEventListener ) { @@ -594,7 +626,7 @@ jQuery.removeEvent = document.removeEventListener ? // #8545, #7054, preventing memory leaks for custom events in IE6-8 // detachEvent needed property on element, by name of that event, to properly expose it to GC - if ( typeof elem[ name ] === "undefined" ) { + if ( typeof elem[ name ] === core_strundefined ) { elem[ name ] = null; } @@ -635,54 +667,51 @@ jQuery.Event = function( src, props ) { this[ jQuery.expando ] = true; }; -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + preventDefault: function() { var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; if ( !e ) { return; } - // if preventDefault exists run it on the original event + // If preventDefault exists, run it on the original event if ( e.preventDefault ) { e.preventDefault(); - // otherwise set the returnValue property of the original event to false (IE) + // Support: IE + // Otherwise set the returnValue property of the original event to false } else { e.returnValue = false; } }, stopPropagation: function() { - this.isPropagationStopped = returnTrue; - var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; if ( !e ) { return; } - // if stopPropagation exists run it on the original event + // If stopPropagation exists, run it on the original event if ( e.stopPropagation ) { e.stopPropagation(); } - // otherwise set the cancelBubble property of the original event to true (IE) + + // Support: IE + // Set the cancelBubble property of the original event to true e.cancelBubble = true; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse + } }; // Create mouseenter/leave events using mouseover/out and event-time checks @@ -727,11 +756,11 @@ if ( !jQuery.support.submitBubbles ) { // Node name check avoids a VML-related crash in IE (#9807) var elem = e.target, form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !jQuery._data( form, "_submit_attached" ) ) { + if ( form && !jQuery._data( form, "submitBubbles" ) ) { jQuery.event.add( form, "submit._submit", function( event ) { event._submit_bubble = true; }); - jQuery._data( form, "_submit_attached", true ); + jQuery._data( form, "submitBubbles", true ); } }); // return undefined since we don't need an event listener @@ -790,13 +819,13 @@ if ( !jQuery.support.changeBubbles ) { jQuery.event.add( this, "beforeactivate._change", function( e ) { var elem = e.target; - if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) { + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { jQuery.event.add( elem, "change._change", function( event ) { if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { jQuery.event.simulate( "change", this.parentNode, event, true ); } }); - jQuery._data( elem, "_change_attached", true ); + jQuery._data( elem, "changeBubbles", true ); } }); }, @@ -846,12 +875,12 @@ if ( !jQuery.support.focusinBubbles ) { jQuery.fn.extend({ on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; + var type, origFn; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { // && selector != null + if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; @@ -954,32 +983,9 @@ jQuery.fn.extend({ }); }, triggerHandler: function( type, data ) { - if ( this[0] ) { - return jQuery.event.trigger( type, data, this[0], true ); + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); } - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; - - if ( rkeyEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; - } - - if ( rmouseEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; } }); diff --git a/src/intro.js b/src/intro.js index 0b98012ec6..c9b1dcdade 100644 --- a/src/intro.js +++ b/src/intro.js @@ -5,11 +5,16 @@ * Includes Sizzle.js * http://sizzlejs.com/ * - * Copyright 2012 jQuery Foundation and other contributors + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: @DATE */ (function( window, undefined ) { -"use strict"; + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; diff --git a/src/manipulation.js b/src/manipulation.js index 5fec948cb4..3a4f9a6881 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -28,19 +28,21 @@ var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figca rscriptType = /^$|\/(?:java|ecma)script/i, rscriptTypeMasked = /^true\/(.*)/, rcleanScript = /^\s*\s*$/g, + + // We have to close these tags to support XHTML (#13200) wrapMap = { - option: [ 1, "", "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, // unless wrapped in a div with non-breaking characters in front of it. - _default: jQuery.support.htmlSerialize ? [ 0, "" ] : [ 1, "X
" ] + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] }, safeFragment = createSafeFragment( document ), fragmentDiv = safeFragment.appendChild( document.createElement("div") ); @@ -192,6 +194,12 @@ jQuery.fn.extend({ while ( elem.firstChild ) { elem.removeChild( elem.firstChild ); } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } } return this; @@ -257,21 +265,17 @@ jQuery.fn.extend({ value = jQuery( value ).not( this ).detach(); } - return this.domManip( [ value ], true, function( elem ) { - var next = this.nextSibling, - parent = this.parentNode; + return value !== "" ? + this.domManip( [ value ], true, function( elem ) { + var next = this.nextSibling, + parent = this.parentNode; - if ( parent && this.nodeType === 1 || this.nodeType === 11 ) { - - jQuery( this ).remove(); - - if ( next ) { - next.parentNode.insertBefore( elem, next ); - } else { - parent.appendChild( elem ); + if ( parent ) { + jQuery( this ).remove(); + parent.insertBefore( elem, next ); } - } - }); + }) : + this.remove(); }, detach: function( selector ) { @@ -283,7 +287,8 @@ jQuery.fn.extend({ // Flatten any nested arrays args = core_concat.apply( [], args ); - var fragment, first, scripts, hasScripts, node, doc, + var first, node, hasScripts, + scripts, doc, fragment, i = 0, l = this.length, set = this, @@ -302,10 +307,8 @@ jQuery.fn.extend({ }); } - if ( this[0] ) { - doc = this[0].ownerDocument; - fragment = doc.createDocumentFragment(); - jQuery.clean( args, doc, fragment, undefined, this ); + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { @@ -436,7 +439,7 @@ function cloneCopyEvent( src, dest ) { } function fixCloneNodeIssues( src, dest ) { - var nodeName, data, e; + var nodeName, e, data; // We do not need to do anything for non-Elements if ( dest.nodeType !== 1 ) { @@ -531,8 +534,8 @@ jQuery.each({ function getAll( context, tag ) { var elems, elem, i = 0, - found = typeof context.getElementsByTagName !== "undefined" ? context.getElementsByTagName( tag || "*" ) : - typeof context.querySelectorAll !== "undefined" ? context.querySelectorAll( tag || "*" ) : + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : undefined; if ( !found ) { @@ -550,7 +553,7 @@ function getAll( context, tag ) { found; } -// Used in clean, fixes the defaultChecked property +// Used in buildFragment, fixes the defaultChecked property function fixDefaultChecked( elem ) { if ( manipulation_rcheckableType.test( elem.type ) ) { elem.defaultChecked = elem.checked; @@ -559,7 +562,7 @@ function fixDefaultChecked( elem ) { jQuery.extend({ clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var destElements, srcElements, node, i, clone, + var destElements, node, clone, i, srcElements, inPage = jQuery.contains( elem.ownerDocument, elem ); if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { @@ -613,36 +616,39 @@ jQuery.extend({ return clone; }, - clean: function( elems, context, fragment, scripts, selection ) { - var elem, i, j, tmp, tag, wrap, tbody, - ret = [], - safe = context === document && safeFragment; + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, - // Ensure that context is a document - if ( !context || typeof context.createDocumentFragment === "undefined" ) { - context = document; - } + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; - for ( i = 0; (elem = elems[i]) != null; i++ ) { if ( elem || elem === 0 ) { + // Add nodes directly if ( jQuery.type( elem ) === "object" ) { - jQuery.merge( ret, elem.nodeType ? [ elem ] : elem ); + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node } else if ( !rhtml.test( elem ) ) { - ret.push( context.createTextNode( elem ) ); + nodes.push( context.createTextNode( elem ) ); // Convert html into DOM nodes } else { - // Ensure a safe container - safe = safe || createSafeFragment( context ); tmp = tmp || safe.appendChild( context.createElement("div") ); // Deserialize a standard representation tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + ( wrap[2] || "" ); + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; // Descend through wrappers to the right content j = wrap[0]; @@ -652,7 +658,7 @@ jQuery.extend({ // Manually add leading whitespace removed by IE if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { - ret.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); } // Remove IE's autoinserted from table fragments @@ -675,7 +681,7 @@ jQuery.extend({ } } - jQuery.merge( ret, tmp.childNodes ); + jQuery.merge( nodes, tmp.childNodes ); // Fix #12392 for WebKit and IE > 9 tmp.textContent = ""; @@ -691,7 +697,7 @@ jQuery.extend({ } } - // Fix #11356: Clear elements from safeFragment + // Fix #11356: Clear elements from fragment if ( tmp ) { safe.removeChild( tmp ); } @@ -699,44 +705,46 @@ jQuery.extend({ // Reset defaultChecked for any radios and checkboxes // about to be appended to the DOM in IE 6/7 (#8060) if ( !jQuery.support.appendChecked ) { - jQuery.grep( getAll( ret, "input" ), fixDefaultChecked ); + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); } - if ( fragment ) { - for ( i = 0; (elem = ret[i]) != null; i++ ) { - safe = jQuery.contains( elem.ownerDocument, elem ); + i = 0; + while ( (elem = nodes[ i++ ]) ) { - // Append to fragment - // #4087 - If origin and destination elements are the same, and this is - // that element, do not append to fragment - if ( !selection || jQuery.inArray( elem, selection ) === -1 ) { - fragment.appendChild( elem ); - } - tmp = getAll( elem, "script" ); + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } - // Preserve script evaluation history - if ( safe ) { - setGlobalEval( tmp ); - } + contains = jQuery.contains( elem.ownerDocument, elem ); - // Capture executables - if ( scripts ) { - for ( j = 0; (elem = tmp[j]) != null; j++ ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); } } } } - elem = tmp = safe = null; + tmp = null; - return ret; + return safe; }, cleanData: function( elems, /* internal */ acceptData ) { - var data, id, elem, type, + var elem, type, id, data, i = 0, internalKey = jQuery.expando, cache = jQuery.cache, @@ -774,7 +782,7 @@ jQuery.extend({ if ( deleteExpando ) { delete elem[ internalKey ]; - } else if ( elem.removeAttribute ) { + } else if ( typeof elem.removeAttribute !== core_strundefined ) { elem.removeAttribute( internalKey ); } else { diff --git a/src/offset.js b/src/offset.js index 04c94cadee..4efb962ff2 100644 --- a/src/offset.js +++ b/src/offset.js @@ -25,7 +25,7 @@ jQuery.fn.offset = function( options ) { // If we don't have gBCR, just use 0,0 rather than error // BlackBerry 5, iOS 3 (original iPhone) - if ( typeof elem.getBoundingClientRect !== "undefined" ) { + if ( typeof elem.getBoundingClientRect !== core_strundefined ) { box = elem.getBoundingClientRect(); } win = getWindow( doc ); diff --git a/src/queue.js b/src/queue.js index c5a0cbd7de..d4c3f0040a 100644 --- a/src/queue.js +++ b/src/queue.js @@ -35,6 +35,7 @@ jQuery.extend({ startLength--; } + hooks.cur = fn; if ( fn ) { // Add a progress sentinel to prevent the fx queue from being diff --git a/src/serialize.js b/src/serialize.js index 6e8353d591..65dee4005b 100644 --- a/src/serialize.js +++ b/src/serialize.js @@ -1,7 +1,7 @@ var r20 = /%20/g, rbracket = /\[\]$/, rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset)$/i, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, rsubmittable = /^(?:input|select|textarea|keygen)/i; jQuery.fn.extend({ diff --git a/src/sizzle b/src/sizzle index 19c7b34403..1c8aec9128 160000 --- a/src/sizzle +++ b/src/sizzle @@ -1 +1 @@ -Subproject commit 19c7b3440385c9f628a7bc1c5769f6946fcc6887 +Subproject commit 1c8aec91284af8d8c14447976235d5dd72b0d75e diff --git a/src/support.js b/src/support.js index c99bb39222..f677c0963e 100644 --- a/src/support.js +++ b/src/support.js @@ -1,6 +1,8 @@ jQuery.support = (function() { - var support, all, a, select, opt, input, fragment, eventName, isSupported, i, + var support, all, a, + input, select, fragment, + opt, eventName, isSupported, i, div = document.createElement("div"); // Setup @@ -131,13 +133,17 @@ jQuery.support = (function() { } // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) for ( i in { submit: true, change: true, focusin: true }) { div.setAttribute( eventName = "on" + i, "t" ); support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; } + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + // Run tests that need a body at doc ready jQuery(function() { var container, marginDiv, tds, @@ -197,7 +203,7 @@ jQuery.support = (function() { !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); } - if ( typeof div.style.zoom !== "undefined" ) { + if ( typeof div.style.zoom !== core_strundefined ) { // Support: IE<8 // Check if natively block-level elements act like inline-block // elements when setting their display to 'inline' and giving @@ -213,9 +219,12 @@ jQuery.support = (function() { div.firstChild.style.width = "5px"; support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); - // Prevent IE 6 from affecting layout for positioned elements #11048 - // Prevent IE from shrinking the body in IE 7 mode #12869 - body.style.zoom = 1; + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } } body.removeChild( container ); diff --git a/src/traversing.js b/src/traversing.js index 99c3810100..8d8b9930f7 100644 --- a/src/traversing.js +++ b/src/traversing.js @@ -12,12 +12,13 @@ var runtil = /Until$/, jQuery.fn.extend({ find: function( selector ) { - var i, ret, self; + var i, ret, self, + len = this.length; if ( typeof selector !== "string" ) { self = this; return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < self.length; i++ ) { + for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } @@ -26,12 +27,12 @@ jQuery.fn.extend({ } ret = []; - for ( i = 0; i < this.length; i++ ) { + for ( i = 0; i < len; i++ ) { jQuery.find( selector, this[ i ], ret ); } // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( jQuery.unique( ret ) ); + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); ret.selector = ( this.selector ? this.selector + " " : "" ) + selector; return ret; }, diff --git a/test/csp.php b/test/csp.php deleted file mode 100644 index 9ab18f3920..0000000000 --- a/test/csp.php +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - CSP Test Page - - - - -

CSP Test Page

- - diff --git a/test/data/ajax/unreleasedXHR.html b/test/data/ajax/unreleasedXHR.html new file mode 100644 index 0000000000..5233354f57 --- /dev/null +++ b/test/data/ajax/unreleasedXHR.html @@ -0,0 +1,25 @@ + + + + +Attempt to block tests because of dangling XHR requests (IE) + + + + + + + diff --git a/test/data/core/cc_on.html b/test/data/core/cc_on.html new file mode 100644 index 0000000000..131e2e8532 --- /dev/null +++ b/test/data/core/cc_on.html @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/test/data/dimensions/documentLarge.html b/test/data/dimensions/documentLarge.html index b2fabd144d..a6598fcc86 100644 --- a/test/data/dimensions/documentLarge.html +++ b/test/data/dimensions/documentLarge.html @@ -11,7 +11,7 @@
- +
diff --git a/test/data/dimensions/documentSmall.html b/test/data/dimensions/documentSmall.html index d5eeacfc2e..63e1c2a8ff 100644 --- a/test/data/dimensions/documentSmall.html +++ b/test/data/dimensions/documentSmall.html @@ -15,7 +15,7 @@
- +
diff --git a/test/data/errorWithJSON.php b/test/data/errorWithJSON.php new file mode 100644 index 0000000000..62b187ecc9 --- /dev/null +++ b/test/data/errorWithJSON.php @@ -0,0 +1,6 @@ + Test case for jQuery ticket #11470 - + + diff --git a/test/data/manipulation/iframe-denied.html b/test/data/manipulation/iframe-denied.html index da205ff49a..14df26a69b 100644 --- a/test/data/manipulation/iframe-denied.html +++ b/test/data/manipulation/iframe-denied.html @@ -6,7 +6,7 @@
- + + + + + + + + + + - - - diff --git a/test/data/support/bodyBackground.html b/test/data/support/bodyBackground.html index ebfa341c4b..8991007cf7 100644 --- a/test/data/support/bodyBackground.html +++ b/test/data/support/bodyBackground.html @@ -17,7 +17,7 @@
- +
+ + + +

CSP Test Page

+ + diff --git a/test/data/support/shrinkWrapBlocks.html b/test/data/support/shrinkWrapBlocks.html index af1493937f..a2097cb21c 100644 --- a/test/data/support/shrinkWrapBlocks.html +++ b/test/data/support/shrinkWrapBlocks.html @@ -12,7 +12,7 @@
- +
+ + - + \n"; - if( $lib == "prototype" ) { // prototype must be included first - array_unshift( $includes, $include ); - } else { - array_push( $includes, $include ); - } - } - - $includes = implode( "\n", $includes ); - $suite = file_get_contents( "index.html" ); - echo str_replace( "", $includes, $suite ); - exit; - } -?> - - - - - Run jQuery Test Suite Polluted - - - - -

jQuery Test Suite

- -

Choose other libraries to include

- -
- $data ) { - echo "
$name"; - $i = 0; - foreach( $data[ "versions" ] as $ver ) { - $i++; - echo ""; - if( !($i % 4) ) echo "
"; - } - echo "
"; - } - ?> - -
- - diff --git a/test/qunit b/test/qunit index 900f72051b..6ca3721222 160000 --- a/test/qunit +++ b/test/qunit @@ -1 +1 @@ -Subproject commit 900f72051b0112342feda3d700a7a049d886b9ce +Subproject commit 6ca3721222109997540bd6d9ccd396902e0ad2f9 diff --git a/test/readywait.html b/test/readywait.html index 9983be4322..7a736bef5a 100644 --- a/test/readywait.html +++ b/test/readywait.html @@ -14,7 +14,7 @@ #output { background-color: green } #expectedOutput { background-color: green } - + diff --git a/test/unit/ajax.js b/test/unit/ajax.js index f88b5d7ccb..d9adb3dbca 100644 --- a/test/unit/ajax.js +++ b/test/unit/ajax.js @@ -1422,6 +1422,18 @@ module( "ajax", { }); + ajaxTest( "#11151 - jQuery.ajax() - parse error body", 2, { + url: url("data/errorWithJSON.php"), + dataFilter: function( string ) { + ok( false, "dataFilter called" ); + return string; + }, + error: function( jqXHR ) { + strictEqual( jqXHR.responseText, "{ \"code\": 40, \"message\": \"Bad Request\" }", "Error body properly set" ); + deepEqual( jqXHR.responseJSON, { code: 40, message: "Bad Request" }, "Error body properly parsed" ); + } + }); + ajaxTest( "#11426 - jQuery.ajax() - loading binary data shouldn't throw an exception in IE", 1, { url: url("data/1x1.jpg"), success: function( data ) { @@ -1483,6 +1495,52 @@ module( "ajax", { }); + ajaxTest( "#13276 - jQuery.ajax() - compatibility between XML documents from ajax requests and parsed string", 1, { + url: "data/dashboard.xml", + dataType: "xml", + success: function( ajaxXML ) { + var parsedXML = jQuery( jQuery.parseXML("blibli") ).find("tab"); + ajaxXML = jQuery( ajaxXML ); + try { + ajaxXML.find("infowindowtab").append( parsedXML ); + } catch( e ) { + strictEqual( e, undefined, "error" ); + return; + } + strictEqual( ajaxXML.find("tab").length, 3, "Parsed node was added properly" ); + } + }); + + ajaxTest( "#13292 - jQuery.ajax() - converter is bypassed for 204 requests", 3, { + url: "data/nocontent.php", + dataType: "testing", + converters: { + "* testing": function() { + throw "converter was called"; + } + }, + success: function( data, status, jqXHR ) { + strictEqual( jqXHR.status, 204, "status code is 204" ); + strictEqual( status, "nocontent", "status text is 'nocontent'" ); + strictEqual( data, undefined, "data is undefined" ); + }, + error: function( _, status, error ) { + ok( false, "error" ); + strictEqual( status, "parsererror", "Parser Error" ); + strictEqual( error, "converter was called", "Converter was called" ); + } + }); + + ajaxTest( "#13388 - jQuery.ajax() - responseXML", 3, { + url: url("data/with_fries.xml"), + dataType: "xml", + success: function( resp, _, jqXHR ) { + notStrictEqual( resp, undefined, "XML document exists" ); + ok( "responseXML" in jqXHR, "jqXHR.responseXML exists" ); + strictEqual( resp, jqXHR.responseXML, "jqXHR.responseXML is set correctly" ); + } + }); + //----------- jQuery.ajaxPrefilter() ajaxTest( "jQuery.ajaxPrefilter() - abort", 1, { diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 072240049c..03c3d62ec8 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -316,7 +316,7 @@ test( "attr(String, Object)", function() { equal( $input.attr("checked"), "checked", "Set checked to 'checked' (verified by .attr)" ); var $radios = jQuery("#checkedtest").find("input[type='radio']"); - $radios.eq( 1 ).click(); + $radios.eq( 1 ).trigger("click"); equal( $radios.eq( 1 ).prop("checked"), true, "Second radio was checked when clicked" ); equal( $radios.eq( 0 ).attr("checked"), "checked", "First radio is still [checked]" ); diff --git a/test/unit/callbacks.js b/test/unit/callbacks.js index bd61207ae3..9482832db2 100644 --- a/test/unit/callbacks.js +++ b/test/unit/callbacks.js @@ -269,6 +269,53 @@ test( "jQuery.Callbacks.remove - should remove all instances", function() { }).remove( fn ).fire(); }); +test( "jQuery.Callbacks.has", function() { + + expect( 13 ); + + var cb = jQuery.Callbacks(); + function getA() { + return "A"; + } + function getB() { + return "B"; + } + function getC() { + return "C"; + } + cb.add(getA, getB, getC); + strictEqual( cb.has(), true, "No arguments to .has() returns whether callback function(s) are attached or not" ); + strictEqual( cb.has(getA), true, "Check if a specific callback function is in the Callbacks list" ); + + cb.remove(getB); + strictEqual( cb.has(getB), false, "Remove a specific callback function and make sure its no longer there" ); + strictEqual( cb.has(getA), true, "Remove a specific callback function and make sure other callback function is still there" ); + + cb.empty(); + strictEqual( cb.has(), false, "Empty list and make sure there are no callback function(s)" ); + strictEqual( cb.has(getA), false, "Check for a specific function in an empty() list" ); + + cb.add(getA, getB, function(){ + strictEqual( cb.has(), true, "Check if list has callback function(s) from within a callback function" ); + strictEqual( cb.has(getA), true, "Check if list has a specific callback from within a callback function" ); + }).fire(); + + strictEqual( cb.has(), true, "Callbacks list has callback function(s) after firing" ); + + cb.disable(); + strictEqual( cb.has(), false, "disabled() list has no callback functions (returns false)" ); + strictEqual( cb.has(getA), false, "Check for a specific function in a disabled() list" ); + + cb = jQuery.Callbacks("unique"); + cb.add(getA); + cb.add(getA); + strictEqual( cb.has(), true, "Check if unique list has callback function(s) attached" ); + cb.lock(); + strictEqual( cb.has(), false, "locked() list is empty and returns false" ); + + +}); + test( "jQuery.Callbacks() - adding a string doesn't cause a stack overflow", function() { expect( 1 ); diff --git a/test/unit/core.js b/test/unit/core.js index db21c41ccb..8a91de4cd5 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -17,6 +17,13 @@ test("Basic requirements", function() { ok( $, "$" ); }); +testIframeWithCallback( "Conditional compilation compatibility (#13274)", "core/cc_on.html", function( cc_on, errors, $ ) { + expect( 3 ); + ok( true, "JScript conditional compilation " + ( cc_on ? "supported" : "not supported" ) ); + deepEqual( errors, [], "No errors" ); + ok( $(), "jQuery executes" ); +}); + test("jQuery()", function() { var elem, i, @@ -27,9 +34,8 @@ test("jQuery()", function() { div = jQuery("

"), exec = false, lng = "", - expected = 22, + expected = 21, attrObj = { - "click": function() { ok( exec, "Click executed." ); }, "text": "test", "class": "test2", "id": "test3" @@ -37,6 +43,10 @@ test("jQuery()", function() { // The $(html, props) signature can stealth-call any $.fn method, check for a // few here but beware of modular builds where these methods may be excluded. + if ( jQuery.fn.click ) { + expected++; + attrObj["click"] = function() { ok( exec, "Click executed." ); }; + } if ( jQuery.fn.width ) { expected++; attrObj["width"] = 10; @@ -126,7 +136,7 @@ test("jQuery()", function() { equal( elem[0].id, "test3", "jQuery() quick setter id"); exec = true; - elem.click(); + elem.trigger("click"); // manually clean up detached elements elem.remove(); @@ -1210,7 +1220,7 @@ test("jQuery.proxy", function(){ }); test("jQuery.parseHTML", function() { - expect( 13 ); + expect( 17 ); var html, nodes; @@ -1237,6 +1247,13 @@ test("jQuery.parseHTML", function() { equal( jQuery.parseHTML( "\t
" )[0].nodeValue, "\t", "Preserve leading whitespace" ); equal( jQuery.parseHTML("
")[0].nodeType, 3, "Leading spaces are treated as text nodes (#11290)" ); + + html = jQuery.parseHTML( "
test div
" ); + equal( html[ 0 ].parentNode.nodeType, 11, "parentNode should be documentFragment" ); + equal( html[ 0 ].innerHTML, "test div", "Content should be preserved" ); + + equal( jQuery.parseHTML("").length, 1, "Incorrect html-strings should not break anything" ); + equal( jQuery.parseHTML("")[ 1 ].parentNode.nodeType, 11, "parentNode should be documentFragment" ); }); test("jQuery.parseJSON", function(){ diff --git a/test/unit/css.js b/test/unit/css.js index e7ada8bf90..303ee55153 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -3,22 +3,14 @@ if ( jQuery.css ) { module("css", { teardown: moduleTeardown }); test("css(String|Hash)", function() { - expect( 46 ); + expect( 41 ); equal( jQuery("#qunit-fixture").css("display"), "block", "Check for css property \"display\"" ); - ok( jQuery("#nothiddendiv").is(":visible"), "Modifying CSS display: Assert element is visible" ); - jQuery("#nothiddendiv").css({ display: "none" }); - ok( !jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is hidden" ); var $child = jQuery("#nothiddendivchild").css({ "width": "20%", "height": "20%" }); notEqual( $child.css("width"), "20px", "Retrieving a width percentage on the child of a hidden div returns percentage" ); notEqual( $child.css("height"), "20px", "Retrieving a height percentage on the child of a hidden div returns percentage" ); - jQuery("#nothiddendiv").css({"display": "block"}); - ok( jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is visible"); - ok( jQuery(window).is(":visible"), "Calling is(':visible') on window does not throw an error in IE."); - ok( jQuery(document).is(":visible"), "Calling is(':visible') on document does not throw an error in IE."); - var div = jQuery( "
" ); // These should be "auto" (or some better value) @@ -206,13 +198,7 @@ test("css() explicit and relative values", function() { }); test("css(String, Object)", function() { - expect(22); - - ok( jQuery("#nothiddendiv").is(":visible"), "Modifying CSS display: Assert element is visible"); - jQuery("#nothiddendiv").css("display", "none"); - ok( !jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is hidden"); - jQuery("#nothiddendiv").css("display", "block"); - ok( jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is visible"); + expect( 19 ); jQuery("#nothiddendiv").css("top", "-1em"); ok( jQuery("#nothiddendiv").css("top"), -16, "Check negative number in EMs." ); @@ -678,19 +664,6 @@ test("jQuery.css(elem, 'height') doesn't clear radio buttons (bug #1095)", funct ok( ! jQuery(":checkbox:last", $checkedtest).attr("checked"), "Check last checkbox still NOT checked." ); }); -test(":visible selector works properly on table elements (bug #4512)", function () { - expect(1); - - jQuery("#table").html("cellcell"); - equal(jQuery("#table td:visible").length, 1, "hidden cell is not perceived as visible"); -}); - -test(":visible selector works properly on children with a hidden parent (bug #4512)", function () { - expect(1); - jQuery("#table").css("display", "none").html("cellcell"); - equal(jQuery("#table td:visible").length, 0, "hidden cell children not perceived as visible"); -}); - test("internal ref to elem.runtimeStyle (bug #7608)", function () { expect(1); var result = true; @@ -910,20 +883,54 @@ test( "cssHooks - expand", function() { test( "css opacity consistency across browsers (#12685)", function() { expect( 4 ); - var fixture = jQuery("#qunit-fixture"), - style = jQuery("").appendTo(fixture), - el = jQuery("
").appendTo(fixture); + var fixture = jQuery("#qunit-fixture"), + style = jQuery("").appendTo(fixture), + el = jQuery("
").appendTo(fixture); + + equal( Math.round( el.css("opacity") * 100 ), 10, "opacity from style sheet (filter:alpha with spaces)" ); + el.removeClass("opacityWithSpaces_t12685").addClass("opacityNoSpaces_t12685"); + equal( Math.round( el.css("opacity") * 100 ), 20, "opacity from style sheet (filter:alpha without spaces)" ); + el.css( "opacity", 0.3 ); + equal( Math.round( el.css("opacity") * 100 ), 30, "override opacity" ); + el.css( "opacity", "" ); + equal( Math.round( el.css("opacity") * 100 ), 20, "remove opacity override" ); +}); + +test( ":visible/:hidden selectors", function() { + expect( 13 ); - equal( Math.round( el.css("opacity") * 100 ), 10, "opacity from style sheet (filter:alpha with spaces)" ); - el.removeClass("opacityWithSpaces_t12685").addClass("opacityNoSpaces_t12685"); - equal( Math.round( el.css("opacity") * 100 ), 20, "opacity from style sheet (filter:alpha without spaces)" ); - el.css( "opacity", 0.3 ); - equal( Math.round( el.css("opacity") * 100 ), 30, "override opacity" ); - el.css( "opacity", "" ); - equal( Math.round( el.css("opacity") * 100 ), 20, "remove opacity override" ); + ok( jQuery("#nothiddendiv").is(":visible"), "Modifying CSS display: Assert element is visible" ); + jQuery("#nothiddendiv").css({ display: "none" }); + ok( !jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is hidden" ); + jQuery("#nothiddendiv").css({"display": "block"}); + ok( jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is visible"); + ok( jQuery(window).is(":visible"), "Calling is(':visible') on window does not throw an error in IE."); + ok( jQuery(document).is(":visible"), "Calling is(':visible') on document does not throw an error in IE."); + + ok( jQuery("#nothiddendiv").is(":visible"), "Modifying CSS display: Assert element is visible"); + jQuery("#nothiddendiv").css("display", "none"); + ok( !jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is hidden"); + jQuery("#nothiddendiv").css("display", "block"); + ok( jQuery("#nothiddendiv").is(":visible"), "Modified CSS display: Assert element is visible"); + + // ok( !jQuery("#siblingspan").is(":visible"), "Span with no content not visible (#13132)" ); + // var $newDiv = jQuery("
").appendTo("#qunit-fixture"); + // equal( $newDiv.find(":visible").length, 0, "Span with no content not visible (#13132)" ); + // var $br = jQuery("
").appendTo("#qunit-fixture"); + // ok( !$br.is(":visible"), "br element not visible (#10406)"); + + var $table = jQuery("#table"); + $table.html("cellcell"); + equal(jQuery("#table td:visible").length, 1, "hidden cell is not perceived as visible (#4512). Works on table elements"); + $table.css("display", "none").html("cellcell"); + equal(jQuery("#table td:visible").length, 0, "hidden cell children not perceived as visible (#4512)"); + + t( "Is Visible", "#qunit-fixture div:visible:lt(2)", ["foo", "nothiddendiv"] ); + t( "Is Not Hidden", "#qunit-fixture:hidden", [] ); + t( "Is Hidden", "#form input:hidden", ["hidden1","hidden2"] ); }); -asyncTest( "Clearing a Cloned Element's Style Shouldn't Clear the Original Element's Style (#8908)", 16, function() { +asyncTest( "Clearing a Cloned Element's Style Shouldn't Clear the Original Element's Style (#8908)", 24, function() { var baseUrl = document.location.href.replace( /([^\/]*)$/, "" ), styles = [{ name: "backgroundAttachment", @@ -961,7 +968,7 @@ asyncTest( "Clearing a Cloned Element's Style Shouldn't Clear the Original Eleme expected: [ "auto auto" ] }]; - jQuery.each( styles, function(index, style) { + jQuery.each(styles, function( index, style ) { var $clone, $clonedChildren, $source = jQuery( "#firstp" ), source = $source[ 0 ], @@ -972,6 +979,7 @@ asyncTest( "Clearing a Cloned Element's Style Shouldn't Clear the Original Eleme if ( source.style[ style.name ] === undefined ) { ok( true, style.name + ": style isn't supported and therefore not an issue" ); ok( true ); + ok( true ); return true; } @@ -985,6 +993,8 @@ asyncTest( "Clearing a Cloned Element's Style Shouldn't Clear the Original Eleme $clonedChildren.css( style.name, "" ); window.setTimeout(function() { + notEqual( $clone.css( style.name ), style.value[ 0 ], "Cloned css was changed" ); + ok( jQuery.inArray( $source.css( style.name ) !== -1, style.value ), "Clearing clone.css() doesn't affect source.css(): " + style.name + "; result: " + $source.css( style.name ) + @@ -1000,4 +1010,27 @@ asyncTest( "Clearing a Cloned Element's Style Shouldn't Clear the Original Eleme window.setTimeout( start, 1000 ); }); +asyncTest( "Make sure initialized display value for disconnected nodes is correct (#13310)", 4, function() { + var display = jQuery("#display").css("display"), + div = jQuery("
"); + + equal( div.css( "display", "inline" ).hide().show().appendTo("body").css( "display" ), "inline", "Initialized display value has returned" ); + div.remove(); + + div.css( "display", "none" ).hide(); + equal( jQuery._data( div[ 0 ], "olddisplay" ), undefined, "olddisplay is undefined after hiding a detached and hidden element" ); + div.remove(); + + div.css( "display", "inline-block" ).hide().appendTo("body").fadeIn(function() { + equal( div.css( "display" ), "inline-block", "Initialized display value has returned" ); + div.remove(); + + start(); + }); + + equal( jQuery._data( jQuery("#display").css( "display", "inline" ).hide()[ 0 ], "olddisplay" ), display, + "display: * !Important value should used as initialized display" ); + jQuery._removeData( jQuery("#display")[ 0 ] ); +}); + } diff --git a/test/unit/data.js b/test/unit/data.js index c09149b65e..8f86d00c4d 100644 --- a/test/unit/data.js +++ b/test/unit/data.js @@ -133,8 +133,16 @@ test("Expando cleanup", 4, function() { jQuery(div).remove(); }); +test("Data is not being set on comment and text nodes", function() { + expect(2); + + ok( !jQuery.hasData( jQuery("").data("foo", 0) ) ); + ok( !jQuery.hasData( jQuery("text").contents().data("foo", 0) ) ); + +}); + test("jQuery.acceptData", function() { - expect(7); + expect(9); ok( jQuery.acceptData( document ), "document" ); ok( jQuery.acceptData( document.documentElement ), "documentElement" ); @@ -149,6 +157,9 @@ test("jQuery.acceptData", function() { var applet = document.createElement("object"); applet.setAttribute("classid", "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"); ok( !jQuery.acceptData( applet ), "applet" ); + + ok( !jQuery.acceptData( document.createComment("") ), "comment" ); + ok( !jQuery.acceptData( document.createTextNode("") ), "text" ); }); test(".data()", function() { diff --git a/test/unit/deferred.js b/test/unit/deferred.js index 86b0b34116..f9bc13a9c6 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -273,7 +273,7 @@ test( "jQuery.Deferred.then - deferred (progress)", function() { test( "jQuery.Deferred.then - context", function() { - expect( 4 ); + expect( 7 ); var context = {}; @@ -284,6 +284,12 @@ test( "jQuery.Deferred.then - context", function() { strictEqual( value, 6, "proper value received" ); }); + jQuery.Deferred().resolve().then(function() { + return jQuery.Deferred().resolveWith(context); + }).done(function() { + strictEqual( this, context, "custom context of returned deferred correctly propagated" ); + }); + var defer = jQuery.Deferred(), piped = defer.then(function( value ) { return value * 3; @@ -295,6 +301,16 @@ test( "jQuery.Deferred.then - context", function() { strictEqual( this, piped, "default context gets updated to latest promise in the chain" ); strictEqual( value, 6, "proper value received" ); }); + + var defer2 = jQuery.Deferred(), + piped2 = defer2.then(); + + defer2.resolve( 2 ); + + piped2.done(function( value ) { + strictEqual( this, piped2, "default context gets updated to latest promise in the chain (without passing function)" ); + strictEqual( value, 2, "proper value received (without passing function)" ); + }); }); test( "jQuery.when", function() { diff --git a/test/unit/effects.js b/test/unit/effects.js index b615dfd6df..e761300a22 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -283,14 +283,31 @@ test("animate native inline width/height", function() { } }); -test("animate block width/height", function() { - expect(3); +test( "animate block width/height", function() { + expect( 3 ); stop(); - jQuery("#foo").css({ display: "block", width: 20, height: 20 }).animate({ width: 42, height: 42 }, 100, function() { - equal( jQuery(this).css("display"), "block", "inline-block was not set on block element when animating width/height" ); - equal( this.offsetWidth, 42, "width was animated" ); - equal( this.offsetHeight, 42, "height was animated" ); - start(); + + jQuery("
").appendTo("#qunit-fixture").css({ + display: "block", + width: 20, + height: 20, + paddingLeft: 60 + }).animate({ + width: 42, + height: 42 + }, { + duration: 100, + step: function() { + if ( jQuery( this ).width() > 42 ) { + ok( false, "width was incorrectly augmented during animation" ); + } + }, + complete: function() { + equal( jQuery( this ).css("display"), "block", "inline-block was not set on block element when animating width/height" ); + equal( jQuery( this ).width(), 42, "width was animated" ); + equal( jQuery( this ).height(), 42, "height was animated" ); + start(); + } }); }); @@ -1770,16 +1787,22 @@ asyncTest("Animation callbacks (#11797)", 15, function() { }); }); -test( "Animate properly sets overflow hidden when animating width/height (#12117)", 4, function() { +test( "Animate properly sets overflow hidden when animating width/height (#12117)", 8, function() { jQuery.each( [ "height", "width" ], function( _, prop ) { jQuery.each( [ 100, 0 ], function( _, value ) { - var div = jQuery("
"), + var div = jQuery("
").css( "overflow", "auto" ), props = {}; props[ prop ] = value; div.animate( props, 1 ); equal( div.css( "overflow" ), "hidden", "overflow: hidden set when animating " + prop + " to " + value ); div.stop(); + if ( jQuery.support.shrinkWrapBlocks ) { + ok( true, "cannot restore overflow, shrinkWrapBlocks" ); + } else { + equal( div.css( "overflow" ), "auto", + "overflow: auto restored after animating " + prop + " to " + value ); + } }); }); }); @@ -1904,4 +1927,124 @@ test( "jQuery.fx.start & jQuery.fx.stop hook points", function() { jQuery.fx.stop = oldStop; }); +test( ".finish() completes all queued animations", function() { + var animations = { + top: 100, + left: 100, + height: 100, + width: 100 + }, + div = jQuery("
"); + + expect( 11 ); + + jQuery.each( animations, function( prop, value ) { + var anim = {}; + anim[ prop ] = value; + // the delay shouldn't matter at all! + div.css( prop, 1 ).animate( anim, function() { + ok( true, "Called animation callback for " + prop ); + }).delay( 100 ); + }); + equal( div.queue().length, 8, "8 animations in the queue" ); + div.finish(); + jQuery.each( animations, function( prop, value ) { + equal( parseFloat( div.css( prop ) ), value, prop + " finished at correct value" ); + }); + equal( div.queue().length, 0, "empty queue when done" ); + equal( div.is(":animated"), false, ":animated doesn't match" ); + + // cleanup + div.remove(); + // leaves a "shadow timer" which does nothing around, need to force a tick + jQuery.fx.tick(); +}); + +test( ".finish( false ) - unqueued animations", function() { + var animations = { + top: 100, + left: 100, + height: 100, + width: 100 + }, + div = jQuery("
"); + + expect( 10 ); + + jQuery.each( animations, function( prop, value ) { + var anim = {}; + anim[ prop ] = value; + div.css( prop, 1 ).animate( anim, { + queue: false, + complete: function() { + ok( true, "Called animation callback for " + prop ); + } + }); + }); + equal( div.queue().length, 0, "0 animations in the queue" ); + div.finish( false ); + jQuery.each( animations, function( prop, value ) { + equal( parseFloat( div.css( prop ) ), value, prop + " finished at correct value" ); + }); + equal( div.is(":animated"), false, ":animated doesn't match" ); + + // cleanup + div.remove(); + // leaves a "shadow timer" which does nothing around, need to force a tick + jQuery.fx.tick(); +}); + +test( ".finish( \"custom\" ) - custom queue animations", function() { + var animations = { + top: 100, + left: 100, + height: 100, + width: 100 + }, + div = jQuery("
"); + + expect( 11 ); + + jQuery.each( animations, function( prop, value ) { + var anim = {}; + anim[ prop ] = value; + div.css( prop, 1 ).animate( anim, { + queue: "custom", + complete: function() { + ok( true, "Called animation callback for " + prop ); + } + }); + }); + equal( div.queue( "custom" ).length, 4, "4 animations in the queue" ); + // start the first animation + div.dequeue( "custom" ); + equal( div.is(":animated"), true, ":animated matches" ); + div.finish( "custom" ); + jQuery.each( animations, function( prop, value ) { + equal( parseFloat( div.css( prop ) ), value, prop + " finished at correct value" ); + }); + equal( div.is(":animated"), false, ":animated doesn't match" ); + + // cleanup + div.remove(); + // leaves a "shadow timer" which does nothing around, need to force a tick + jQuery.fx.tick(); +}); + +test( ".finish() calls finish of custom queue functions", function() { + function queueTester( next ) { + + } + var div = jQuery( "
" ); + + expect( 3 ); + queueTester.finish = function() { + ok( true, "Finish called on custom queue function" ); + }; + + div.queue( queueTester ).queue( queueTester ).queue( queueTester ).finish(); + + div.remove(); +}); + } // if ( jQuery.fx ) diff --git a/test/unit/event.js b/test/unit/event.js index ff2ea1dd51..c3239f9020 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -4,12 +4,12 @@ test("null or undefined handler", function() { expect(2); // Supports Fixes bug #7229 try { - jQuery("#firstp").click(null); + jQuery("#firstp").on( "click", null ); ok(true, "Passing a null handler will not throw an exception"); } catch (e) {} try { - jQuery("#firstp").click(undefined); + jQuery("#firstp").on( "click", undefined ); ok(true, "Passing an undefined handler will not throw an exception"); } catch (e) {} }); @@ -66,7 +66,7 @@ test("bind(), with data", function() { ok( event.data, "bind() with data, check passed data exists" ); equal( event.data["foo"], "bar", "bind() with data, Check value of passed data" ); }; - jQuery("#firstp").bind("click", {"foo": "bar"}, handler).click().unbind("click", handler); + jQuery("#firstp").bind("click", {"foo": "bar"}, handler).trigger("click").unbind("click", handler); ok( !jQuery._data(jQuery("#firstp")[0], "events"), "Event handler unbound when using data." ); @@ -74,7 +74,7 @@ test("bind(), with data", function() { var handler2 = function(event) { equal( event.data, test, "bind() with function data, Check value of passed data" ); }; - jQuery("#firstp").bind("click", test, handler2).click().unbind("click", handler2); + jQuery("#firstp").bind("click", test, handler2).trigger("click").unbind("click", handler2); }); test("click(), with data", function() { @@ -83,7 +83,7 @@ test("click(), with data", function() { ok( event.data, "bind() with data, check passed data exists" ); equal( event.data["foo"], "bar", "bind() with data, Check value of passed data" ); }; - jQuery("#firstp").click({"foo": "bar"}, handler).click().unbind("click", handler); + jQuery("#firstp").on( "click", {"foo": "bar"}, handler).trigger("click").unbind("click", handler); ok( !jQuery._data(jQuery("#firstp")[0], "events"), "Event handler unbound when using data." ); }); @@ -391,7 +391,7 @@ test("bind/delegate bubbling, isDefaultPrevented", function() { $jq[0].click(); // IE } }; - $anchor2.click(function(e) { + $anchor2.on( "click", function(e) { e.preventDefault(); }); $main.delegate("#foo", "click", function(e) { @@ -408,7 +408,7 @@ test("bind/delegate bubbling, isDefaultPrevented", function() { fakeClick( $anchor2 ); $anchor2.unbind( "click" ); $main.undelegate( "click" ); - $anchor2.click(function(e) { + $anchor2.on( "click", function(e) { // Let the default action occur }); $main.delegate("#foo", "click", function(e) { @@ -427,7 +427,7 @@ test("bind(), iframes", function() { jQuery("div", doc).bind("click", function() { ok( true, "Binding to element inside iframe" ); - }).click().unbind("click"); + }).trigger("click").unbind("click"); }); test("bind(), trigger change on select", function() { @@ -489,7 +489,7 @@ test("bind(), namespaced events, cloned events", 18, function() { }).trigger("tester"); // Make sure events stick with appendTo'd elements (which are cloned) #2027 - jQuery("test").click(function(){ return false; }).appendTo("#qunit-fixture"); + jQuery("test").on( "click", function(){ return false; }).appendTo("#qunit-fixture"); ok( jQuery("a.test:first").triggerHandler("click") === false, "Handler is bound to appendTo'd elements" ); }); @@ -603,8 +603,8 @@ test("bind(), with different this object", function() { }; jQuery("#firstp") - .bind("click", jQuery.proxy(handler1, thisObject)).click().unbind("click", handler1) - .bind("click", data, jQuery.proxy(handler2, thisObject)).click().unbind("click", handler2); + .bind("click", jQuery.proxy(handler1, thisObject)).trigger("click").unbind("click", handler1) + .bind("click", data, jQuery.proxy(handler2, thisObject)).trigger("click").unbind("click", handler2); ok( !jQuery._data(jQuery("#firstp")[0], "events"), "Event handler unbound when using different this object and data." ); }); @@ -738,7 +738,7 @@ test("unbind(type)", function() { jQuery( document ) .bind( "click", func ) .unbind( "click", func ) - .click() + .trigger("click") .unbind( "click" ); }); @@ -778,33 +778,35 @@ test("unbind(eventObject)", function() { assert( 0 ); }); -test("hover() mouseenter mouseleave", function() { - expect(1); +if ( jQuery.fn.hover ) { + test("hover() mouseenter mouseleave", function() { + expect(1); - var times = 0, - handler1 = function( event ) { ++times; }, - handler2 = function( event ) { ++times; }; + var times = 0, + handler1 = function( event ) { ++times; }, + handler2 = function( event ) { ++times; }; - jQuery("#firstp") - .hover(handler1, handler2) - .mouseenter().mouseleave() - .unbind("mouseenter", handler1) - .unbind("mouseleave", handler2) - .hover(handler1) - .mouseenter().mouseleave() - .unbind("mouseenter mouseleave", handler1) - .mouseenter().mouseleave(); + jQuery("#firstp") + .hover(handler1, handler2) + .mouseenter().mouseleave() + .unbind("mouseenter", handler1) + .unbind("mouseleave", handler2) + .hover(handler1) + .mouseenter().mouseleave() + .unbind("mouseenter mouseleave", handler1) + .mouseenter().mouseleave(); - equal( times, 4, "hover handlers fired" ); + equal( times, 4, "hover handlers fired" ); -}); + }); +} test("mouseover triggers mouseenter", function() { expect(1); var count = 0, elem = jQuery(""); - elem.mouseenter(function () { + elem.on( "mouseenter", function () { count++; }); elem.trigger("mouseover"); @@ -835,65 +837,71 @@ test("withinElement implemented with jQuery.contains()", function() { test("mouseenter, mouseleave don't catch exceptions", function() { expect(2); - var elem = jQuery("#firstp").hover(function() { throw "an Exception"; }); + var elem = jQuery("#firstp").on( "mouseenter mouseleave", function() { + throw "an Exception"; + }); try { - elem.mouseenter(); + elem.trigger("mouseenter"); } catch (e) { equal( e, "an Exception", "mouseenter doesn't catch exceptions" ); } try { - elem.mouseleave(); + elem.trigger("mouseleave"); } catch (e) { equal( e, "an Exception", "mouseleave doesn't catch exceptions" ); } }); -test("trigger() shortcuts", function() { - expect(6); +if ( jQuery.fn.click ) { - var elem = jQuery("
  • Change location
  • ").prependTo("#firstUL"); - elem.find("a").bind("click", function() { - var close = jQuery("spanx", this); // same with jQuery(this).find("span"); - equal( close.length, 0, "Context element does not exist, length must be zero" ); - ok( !close[0], "Context element does not exist, direct access to element must return undefined" ); - return false; - }).click(); + test("trigger() shortcuts", function() { + expect(6); - // manually clean up detached elements - elem.remove(); + var elem = jQuery("
  • Change location
  • ").prependTo("#firstUL"); + elem.find("a").bind("click", function() { + var close = jQuery("spanx", this); // same with jQuery(this).find("span"); + equal( close.length, 0, "Context element does not exist, length must be zero" ); + ok( !close[0], "Context element does not exist, direct access to element must return undefined" ); + return false; + }).click(); - jQuery("#check1").click(function() { - ok( true, "click event handler for checkbox gets fired twice, see #815" ); - }).click(); + // manually clean up detached elements + elem.remove(); - var counter = 0; - jQuery("#firstp")[0].onclick = function(event) { - counter++; - }; - jQuery("#firstp").click(); - equal( counter, 1, "Check that click, triggers onclick event handler also" ); + jQuery("#check1").click(function() { + ok( true, "click event handler for checkbox gets fired twice, see #815" ); + }).click(); - var clickCounter = 0; - jQuery("#simon1")[0].onclick = function(event) { - clickCounter++; - }; - jQuery("#simon1").click(); - equal( clickCounter, 1, "Check that click, triggers onclick event handler on an a tag also" ); + var counter = 0; + jQuery("#firstp")[0].onclick = function(event) { + counter++; + }; + jQuery("#firstp").click(); + equal( counter, 1, "Check that click, triggers onclick event handler also" ); - elem = jQuery("").load(function(){ - ok( true, "Trigger the load event, using the shortcut .load() (#2819)"); - }).load(); + var clickCounter = 0; + jQuery("#simon1")[0].onclick = function(event) { + clickCounter++; + }; + jQuery("#simon1").click(); + equal( clickCounter, 1, "Check that click, triggers onclick event handler on an a tag also" ); - // manually clean up detached elements - elem.remove(); + elem = jQuery("").load(function(){ + ok( true, "Trigger the load event, using the shortcut .load() (#2819)"); + }).load(); - // test that special handlers do not blow up with VML elements (#7071) - jQuery("").appendTo("head"); - jQuery(" ").appendTo("#form"); - jQuery("#oval").click().keydown(); -}); + // manually clean up detached elements + elem.remove(); + + // test that special handlers do not blow up with VML elements (#7071) + jQuery("").appendTo("head"); + jQuery(" ").appendTo("#form"); + jQuery("#oval").click().keydown(); + }); + +} test("trigger() bubbling", function() { expect(18); @@ -1007,7 +1015,7 @@ test("trigger(type, [data], [fn])", function() { var form = jQuery("
    ").appendTo("body"); // Make sure it can be prevented locally - form.submit(function(){ + form.on( "submit", function(){ ok( true, "Local bind still works." ); return false; }); @@ -1017,7 +1025,7 @@ test("trigger(type, [data], [fn])", function() { form.unbind("submit"); - jQuery(document).submit(function(){ + jQuery(document).on( "submit", function(){ ok( true, "Make sure bubble works up to document." ); return false; }); @@ -1050,7 +1058,7 @@ test( "submit event bubbles on copied forms (#11649)", function() { $fixture.on( "submit", "form", delegatedSubmit ); // Trigger form submission to introduce the _submit_attached property - $testForm.on( "submit", noSubmit ).find("input[name=sub1]").click(); + $testForm.on( "submit", noSubmit ).find("input[name=sub1]").trigger("click"); // Copy the form via .clone() and .html() $formByClone = $testForm.clone( true, true ).removeAttr("id"); @@ -1058,7 +1066,7 @@ test( "submit event bubbles on copied forms (#11649)", function() { $wrapperDiv.append( $formByClone, $formByHTML ); // Check submit bubbling on the copied forms - $wrapperDiv.find("form").on( "submit", noSubmit ).find("input[name=sub1]").click(); + $wrapperDiv.find("form").on( "submit", noSubmit ).find("input[name=sub1]").trigger("click"); // Clean up $wrapperDiv.remove(); @@ -1083,7 +1091,7 @@ test( "change event bubbles on copied forms (#11796)", function(){ $fixture.on( "change", "form", delegatedChange ); // Trigger change event to introduce the _change_attached property - $form.find("select[name=select1]").val("1").change(); + $form.find("select[name=select1]").val("1").trigger("change"); // Copy the form via .clone() and .html() $formByClone = $form.clone( true, true ).removeAttr("id"); @@ -1091,7 +1099,7 @@ test( "change event bubbles on copied forms (#11796)", function(){ $wrapperDiv.append( $formByClone, $formByHTML ); // Check change bubbling on the copied forms - $wrapperDiv.find("form select[name=select1]").val("2").change(); + $wrapperDiv.find("form select[name=select1]").val("2").trigger("change"); // Clean up $wrapperDiv.remove(); @@ -1203,7 +1211,7 @@ test(".trigger() bubbling on disconnected elements (#10489)", function() { .on( "click", function() { ok( true, "click fired on p" ); }) - .click() + .trigger("click") .off( "click" ) .end() .off( "click" ) @@ -1231,17 +1239,21 @@ test(".trigger() doesn't bubble load event (#10717)", function() { jQuery( window ).off( "load" ); }); -test("Delegated events in SVG (#10791)", function() { +test("Delegated events in SVG (#10791; #13180)", function() { expect(2); - var svg = jQuery( + var useElem, e, + svg = jQuery( "" + + "" + "" + "" + + "" + "" - ).appendTo( "body" ); + ); - jQuery( "body" ) + jQuery("#qunit-fixture") + .append( svg ) .on( "click", "#svg-by-id", function() { ok( true, "delegated id selector" ); }) @@ -1249,11 +1261,19 @@ test("Delegated events in SVG (#10791)", function() { ok( true, "delegated class selector" ); }) .find( "#svg-by-id, [class~='svg-by-class']" ) - .trigger( "click" ) - .end() - .off( "click" ); + .trigger("click") + .end(); + + // Fire a native click on an SVGElementInstance (the instance tree of an SVG ) + // to confirm that it doesn't break our event delegation handling (#13180) + useElem = svg.find("#use")[0]; + if ( document.createEvent && useElem && useElem.instanceRoot ) { + e = document.createEvent("MouseEvents"); + e.initEvent( "click", true, true ); + useElem.instanceRoot.dispatchEvent( e ); + } - svg.remove(); + jQuery("#qunit-fixture").off("click"); }); test("Delegated events in forms (#10844; #11145; #8165; #11382, #11764)", function() { @@ -1458,7 +1478,7 @@ test("jQuery.Event.currentTarget", function(){ .on( "click", function( e ){ equal( e.currentTarget, this, "Check currentTarget on event" ); }) - .click() + .trigger("click") .off( "click" ) .end() .off( "click" ); @@ -1654,7 +1674,7 @@ test(".delegate()/.undelegate()", function() { jQuery("#body").delegate("#nothiddendivchild", "click", function(e){ jQuery("#nothiddendivchild").html(""); }); jQuery("#body").delegate("#nothiddendivchild", "click", function(e){ if(e.target) {livec++;} }); - jQuery("#nothiddendiv span").click(); + jQuery("#nothiddendiv span").trigger("click"); equal( jQuery("#nothiddendiv span").length, 0, "Verify that first handler occurred and modified the DOM." ); equal( livec, 1, "Verify that second handler occurred even with nuked target." ); @@ -1669,7 +1689,7 @@ test(".delegate()/.undelegate()", function() { jQuery("#body").delegate("span#liveSpan1 a", "click", function(){ lived++; return false; }); jQuery("#body").delegate("span#liveSpan1", "click", function(){ livee++; }); - jQuery("span#liveSpan1 a").click(); + jQuery("span#liveSpan1 a").trigger("click"); equal( lived, 1, "Verify that only one first handler occurred." ); equal( livee, 0, "Verify that second handler doesn't." ); @@ -1679,7 +1699,7 @@ test(".delegate()/.undelegate()", function() { lived = 0; livee = 0; - jQuery("span#liveSpan2 a").click(); + jQuery("span#liveSpan2 a").trigger("click"); equal( lived, 1, "Verify that only one first handler occurred." ); equal( livee, 0, "Verify that second handler doesn't." ); @@ -1694,7 +1714,7 @@ test(".delegate()/.undelegate()", function() { equal( e.target.nodeName.toUpperCase(), "A", "Check the event.target within a delegate handler" ); }); - jQuery("span#liveSpan1 a").click(); + jQuery("span#liveSpan1 a").trigger("click"); jQuery("#body").undelegate("span#liveSpan1", "click"); @@ -1746,7 +1766,7 @@ test("jQuery.off using dispatched jQuery.Event", function() { equal( ++count, 1, "event called once before removal" ); jQuery().off( event ); }) - .find("a").click().click().end() + .find("a").trigger("click").trigger("click").end() .remove(); }); @@ -1764,7 +1784,7 @@ test( "delegated event with delegateTarget-relative selector", function() { ok( this.id === "a0_0" , "first li under #u10 was clicked" ); }) .end() - .find("a").click().end() + .find("a").trigger("click").end() .find("#ul0").off(); // Non-positional selector (#12383) @@ -1780,11 +1800,36 @@ test( "delegated event with delegateTarget-relative selector", function() { .on( "click", "li.test a", function() { ok( true, "li.test is below the delegation point." ); }) - .find("#a0_0").click(); + .find("#a0_0").trigger("click"); markup.remove(); }); +test( "delegated event with selector matching Object.prototype property (#13203)", function() { + expect(1); + + var matched = 0; + + jQuery("#foo").on( "click", "toString", function( e ) { + matched++; + }); + + jQuery("#anchor2").trigger("click"); + + equal( matched, 0, "Nothing matched 'toString'" ); +}); + +test( "delegated event with intermediate DOM manipulation (#13208)", function() { + expect(1); + + jQuery("#foo").on( "click", "#sap", function() {}); + jQuery("#sap").on( "click", "#anchor2", function() { + jQuery( this.parentNode ).remove(); + ok( true, "Element removed" ); + }); + jQuery("#anchor2").trigger("click"); +}); + test("stopPropagation() stops directly-bound events on delegated target", function() { expect(1); @@ -1797,7 +1842,7 @@ test("stopPropagation() stops directly-bound events on delegated target", functi e.stopPropagation(); ok( true, "delegated handler was called" ); }) - .find("a").click().end() + .find("a").trigger("click").end() .remove(); }); @@ -1933,7 +1978,7 @@ test("delegate with submit", function() { ev.preventDefault(); }); - jQuery("#testForm input[name=sub1]").submit(); + jQuery("#testForm input[name=sub1]").trigger("submit"); equal( count1, 1, "Verify form submit." ); equal( count2, 1, "Verify body submit." ); @@ -1978,24 +2023,24 @@ test("inline handler returning false stops default", function() { expect(1); var markup = jQuery(""); - markup.click(function(e) { + markup.on( "click", function(e) { ok( e.isDefaultPrevented(), "inline handler prevented default"); return false; }); - markup.find("a").click(); + markup.find("a").trigger("click"); markup.off("click"); }); test("window resize", function() { expect(2); - jQuery(window).unbind(); + jQuery(window).off(); - jQuery(window).bind("resize", function(){ + jQuery(window).on( "resize", function(){ ok( true, "Resize event fired." ); - }).resize().unbind("resize"); + }).trigger("resize").off("resize"); - ok( !jQuery._data(window, "__events__"), "Make sure all the events are gone." ); + ok( !jQuery._data(window, "events"), "Make sure all the events are gone." ); }); test("focusin bubbles", function() { @@ -2069,7 +2114,7 @@ test(".on and .off", function() { .one( "click", 7, function( e, trig ) { counter += e.data + (trig || 11); // once, 7+11=18 }) - .click() + .trigger("click") .trigger( "click", 17 ) .off( "click" ); equal( counter, 54, "direct event bindings with data" ); @@ -2084,7 +2129,7 @@ test(".on and .off", function() { counter += e.data + (trig || 11); // once, 7+11=18 }) .find("em") - .click() + .trigger("click") .trigger( "click", 17 ) .end() .off( "click", "em" ); @@ -2269,7 +2314,7 @@ test("clone() delegated events (#11076)", function() { .on( "click", "td:last-child", clicked ), clone = table.clone( true ); - clone.find("td").click(); + clone.find("td").trigger("click"); equal( counter["center"], 1, "first child" ); equal( counter["fold"], 1, "last child" ); equal( counter["centerfold"], 2, "all children" ); @@ -2300,7 +2345,7 @@ test("checkbox state (#3827)", function() { // jQuery click cb.checked = true; equal( cb.checked, true, "jQuery - checkbox is initially checked" ); - jQuery( cb ).click(); + jQuery( cb ).trigger("click"); equal( cb.checked, false, "jQuery - checkbox is no longer checked" ); // Handlers only; checkbox state remains false @@ -2311,7 +2356,7 @@ test("focus-blur order (#12868)", function() { expect( 5 ); var $text = jQuery("#text1"), - $radio = jQuery("#radio1").focus(), + $radio = jQuery("#radio1").trigger("focus"), order; // IE6-10 fire focus/blur events asynchronously; this is the resulting mess. @@ -2339,7 +2384,7 @@ test("focus-blur order (#12868)", function() { // Enabled input getting focus order = 0; equal( document.activeElement, $radio[0], "radio has focus" ); - $text.focus(); + $text.trigger("focus"); setTimeout( function() { equal( document.activeElement, $text[0], "text has focus" ); @@ -2575,7 +2620,7 @@ test( "make sure events cloned correctly", 18, function() { ok( true, "Change on original child element is fired" ); }); - fixture.clone().click().change(); // 0 events should be fired + fixture.clone().trigger("click").trigger("change"); // 0 events should be fired clone = fixture.clone( true ); @@ -2591,15 +2636,15 @@ test( "make sure events cloned correctly", 18, function() { p.off(); checkbox.off(); - p.click(); // 0 should be fired - checkbox.change(); // 0 should be fired + p.trigger("click"); // 0 should be fired + checkbox.trigger("change"); // 0 should be fired - clone.find("p:first").trigger( "click", true ); // 3 events should fire + clone.find("p:first").trigger( "click", true ); // 3 events should fire clone.find("#check1").trigger( "change", true ); // 3 events should fire clone.remove(); - clone.find("p:first").click(); // 0 should be fired - clone.find("#check1").change(); // 0 events should fire + clone.find("p:first").trigger("click"); // 0 should be fired + clone.find("#check1").trigger("change"); // 0 events should fire }); test( "Check order of focusin/focusout events", 2, function() { @@ -2620,12 +2665,26 @@ test( "Check order of focusin/focusout events", 2, function() { }); // gain focus - input.focus(); + input.trigger("focus"); // then lose it - jQuery("#search").focus(); + jQuery("#search").trigger("focus"); // cleanup input.off(); }); +test( "String.prototype.namespace does not cause trigger() to throw (#13360)", function() { + expect( 1 ); + var errored = false; + + String.prototype.namespace = function() {}; + + try { + jQuery("

    ").trigger("foo.bar"); + } catch( e ) { + errored = true; + } + equal( errored, false, "trigger() did not throw exception" ); + delete String.prototype.namespace; +}); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 86b26e040c..b0736fbf6b 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -121,7 +121,7 @@ var testWrap = function( val ) { equal( result.text(), defaultText, "Check for element wrapping" ); QUnit.reset(); - jQuery("#check1").click(function() { + jQuery("#check1").on( "click", function() { var checkbox = this; ok( checkbox.checked, "Checkbox's state is erased after wrap() action, see #769" ); @@ -168,7 +168,7 @@ var testWrap = function( val ) { equal( j[ 0 ].parentNode.nodeName.toLowerCase(), "div", "Wrapping works." ); // Wrap an element with a jQuery set and event - result = jQuery("

    ").click(function() { + result = jQuery("
    ").on( "click", function() { ok( true, "Event triggered." ); // Remove handlers on detached elements @@ -395,13 +395,13 @@ var testAppendForObject = function( valueObj, isFragment ) { var testAppend = function( valueObj ) { - expect( 59 ); + expect( 78 ); testAppendForObject( valueObj, false ); testAppendForObject( valueObj, true ); var defaultText, result, message, iframe, iframeDoc, j, d, - $input, $radioChecked, $radioUnchecked, $radioParent; + $input, $radioChecked, $radioUnchecked, $radioParent, $map, $table; defaultText = "Try them out:"; result = jQuery("#first").append( valueObj("buga") ); @@ -446,24 +446,36 @@ var testAppend = function( valueObj ) { jQuery("
    ").appendTo("#form").append( valueObj("test") ); t( "Append legend", "#legend", [ "legend" ] ); + $map = jQuery("").append( valueObj("jQuery") ); + + equal( $map[ 0 ].childNodes.length, 1, "The area was inserted." ); + equal( $map[ 0 ].firstChild.nodeName.toLowerCase(), "area", "The area was inserted." ); + jQuery("#select1").append( valueObj("") ); equal( jQuery("#select1 option:last").text(), "Test", "Appending OPTION (all caps)" ); - jQuery("#table").append( valueObj("") ); - equal( jQuery("#table colgroup").length, 1, "Append colgroup" ); + jQuery("#select1").append( valueObj("") ); + equal( jQuery("#select1 optgroup").attr("label"), "optgroup", "Label attribute in newly inserted optgroup is correct" ); + equal( jQuery("#select1 option:last").text(), "optgroup", "Appending optgroup" ); + + $table = jQuery("#table").empty(); + + jQuery.each( "thead tbody tfoot colgroup caption tr th td".split(" "), function( i, name ) { + $table.append( valueObj( "<" + name + "/>" ) ); + ok( $table.find( name ).length >= 1, "Append " + name ); + ok( jQuery.parseHTML( "<" + name + "/>" ).length, name + " wrapped correctly" ); + }); jQuery("#table colgroup").append( valueObj("") ); equal( jQuery("#table colgroup col").length, 1, "Append col" ); - jQuery("#table").append( valueObj("") ); - equal( jQuery("#table caption").length, 1, "Append caption" ); - jQuery("#form") .append( valueObj("") ) .append( valueObj("") ); t( "Append Select", "#appendSelect1, #appendSelect2", [ "appendSelect1", "appendSelect2" ] ); equal( "Two nodes", jQuery("
    ").append( "Two", " nodes" ).text(), "Appending two text nodes (#4011)" ); + equal( jQuery("
    ").append( "1", "", 3 ).text(), "13", "If median is false-like value, subsequent arguments should not be ignored" ); // using contents will get comments regular, text, and comment nodes j = jQuery("#nonnodes").contents(); @@ -481,7 +493,7 @@ var testAppend = function( valueObj ) { $radioChecked = jQuery("input:radio[name='R1']").eq( 1 ); $radioParent = $radioChecked.parent(); $radioUnchecked = jQuery("").appendTo( $radioParent ); - $radioChecked.click(); + $radioChecked.trigger("click"); $radioUnchecked[ 0 ].checked = false; $radioParent.wrap("
    "); equal( $radioChecked[ 0 ].checked, true, "Reappending radios uphold which radio is checked" ); @@ -629,7 +641,7 @@ test( "append the same fragment with events (Bug #6997, 5566)", function() { // native event handlers on the original object don't get disturbed when they are // modified on the clone if ( doExtra ) { - element = jQuery("div:first").click(function() { + element = jQuery("div:first").on( "click", function() { ok( true, "Event exists on original after being unbound on clone" ); jQuery( this ).unbind("click"); }); @@ -641,20 +653,20 @@ test( "append the same fragment with events (Bug #6997, 5566)", function() { clone.remove(); } - element = jQuery("").click(function() { + element = jQuery("").on( "click", function() { ok( true, "Append second element events work" ); }); jQuery("#listWithTabIndex li").append( element ) - .find("a.test6997").eq( 1 ).click(); + .find("a.test6997").eq( 1 ).trigger("click"); - element = jQuery("
  • ").click(function() { + element = jQuery("
  • ").on( "click", function() { ok( true, "Before second element events work" ); start(); }); jQuery("#listWithTabIndex li").before( element ); - jQuery("#listWithTabIndex li.test6997").eq( 1 ).click(); + jQuery("#listWithTabIndex li.test6997").eq( 1 ).trigger("click"); }); test( "append HTML5 sectioning elements (Bug #6485)", function() { @@ -672,24 +684,6 @@ test( "append HTML5 sectioning elements (Bug #6485)", function() { equal( aside.length, 1, "HTML5 elements do not collapse their children" ); }); -test( "jQuery.clean, #12392", function() { - - expect( 6 ); - - var elems = jQuery.clean( [ "
    test div
    ", "

    test p

    " ] ); - - ok( elems[ 0 ].parentNode == null || elems[ 0 ].parentNode.nodeType === 11, "parentNode should be documentFragment or null" ); - ok( elems[ 1 ].parentNode == null || elems[ 1 ].parentNode.nodeType === 11, "parentNode should be documentFragment or null" ); - - equal( elems[ 0 ].innerHTML, "test div", "Content should be preserved" ); - equal( elems[ 1 ].innerHTML, "test p", "Content should be preserved" ); - - equal( jQuery.clean([ "" ]).length, 1, "Incorrect html-strings should not break anything" ); - - elems = jQuery.clean([ "" ]); - ok( elems[ 1 ].parentNode == null || elems[ 1 ].parentNode.nodeType === 11, "parentNode should be documentFragment or null" ); -}); - if ( jQuery.css ) { test( "HTML5 Elements inherit styles from style rules (Bug #10501)", function() { @@ -814,13 +808,13 @@ test( "appendTo(String|Element|Array|jQuery)", function() { t( "Append select", "#foo select", [ "select1" ] ); QUnit.reset(); - div = jQuery("
    ").click(function() { + div = jQuery("
    ").on( "click", function() { ok( true, "Running a cloned click." ); }); div.appendTo("#qunit-fixture, #moretests"); - jQuery("#qunit-fixture div:last").click(); - jQuery("#moretests div:last").click(); + jQuery("#qunit-fixture div:last").trigger("click"); + jQuery("#moretests div:last").trigger("click"); QUnit.reset(); div = jQuery("
    ").appendTo("#qunit-fixture, #moretests"); @@ -1154,104 +1148,92 @@ test( "insertAfter(String|Element|Array|jQuery)", function() { var testReplaceWith = function( val ) { - expect( 22 ); + var tmp, y, child, child2, set, non_existent, $div, + expected = 23; - var tmp, y, child, child2, set, non_existant, $div; + expect( expected ); - jQuery("#yahoo").replaceWith(val( "buga" )); - ok( jQuery("#replace")[ 0 ], "Replace element with string" ); + jQuery("#yahoo").replaceWith( val("buga") ); + ok( jQuery("#replace")[ 0 ], "Replace element with element from string" ); ok( !jQuery("#yahoo")[ 0 ], "Verify that original element is gone, after string" ); - QUnit.reset(); - jQuery("#yahoo").replaceWith(val( document.getElementById("first") )); + jQuery("#anchor2").replaceWith( val(document.getElementById("first")) ); ok( jQuery("#first")[ 0 ], "Replace element with element" ); - ok( !jQuery("#yahoo")[ 0 ], "Verify that original element is gone, after element" ); + ok( !jQuery("#anchor2")[ 0 ], "Verify that original element is gone, after element" ); - QUnit.reset(); - jQuery("#qunit-fixture").append("
    "); - jQuery("#baz").replaceWith("Baz"); + jQuery("#qunit-fixture").append("
    "); + jQuery("#baz").replaceWith( val("Baz") ); equal( jQuery("#bar").text(),"Baz", "Replace element with text" ); ok( !jQuery("#baz")[ 0 ], "Verify that original element is gone, after element" ); - QUnit.reset(); - jQuery("#yahoo").replaceWith( val([ document.getElementById("first"), document.getElementById("mark") ]) ); + jQuery("#google").replaceWith( val([ document.getElementById("first"), document.getElementById("mark") ]) ); ok( jQuery("#first")[ 0 ], "Replace element with array of elements" ); ok( jQuery("#mark")[ 0 ], "Replace element with array of elements" ); - ok( !jQuery("#yahoo")[ 0 ], "Verify that original element is gone, after array of elements" ); + ok( !jQuery("#google")[ 0 ], "Verify that original element is gone, after array of elements" ); - QUnit.reset(); - jQuery("#yahoo").replaceWith( val(jQuery("#mark, #first")) ); + jQuery("#groups").replaceWith( val(jQuery("#mark, #first")) ); ok( jQuery("#first")[ 0 ], "Replace element with set of elements" ); ok( jQuery("#mark")[ 0 ], "Replace element with set of elements" ); - ok( !jQuery("#yahoo")[ 0 ], "Verify that original element is gone, after set of elements" ); + ok( !jQuery("#groups")[ 0 ], "Verify that original element is gone, after set of elements" ); - QUnit.reset(); - tmp = jQuery("
    ").appendTo("body").click(function() { + tmp = jQuery("content")[0]; + jQuery("#anchor1").contents().replaceWith( val(tmp) ); + deepEqual( jQuery("#anchor1").contents().get(), [ tmp ], "Replace text node with element" ); + + + tmp = jQuery("
    ").appendTo("#qunit-fixture").on( "click", function() { ok( true, "Newly bound click run." ); }); - y = jQuery("
    ").appendTo("body").click(function() { - ok( true, "Previously bound click run." ); + y = jQuery("
    ").appendTo("#qunit-fixture").on( "click", function() { + ok( false, "Previously bound click run." ); }); - child = y.append("test").find("b").click(function() { + child = y.append("test").find("b").on( "click", function() { ok( true, "Child bound click run." ); return false; }); - y.replaceWith( tmp ); - - tmp.click(); - y.click(); // Shouldn't be run - child.click(); // Shouldn't be run + y.replaceWith( val(tmp) ); - tmp.remove(); - y.remove(); - child.remove(); + tmp.trigger("click"); + y.trigger("click"); // Shouldn't be run + child.trigger("click"); // Shouldn't be run - QUnit.reset(); - y = jQuery("
    ").appendTo("body").click(function() { - ok( true, "Previously bound click run." ); + y = jQuery("
    ").appendTo("#qunit-fixture").on( "click", function() { + ok( false, "Previously bound click run." ); }); - child2 = y.append("test").find("u").click(function() { + child2 = y.append("test").find("u").on( "click", function() { ok( true, "Child 2 bound click run." ); return false; }); - y.replaceWith( child2 ); + y.replaceWith( val(child2) ); - child2.click(); + child2.trigger("click"); - y.remove(); - child2.remove(); - QUnit.reset(); - - set = jQuery("
    ").replaceWith(val("test")); + set = jQuery("
    ").replaceWith( val("test") ); equal( set[0].nodeName.toLowerCase(), "div", "No effect on a disconnected node." ); equal( set.length, 1, "No effect on a disconnected node." ); equal( set[0].childNodes.length, 0, "No effect on a disconnected node." ); - non_existant = jQuery("#does-not-exist").replaceWith( val("should not throw an error") ); - equal( non_existant.length, 0, "Length of non existant element." ); - $div = jQuery("
    ").appendTo("body"); - // TODO: Work on jQuery(...) inline script execution - //$div.replaceWith("
    "); - equal(jQuery(".replacewith").length, 1, "Check number of elements in page."); - jQuery(".replacewith").remove(); + non_existent = jQuery("#does-not-exist").replaceWith( val("should not throw an error") ); + equal( non_existent.length, 0, "Length of non existent element." ); - QUnit.reset(); + $div = jQuery("
    ").appendTo("#qunit-fixture"); + $div.replaceWith( val("
    ") ); jQuery("#qunit-fixture").append("
    "); equal( jQuery("#qunit-fixture").find("div[id=replaceWith]").length, 1, "Make sure only one div exists." ); - jQuery("#replaceWith").replaceWith( val("
    ") ); - equal( jQuery("#qunit-fixture").find("div[id=replaceWith]").length, 1, "Make sure only one div exists." ); - + equal( jQuery("#qunit-fixture").find("div[id=replaceWith]").length, 1, "Make sure only one div exists after replacement." ); jQuery("#replaceWith").replaceWith( val("
    ") ); - equal( jQuery("#qunit-fixture").find("div[id=replaceWith]").length, 1, "Make sure only one div exists." ); + equal( jQuery("#qunit-fixture").find("div[id=replaceWith]").length, 1, "Make sure only one div exists after subsequent replacement." ); + + return expected; }; test( "replaceWith(String|Element|Array|jQuery)", function() { @@ -1259,17 +1241,13 @@ test( "replaceWith(String|Element|Array|jQuery)", function() { }); test( "replaceWith(Function)", function() { - testReplaceWith( manipulationFunctionReturningObj ); - - expect( 23 ); + expect( testReplaceWith(manipulationFunctionReturningObj) + 1 ); - var y = jQuery("#yahoo")[ 0 ]; + var y = jQuery("#foo")[ 0 ]; - jQuery(y).replaceWith(function() { + jQuery( y ).replaceWith(function() { equal( this, y, "Make sure the context is coming in correctly." ); }); - - QUnit.reset(); }); test( "replaceWith(string) for more than one element", function() { @@ -1283,6 +1261,15 @@ test( "replaceWith(string) for more than one element", function() { equal(jQuery("#foo p").length, 0, "verify that all the three original element have been replaced"); }); +test( "replaceWith(\"\") (#13401)", 4, function() { + expect( 1 ); + + var div = jQuery("

    "); + + div.children().replaceWith(""); + equal( div.html().toLowerCase(), "", "Replacing with empty string removes element" ); +}); + test( "replaceAll(String|Element|Array|jQuery)", function() { expect( 10 ); @@ -1351,7 +1338,7 @@ test( "clone()", function() { equal( jQuery("#nonnodes").contents().clone().length, 3, "Check node,textnode,comment clone works (some browsers delete comments on clone)" ); // Verify that clones of clones can keep event listeners - div = jQuery("
    • test
    ").click(function() { + div = jQuery("
    • test
    ").on( "click", function() { ok( true, "Bound event still exists." ); }); clone = div.clone( true ); div.remove(); @@ -1366,7 +1353,7 @@ test( "clone()", function() { // Verify that cloned children can keep event listeners div = jQuery("
    ").append([ document.createElement("table"), document.createElement("table") ]); - div.find("table").click(function() { + div.find("table").on( "click", function() { ok( true, "Bound event still exists." ); }); @@ -1380,7 +1367,7 @@ test( "clone()", function() { clone.remove(); // Make sure that doing .clone() doesn't clone event listeners - div = jQuery("
    • test
    ").click(function() { + div = jQuery("
    • test
    ").on( "click", function() { ok( false, "Bound event still exists after .clone()." ); }); clone = div.clone(); @@ -1798,9 +1785,9 @@ test( "remove() event cleaning ", 1, function() { count = 0; first = jQuery("#ap").children(":first"); - cleanUp = first.click(function() { + cleanUp = first.on( "click", function() { count++; - }).remove().appendTo("#qunit-fixture").click(); + }).remove().appendTo("#qunit-fixture").trigger("click"); strictEqual( 0, count, "Event handler has been removed" ); @@ -1817,9 +1804,9 @@ test( "detach() event cleaning ", 1, function() { count = 0; first = jQuery("#ap").children(":first"); - cleanUp = first.click(function() { + cleanUp = first.on( "click", function() { count++; - }).detach().appendTo("#qunit-fixture").click(); + }).detach().appendTo("#qunit-fixture").trigger("click"); strictEqual( 1, count, "Event handler has not been removed" ); @@ -1829,7 +1816,7 @@ test( "detach() event cleaning ", 1, function() { test("empty()", function() { - expect( 3 ); + expect( 6 ); equal( jQuery("#ap").children().empty().text().length, 0, "Check text is removed" ); equal( jQuery("#ap").children().length, 4, "Check elements are not removed" ); @@ -1838,7 +1825,13 @@ test("empty()", function() { var j = jQuery("#nonnodes").contents(); j.empty(); equal( j.html(), "", "Check node,textnode,comment empty works" ); -}); + + // Ensure oldIE empties selects (#12336) + notEqual( $("#select1").find("option").length, 0, "Have some initial options" ); + $("#select1").empty(); + equal( $("#select1").find("option").length, 0, "No more option elements found" ); + equal( $("#select1")[0].options.length, 0, "options.length cleared as well" ); + }); test( "jQuery.cleanData", function() { @@ -1896,13 +1889,13 @@ test( "jQuery.cleanData", function() { div.remove(); function getDiv() { - var div = jQuery("
    ").click(function() { + var div = jQuery("
    ").on( "click", function() { ok( true, type + " " + pos + " Click event fired." ); - }).focus(function() { + }).on( "focus", function() { ok( true, type + " " + pos + " Focus event fired." ); - }).find("div").click(function() { + }).find("div").on( "click", function() { ok( false, type + " " + pos + " Click event fired." ); - }).focus(function() { + }).on( "focus", function() { ok( false, type + " " + pos + " Focus event fired." ); }).end().appendTo("body"); @@ -2240,9 +2233,19 @@ test( "insertAfter, insertBefore, etc do not work when destination is original e }); test( "Index for function argument should be received (#13094)", 2, function() { - var i = 0; + var i = 0; + + jQuery("
    ").before(function( index ) { + equal( index, i++, "Index should be correct" ); + }); + +}); + +test( "Make sure jQuery.fn.remove can work on elements in documentFragment", 1, function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement("div") ); + + jQuery( div ).remove(); - jQuery("
    ").before(function( index ) { - equal( index, i++, "Index should be correct" ); - }); + equal( fragment.childNodes.length, 0, "div element was removed from documentFragment" ); }); diff --git a/test/unit/selector.js b/test/unit/selector.js index 21113207c6..22d7305eb0 100644 --- a/test/unit/selector.js +++ b/test/unit/selector.js @@ -63,29 +63,6 @@ test("attributes - jQuery only", function() { ); }); -if ( jQuery.css ) { - test("pseudo - visibility", function() { - expect( 9 ); - - t( "Is Visible", "#qunit-fixture div:visible:lt(2)", ["foo", "nothiddendiv"] ); - t( "Is Not Hidden", "#qunit-fixture:hidden", [] ); - t( "Is Hidden", "#form input:hidden", ["hidden1","hidden2"] ); - - var $div = jQuery("
    ").appendTo("body"); - $div.css({ fontSize: 0, lineHeight: 0 });// IE also needs to set font-size and line-height to 0 - $div.css( "width", 1 ).css( "height", 0 ); - t( "Is Visible", "#nothiddendivchild:visible", ["nothiddendivchild"] ); - t( "Is Not Visible", "#nothiddendivchild:hidden", [] ); - $div.css( "width", 0 ).css( "height", 1 ); - t( "Is Visible", "#nothiddendivchild:visible", ["nothiddendivchild"] ); - t( "Is Not Visible", "#nothiddendivchild:hidden", [] ); - $div.css( "width", 1 ).css( "height", 1 ); - t( "Is Visible", "#nothiddendivchild:visible", ["nothiddendivchild"] ); - t( "Is Not Visible", "#nothiddendivchild:hidden", [] ); - $div.remove(); - }); -} - test("disconnected nodes", function() { expect( 4 ); var $opt = jQuery("").attr("value", "whipit").appendTo("#qunit-fixture").detach(); @@ -201,7 +178,8 @@ testIframe("selector/html5_selector", "attributes - jQuery.attr", function( jQue testIframe("selector/sizzle_cache", "Sizzle cache collides with multiple Sizzles on a page", function( jQuery, window, document ) { var $cached = window["$cached"]; - expect(3); + expect(4); + notStrictEqual( jQuery, $cached, "Loaded two engines" ); deepEqual( $cached(".test a").get(), [ document.getElementById("collision") ], "Select collision anchor with first sizzle" ); equal( jQuery(".evil a").length, 0, "Select nothing with second sizzle" ); equal( jQuery(".evil a").length, 0, "Select nothing again with second sizzle" ); diff --git a/test/unit/serialize.js b/test/unit/serialize.js index ab5d1c4271..eff2a00869 100644 --- a/test/unit/serialize.js +++ b/test/unit/serialize.js @@ -112,8 +112,9 @@ test("serialize()", function() { // Add html5 elements only for serialize because selector can't yet find them on non-html5 browsers jQuery("#search").after( - ""+ - "" + "" + + "" + + "" ); equal( jQuery("#form").serialize(), diff --git a/test/unit/support.js b/test/unit/support.js index 76a0af7b74..4c6a49d553 100644 --- a/test/unit/support.js +++ b/test/unit/support.js @@ -6,36 +6,30 @@ test("boxModel", function() { equal( jQuery.support.boxModel, document.compatMode === "CSS1Compat" , "jQuery.support.boxModel is sort of tied to quirks mode but unstable since 1.8" ); }); +test( "zoom of doom (#13089)", function() { + expect( 1 ); + + if ( jQuery.support.inlineBlockNeedsLayout ) { + ok( document.body.style.zoom, "Added a zoom to the body (#11048, #12869)" ); + } else { + ok( !document.body.style.zoom, "No zoom added to the body" ); + } +}); if ( jQuery.css ) { testIframeWithCallback( "body background is not lost if set prior to loading jQuery (#9239)", "support/bodyBackground.html", function( color, support ) { expect( 2 ); - var i, - passed = true, - okValue = { + var okValue = { "#000000": true, "rgb(0, 0, 0)": true }; ok( okValue[ color ], "color was not reset (" + color + ")" ); - for ( i in jQuery.support ) { - if ( jQuery.support[ i ] !== support[ i ] ) { - passed = false; - strictEqual( jQuery.support[ i ], support[ i ], "Support property " + i + " is different" ); - } - } - for ( i in support ) { - if ( !( i in jQuery.support ) ) { - passed = false; - strictEqual( jQuery.support[ i ], support[ i ], "Unexpected property: " + i ); - } - } - - ok( passed, "Same support properties" ); + deepEqual( jQuery.extend( {}, support ), jQuery.support, "Same support properties" ); }); } testIframeWithCallback( "A background on the testElement does not cause IE8 to crash (#9823)", "support/testElementCrash.html", function() { - expect(1); + expect( 1 ); ok( true, "IE8 does not crash" ); }); @@ -81,7 +75,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":true, "ajax":true, "cors":true, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": true }; } else if ( /opera.*version\/12\.1/i.test( userAgent ) ) { expected = { @@ -114,7 +109,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":true, "ajax":true, "cors":true, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": true }; } else if ( /msie 10\.0/i.test( userAgent ) ) { expected = { @@ -147,7 +143,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":true, "ajax":true, "cors":true, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": false }; } else if ( /msie 9\.0/i.test( userAgent ) ) { expected = { @@ -180,7 +177,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":true, "ajax":true, "cors":false, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": false }; } else if ( /msie 8\.0/i.test( userAgent ) ) { expected = { @@ -213,7 +211,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":false, "ajax":true, "cors":false, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": true }; } else if ( /msie 7\.0/i.test( userAgent ) ) { expected = { @@ -246,7 +245,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "shrinkWrapBlocks": false, "submitBubbles": false, "tbody": false, - "style": false + "style": false, + "clearCloneStyle": true }; } else if ( /msie 6\.0/i.test( userAgent ) ) { expected = { @@ -279,7 +279,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":false, "ajax":true, "cors":false, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": true }; } else if ( /5\.1\.1 safari/i.test( userAgent ) ) { expected = { @@ -312,7 +313,8 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":true, "ajax":true, "cors":true, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": true }; } else if ( /firefox/i.test( userAgent ) ) { expected = { @@ -345,13 +347,14 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo "reliableHiddenOffsets":true, "ajax":true, "cors":true, - "doesNotIncludeMarginInBodyOffset":true + "doesNotIncludeMarginInBodyOffset":true, + "clearCloneStyle": true }; } if ( expected ) { test("Verify that the support tests resolve as expected per browser", function() { - expect( 30 ); + expect( 31 ); for ( var i in expected ) { if ( jQuery.ajax || i !== "ajax" && i !== "cors" ) { @@ -364,3 +367,14 @@ testIframeWithCallback( "box-sizing does not affect jQuery.support.shrinkWrapBlo } })(); + +// Support: Safari 5.1 +// Shameless browser-sniff, but Safari 5.1 mishandles CSP +if ( !( typeof navigator !== "undefined" && + (/ AppleWebKit\/\d.*? Version\/(\d+)/.exec(navigator.userAgent) || [])[1] < 6 ) ) { + + testIframeWithCallback( "Check CSP (https://developer.mozilla.org/en-US/docs/Security/CSP) restrictions", "support/csp.php", function( support ) { + expect( 1 ); + deepEqual( jQuery.extend( {}, support ), jQuery.support, "No violations of CSP polices" ); + }); +}