diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt index ea336914d..532704636 100644 --- a/MIT-LICENSE.txt +++ b/MIT-LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2010 John Resig, http://jquery.com/ +Copyright (c) 2011 John Resig, http://jquery.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Makefile b/Makefile index 935f69c12..6a71722be 100644 --- a/Makefile +++ b/Makefile @@ -24,10 +24,9 @@ BASE_FILES = ${SRC_DIR}/core.js\ ${SRC_DIR}/manipulation.js\ ${SRC_DIR}/css.js\ ${SRC_DIR}/ajax.js\ - ${SRC_DIR}/xhr.js\ - ${SRC_DIR}/transports/jsonp.js\ - ${SRC_DIR}/transports/script.js\ - ${SRC_DIR}/transports/xhr.js\ + ${SRC_DIR}/ajax/jsonp.js\ + ${SRC_DIR}/ajax/script.js\ + ${SRC_DIR}/ajax/xhr.js\ ${SRC_DIR}/effects.js\ ${SRC_DIR}/offset.js\ ${SRC_DIR}/dimensions.js @@ -79,13 +78,13 @@ init: jquery: ${JQ} jq: ${JQ} -${JQ}: ${MODULES} ${DIST_DIR} +${JQ}: ${MODULES} | ${DIST_DIR} @@echo "Building" ${JQ} @@cat ${MODULES} | \ sed 's/.function..jQuery...{//' | \ sed 's/}...jQuery..;//' | \ - sed 's/Date:./&'"${DATE}"'/' | \ + sed 's/@DATE/'"${DATE}"'/' | \ ${VER} > ${JQ}; ${SRC_DIR}/selector.js: ${SIZZLE_DIR}/sizzle.js diff --git a/Rakefile b/Rakefile index 5ea143b66..bf7ee2b58 100644 --- a/Rakefile +++ b/Rakefile @@ -9,7 +9,27 @@ test_dir = File.join( prefix, 'test' ) # setting DIST_DIR before calling rake dist_dir = ENV['DIST_DIR'] || File.join( prefix, 'dist' ) -base_files = %w{intro core support data queue attributes event selector traversing manipulation css ajax xhr transports/jsonp transports/script transports/xhr effects offset dimensions outro}.map { |js| File.join( src_dir, "#{js}.js" ) } +base_files = %w{ + intro + core + support + data + queue + attributes + event + selector + traversing + manipulation + css + ajax + ajax/jsonp + ajax/script + ajax/xhr + effects + offset + dimensions + outro +}.map { |js| File.join( src_dir, "#{js}.js" ) } # Sizzle, QUnit and jQuery files/dirs sizzle_dir = File.join( src_dir, "sizzle" ) @@ -31,7 +51,7 @@ rhino = "java -jar #{build_dir}/js.jar" minfier = "java -jar #{build_dir}/google-compiler-20100917.jar" # Turn off output other than needed from `sh` and file commands -verbose(false) +verbose(false) # Tasks task :default => "all" @@ -51,7 +71,7 @@ task :min => jq_min task :init => [sizzle, qunit] do sizzle_git = File.join(sizzle_dir, '.git') qunit_git = File.join(qunit_dir, '.git') - + puts "Updating SizzleJS with latest..." sh "git --git-dir=#{sizzle_git} pull -q origin master" @@ -61,7 +81,7 @@ end desc "Removes dist folder, selector.js, and Sizzle/QUnit" task :clean do - puts "Removing Distribution directory: #{dist_dir}..." + puts "Removing Distribution directory: #{dist_dir}..." rm_rf dist_dir puts "Removing built copy of Sizzle..." @@ -87,9 +107,13 @@ directory dist_dir file jq => [dist_dir, base_files].flatten do puts "Building jquery.js..." - + File.open(jq, 'w') do |f| - f.write cat(base_files).gsub(/(Date:.)/, "\\1#{date}" ).gsub(/@VERSION/, version) + f.write cat(base_files). + gsub(/@DATE/, date). + gsub(/@VERSION/, version). + gsub(/.function..jQuery...\{/, ''). + gsub(/\}...jQuery..;/, '') end end @@ -97,9 +121,9 @@ file jq_min => jq do puts "Building jquery.min.js..." sh "#{minfier} --js #{jq} --warning_level QUIET --js_output_file #{jq_min}" - + min = File.read( jq_min ) - + # Equivilent of "head" File.open(jq_min, 'w') do |f| f.write File.readlines(jq)[0..14].join() @@ -107,12 +131,12 @@ file jq_min => jq do end end -file selector => [sizzle, :init] do +file selector => [sizzle, :init] do puts "Building selector code from Sizzle..." - + File.open(selector, 'w') do |f| - f.write File.read(sizzle).gsub( - /^.+EXPOSE$\n/, + f.write File.read(sizzle).gsub( + /^.+EXPOSE$\n/, '\0' + File.read( File.join( src_dir, 'sizzle-jquery.js' )) ).gsub( /^window.Sizzle.+$\n/, '' diff --git a/build.xml b/build.xml index f6650f440..87b31e192 100644 --- a/build.xml +++ b/build.xml @@ -63,10 +63,9 @@ - - - - + + + @@ -77,7 +76,7 @@ - + diff --git a/src/ajax.js b/src/ajax.js index da130faed..645163ad2 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -1,11 +1,20 @@ (function( jQuery ) { - -var rscript = /)<[^<]*)*<\/script>/gi, - rselectTextarea = /^(?:select|textarea)/i, - rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + +var r20 = /%20/g, rbracket = /\[\]$/, + rhash = /#.*$/, + rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL + rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + rnoContent = /^(?:GET|HEAD)$/, rquery = /\?/, - r20 = /%20/g, + rscript = /)<[^<]*)*<\/script>/gi, + rselectTextarea = /^(?:select|textarea)/i, + rts = /([?&])_=[^&]*/, + rurl = /^(\w+:)?\/\/([^\/?#:]+)(?::(\d+))?/, + rCRLF = /\r?\n/g, + + // Slice function + sliceFunc = Array.prototype.slice, // Keep a copy of the old load method _load = jQuery.fn.load; @@ -43,35 +52,43 @@ jQuery.fn.extend({ type = "POST"; } } - + var self = this; - + // Request the remote document jQuery.ajax({ url: url, type: type, dataType: "html", data: params, - complete: function( res, status ) { + // Complete callback (responseText is used internally) + complete: function( jXHR, status, responseText ) { + // Store the response as specified by the jXHR object + responseText = jXHR.responseText; // If successful, inject the HTML into all the matched elements - if ( status === "success" || status === "notmodified" ) { + if ( jXHR.isResolved() ) { + // #4825: Get the actual response in case + // a dataFilter is present in ajaxSettings + jXHR.done(function( r ) { + responseText = r; + }); // See if a selector was specified self.html( selector ? // Create a dummy div to hold the results jQuery("
") // inject the contents of the document in, removing the scripts // to avoid any 'Permission Denied' errors in IE - .append(res.responseText.replace(rscript, "")) + .append(responseText.replace(rscript, "")) // Locate the specified elements .find(selector) : // If not, just inject the full result - res.responseText ); + responseText ); } if ( callback ) { - self.each( callback, [res.responseText, status, res] ); + self.each( callback, [responseText, status, jXHR] ); } } }); @@ -99,9 +116,9 @@ jQuery.fn.extend({ null : jQuery.isArray(val) ? jQuery.map( val, function(val, i){ - return {name: elem.name, value: val}; + return { name: elem.name, value: val.replace(rCRLF, "\r\n") }; }) : - {name: elem.name, value: val}; + { name: elem.name, value: val.replace(rCRLF, "\r\n") }; }).get(); } }); @@ -113,9 +130,8 @@ jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".sp }; }); -jQuery.extend({ - - get: function( url, data, callback, type ) { +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { // shift arguments if data argument was omited if ( jQuery.isFunction( data ) ) { type = type || callback; @@ -124,13 +140,16 @@ jQuery.extend({ } return jQuery.ajax({ - type: "GET", + type: method, url: url, data: data, success: callback, dataType: type }); - }, + }; +}); + +jQuery.extend({ getScript: function( url, callback ) { return jQuery.get(url, null, callback, "script"); @@ -140,25 +159,9 @@ jQuery.extend({ return jQuery.get(url, data, callback, "json"); }, - post: function( url, data, callback, type ) { - // shift arguments if data argument was omited - if ( jQuery.isFunction( data ) ) { - type = type || callback; - callback = data; - data = {}; - } - - return jQuery.ajax({ - type: "POST", - url: url, - data: data, - success: callback, - dataType: type - }); - }, - ajaxSetup: function( settings ) { - jQuery.extend( jQuery.ajaxSettings, settings ); + jQuery.extend( true, jQuery.ajaxSettings, settings ); + return this; }, ajaxSettings: { @@ -181,12 +184,7 @@ jQuery.extend({ xhr: function() { return new window.XMLHttpRequest(); }, - xhrResponseFields: { - xml: "XML", - text: "Text", - json: "JSON" - }, - + accepts: { xml: "application/xml, text/xml", html: "text/html", @@ -194,97 +192,523 @@ jQuery.extend({ json: "application/json, text/javascript", "*": "*/*" }, - - autoDataType: { + + contents: { xml: /xml/, html: /html/, json: /json/ }, - + // Prefilters // 1) They are useful to introduce custom dataTypes (see transport/jsonp for an example) // 2) These are called: // * BEFORE asking for a transport // * AFTER param serialization (s.data is a string if s.processData is true) - // 3) They MUST be order agnostic - prefilters: [], - + // 3) key is the dataType + // 4) the catchall symbol "*" can be used + // 5) execution will start with transport dataType and THEN continue down to "*" if needed + prefilters: {}, + // Transports bindings // 1) key is the dataType // 2) the catchall symbol "*" can be used // 3) selection will start with transport dataType and THEN go to "*" if needed - transports: { - }, - - // Checkers - // 1) key is dataType - // 2) they are called to control successful response - // 3) error throws is used as error data - dataCheckers: { - - // Check if data is a string - "text": function(data) { - if ( typeof data != "string" ) { - jQuery.error("typeerror"); - } - }, - - // Check if xml has been properly parsed - "xml": function(data) { - var documentElement = data ? data.documentElement : data; - if ( ! documentElement || ! documentElement.nodeName ) { - jQuery.error("typeerror"); - } - if ( documentElement.nodeName == "parsererror" ) { - jQuery.error("parsererror"); - } - } - }, - + transports: {}, + // List of data converters - // 1) key format is "source_type => destination_type" (spaces required) + // 1) key format is "source_type destination_type" (a single space in-between) // 2) the catchall symbol "*" can be used for source_type - dataConverters: { - + converters: { + // Convert anything to text - "* => text": function(data) { - return "" + data; - }, - - // Text to html (no transformation) - "text => html": function(data) { - return data; - }, - + "* text": window.String, + + // Text to html (true = no transformation) + "text html": true, + // Evaluate text as a json expression - "text => json": jQuery.parseJSON, - + "text json": jQuery.parseJSON, + // Parse text as xml - "text => xml": function(data) { - var xml, parser; - if ( window.DOMParser ) { // Standard - parser = new DOMParser(); - xml = parser.parseFromString(data,"text/xml"); - } else { // IE - xml = new ActiveXObject("Microsoft.XMLDOM"); - xml.async="false"; - xml.loadXML(data); - } - return xml; - } + "text xml": jQuery.parseXML } }, // Main method - ajax: function( url , s ) { - + // (s is used internally) + ajax: function( url , options , s ) { + + // Handle varargs if ( arguments.length === 1 ) { - s = url; - url = s ? s.url : undefined; + options = url; + url = options ? options.url : undefined; } - - return jQuery.xhr().open( s ? s.type : undefined , url ).send( undefined , s ); - + + // Force options to be an object + options = options || {}; + + // Get the url if provided separately + options.url = url || options.url; + + // Create the final options object + s = jQuery.extend( true , {} , jQuery.ajaxSettings , options ); + + // We force the original context + // (plain objects used as context get extended) + s.context = options.context; + + var // jQuery lists + jQuery_lastModified = jQuery.lastModified, + jQuery_etag = jQuery.etag, + // Callbacks contexts + callbackContext = s.context || s, + globalEventContext = s.context ? jQuery( s.context ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery._Deferred(), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // Headers (they are sent all at once) + requestHeaders = {}, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + loc = document.location, + parts, + // The jXHR state + state = 0, + // Loop variable + i, + // Fake xhr + jXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function(name,value) { + if ( state === 0 ) { + requestHeaders[ name.toLowerCase() ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + // (match is used internally) + getResponseHeader: function( key , match ) { + + if ( state !== 2 ) { + return null; + } + + if ( responseHeaders === undefined ) { + + responseHeaders = {}; + + if ( typeof responseHeadersString === "string" ) { + + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + } + } + } + return responseHeaders[ key.toLowerCase() ]; + }, + + // Cancel the request + abort: function( statusText ) { + if ( transport && state !== 2 ) { + transport.abort( statusText || "abort" ); + done( 0 , statusText ); + } + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status , statusText , response , headers) { + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Set readyState + jXHR.readyState = status ? 4 : 0; + + // Cache response headers + responseHeadersString = headers || ""; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout(timeoutTimer); + } + + var // Reference url + url = s.url, + // and ifModified status + ifModified = s.ifModified, + + // Is it a success? + isSuccess = 0, + // Stored success + success, + // Stored error + error; + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + var lastModified = jXHR.getResponseHeader("Last-Modified"), + etag = jXHR.getResponseHeader("Etag"); + + if (lastModified) { + jQuery_lastModified[ s.url ] = lastModified; + } + if (etag) { + jQuery_etag[ s.url ] = etag; + } + } + + // If not modified + if ( status === 304 ) { + + // Set the statusText accordingly + statusText = "notmodified"; + // Mark as a success + isSuccess = 1; + + // If we have data + } else { + + // Set the statusText accordingly + statusText = "success"; + + // Chain data conversions and determine the final value + // (if an exception is thrown in the process, it'll be notified as an error) + try { + + var i, + // Current dataType + current, + // Previous dataType + prev, + // Conversion function + conv, + // Conversion functions (when text is used in-between) + conv1, + conv2, + // Local references to dataTypes & converters + dataTypes = s.dataTypes, + converters = s.converters, + // DataType to responseXXX field mapping + responses = { + "xml": "XML", + "text": "Text" + }; + + // For each dataType in the chain + for( i = 0 ; i < dataTypes.length ; i++ ) { + + current = dataTypes[ i ]; + + // If a responseXXX field for this dataType exists + // and if it hasn't been set yet + if ( responses[ current ] ) { + // Set it + jXHR[ "response" + responses[ current ] ] = response; + // Mark it as set + responses[ current ] = 0; + } + + // If this is not the first element + if ( i ) { + + // Get the dataType to convert from + prev = dataTypes[ i - 1 ]; + + // If no catch-all and dataTypes are actually different + if ( prev !== "*" && current !== "*" && prev !== current ) { + + // Get the converter + conv = converters[ prev + " " + current ] || + converters[ "* " + current ]; + + conv1 = conv2 = 0; + + // If there is no direct converter and none of the dataTypes is text + if ( ! conv && prev !== "text" && current !== "text" ) { + // Try with text in-between + conv1 = converters[ prev + " text" ] || converters[ "* text" ]; + conv2 = converters[ "text " + current ]; + // Revert back to a single converter + // if one of the converter is an equivalence + if ( conv1 === true ) { + conv = conv2; + } else if ( conv2 === true ) { + conv = conv1; + } + } + // If we found no converter, dispatch an error + if ( ! ( conv || conv1 && conv2 ) ) { + throw conversion; + } + // If found converter is not an equivalence + if ( conv !== true ) { + // Convert with 1 or 2 converters accordingly + response = conv ? conv( response ) : conv2( conv1( response ) ); + } + } + // If it is the first element of the chain + // and we have a dataFilter + } else if ( s.dataFilter ) { + // Apply the dataFilter + response = s.dataFilter( response , current ); + // Get dataTypes again in case the filter changed them + dataTypes = s.dataTypes; + } + } + // End of loop + + // We have a real success + success = response; + isSuccess = 1; + + // If an exception was thrown + } catch(e) { + + // We have a parsererror + statusText = "parsererror"; + error = "" + e; + + } + } + + // if not success, mark it as an error + } else { + + error = statusText = statusText || "error"; + + // Set responseText if needed + if ( response ) { + jXHR.responseText = response; + } + } + + // Set data for the fake xhr object + jXHR.status = status; + jXHR.statusText = statusText; + + // Success/Error + if ( isSuccess ) { + deferred.fire( callbackContext , [ success , statusText , jXHR ] ); + } else { + deferred.fireReject( callbackContext , [ jXHR , statusText , error ] ); + } + + // Status-dependent callbacks + jXHR.statusCode( statusCode ); + + if ( s.global ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ) , + [ jXHR , s , isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fire( callbackContext, [ jXHR , statusText ] ); + + if ( s.global ) { + globalEventContext.trigger( "ajaxComplete" , [ jXHR , s] ); + // Handle the global AJAX counter + if ( ! --jQuery.active ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jXHR ); + jXHR.success = jXHR.done; + jXHR.error = jXHR.fail; + jXHR.complete = completeDeferred.done; + + // Status-dependent callbacks + jXHR.statusCode = function( map ) { + if ( map ) { + var resolved = jXHR.isResolved(), + tmp; + if ( resolved || jXHR.isRejected() ) { + tmp = map[ jXHR.status ]; + if ( tmp ) { + if ( map === statusCode ) { + delete statusCode[ jXHR.status ]; + } + jXHR[ resolved ? "done" : "fail" ]( tmp ); + } + } else { + for( tmp in map ) { + statusCode[ tmp ] = [ statusCode[ tmp ] , map[ tmp ] ]; + } + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + s.url = ( "" + s.url ).replace( rhash , "" ); + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = ! rnoContent.test( s.type ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( /\s+/ ); + + // Determine if a cross-domain request is in order + if ( ! s.crossDomain ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( + parts && + ( parts[ 1 ] && parts[ 1 ] != loc.protocol || + parts[ 2 ] != loc.hostname || + ( parts[ 3 ] || 80 ) != ( loc.port || 80 ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data != "string" ) { + s.data = jQuery.param( s.data , s.traditional ); + } + + // Get transport + transport = jQuery.ajaxPrefilter( s , options ).ajaxTransport( s ); + + // Watch for a new set of requests + if ( s.global && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // If no transport, we auto-abort + if ( ! transport ) { + + done( 0 , "transport not found" ); + jXHR = false; + + } else { + + // More options handling for requests with no content + if ( ! s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + } + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts , "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( (ret == s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : ""); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + requestHeaders[ "content-type" ] = s.contentType; + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery_lastModified[ s.url ] ) { + requestHeaders[ "if-modified-since" ] = jQuery_lastModified[ s.url ]; + } + if ( jQuery_etag[ s.url ] ) { + requestHeaders[ "if-none-match" ] = jQuery_etag[ s.url ]; + } + } + + // Set the Accepts header for the server, depending on the dataType + requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) : + s.accepts[ "*" ]; + + // Check for headers option + for ( i in s.headers ) { + requestHeaders[ i.toLowerCase() ] = s.headers[ i ]; + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext , jXHR , s ) === false || state === 2 ) ) { + + // Abort if not done already + done( 0 , "abort" ); + jXHR = false; + + } else { + + // Set state as sending + state = 1; + jXHR.readyState = 1; + + // Install callbacks on deferreds + for ( i in { success:1, error:1, complete:1 } ) { + jXHR[ i ]( s[ i ] ); + } + + // Send global event + if ( s.global ) { + globalEventContext.trigger( "ajaxSend" , [ jXHR , s ] ); + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout(function(){ + jXHR.abort( "timeout" ); + }, s.timeout); + } + + // Try to send + try { + transport.send(requestHeaders, done); + } catch (e) { + // Propagate exception as error if not done + if ( status === 1 ) { + + done(0, "error", "" + e); + jXHR = false; + + // Simply rethrow otherwise + } else { + jQuery.error(e); + } + } + } + } + + return jXHR; }, // Serialize an array of form elements or a set of @@ -296,19 +720,19 @@ jQuery.extend({ value = jQuery.isFunction(value) ? value() : value; s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value); }; - + // Set traditional to true for jQuery <= 1.3.2 behavior. if ( traditional === undefined ) { traditional = jQuery.ajaxSettings.traditional; } - + // If an array was passed in, assume that it is an array of form elements. if ( jQuery.isArray(a) || a.jquery ) { // Serialize the form elements jQuery.each( a, function() { add( this.name, this.value ); }); - + } else { // If traditional, encode the "old" way (the way 1.3.2 or older // did it), otherwise encode params recursively. @@ -341,9 +765,11 @@ function buildParams( prefix, obj, traditional, add ) { buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add ); } }); - + } else if ( !traditional && obj != null && typeof obj === "object" ) { - if ( jQuery.isEmptyObject( obj ) ) { + // If we see an array here, it is empty and should be treated as an empty + // object + if ( jQuery.isArray( obj ) || jQuery.isEmptyObject( obj ) ) { add( prefix, "" ); // Serialize object item. @@ -352,7 +778,7 @@ function buildParams( prefix, obj, traditional, add ) { buildParams( prefix + "[" + k + "]", v, traditional, add ); }); } - + } else { // Serialize scalar item. add( prefix, obj ); @@ -372,6 +798,172 @@ jQuery.extend({ }); +//Execute or select from functions in a given structure of options +function ajax_selectOrExecute( structure , s ) { + + var dataTypes = s.dataTypes, + transportDataType, + list, + selected, + i, + length, + checked = {}, + flag, + noSelect = structure !== "transports"; + + function initSearch( dataType ) { + + flag = transportDataType !== dataType && ! checked[ dataType ]; + + if ( flag ) { + + checked[ dataType ] = 1; + transportDataType = dataType; + list = s[ structure ][ dataType ]; + i = -1; + length = list ? list.length : 0 ; + } + + return flag; + } + + initSearch( dataTypes[ 0 ] ); + + for ( i = 0 ; ( noSelect || ! selected ) && i <= length ; i++ ) { + + if ( i === length ) { + + initSearch( "*" ); + + } else { + + selected = list[ i ]( s , determineDataType ); + + // If we got redirected to another dataType + // Search there (if not in progress or already tried) + if ( typeof( selected ) === "string" && + initSearch( selected ) ) { + + dataTypes.unshift( selected ); + selected = 0; + } + } + } + + return noSelect ? jQuery : selected; +} + +// Add an element to one of the structures in ajaxSettings +function ajax_addElement( structure , args ) { + + var i, + start = 0, + length = args.length, + dataTypes = [ "*" ], + dLength = 1, + dataType, + functors = [], + first, + append, + list; + + if ( length ) { + + first = jQuery.type( args[ 0 ] ); + + if ( first === "object" ) { + return ajax_selectOrExecute( structure , args[ 0 ] ); + } + + structure = jQuery.ajaxSettings[ structure ]; + + if ( first !== "function" ) { + + dataTypes = args[ 0 ].toLowerCase().split(/\s+/); + dLength = dataTypes.length; + start = 1; + + } + + if ( dLength && start < length ) { + + functors = sliceFunc.call( args , start ); + + for( i = 0 ; i < dLength ; i++ ) { + + dataType = dataTypes[ i ]; + + first = /^\+/.test( dataType ); + + if (first) { + dataType = dataType.substr(1); + } + + if ( dataType !== "" ) { + + append = Array.prototype[ first ? "unshift" : "push" ]; + list = structure[ dataType ] = structure[ dataType ] || []; + append.apply( list , functors ); + } + } + } + } + + return jQuery; +} + +// Install prefilter & transport methods +jQuery.each( [ "Prefilter" , "Transport" ] , function( _ , name ) { + _ = name.toLowerCase() + "s"; + jQuery[ "ajax" + name ] = function() { + return ajax_addElement( _ , arguments ); + }; +} ); + +// Utility function that handles dataType when response is received +// (for those transports that can give text or xml responses) +function determineDataType( s , ct , text , xml ) { + + var contents = s.contents, + type, + regexp, + dataTypes = s.dataTypes, + transportDataType = dataTypes[0], + response; + + // Auto (xml, json, script or text determined given headers) + if ( transportDataType === "*" ) { + + for ( type in contents ) { + if ( ( regexp = contents[ type ] ) && regexp.test( ct ) ) { + transportDataType = dataTypes[0] = type; + break; + } + } + } + + // xml and parsed as such + if ( transportDataType === "xml" && + xml && + xml.documentElement /* #4958 */ ) { + + response = xml; + + // Text response was provided + } else { + + response = text; + + // If it's not really text, defer to converters + if ( transportDataType !== "text" ) { + dataTypes.unshift( "text" ); + } + + } + + return response; +} + /* * Create the request object; Microsoft failed to properly * implement the XMLHttpRequest in IE7 (can't request local files), @@ -386,7 +978,7 @@ if ( window.ActiveXObject ) { return new window.XMLHttpRequest(); } catch( xhrError ) {} } - + try { return new window.ActiveXObject("Microsoft.XMLHTTP"); } catch( activeError ) {} diff --git a/src/ajax/jsonp.js b/src/ajax/jsonp.js new file mode 100644 index 000000000..1df5dd427 --- /dev/null +++ b/src/ajax/jsonp.js @@ -0,0 +1,83 @@ +(function( jQuery ) { + +var jsc = jQuery.now(), + jsre = /(\=)(?:\?|%3F)(&|$)|()(?:\?\?|%3F%3F)()/i, + rquery_jsonp = /\?/; + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + return "jsonp" + jsc++; + } + +// Normalize jsonp queries +// 1) put callback parameter in url or data +// 2) sneakily ensure transportDataType is always jsonp for jsonp requests +}).ajaxPrefilter("json jsonp", function(s, originalSettings) { + + if ( s.dataTypes[ 0 ] === "jsonp" || + originalSettings.jsonp || + originalSettings.jsonpCallback || + jsre.test(s.url) || + typeof(s.data) === "string" && jsre.test(s.data) ) { + + var jsonpCallback = s.jsonpCallback = + jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback, + url = s.url.replace(jsre, "$1" + jsonpCallback + "$2"), + data = s.url === url && typeof(s.data) === "string" ? s.data.replace(jsre, "$1" + jsonpCallback + "$2") : s.data; + + if ( url === s.url && data === s.data ) { + url += (rquery_jsonp.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback; + } + + s.url = url; + s.data = data; + s.dataTypes[ 0 ] = "jsonp"; + } + +// Bind transport to jsonp dataType +}).ajaxTransport("jsonp", function(s) { + + // Put callback in place + var responseContainer, + jsonpCallback = s.jsonpCallback, + previous = window[ jsonpCallback ]; + + window [ jsonpCallback ] = function( response ) { + responseContainer = [response]; + }; + + s.complete = [function() { + + // Set callback back to previous value + window[ jsonpCallback ] = previous; + + // Call if it was a function and we have a response + if ( previous) { + if ( responseContainer && jQuery.isFunction ( previous ) ) { + window[ jsonpCallback ] ( responseContainer[0] ); + } + } else { + // else, more memory leak avoidance + try{ delete window[ jsonpCallback ]; } catch(e){} + } + + }, s.complete ]; + + // Sneakily ensure this will be handled as json + s.dataTypes[ 0 ] = "json"; + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( ! responseContainer ) { + jQuery.error( jsonpCallback + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // Delegate to script transport + return "script"; +}); + +})( jQuery ); diff --git a/src/transports/script.js b/src/ajax/script.js similarity index 65% rename from src/transports/script.js rename to src/ajax/script.js index fe3873557..8e2e89ac5 100644 --- a/src/transports/script.js +++ b/src/ajax/script.js @@ -1,39 +1,38 @@ (function( jQuery ) { -// Install text to script executor -jQuery.extend( true, jQuery.ajaxSettings , { +// Install script dataType +jQuery.ajaxSetup({ accepts: { script: "text/javascript, application/javascript" }, - - autoDataType: { + + contents: { script: /javascript/ }, - - dataConverters: { - "text => script": jQuery.globalEval + + converters: { + "text script": jQuery.globalEval } -} ); // Bind script tag hack transport -jQuery.xhr.bindTransport("script", function(s) { - +}).ajaxTransport("script", function(s) { + // Handle cache special case if ( s.cache === undefined ) { s.cache = false; } - + // This transport only deals with cross domain get requests if ( s.crossDomain && s.async && ( s.type === "GET" || ! s.data ) ) { - + s.global = false; - + var script, head = document.getElementsByTagName("head")[0] || document.documentElement; - + return { - + send: function(_, callback) { script = document.createElement("script"); @@ -43,37 +42,39 @@ jQuery.xhr.bindTransport("script", function(s) { if ( s.scriptCharset ) { script.charset = s.scriptCharset; } - + script.src = s.url; - + // Attach handlers for all browsers - script.onload = script.onreadystatechange = function(statusText) { - - if ( (!script.readyState || - script.readyState === "loaded" || script.readyState === "complete") ) { - + script.onload = script.onreadystatechange = function( _ , isAbort ) { + + if ( ! script.readyState || /loaded|complete/.test( script.readyState ) ) { + // Handle memory leak in IE script.onload = script.onreadystatechange = null; - + // Remove the script if ( head && script.parentNode ) { head.removeChild( script ); } - - script = undefined; - - // Callback & dereference - callback(statusText ? 0 : 200, statusText || "success"); + + // Dereference the script + script = 0; + + // Callback if not abort + if ( ! isAbort ) { + callback( 200, "success" ); + } } }; // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709 and #4378). head.insertBefore( script, head.firstChild ); }, - - abort: function(statusText) { + + abort: function() { if ( script ) { - script.onload(statusText); + script.onload(0,1); } } }; diff --git a/src/transports/xhr.js b/src/ajax/xhr.js similarity index 58% rename from src/transports/xhr.js rename to src/ajax/xhr.js index 783ee4604..34aa832fe 100644 --- a/src/transports/xhr.js +++ b/src/ajax/xhr.js @@ -1,29 +1,47 @@ (function( jQuery ) { -var // Next fake timer id - xhrPollingId = jQuery.now(), - - // Callbacks hashtable +var // Next active xhr id + xhrId = jQuery.now(), + + // active xhrs xhrs = {}, - // #5280: see end of file - xhrUnloadAbortMarker = []; + // #5280: see below + xhrUnloadAbortInstalled; + + +jQuery.ajaxTransport( function( s , determineDataType ) { - -jQuery.xhr.bindTransport( function( s , determineDataType ) { - // Cross domain only allowed if supported through XMLHttpRequest if ( ! s.crossDomain || jQuery.support.cors ) { - + var callback; - + return { - + send: function(headers, complete) { - + + // #5280: we need to abort on unload or IE will keep connections alive + if ( ! xhrUnloadAbortInstalled ) { + + xhrUnloadAbortInstalled = 1; + + jQuery(window).bind( "unload" , function() { + + // Abort all pending requests + jQuery.each(xhrs, function(_, xhr) { + if ( xhr.onreadystatechange ) { + xhr.onreadystatechange( 1 ); + } + }); + + }); + } + + // Get a new xhr var xhr = s.xhr(), handle; - + // Open the socket // Passing null username, generates a login popup on Opera (#2865) if ( s.username ) { @@ -31,24 +49,24 @@ jQuery.xhr.bindTransport( function( s , determineDataType ) { } else { xhr.open(s.type, s.url, s.async); } - + // Requested-With header // Not set for crossDomain requests with no content // (see why at http://trac.dojotoolkit.org/ticket/9486) - // Won't change header if already provided in beforeSend + // Won't change header if already provided if ( ! ( s.crossDomain && ! s.hasContent ) && ! headers["x-requested-with"] ) { headers["x-requested-with"] = "XMLHttpRequest"; } - + // Need an extra try/catch for cross domain requests in Firefox 3 try { - + jQuery.each(headers, function(key,value) { xhr.setRequestHeader(key,value); }); - + } catch(_) {} - + // Do send the request try { xhr.send( ( s.hasContent && s.data ) || null ); @@ -56,55 +74,48 @@ jQuery.xhr.bindTransport( function( s , determineDataType ) { complete(0, "error", "" + e); return; } - + // Listener - callback = function ( abortStatusText ) { - + callback = function( _ , isAbort ) { + // Was never called and is aborted or complete - if ( callback && ( abortStatusText || xhr.readyState === 4 ) ) { - - // Do not listen anymore + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = 0; + + // Do not keep as active anymore + // and store back into pool if (handle) { xhr.onreadystatechange = jQuery.noop; delete xhrs[ handle ]; - handle = undefined; } - - callback = 0; - - // Get info - var status, statusText, response, responseHeaders; - - if ( abortStatusText ) { - + + // If it's an abort + if ( isAbort ) { + + // Abort it manually if needed if ( xhr.readyState !== 4 ) { xhr.abort(); } - - // Stop here if unloadAbort - if ( abortStatusText === xhrUnloadAbortMarker ) { - return; - } - - status = 0; - statusText = abortStatusText; - } else { - - status = xhr.status; - + + // Get info + var status = xhr.status, + statusText, + response, + responseHeaders = xhr.getAllResponseHeaders(); + try { // Firefox throws an exception when accessing statusText for faulty cross-domain requests - + statusText = xhr.statusText; - + } catch( e ) { - + statusText = ""; // We normalize with Webkit giving an empty statusText - + } - - responseHeaders = xhr.getAllResponseHeaders(); - + // Filter status for non standard behaviours // (so many they seem to be the actual "standard") status = @@ -129,63 +140,44 @@ jQuery.xhr.bindTransport( function( s , determineDataType ) { : status ); - - // Guess response if needed & update datatype accordingly - if ( status >= 200 && status < 300 ) { - response = - determineDataType( - s, - xhr.getResponseHeader("content-type"), - xhr.responseText, - xhr.responseXML ); - } + + // Guess response & update dataType accordingly + response = + determineDataType( + s, + xhr.getResponseHeader("content-type"), + xhr.responseText, + xhr.responseXML ); + + // Call complete + complete(status,statusText,response,responseHeaders); } - - // Call complete - complete(status,statusText,response,responseHeaders); } }; - + // if we're in sync mode // or it's in cache and has been retrieved directly (IE6 & IE7) // we need to manually fire the callback if ( ! s.async || xhr.readyState === 4 ) { - + callback(); - + } else { - - // Listener is externalized to handle abort on unload - handle = xhrPollingId++; + + // Add to list of active xhrs + handle = xhrId++; xhrs[ handle ] = xhr; - xhr.onreadystatechange = function() { - callback(); - }; - } + xhr.onreadystatechange = callback; + } }, - - abort: function(statusText) { + + abort: function() { if ( callback ) { - callback(statusText); + callback(0,1); } } }; } }); -// #5280: we need to abort on unload or IE will keep connections alive -jQuery(window).bind( "unload" , function() { - - // Abort all pending requests - jQuery.each(xhrs, function(_, xhr) { - if ( xhr.onreadystatechange ) { - xhr.onreadystatechange( xhrUnloadAbortMarker ); - } - }); - - // Resest polling structure to be safe - xhrs = {}; - -}); - })( jQuery ); diff --git a/src/attributes.js b/src/attributes.js index 78b1bfd20..fec132340 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -182,7 +182,7 @@ jQuery.fn.extend({ var option = options[ i ]; // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { // Get the specific value for the option diff --git a/src/core.js b/src/core.js index 346e52d70..4361577e2 100644 --- a/src/core.js +++ b/src/core.js @@ -56,12 +56,12 @@ var jQuery = function( selector, context ) { // For matching the engine and version of the browser browserMatch, - + // Has the ready events already been bound? readyBound = false, - - // The functions to execute on DOM ready - readyList = [], + + // The deferred used on DOM ready + readyList, // The ready event handler DOMContentLoaded, @@ -73,7 +73,7 @@ var jQuery = function( selector, context ) { slice = Array.prototype.slice, trim = String.prototype.trim, indexOf = Array.prototype.indexOf, - + // [[Class]] -> type pairs class2type = {}; @@ -92,7 +92,7 @@ jQuery.fn = jQuery.prototype = { this.length = 1; return this; } - + // The body element only exists once, optimize finding it if ( selector === "body" && !context && document.body ) { this.context = document; @@ -129,11 +129,11 @@ jQuery.fn = jQuery.prototype = { } else { ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; + selector = (ret.cacheable ? jQuery(ret.fragment).clone()[0] : ret.fragment).childNodes; } - + return jQuery.merge( this, selector ); - + // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); @@ -226,7 +226,7 @@ jQuery.fn = jQuery.prototype = { if ( jQuery.isArray( elems ) ) { push.apply( ret, elems ); - + } else { jQuery.merge( ret, elems ); } @@ -252,25 +252,15 @@ jQuery.fn = jQuery.prototype = { each: function( callback, args ) { return jQuery.each( this, callback, args ); }, - - ready: function( fn ) { + + ready: function() { // Attach the listeners jQuery.bindReady(); - // If the DOM is already ready - if ( jQuery.isReady ) { - // Execute the function immediately - fn.call( document, jQuery ); - - // Otherwise, remember the function for later - } else if ( readyList ) { - // Add the function to the wait list - readyList.push( fn ); - } - - return this; + // Change ready & apply + return ( jQuery.fn.ready = readyList.done ).apply( this , arguments ); }, - + eq: function( i ) { return i === -1 ? this.slice( i ) : @@ -295,7 +285,7 @@ jQuery.fn = jQuery.prototype = { return callback.call( elem, i, elem ); })); }, - + end: function() { return this.prevObject || jQuery(null); }, @@ -384,14 +374,14 @@ jQuery.extend({ return jQuery; }, - + // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, - + // Handle when the DOM is ready ready: function( wait ) { // A third-party is pushing the ready event forwards @@ -415,27 +405,15 @@ jQuery.extend({ } // If there are functions bound, to execute - if ( readyList ) { - // Execute all of them - var fn, - i = 0, - ready = readyList; + readyList.fire( document , [ jQuery ] ); - // Reset the list of functions - readyList = null; - - while ( (fn = ready[ i++ ]) ) { - fn.call( document, jQuery ); - } - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger( "ready" ).unbind( "ready" ); - } + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); } } }, - + bindReady: function() { if ( readyBound ) { return; @@ -454,7 +432,7 @@ jQuery.extend({ if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - + // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); @@ -463,7 +441,7 @@ jQuery.extend({ // ensure firing before onload, // maybe late but safe also for iframes document.attachEvent("onreadystatechange", DOMContentLoaded); - + // A fallback to window.onload, that will always work window.attachEvent( "onload", jQuery.ready ); @@ -514,20 +492,20 @@ jQuery.extend({ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } - + // Not own constructor property must be Object if ( obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } - + // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. - + var key; for ( key in obj ) {} - + return key === undefined || hasOwn.call( obj, key ); }, @@ -537,11 +515,11 @@ jQuery.extend({ } return true; }, - + error: function( msg ) { throw msg; }, - + parseJSON: function( data ) { if ( typeof data !== "string" || !data ) { return null; @@ -549,7 +527,7 @@ jQuery.extend({ // Make sure leading/trailing whitespace is removed (IE can't handle it) data = jQuery.trim( data ); - + // Make sure the incoming data is actual JSON // Logic borrowed from http://json.org/json2.js if ( rvalidchars.test(data.replace(rvalidescape, "@") @@ -566,6 +544,28 @@ jQuery.extend({ } }, + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, + noop: function() {}, // Evalulates a script in a global context @@ -691,7 +691,7 @@ jQuery.extend({ for ( var l = second.length; j < l; j++ ) { first[ i++ ] = second[ j ]; } - + } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; @@ -772,7 +772,7 @@ jQuery.extend({ // The value/s can be optionally by executed if its a function access: function( elems, key, value, exec, fn, pass ) { var length = elems.length; - + // Setting many attributes if ( typeof key === "object" ) { for ( var k in key ) { @@ -780,19 +780,19 @@ jQuery.extend({ } return elems; } - + // Setting one attribute if ( value !== undefined ) { // Optionally, function values get executed if exec is true exec = !pass && exec && jQuery.isFunction(value); - + for ( var i = 0; i < length; i++ ) { fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); } - + return elems; } - + // Getting an attribute return length ? fn( elems[0], key ) : undefined; }, @@ -801,6 +801,151 @@ jQuery.extend({ return (new Date()).getTime(); }, + // Create a simple deferred (one callbacks list) + _Deferred: function() { + + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function () { + + if ( ! cancelled ) { + + var args = arguments, + i, + length, + elem, + type, + _fired; + + if ( fired ) { + _fired = fired; + fired = 0; + } + + for ( i = 0, length = args.length ; i < length ; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred , elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + + if ( _fired ) { + deferred.fire( _fired[ 0 ] , _fired[ 1 ] ); + } + } + + return this; + }, + + // resolve with given context and args + fire: function( context , args ) { + if ( ! cancelled && ! fired && ! firing ) { + + firing = 1; + + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context , args ); + } + } + finally { + fired = [ context , args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.fire( jQuery.isFunction( this.promise ) ? this.promise() : this , arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + // Typical success/error system + Deferred: function( func ) { + + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(); + + // Add errorDeferred methods and redefine cancel + jQuery.extend( deferred , { + + then: function( doneCallbacks , failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + fail: failDeferred.done, + fireReject: failDeferred.fire, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + obj = obj || {}; + jQuery.each( "then done fail isResolved isRejected".split( " " ) , function( _ , method ) { + obj[ method ] = deferred[ method ]; + }); + obj.promise = function() { + return obj; + }; + return obj; + } + + } ); + + // Make sure only one callback list will be used + deferred.then( failDeferred.cancel , deferred.cancel ); + + // Unexpose cancel + delete deferred.cancel; + + // Call given func if any + if ( func ) { + func.call( deferred , deferred ); + } + + return deferred; + }, + + // Deferred helper + when: function( object ) { + object = object && jQuery.isFunction( object.promise ) ? + object : + jQuery.Deferred().resolve( object ); + return object.promise(); + }, + // Use of jQuery.browser is frowned upon. // More details: http://docs.jquery.com/Utilities/jQuery.browser uaMatch: function( ua ) { @@ -818,6 +963,9 @@ jQuery.extend({ browser: {} }); +// Create readyList deferred +readyList = jQuery._Deferred(); + // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); diff --git a/src/css.js b/src/css.js index 8a83c6072..a6e2bb614 100644 --- a/src/css.js +++ b/src/css.js @@ -12,9 +12,6 @@ var ralpha = /alpha\([^)]*\)/i, cssHeight = [ "Top", "Bottom" ], curCSS, - getComputedStyle, - currentStyle, - fcamelCase = function( all, letter ) { return letter.toUpperCase(); }; @@ -172,10 +169,6 @@ jQuery.each(["height", "width"], function( i, name ) { if ( val <= 0 ) { val = curCSS( elem, name, name ); - if ( val === "0px" && currentStyle ) { - val = currentStyle( elem, name, name ); - } - if ( val != null ) { // Should return "auto" instead of 0, use 0 for // temporary backwards-compat @@ -241,7 +234,7 @@ if ( !jQuery.support.opacity ) { } if ( document.defaultView && document.defaultView.getComputedStyle ) { - getComputedStyle = function( elem, newName, name ) { + curCSS = function( elem, newName, name ) { var ret, defaultView, computedStyle; name = name.replace( rupper, "-$1" ).toLowerCase(); @@ -259,10 +252,8 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) { return ret; }; -} - -if ( document.documentElement.currentStyle ) { - currentStyle = function( elem, name ) { +} else if ( document.documentElement.currentStyle ) { + curCSS = function( elem, name ) { var left, rsLeft, ret = elem.currentStyle && elem.currentStyle[ name ], style = elem.style; @@ -291,8 +282,6 @@ if ( document.documentElement.currentStyle ) { }; } -curCSS = getComputedStyle || currentStyle; - function getWH( elem, name, extra ) { var which = name === "width" ? cssWidth : cssHeight, val = name === "width" ? elem.offsetWidth : elem.offsetHeight; diff --git a/src/data.js b/src/data.js index f1e031fff..4d1d1bd55 100644 --- a/src/data.js +++ b/src/data.js @@ -9,8 +9,9 @@ jQuery.extend({ // Please use with caution uuid: 0, - // Unique for each copy of jQuery on the page - expando: "jQuery" + jQuery.now(), + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), // The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. @@ -21,6 +22,14 @@ jQuery.extend({ "applet": true }, + hasData: function( elem ) { + if ( elem.nodeType ) { + elem = jQuery.cache[ elem[jQuery.expando] ]; + } + + return !!elem && !jQuery.isEmptyObject(elem); + }, + data: function( elem, name, data ) { if ( !jQuery.acceptData( elem ) ) { return; @@ -144,7 +153,7 @@ jQuery.fn.extend({ var attr = this[0].attributes, name; for ( var i = 0, l = attr.length; i < l; i++ ) { name = attr[i].name; - + if ( name.indexOf( "data-" ) === 0 ) { name = name.substr( 5 ); dataAttr( this[0], name, data[ name ] ); diff --git a/src/dimensions.js b/src/dimensions.js index b4dae83c4..a292899b9 100644 --- a/src/dimensions.js +++ b/src/dimensions.js @@ -25,7 +25,7 @@ jQuery.each([ "Height", "Width" ], function( i, name ) { if ( !elem ) { return size == null ? null : this; } - + if ( jQuery.isFunction( size ) ) { return this.each(function( i ) { var self = jQuery( this ); diff --git a/src/effects.js b/src/effects.js index 600707427..bd57ffc3d 100644 --- a/src/effects.js +++ b/src/effects.js @@ -61,7 +61,7 @@ jQuery.fn.extend({ } else { for ( var i = 0, j = this.length; i < j; i++ ) { var display = jQuery.css( this[i], "display" ); - + if ( display !== "none" && !jQuery.data( this[i], "olddisplay" ) ) { jQuery.data( this[i], "olddisplay", display ); } @@ -337,7 +337,7 @@ jQuery.fx.prototype = { } var r = parseFloat( jQuery.css( this.elem, this.prop ) ); - return r && r > -10000 ? r : 0; + return r || 0; }, // Start an animation from one number to another diff --git a/src/event.js b/src/event.js index fd470e718..675e5fff3 100644 --- a/src/event.js +++ b/src/event.js @@ -63,7 +63,7 @@ jQuery.event = { var eventKey = elem.nodeType ? "events" : "__events__", events = elemData[ eventKey ], eventHandle = elemData.handle; - + if ( typeof events === "function" ) { // On plain objects events is a fn that holds the the data // which prevents this data from being JSON serialized @@ -143,9 +143,9 @@ jQuery.event = { } } } - - if ( special.add ) { - special.add.call( elem, handleObj ); + + if ( special.add ) { + special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; @@ -184,7 +184,7 @@ jQuery.event = { if ( !elemData || !events ) { return; } - + if ( typeof events === "function" ) { elemData = events; events = events.events; @@ -222,7 +222,7 @@ jQuery.event = { namespaces = type.split("."); type = namespaces.shift(); - namespace = new RegExp("(^|\\.)" + + namespace = new RegExp("(^|\\.)" + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); } @@ -384,7 +384,7 @@ jQuery.event = { isClick = jQuery.nodeName( target, "a" ) && targetType === "click", special = jQuery.event.special[ targetType ] || {}; - if ( (!special._default || special._default.call( elem, event ) === false) && + if ( (!special._default || special._default.call( elem, event ) === false) && !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { try { @@ -454,7 +454,7 @@ jQuery.event = { event.handler = handleObj.handler; event.data = handleObj.data; event.handleObj = handleObj; - + var ret = handleObj.handler.apply( this, args ); if ( ret !== undefined ) { @@ -553,7 +553,7 @@ jQuery.event = { add: function( handleObj ) { jQuery.event.add( this, liveConvert( handleObj.origType, handleObj.selector ), - jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); }, remove: function( handleObj ) { @@ -583,7 +583,7 @@ jQuery.removeEvent = document.removeEventListener ? if ( elem.removeEventListener ) { elem.removeEventListener( type, handle, false ); } - } : + } : function( elem, type, handle ) { if ( elem.detachEvent ) { elem.detachEvent( "on" + type, handle ); @@ -600,6 +600,12 @@ jQuery.Event = function( src ) { if ( src && src.type ) { this.originalEvent = src; this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + // Event type } else { this.type = src; @@ -630,7 +636,7 @@ jQuery.Event.prototype = { if ( !e ) { return; } - + // if preventDefault exists run it on the original event if ( e.preventDefault ) { e.preventDefault(); @@ -726,7 +732,7 @@ if ( !jQuery.support.submitBubbles ) { return trigger( "submit", this, arguments ); } }); - + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { var elem = e.target, type = elem.type; @@ -788,7 +794,7 @@ if ( !jQuery.support.changeBubbles ) { if ( e.type !== "focusout" || elem.type !== "radio" ) { jQuery.data( elem, "_change_data", val ); } - + if ( data === undefined || val === data ) { return; } @@ -802,7 +808,7 @@ if ( !jQuery.support.changeBubbles ) { jQuery.event.special.change = { filters: { - focusout: testChange, + focusout: testChange, beforedeactivate: testChange, @@ -873,15 +879,15 @@ if ( document.addEventListener ) { if ( focusCounts[fix]++ === 0 ) { document.addEventListener( orig, handler, true ); } - }, - teardown: function() { + }, + teardown: function() { if ( --focusCounts[fix] === 0 ) { document.removeEventListener( orig, handler, true ); } } }; - function handler( e ) { + function handler( e ) { e = jQuery.event.fix( e ); e.type = fix; return jQuery.event.trigger( e, null, e.target ); @@ -898,7 +904,7 @@ jQuery.each(["bind", "one"], function( i, name ) { } return this; } - + if ( jQuery.isFunction( data ) || data === false ) { fn = data; data = undefined; @@ -938,20 +944,20 @@ jQuery.fn.extend({ return this; }, - + delegate: function( selector, types, data, fn ) { return this.live( types, data, fn, selector ); }, - + undelegate: function( selector, types, fn ) { if ( arguments.length === 0 ) { return this.unbind( "live" ); - + } else { return this.die( types, null, fn, selector ); } }, - + trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); @@ -1008,12 +1014,12 @@ jQuery.each(["live", "die"], function( i, name ) { var type, i = 0, match, namespaces, preType, selector = origSelector || this.selector, context = origSelector ? this : jQuery( this.context ); - + if ( typeof types === "object" && !types.preventDefault ) { for ( var key in types ) { context[ name ]( key, data, types[key], selector ); } - + return this; } @@ -1060,7 +1066,7 @@ jQuery.each(["live", "die"], function( i, name ) { context.unbind( "live." + liveConvert( type, selector ), fn ); } } - + return this; }; }); @@ -1079,7 +1085,7 @@ function liveHandler( event ) { if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { return; } - + if ( event.namespace ) { namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); } @@ -1177,21 +1183,4 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl } }); -// Prevent memory leaks in IE -// Window isn't included so as not to unbind existing unload events -// More info: -// - http://isaacschlueter.com/2006/10/msie-memory-leaks/ -if ( window.attachEvent && !window.addEventListener ) { - jQuery(window).bind("unload", function() { - for ( var id in jQuery.cache ) { - if ( jQuery.cache[ id ].handle ) { - // Try/Catch is to handle iframes being unloaded, see #4280 - try { - jQuery.event.remove( jQuery.cache[ id ].handle.elem ); - } catch(e) {} - } - } - }); -} - })( jQuery ); diff --git a/src/intro.js b/src/intro.js index cb15705a6..a75f3112f 100644 --- a/src/intro.js +++ b/src/intro.js @@ -11,7 +11,7 @@ * Copyright 2010, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * - * Date: + * Date: @DATE */ (function( window, undefined ) { diff --git a/src/manipulation.js b/src/manipulation.js index 7dea3493c..cf533c818 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -370,14 +370,18 @@ function root( elem, cur ) { } function cloneCopyEvent(orig, ret) { - var i = 0; - - ret.each(function() { - if ( this.nodeType !== 1 || this.nodeName !== (orig[i] && orig[i].nodeName) ) { + ret.each(function (nodeIndex) { + if ( this.nodeType !== 1 || !jQuery.hasData(orig[nodeIndex]) ) { return; } - var oldData = jQuery.data( orig[i++] ), + // XXX remove for 1.5 RC or merge back in if there is actually a reason for this check that has been + // unexposed by unit tests + if ( this.nodeName !== (orig[nodeIndex] && orig[nodeIndex].nodeName) ) { + throw "Cloned data mismatch"; + } + + var oldData = jQuery.data( orig[nodeIndex] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events; @@ -386,8 +390,8 @@ function cloneCopyEvent(orig, ret) { curData.events = {}; for ( var type in events ) { - for ( var handler in events[ type ] ) { - jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + for ( var i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( this, type, events[ type ][ i ], events[ type ][ i ].data ); } } } @@ -416,15 +420,29 @@ function cloneFixAttributes(src, dest) { if ( nodeName === "object" ) { dest.outerHTML = src.outerHTML; - // IE6-8 fails to persist the checked state of a cloned checkbox - // or radio button - } else if ( nodeName === "input" && src.checked ) { - dest.defaultChecked = dest.checked = src.checked; + } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + if ( src.checked ) { + dest.defaultChecked = dest.checked = src.checked; + } + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } // IE6-8 fails to return the selected option to the default selected // state when cloning options } else if ( nodeName === "option" ) { dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; } // Event data gets referenced instead of copied if the expando @@ -436,12 +454,12 @@ jQuery.buildFragment = function( args, nodes, scripts ) { var fragment, cacheable, cacheresults, doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document); - // Only cache "small" (1/2 KB) strings that are associated with the main document + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document // Cloning options loses the selected state, so don't cache them // IE 6 doesn't like it when you put or elements in a fragment // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document && - !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) { + args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) { cacheable = true; cacheresults = jQuery.fragments[ args[0] ]; @@ -613,6 +631,11 @@ jQuery.extend({ jQuery.removeEvent( elem, type, data.handle ); } } + + // Null the DOM reference to avoid IE6/7/8 leak (#7054) + if ( data.handle ) { + data.handle.elem = null; + } } if ( deleteExpando ) { diff --git a/src/offset.js b/src/offset.js index 3fb2917b2..2040c9d83 100644 --- a/src/offset.js +++ b/src/offset.js @@ -7,7 +7,7 @@ if ( "getBoundingClientRect" in document.documentElement ) { jQuery.fn.offset = function( options ) { var elem = this[0], box; - if ( options ) { + if ( options ) { return this.each(function( i ) { jQuery.offset.setOffset( this, options, i ); }); @@ -49,7 +49,7 @@ if ( "getBoundingClientRect" in document.documentElement ) { jQuery.fn.offset = function( options ) { var elem = this[0]; - if ( options ) { + if ( options ) { return this.each(function( i ) { jQuery.offset.setOffset( this, options, i ); }); @@ -168,7 +168,7 @@ jQuery.offset = { return { top: top, left: left }; }, - + setOffset: function( elem, options, i ) { var position = jQuery.css( elem, "position" ); @@ -202,7 +202,7 @@ jQuery.offset = { if (options.left != null) { props.left = (options.left - curOffset.left) + curLeft; } - + if ( "using" in options ) { options.using.call( elem, props ); } else { @@ -262,7 +262,7 @@ jQuery.each( ["Left", "Top"], function( i, name ) { jQuery.fn[ method ] = function(val) { var elem = this[0], win; - + if ( !elem ) { return null; } diff --git a/src/support.js b/src/support.js index 67b41c4a7..e4c3ea916 100644 --- a/src/support.js +++ b/src/support.js @@ -147,7 +147,7 @@ jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2; } - div.innerHTML = "
t
"; + div.innerHTML = "
t
"; var tds = div.getElementsByTagName("td"); // Check if table cells still have offsetWidth/Height when they are set diff --git a/src/transports/jsonp.js b/src/transports/jsonp.js deleted file mode 100644 index d9e77f2f3..000000000 --- a/src/transports/jsonp.js +++ /dev/null @@ -1,89 +0,0 @@ -(function( jQuery ) { - -var jsc = jQuery.now(), - jsre = /\=\?(&|$)/, - rquery_jsonp = /\?/; - -// Default jsonp callback name -jQuery.ajaxSettings.jsonpCallback = function() { - return "jsonp" + jsc++; -}; - -// Normalize jsonp queries -// 1) put callback parameter in url or data -// 2) ensure transportDataType is json -// 3) ensure options jsonp is always provided so that jsonp requests are always -// json request with the jsonp option set -jQuery.xhr.prefilter( function(s) { - - var transportDataType = s.dataTypes[0]; - - if ( s.jsonp || - transportDataType === "jsonp" || - transportDataType === "json" && ( jsre.test(s.url) || typeof(s.data) === "string" && jsre.test(s.data) ) ) { - - var jsonp = s.jsonp = s.jsonp || "callback", - jsonpCallback = s.jsonpCallback = - jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback, - url = s.url.replace(jsre, "=" + jsonpCallback + "$1"), - data = s.url == url && typeof(s.data) === "string" ? s.data.replace(jsre, "=" + jsonpCallback + "$1") : s.data; - - if ( url == s.url && data == s.data ) { - url = url += (rquery_jsonp.test( url ) ? "&" : "?") + jsonp + "=" + jsonpCallback; - } - - s.url = url; - s.data = data; - - s.dataTypes[0] = "json"; - } - -}); - -// Bind transport to json dataType -jQuery.xhr.bindTransport("json", function(s) { - - if ( s.jsonp ) { - - // Put callback in place - var responseContainer, - jsonpCallback = s.jsonpCallback, - previous = window[ jsonpCallback ]; - - window [ jsonpCallback ] = function( response ) { - responseContainer = [response]; - }; - - s.complete = [function() { - - // Set callback back to previous value - window[ jsonpCallback ] = previous; - - // Call if it was a function and we have a response - if ( previous) { - if ( responseContainer && jQuery.isFunction ( previous ) ) { - window[ jsonpCallback ] ( responseContainer[0] ); - } - } else { - // else, more memory leak avoidance - try{ delete window[ jsonpCallback ]; } catch(e){} - } - - }, s.complete ]; - - // Use data converter to retrieve json after script execution - s.dataConverters["script => json"] = function() { - if ( ! responseContainer ) { - jQuery.error("Callback '" + jsonpCallback + "' was not called"); - } - return responseContainer[ 0 ]; - }; - - // Delegate to script transport - return "script"; - - } - -}); - -})( jQuery ); diff --git a/src/traversing.js b/src/traversing.js index 15446bd8b..689e90196 100644 --- a/src/traversing.js +++ b/src/traversing.js @@ -51,7 +51,7 @@ jQuery.fn.extend({ filter: function( selector ) { return this.pushStack( winnow(this, selector, true), "filter", selector ); }, - + is: function( selector ) { return !!selector && jQuery.filter( selector, this ).length > 0; }, @@ -69,7 +69,7 @@ jQuery.fn.extend({ selector = selectors[i]; if ( !matches[selector] ) { - matches[selector] = jQuery.expr.match.POS.test( selector ) ? + matches[selector] = jQuery.expr.match.POS.test( selector ) ? jQuery( selector, context || this.context ) : selector; } @@ -92,7 +92,7 @@ jQuery.fn.extend({ return ret; } - var pos = POS.test( selectors ) ? + var pos = POS.test( selectors ) ? jQuery( selectors, context || this.context ) : null; for ( i = 0, l = this.length; i < l; i++ ) { @@ -113,10 +113,10 @@ jQuery.fn.extend({ } ret = ret.length > 1 ? jQuery.unique(ret) : ret; - + return this.pushStack( ret, "closest", selectors ); }, - + // Determine the position of an element within // the matched set of elements index: function( elem ) { @@ -197,7 +197,7 @@ jQuery.each({ }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var ret = jQuery.map( this, fn, until ); - + if ( !runtil.test( name ) ) { selector = until; } @@ -226,7 +226,7 @@ jQuery.extend({ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : jQuery.find.matches(expr, elems); }, - + dir: function( elem, dir, until ) { var matched = [], cur = elem[ dir ]; diff --git a/src/xhr.js b/src/xhr.js deleted file mode 100644 index 57903e046..000000000 --- a/src/xhr.js +++ /dev/null @@ -1,909 +0,0 @@ -(function( jQuery ) { - -var rquery_xhr = /\?/, - rhash = /#.*$/, - rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL - rnoContent = /^(?:GET|HEAD)$/, - rts = /([?&])_=[^&]*/, - rurl = /^(\w+:)?\/\/([^\/?#]+)/, - - sliceFunc = Array.prototype.slice, - - isFunction = jQuery.isFunction; - -// Creates a jQuery xhr object -jQuery.xhr = function( _native ) { - - if ( _native ) { - return jQuery.ajaxSettings.xhr(); - } - - function reset(force) { - - // We only need to reset if we went through the init phase - // (with the exception of object creation) - if ( force || internal ) { - - // Reset callbacks lists - callbacksLists = { - success: createCBList(), - error: createCBList(), - complete: createCBList() - }; - - // Reset private variables - requestHeaders = {}; - responseHeadersString = responseHeaders = internal = done = timeoutTimer = s = undefined; - - // Reset state - xhr.readyState = 0; - sendFlag = 0; - - // Remove responseX fields - for ( var name in xhr ) { - if ( /^response/.test(name) ) { - delete xhr[name]; - } - } - } - } - - function init() { - - var // Options extraction - - // Remove hash character (#7531: first for string promotion) - url = s.url = ( "" + s.url ).replace( rhash , "" ), - - // Uppercase the type - type = s.type = s.type.toUpperCase(), - - // Determine if request has content - hasContent = s.hasContent = ! rnoContent.test( type ), - - // Extract dataTypes list - dataType = s.dataType, - dataTypes = s.dataTypes = dataType ? jQuery.trim(dataType).toLowerCase().split(/\s+/) : ["*"], - - // Determine if a cross-domain request is in order - parts = rurl.exec( url.toLowerCase() ), - loc = location, - crossDomain = s.crossDomain = !!( parts && ( parts[1] && parts[1] != loc.protocol || parts[2] != loc.host ) ), - - // Get other options locally - data = s.data, - originalContentType = s.contentType, - prefilters = s.prefilters, - accepts = s.accepts, - headers = s.headers, - - // Other Variables - transportDataType, - i; - - // Convert data if not already a string - if ( data && s.processData && typeof data != "string" ) { - data = s.data = jQuery.param( data , s.traditional ); - } - - // Apply option prefilters - for (i in prefilters) { - prefilters[i](s); - } - - // Get internal - internal = selectTransport( s ); - - // Re-actualize url & data - url = s.url; - data = s.data; - - // If internal was found - if ( internal ) { - - // Get transportDataType - transportDataType = dataTypes[0]; - - // More options handling for requests with no content - if ( ! hasContent ) { - - // If data is available, append data to url - if ( data ) { - url += (rquery_xhr.test(url) ? "&" : "?") + data; - } - - // Add anti-cache in url if needed - if ( s.cache === false ) { - - var ts = jQuery.now(), - // try replacing _= if it is there - ret = url.replace(rts, "$1_=" + ts ); - - // if nothing was replaced, add timestamp to the end - url = ret + ((ret == url) ? (rquery_xhr.test(url) ? "&" : "?") + "_=" + ts : ""); - } - - s.url = url; - } - - // Set the correct header, if data is being sent - if ( ( data && hasContent ) || originalContentType ) { - requestHeaders["content-type"] = s.contentType; - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery_lastModified[url] ) { - requestHeaders["if-modified-since"] = jQuery_lastModified[url]; - } - if ( jQuery_etag[url] ) { - requestHeaders["if-none-match"] = jQuery_etag[url]; - } - } - - // Set the Accepts header for the server, depending on the dataType - requestHeaders.accept = transportDataType && accepts[ transportDataType ] ? - accepts[ transportDataType ] + ( transportDataType !== "*" ? ", */*; q=0.01" : "" ) : - accepts[ "*" ]; - - // Check for headers option - for ( i in headers ) { - requestHeaders[ i.toLowerCase() ] = headers[ i ]; - } - } - - callbackContext = s.context || s; - globalEventContext = s.context ? jQuery(s.context) : jQuery.event; - - for ( i in callbacksLists ) { - callbacksLists[i].bind(s[i]); - } - - // Watch for a new set of requests - if ( s.global && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - done = whenDone; - } - - function whenDone(status, statusText, response, headers) { - - // Called once - done = undefined; - - // Reset sendFlag - sendFlag = 0; - - // Cache response headers - responseHeadersString = headers || ""; - - // Clear timeout if it exists - if ( timeoutTimer ) { - clearTimeout(timeoutTimer); - } - - var // Reference url - url = s.url, - // and ifModified status - ifModified = s.ifModified, - - // Is it a success? - isSuccess = 0, - // Stored success - success, - // Stored error - error = statusText; - - // If not timeout, force a jQuery-compliant status text - if ( statusText != "timeout" ) { - statusText = ( status >= 200 && status < 300 ) ? - "success" : - ( status === 304 ? "notmodified" : "error" ); - } - - // If successful, handle type chaining - if ( statusText === "success" || statusText === "notmodified" ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( ifModified ) { - var lastModified = xhr.getResponseHeader("Last-Modified"), - etag = xhr.getResponseHeader("Etag"); - - if (lastModified) { - jQuery_lastModified[url] = lastModified; - } - if (etag) { - jQuery_etag[url] = etag; - } - } - - if ( ifModified && statusText === "notmodified" ) { - - success = null; - isSuccess = 1; - - } else { - // Chain data conversions and determine the final value - // (if an exception is thrown in the process, it'll be notified as an error) - try { - - function checkData(data) { - if ( data !== undefined ) { - var testFunction = s.dataCheckers[srcDataType]; - if ( isFunction( testFunction ) ) { - testFunction(data); - } - } - } - - function convertData (data) { - var conversionFunction = dataConverters[srcDataType+" => "+destDataType] || - dataConverters["* => "+destDataType], - noFunction = ! isFunction( conversionFunction ); - if ( noFunction ) { - if ( srcDataType != "text" && destDataType != "text" ) { - // We try to put text inbetween - var first = dataConverters[srcDataType+" => text"] || - dataConverters["* => text"], - second = dataConverters["text => "+destDataType] || - dataConverters["* => "+destDataType], - areFunctions = isFunction( first ) && isFunction( second ); - if ( areFunctions ) { - conversionFunction = function (data) { - return second( first ( data ) ); - }; - } - noFunction = ! areFunctions; - } - if ( noFunction ) { - jQuery.error( "no data converter between " + srcDataType + " and " + destDataType ); - } - - } - return conversionFunction(data); - } - - var dataTypes = s.dataTypes, - i, - length, - data = response, - dataConverters = s.dataConverters, - srcDataType, - destDataType, - responseTypes = s.xhrResponseFields; - - for ( i = 0, length = dataTypes.length ; i < length ; i++ ) { - - destDataType = dataTypes[i]; - - if ( !srcDataType ) { // First time - - // Copy type - srcDataType = destDataType; - // Check - checkData(data); - // Apply dataFilter - if ( isFunction( s.dataFilter ) ) { - data = s.dataFilter(data, s.dataType); - // Recheck data - checkData(data); - } - - } else { // Subsequent times - - // handle auto - // JULIAN: for reasons unknown to me === doesn't work here - if (destDataType == "*") { - - destDataType = srcDataType; - - } else if ( srcDataType != destDataType ) { - - // Convert - data = convertData(data); - // Copy type & check - srcDataType = destDataType; - checkData(data); - - } - - } - - // Copy response into the xhr if it hasn't been already - var responseDataType, - responseType = responseTypes[srcDataType]; - - if ( responseType ) { - - responseDataType = srcDataType; - - } else { - - responseType = responseTypes[ responseDataType = "text" ]; - - } - - if ( responseType !== 1 ) { - xhr[ "response" + responseType ] = data; - responseTypes[ responseType ] = 1; - } - - } - - // We have a real success - success = data; - isSuccess = 1; - - } catch(e) { - - statusText = "parsererror"; - error = "" + e; - - } - } - - } else { // if not success, mark it as an error - - error = error || statusText; - - } - - // Set data for the fake xhr object - xhr.status = status; - xhr.statusText = statusText; - - // Keep local copies of vars in case callbacks re-use the xhr - var _s = s, - _callbacksLists = callbacksLists, - _callbackContext = callbackContext, - _globalEventContext = globalEventContext; - - // Set state if the xhr hasn't been re-used - function _setState( value ) { - if ( xhr.readyState && s === _s ) { - setState( value ); - } - } - - // Really completed? - if ( status && s.async ) { - setState( 2 ); - _setState( 3 ); - } - - // We're done - _setState( 4 ); - - // Success - _callbacksLists.success.fire( isSuccess , _callbackContext , success, statusText, xhr); - if ( isSuccess && _s.global ) { - _globalEventContext.trigger( "ajaxSuccess", [xhr, _s, success] ); - } - // Error - _callbacksLists.error.fire( ! isSuccess , _callbackContext , xhr, statusText, error); - if ( !isSuccess && _s.global ) { - _globalEventContext.trigger( "ajaxError", [xhr, _s, error] ); - } - // Complete - _callbacksLists.complete.fire( 1 , _callbackContext, xhr, statusText); - if ( _s.global ) { - _globalEventContext.trigger( "ajaxComplete", [xhr, _s] ); - // Handle the global AJAX counter - if ( ! --jQuery.active ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - // Ready state control - function checkState( expected , test ) { - if ( expected !== true && ( expected === false || test === false || xhr.readyState !== expected ) ) { - jQuery.error("INVALID_STATE_ERR"); - } - } - - // Ready state change - function setState( value ) { - xhr.readyState = value; - if ( isFunction( xhr.onreadystatechange ) ) { - xhr.onreadystatechange(); - } - } - - var // jQuery lists - jQuery_lastModified = jQuery.lastModified, - jQuery_etag = jQuery.etag, - // Options object - s, - // Callback stuff - callbackContext, - globalEventContext, - callbacksLists, - // Headers (they are sent all at once) - requestHeaders, - // Response headers - responseHeadersString, - responseHeaders, - // Done callback - done, - // transport - internal, - // timeout handle - timeoutTimer, - // The send flag - sendFlag, - // Fake xhr - xhr = { - // state - readyState: 0, - - // Callback - onreadystatechange: null, - - // Open - open: function(type, url, async, username, password) { - - xhr.abort(); - reset(); - - s = { - type: type, - url: url, - async: async, - username: username, - password: password - }; - - setState(1); - - return xhr; - }, - - // Send - send: function(data, moreOptions) { - - checkState(1 , !sendFlag); - - s.data = data; - - s = jQuery.extend( true, - {}, - jQuery.ajaxSettings, - s, - moreOptions || ( moreOptions === false ? { global: false } : {} ) ); - - if ( moreOptions ) { - // We force the original context - // (plain objects used as context get extended) - s.context = moreOptions.context; - } - - init(); - - // If not internal, abort - if ( ! internal ) { - done( 0 , "transport not found" ); - return false; - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend ) { - - var _s = s; - - if ( s.beforeSend.call(callbackContext, xhr, s) === false || ! xhr.readyState || _s !== s ) { - - // Abort if not done - if ( xhr.readyState && _s === s ) { - xhr.abort(); - } - - // Handle the global AJAX counter - if ( _s.global && ! --jQuery.active ) { - jQuery.event.trigger( "ajaxStop" ); - } - - return false; - } - } - - sendFlag = 1; - - // Send global event - if ( s.global ) { - globalEventContext.trigger("ajaxSend", [xhr, s]); - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = setTimeout(function(){ - xhr.abort("timeout"); - }, s.timeout); - } - - if ( s.async ) { - setState(1); - } - - try { - - internal.send(requestHeaders, done); - return xhr; - - } catch (e) { - - if ( done ) { - - done(0, "error", "" + e); - - } else { - - jQuery.error(e); - - } - } - - return false; - }, - - // Caches the header - setRequestHeader: function(name,value) { - checkState(1, !sendFlag); - requestHeaders[ name.toLowerCase() ] = value; - return xhr; - }, - - // Raw string - getAllResponseHeaders: function() { - return xhr.readyState <= 1 ? "" : responseHeadersString; - }, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - - if ( xhr.readyState <= 1 ) { - - return null; - - } - - if ( responseHeaders === undefined ) { - - responseHeaders = {}; - - if ( typeof responseHeadersString === "string" ) { - - var match; - - while( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; - } - } - } - return responseHeaders[ key.toLowerCase() ]; - }, - - // Cancel the request - abort: function(statusText) { - if (internal) { - internal.abort( statusText || "abort" ); - } - xhr.readyState = 0; - } - }; - - // Init data (so that we can bind callbacks early - reset(1); - - // Install callbacks related methods - jQuery.each(callbacksLists, function(name) { - var list; - xhr[name] = function() { - list = callbacksLists[name]; - if ( list ) { - list.bind.apply(list, arguments ); - } - return this; - }; - }); - - // Return the xhr emulation - return xhr; -}; - -// Create a callback list -function createCBList() { - - var functors = [], - autoFire = 0, - fireArgs, - list = { - - fire: function( flag , context ) { - - // Save info for later bindings - fireArgs = arguments; - - // Remove autoFire to keep bindings in order - autoFire = 0; - - var args = sliceFunc.call( fireArgs , 2 ); - - // Execute callbacks - while ( flag && functors.length ) { - flag = functors.shift().apply( context , args ) !== false; - } - - // Clean if asked to stop - if ( ! flag ) { - clean(); - } - - // Set autoFire - autoFire = 1; - }, - - bind: function() { - - var args = arguments, - i = 0, - length = args.length, - func; - - for ( ; i < length ; i++ ) { - - func = args[ i ]; - - if ( jQuery.isArray(func) ) { - - list.bind.apply( list , func ); - - } else if ( isFunction(func) ) { - - // Add if not already in - if ( ! pos( func ) ) { - functors.push( func ); - } - } - } - - if ( autoFire ) { - list.fire.apply( list , fireArgs ); - } - }, - - unbind: function() { - - var i = 0, - args = arguments, - length = args.length, - func, - position; - - if ( length ) { - - for( ; i < length ; i++ ) { - func = args[i]; - if ( jQuery.isArray(func) ) { - list.unbind.apply(list,func); - } else if ( isFunction(func) ) { - position = pos(func); - if ( position ) { - functors.splice(position-1,1); - } - } - } - - } else { - - functors = []; - - } - - } - - }; - - // Get the index of the functor in the list (1-based) - function pos( func ) { - for (var i = 0, length = functors.length; i < length && functors[i] !== func; i++) { - } - return i < length ? ( i + 1 ) : 0; - } - - // Clean the object - function clean() { - // Empty callbacks list - functors = []; - // Inhibit methods - for (var i in list) { - list[i] = jQuery.noop; - } - } - - return list; -} - -jQuery.extend(jQuery.xhr, { - - // Add new prefilter - prefilter: function (functor) { - if ( isFunction(functor) ) { - jQuery.ajaxSettings.prefilters.push( functor ); - } - return this; - }, - - // Bind a transport to one or more dataTypes - bindTransport: function () { - - var args = arguments, - i, - start = 0, - length = args.length, - dataTypes = [ "*" ], - functors = [], - functor, - first, - append, - list, - transports = jQuery.ajaxSettings.transports; - - if ( length ) { - - if ( ! isFunction( args[ 0 ] ) ) { - - dataTypes = args[ 0 ].toLowerCase().split(/\s+/); - start = 1; - - } - - if ( dataTypes.length && start < length ) { - - for ( i = start; i < length; i++ ) { - functor = args[i]; - if ( isFunction(functor) ) { - functors.push( functor ); - } - } - - if ( functors.length ) { - - jQuery.each ( dataTypes, function( _ , dataType ) { - - first = /^\+/.test( dataType ); - - if (first) { - dataType = dataType.substr(1); - } - - if ( dataType !== "" ) { - - append = Array.prototype[ first ? "unshift" : "push" ]; - - list = transports[ dataType ]; - - jQuery.each ( functors, function( _ , functor ) { - - if ( ! list ) { - - list = transports[ dataType ] = [ functor ]; - - } else { - - append.call( list , functor ); - } - } ); - } - - } ); - } - } - } - - return this; - } - - -}); - -// Select a transport given options -function selectTransport( s ) { - - var dataTypes = s.dataTypes, - transportDataType, - transportsList, - transport, - i, - length, - checked = {}, - flag; - - function initSearch( dataType ) { - - flag = transportDataType !== dataType && ! checked[ dataType ]; - - if ( flag ) { - - checked[ dataType ] = 1; - transportDataType = dataType; - transportsList = s.transports[ dataType ]; - i = -1; - length = transportsList ? transportsList.length : 0 ; - } - - return flag; - } - - initSearch( dataTypes[ 0 ] ); - - for ( i = 0 ; ! transport && i <= length ; i++ ) { - - if ( i === length ) { - - initSearch( "*" ); - - } else { - - transport = transportsList[ i ]( s , determineDataType ); - - // If we got redirected to another dataType - // Search there (if not in progress or already tried) - if ( typeof( transport ) === "string" && - initSearch( transport ) ) { - - dataTypes.unshift( transport ); - transport = 0; - } - } - } - - return transport; -} - -// Utility function that handles dataType when response is received -// (for those transports that can give text or xml responses) -function determineDataType( s , ct , text , xml ) { - - var autoDataType = s.autoDataType, - type, - regexp, - dataTypes = s.dataTypes, - transportDataType = dataTypes[0], - response; - - // Auto (xml, json, script or text determined given headers) - if ( transportDataType === "*" ) { - - for ( type in autoDataType ) { - if ( ( regexp = autoDataType[ type ] ) && regexp.test( ct ) ) { - transportDataType = dataTypes[0] = type; - break; - } - } - } - - // xml and parsed as such - if ( transportDataType === "xml" && - xml && - xml.documentElement /* #4958 */ ) { - - response = xml; - - // Text response was provided - } else { - - response = text; - - // If it's not really text, defer to dataConverters - if ( transportDataType !== "text" ) { - dataTypes.unshift( "text" ); - } - - } - - return response; -} - -})( jQuery ); diff --git a/test/data/errorWithText.php b/test/data/errorWithText.php new file mode 100644 index 000000000..abd873217 --- /dev/null +++ b/test/data/errorWithText.php @@ -0,0 +1,5 @@ + $value ) { - - if ( substr( $key , 0 , 5 ) == "HTTP_" ) { - - $key = str_replace( "_" , "-" , substr( $key , 5) ); - $headers[ $key ] = $value; +foreach( $_SERVER as $key => $value ) { - } - -} + $key = str_replace( "_" , "-" , substr( $key , 0 , 5 ) == "HTTP_" ? substr( $key , 5 ) : $key ); + $headers[ $key ] = $value; + +} foreach( explode( "_" , $_GET[ "keys" ] ) as $key ) { - echo "$key: " . $headers[ strtoupper( $key ) ] . "\n"; + echo "$key: " . @$headers[ strtoupper( $key ) ] . "\n"; } diff --git a/test/data/jsonp.php b/test/data/jsonp.php index 9ae1d8487..6c13d72e9 100644 --- a/test/data/jsonp.php +++ b/test/data/jsonp.php @@ -1,6 +1,10 @@ -$value ) echo "$value"; -?> +?>
$value ) echo "$value"; -?> +?>
\ No newline at end of file diff --git a/test/delegatetest.html b/test/delegatetest.html index 327085c84..6479d26ec 100644 --- a/test/delegatetest.html +++ b/test/delegatetest.html @@ -206,7 +206,7 @@ $(document).bind("focusin", function() { jQuery("#boundFocus").blink(); }); - + $(document).bind("focusout", function() { jQuery("#boundBlur").blink(); }); @@ -229,14 +229,14 @@ $(document).bind("change", function(){ jQuery("#boundChange").blink(); }); - + $("#text_submit").addSubmitTest("#textSubmit", true); $("#password_submit").addSubmitTest("#passwordSubmit", true); $("#submit_submit").addSubmitTest("#submitSubmit", true); $(document).bind("submit", function(){ jQuery("#boundSubmit").blink(); }); - + diff --git a/test/index.html b/test/index.html index 238b7d582..bbeda63a6 100644 --- a/test/index.html +++ b/test/index.html @@ -20,10 +20,9 @@ - - - - + + + diff --git a/test/polluted.php b/test/polluted.php index 3ddb7acd2..55df0dd89 100644 --- a/test/polluted.php +++ b/test/polluted.php @@ -15,7 +15,7 @@ $suite = file_get_contents('index.html'); echo str_replace( '', $includes, $suite ); exit; - } + } ?> @@ -43,7 +43,7 @@

jQuery Test Suite

Choose other libraries to include

- +
").load(url("data/name.html"), function(responseText) { + strictEqual( div.html(), "Hello World" , "Test div was filled with filtered data" ); + strictEqual( responseText, "Hello World" , "Test callback receives filtered data" ); + jQuery.ajaxSetup({ dataFilter: 0 }); + start(); + }); +}); + test("load(String, Object, Function)", function() { expect(2); stop(); @@ -1150,10 +1115,10 @@ test("jQuery.getScript(String, Function) - no callback", function() { }); test("jQuery.ajax() - JSONP, Local", function() { - expect(9); + expect(14); var count = 0; - function plus(){ if ( ++count == 9 ) start(); } + function plus(){ if ( ++count == 14 ) start(); } stop(); @@ -1197,6 +1162,75 @@ test("jQuery.ajax() - JSONP, Local", function() { } }); + jQuery.ajax({ + url: "data/jsonp.php?callback=??", + dataType: "jsonp", + success: function(data){ + ok( data.data, "JSON results returned (GET, url context-free callback)" ); + plus(); + }, + error: function(data){ + ok( false, "Ajax error JSON (GET, url context-free callback)" ); + plus(); + } + }); + + jQuery.ajax({ + url: "data/jsonp.php", + dataType: "jsonp", + data: "callback=??", + success: function(data){ + ok( data.data, "JSON results returned (GET, data context-free callback)" ); + plus(); + }, + error: function(data){ + ok( false, "Ajax error JSON (GET, data context-free callback)" ); + plus(); + } + }); + + jQuery.ajax({ + url: "data/jsonp.php/??", + dataType: "jsonp", + success: function(data){ + ok( data.data, "JSON results returned (GET, REST-like)" ); + plus(); + }, + error: function(data){ + ok( false, "Ajax error JSON (GET, REST-like)" ); + plus(); + } + }); + + jQuery.ajax({ + url: "data/jsonp.php/???json=1", + dataType: "jsonp", + success: function(data){ + strictEqual( jQuery.type(data), "array", "JSON results returned (GET, REST-like with param)" ); + plus(); + }, + error: function(data){ + ok( false, "Ajax error JSON (GET, REST-like with param)" ); + plus(); + } + }); + + jQuery.ajax({ + url: "data/jsonp.php", + dataType: "jsonp", + data: { + callback: "?" + }, + success: function(data){ + ok( data.data, "JSON results returned (GET, processed data callback)" ); + plus(); + }, + error: function(data){ + ok( false, "Ajax error JSON (GET, processed data callback)" ); + plus(); + } + }); + jQuery.ajax({ url: "data/jsonp.php", dataType: "jsonp", @@ -1384,7 +1418,7 @@ test("jQuery.ajax() - script, Remote with POST", function() { expect(3); var base = window.location.href.replace(/[^\/]*$/, ""); - + stop(); jQuery.ajax({ @@ -1482,12 +1516,12 @@ test("jQuery.ajax() - json by content-type disabled with options", function() { jQuery.ajax({ url: url("data/json.php"), data: { header: "json", json: "array" }, - autoDataType: { + contents: { json: false }, success: function( text ) { equals( typeof text , "string" , "json wasn't auto-determined" ); - var json = this.dataConverters["text => json"]( text ); + var json = jQuery.parseJSON( text ); ok( json.length >= 2, "Check length"); equals( json[0].name, 'John', 'Check JSON: first, name' ); equals( json[0].age, 21, 'Check JSON: first, age' ); @@ -1525,7 +1559,7 @@ test("jQuery.getJSON(String, Function) - JSON object", function() { test("jQuery.getJSON - Using Native JSON", function() { expect(2); - + var old = window.JSON; JSON = { parse: function(str){ @@ -1714,7 +1748,7 @@ test("data option: evaluate function values (#2806)", function() { equals( result, "key=value" ); start(); } - }) + }); }); test("data option: empty bodies for non-GET requests", function() { @@ -1727,7 +1761,7 @@ test("data option: empty bodies for non-GET requests", function() { equals( result, "" ); start(); } - }) + }); }); test("jQuery.ajax - If-Modified-Since support", function() { @@ -1740,19 +1774,19 @@ test("jQuery.ajax - If-Modified-Since support", function() { jQuery.ajax({ url: url, ifModified: true, - success: function(data, status) { + success: function(data, status) { equals(status, "success"); - + jQuery.ajax({ url: url, ifModified: true, - success: function(data, status) { + success: function(data, status) { if ( data === "FAIL" ) { ok(true, "Opera is incapable of doing .setRequestHeader('If-Modified-Since')."); ok(true, "Opera is incapable of doing .setRequestHeader('If-Modified-Since')."); } else { equals(status, "notmodified"); - ok(data == null, "response body should be empty") + ok(data == null, "response body should be empty"); } start(); }, @@ -1787,19 +1821,19 @@ test("jQuery.ajax - Etag support", function() { jQuery.ajax({ url: url, ifModified: true, - success: function(data, status) { + success: function(data, status) { equals(status, "success"); - + jQuery.ajax({ url: url, ifModified: true, - success: function(data, status) { + success: function(data, status) { if ( data === "FAIL" ) { ok(true, "Opera is incapable of doing .setRequestHeader('If-None-Match')."); ok(true, "Opera is incapable of doing .setRequestHeader('If-None-Match')."); } else { equals(status, "notmodified"); - ok(data == null, "response body should be empty") + ok(data == null, "response body should be empty"); } start(); }, @@ -1826,38 +1860,44 @@ test("jQuery.ajax - Etag support", function() { test("jQuery ajax - failing cross-domain", function() { expect( 2 ); - + stop(); - + var i = 2; - - jQuery.ajax({ - url: 'http://somewebsitethatdoesnotexist.com', + + if ( jQuery.ajax({ + url: 'http://somewebsitethatdoesnotexist-67864863574657654.com', success: function(){ ok( false , "success" ); }, error: function(xhr,_,e){ ok( true , "file not found: " + xhr.status + " => " + e ); }, complete: function() { if ( ! --i ) start(); } - }); - - jQuery.ajax({ + }) === false ) { + ok( true , "no transport" ); + if ( ! --i ) start(); + } + + if ( jQuery.ajax({ url: 'http://www.google.com', success: function(){ ok( false , "success" ); }, error: function(xhr,_,e){ ok( true , "access denied: " + xhr.status + " => " + e ); }, complete: function() { if ( ! --i ) start(); } - }); - + }) === false ) { + ok( true , "no transport" ); + if ( ! --i ) start(); + } + }); test("jQuery ajax - atom+xml", function() { stop(); - + jQuery.ajax({ url: url( 'data/atom+xml.php' ), success: function(){ ok( true , "success" ); }, error: function(){ ok( false , "error" ); }, complete: function() { start(); } }); - + }); test("jQuery.ajax - active counter", function() { @@ -1875,6 +1915,76 @@ test( "jQuery.ajax - Location object as url (#7531)", 1, function () { ok( success, "document.location did not generate exception" ); }); +test( "jQuery.ajax - statusCode" , function() { + + var count = 10; + + expect( 16 ); + stop(); + + function countComplete() { + if ( ! --count ) { + start(); + } + } + + function createStatusCodes( name , isSuccess ) { + name = "Test " + name + " " + ( isSuccess ? "success" : "error" ); + return { + 200: function() { + ok( isSuccess , name ); + }, + 404: function() { + ok( ! isSuccess , name ); + } + } + } + + jQuery.each( { + "data/name.html": true, + "data/someFileThatDoesNotExist.html": false + } , function( uri , isSuccess ) { + + jQuery.ajax( url( uri ) , { + statusCode: createStatusCodes( "in options" , isSuccess ), + complete: countComplete + }); + + jQuery.ajax( url( uri ) , { + complete: countComplete + }).statusCode( createStatusCodes( "immediately with method" , isSuccess ) ); + + jQuery.ajax( url( uri ) , { + complete: function(jXHR) { + jXHR.statusCode( createStatusCodes( "on complete" , isSuccess ) ); + countComplete(); + } + }); + + jQuery.ajax( url( uri ) , { + complete: function(jXHR) { + setTimeout( function() { + jXHR.statusCode( createStatusCodes( "very late binding" , isSuccess ) ); + countComplete(); + } , 100 ); + } + }); + + jQuery.ajax( url( uri ) , { + statusCode: createStatusCodes( "all (options)" , isSuccess ), + complete: function(jXHR) { + jXHR.statusCode( createStatusCodes( "all (on complete)" , isSuccess ) ); + setTimeout( function() { + jXHR.statusCode( createStatusCodes( "all (very late binding)" , isSuccess ) ); + countComplete(); + } , 100 ); + } + }).statusCode( createStatusCodes( "all (immediately with method)" , isSuccess ) ); + + }); + +}); + } //} \ No newline at end of file diff --git a/test/unit/attributes.js b/test/unit/attributes.js index f9506b30b..a1ab58179 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -4,13 +4,13 @@ var bareObj = function(value) { return value; }; var functionReturningObj = function(value) { return (function() { return value; }); }; test("jQuery.props: itegrity test", function() { - + expect(1); - + // This must be maintained and equal jQuery.props - // Ensure that accidental or erroneous property + // Ensure that accidental or erroneous property // overwrites don't occur - // This is simply for better code coverage and future proofing. + // This is simply for better code coverage and future proofing. var propsShouldBe = { "for": "htmlFor", "class": "className", @@ -23,7 +23,7 @@ test("jQuery.props: itegrity test", function() { usemap: "useMap", frameborder: "frameBorder" }; - + same(propsShouldBe, jQuery.props, "jQuery.props passes integrity check"); }); @@ -33,7 +33,7 @@ test("attr(String)", function() { // This one sometimes fails randomly ?! equals( jQuery('#text1').attr('value'), "Test", 'Check for value attribute' ); - + equals( jQuery('#text1').attr('value', "Test2").attr('defaultValue'), "Test", 'Check for defaultValue attribute' ); equals( jQuery('#text1').attr('type'), "text", 'Check for type attribute' ); equals( jQuery('#radio1').attr('type'), "radio", 'Check for type attribute' ); @@ -255,30 +255,30 @@ test("attr(String, Object)", function() { test("attr(jquery_method)", function(){ expect(7); - + var $elem = jQuery("
"), elem = $elem[0]; - - // one at a time + + // one at a time $elem.attr({'html': 'foo'}, true); equals( elem.innerHTML, 'foo', 'attr(html)'); - + $elem.attr({'text': 'bar'}, true); equals( elem.innerHTML, 'bar', 'attr(text)'); - + $elem.attr({'css': {color:'red'}}, true); ok( /^(#ff0000|red)$/i.test(elem.style.color), 'attr(css)'); - + $elem.attr({'height': 10}, true); equals( elem.style.height, '10px', 'attr(height)'); - + // Multiple attributes - + $elem.attr({ width:10, css:{ paddingLeft:1, paddingRight:1 } }, true); - + equals( elem.style.width, '10px', 'attr({...})'); equals( elem.style.paddingLeft, '1px', 'attr({...})'); equals( elem.style.paddingRight, '1px', 'attr({...})'); @@ -491,7 +491,7 @@ test( "val(Array of Numbers) (Bug #7123)", function() { ok( elements[1].checked, "Second element was checked" ); ok( !elements[2].checked, "Third element was unchecked" ); ok( !elements[3].checked, "Fourth element remained unchecked" ); - + elements.remove(); }); @@ -672,7 +672,7 @@ test("removeClass(Function) with incoming value", function() { ok( !$divs.is('.test'), "Remove Class" ); - QUnit.reset(); + QUnit.reset(); }); var testToggleClass = function(valueObj) { @@ -740,21 +740,21 @@ test("toggleClass(Fucntion[, boolean]) with incoming value", function() { var e = jQuery("#firstp"), old = e.attr("class"); ok( !e.is(".test"), "Assert class not present" ); - + e.toggleClass(function(i, val) { equals( val, old, "Make sure the incoming value is correct." ); return "test"; }); ok( e.is(".test"), "Assert class present" ); - + old = e.attr("class"); - + e.toggleClass(function(i, val) { equals( val, old, "Make sure the incoming value is correct." ); return "test"; }); ok( !e.is(".test"), "Assert class not present" ); - + old = e.attr("class"); // class name with a boolean @@ -764,18 +764,18 @@ test("toggleClass(Fucntion[, boolean]) with incoming value", function() { return "test"; }, false ); ok( !e.is(".test"), "Assert class not present" ); - + old = e.attr("class"); - + e.toggleClass(function(i, val, state) { equals( val, old, "Make sure the incoming value is correct." ); equals( state, true, "Make sure that the state is passed in." ); return "test"; }, true ); ok( e.is(".test"), "Assert class present" ); - + old = e.attr("class"); - + e.toggleClass(function(i, val, state) { equals( val, old, "Make sure the incoming value is correct." ); equals( state, false, "Make sure that the state is passed in." ); @@ -790,25 +790,25 @@ test("toggleClass(Fucntion[, boolean]) with incoming value", function() { test("addClass, removeClass, hasClass", function() { expect(17); - + var jq = jQuery("

Hi

"), x = jq[0]; - + jq.addClass("hi"); equals( x.className, "hi", "Check single added class" ); - + jq.addClass("foo bar"); equals( x.className, "hi foo bar", "Check more added classes" ); - + jq.removeClass(); equals( x.className, "", "Remove all classes" ); - + jq.addClass("hi foo bar"); jq.removeClass("foo"); equals( x.className, "hi bar", "Check removal of one class" ); - + ok( jq.hasClass("hi"), "Check has1" ); ok( jq.hasClass("bar"), "Check has2" ); - + var jq = jQuery("

"); ok( jq.hasClass("class1"), "Check hasClass with line feed" ); ok( jq.is(".class1"), "Check is with line feed" ); @@ -817,7 +817,7 @@ test("addClass, removeClass, hasClass", function() { ok( jq.hasClass("cla.ss3"), "Check hasClass with dot" ); ok( jq.hasClass("class4"), "Check hasClass with carriage return" ); ok( jq.is(".class4"), "Check is with carriage return" ); - + jq.removeClass("class2"); ok( jq.hasClass("class2")==false, "Check the class has been properly removed" ); jq.removeClass("cla"); diff --git a/test/unit/core.js b/test/unit/core.js index 705778370..bfb2f1cf4 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -12,7 +12,7 @@ test("Basic requirements", function() { }); test("jQuery()", function() { - expect(23); + expect(24); // Basic constructor's behavior @@ -84,6 +84,11 @@ test("jQuery()", function() { exec = true; elem.click(); + + for ( var i = 0; i < 3; ++i ) { + elem = jQuery(""); + } + equals( elem[0].defaultValue, "TEST", "Ensure cached nodes are cloned properly (Bug #6655)" ); }); test("selector state", function() { @@ -151,7 +156,7 @@ test("selector state", function() { test = jQuery("#main").eq(0); equals( test.selector, "#main.slice(0,1)", "#main eq Selector" ); equals( test.context, document, "#main eq Context" ); - + var d = "
"; equals( jQuery(d).appendTo(jQuery(d)).selector, @@ -253,38 +258,38 @@ test("isPlainObject", function() { // The use case that we want to match ok(jQuery.isPlainObject({}), "{}"); - + // Not objects shouldn't be matched ok(!jQuery.isPlainObject(""), "string"); ok(!jQuery.isPlainObject(0) && !jQuery.isPlainObject(1), "number"); ok(!jQuery.isPlainObject(true) && !jQuery.isPlainObject(false), "boolean"); ok(!jQuery.isPlainObject(null), "null"); ok(!jQuery.isPlainObject(undefined), "undefined"); - + // Arrays shouldn't be matched ok(!jQuery.isPlainObject([]), "array"); - + // Instantiated objects shouldn't be matched ok(!jQuery.isPlainObject(new Date), "new Date"); - + var fn = function(){}; - + // Functions shouldn't be matched ok(!jQuery.isPlainObject(fn), "fn"); - + // Again, instantiated objects shouldn't be matched ok(!jQuery.isPlainObject(new fn), "new fn (no methods)"); - + // Makes the function a little more realistic // (and harder to detect, incidentally) fn.prototype = {someMethod: function(){}}; - + // Again, instantiated objects shouldn't be matched ok(!jQuery.isPlainObject(new fn), "new fn"); // DOM Element ok(!jQuery.isPlainObject(document.createElement("div")), "DOM Element"); - + // Window ok(!jQuery.isPlainObject(window), "window"); @@ -298,7 +303,7 @@ test("isPlainObject", function() { document.body.removeChild( iframe ); start(); }; - + var doc = iframe.contentDocument || iframe.contentWindow.document; doc.open(); doc.write(""); @@ -659,7 +664,7 @@ test("jQuery.merge()", function() { // Fixed at [5998], #3641 same( parse([-2,-1], [0,1,2]), [-2,-1,0,1,2], "Second array including a zero (falsy)"); - + // After fixing #5527 same( parse([], [null, undefined]), [null, undefined], "Second array including null and undefined values"); same( parse({length:0}, [1,2]), {length:2, 0:1, 1:2}, "First array like"); @@ -694,7 +699,7 @@ test("jQuery.extend(Object, Object)", function() { equals( deep1.foo2, document, "Make sure that a deep clone was not attempted on the document" ); ok( jQuery.extend(true, {}, nestedarray).arr !== arr, "Deep extend of object must clone child array" ); - + // #5991 ok( jQuery.isArray( jQuery.extend(true, { arr: {} }, nestedarray).arr ), "Cloned array heve to be an Array" ); ok( jQuery.isPlainObject( jQuery.extend(true, { arr: arr }, { arr: {} }).arr ), "Cloned object heve to be an plain object" ); @@ -715,13 +720,13 @@ test("jQuery.extend(Object, Object)", function() { empty = {}; jQuery.extend(true, empty, optionsWithCustomObject); ok( empty.foo && empty.foo.date === customObject, "Custom objects copy correctly (no methods)" ); - + // Makes the class a little more realistic myKlass.prototype = { someMethod: function(){} }; empty = {}; jQuery.extend(true, empty, optionsWithCustomObject); ok( empty.foo && empty.foo.date === customObject, "Custom objects copy correctly" ); - + var ret = jQuery.extend(true, { foo: 4 }, { foo: new Number(5) } ); ok( ret.foo == 5, "Wrapped numbers copy correctly" ); @@ -849,10 +854,10 @@ test("jQuery.makeArray", function(){ test("jQuery.isEmptyObject", function(){ expect(2); - + equals(true, jQuery.isEmptyObject({}), "isEmptyObject on empty object literal" ); equals(false, jQuery.isEmptyObject({a:1}), "isEmptyObject on non-empty object literal" ); - + // What about this ? // equals(true, jQuery.isEmptyObject(null), "isEmptyObject on null" ); }); @@ -878,23 +883,23 @@ test("jQuery.proxy", function(){ test("jQuery.parseJSON", function(){ expect(8); - + equals( jQuery.parseJSON(), null, "Nothing in, null out." ); equals( jQuery.parseJSON( null ), null, "Nothing in, null out." ); equals( jQuery.parseJSON( "" ), null, "Nothing in, null out." ); - + same( jQuery.parseJSON("{}"), {}, "Plain object parsing." ); same( jQuery.parseJSON('{"test":1}'), {"test":1}, "Plain object parsing." ); same( jQuery.parseJSON('\n{"test":1}'), {"test":1}, "Make sure leading whitespaces are handled." ); - + try { jQuery.parseJSON("{a:1}"); ok( false, "Test malformed JSON string." ); } catch( e ) { ok( true, "Test malformed JSON string." ); } - + try { jQuery.parseJSON("{'a':1}"); ok( false, "Test malformed JSON string." ); @@ -902,3 +907,159 @@ test("jQuery.parseJSON", function(){ ok( true, "Test malformed JSON string." ); } }); + +test("jQuery._Deferred()", function() { + + expect( 10 ); + + var deferred, + object, + test; + + deferred = jQuery._Deferred(); + + test = false; + + deferred.done( function( value ) { + equals( value , "value" , "Test pre-resolve callback" ); + test = true; + } ); + + deferred.resolve( "value" ); + + ok( test , "Test pre-resolve callbacks called right away" ); + + test = false; + + deferred.done( function( value ) { + equals( value , "value" , "Test post-resolve callback" ); + test = true; + } ); + + ok( test , "Test post-resolve callbacks called right away" ); + + deferred.cancel(); + + test = true; + + deferred.done( function() { + ok( false , "Cancel was ignored" ); + test = false; + } ); + + ok( test , "Test cancel" ); + + deferred = jQuery._Deferred().resolve(); + + try { + deferred.done( function() { + throw "Error"; + } , function() { + ok( true , "Test deferred do not cancel on exception" ); + } ); + } catch( e ) { + strictEqual( e , "Error" , "Test deferred propagates exceptions"); + deferred.done(); + } + + test = ""; + deferred = jQuery._Deferred().done( function() { + + test += "A"; + + }, function() { + + test += "B"; + + } ).resolve(); + + strictEqual( test , "AB" , "Test multiple done parameters" ); + + test = ""; + + deferred.done( function() { + + deferred.done( function() { + + test += "C"; + + } ); + + test += "A"; + + }, function() { + + test += "B"; + } ); + + strictEqual( test , "ABC" , "Test done callbacks order" ); + + deferred = jQuery._Deferred(); + + deferred.fire( jQuery , [ document ] ).done( function( doc ) { + ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" ); + }); +}); + +test("jQuery.Deferred()", function() { + + expect( 4 ); + + jQuery.Deferred( function( defer ) { + strictEqual( this , defer , "Defer passed as this & first argument" ); + this.resolve( "done" ); + }).then( function( value ) { + strictEqual( value , "done" , "Passed function executed" ); + }); + + jQuery.Deferred().resolve().then( function() { + ok( true , "Success on resolve" ); + }, function() { + ok( false , "Error on resolve" ); + }); + + jQuery.Deferred().reject().then( function() { + ok( false , "Success on reject" ); + }, function() { + ok( true , "Error on reject" ); + }); +}); + +test("jQuery.when()", function() { + + expect( 21 ); + + // Some other objects + jQuery.each( { + + "an empty string": "", + "a non-empty string": "some string", + "zero": 0, + "a number other than zero": 1, + "true": true, + "false": false, + "null": null, + "undefined": undefined, + "a plain object": {} + + } , function( message , value ) { + + ok( jQuery.isFunction( jQuery.when( value ).then( function( resolveValue ) { + strictEqual( resolveValue , value , "Test the promise was resolved with " + message ); + } ).promise ) , "Test " + message + " triggers the creation of a new Promise" ); + + } ); + + var cache, i; + + for( i = 1 ; i < 4 ; i++ ) { + jQuery.when( cache || jQuery.Deferred( function() { + this.resolve( i ); + }) ).then( function( value ) { + strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) ); + cache = value; + }, function() { + ok( false , "Fail called" ); + }); + } +}); diff --git a/test/unit/css.js b/test/unit/css.js index cddd90256..fbbf937ca 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -178,24 +178,24 @@ if ( !jQuery.support.opacity ) { test("css(String, Function)", function() { expect(3); - + var sizes = ["10px", "20px", "30px"]; - - jQuery("
" + - "
" + + + jQuery("
" + + "
" + "
") .appendTo("body"); - + var index = 0; - + jQuery("#cssFunctionTest div").css("font-size", function() { var size = sizes[index]; index++; return size; }); - + index = 0; - + jQuery("#cssFunctionTest div").each(function() { var computedSize = jQuery(this).css("font-size") var expectedSize = sizes[index] @@ -208,24 +208,24 @@ test("css(String, Function)", function() { test("css(String, Function) with incoming value", function() { expect(3); - + var sizes = ["10px", "20px", "30px"]; - - jQuery("
" + - "
" + + + jQuery("
" + + "
" + "
") .appendTo("body"); - + var index = 0; - + jQuery("#cssFunctionTest div").css("font-size", function() { var size = sizes[index]; index++; return size; }); - + index = 0; - + jQuery("#cssFunctionTest div").css("font-size", function(i, computedSize) { var expectedSize = sizes[index] equals( computedSize, expectedSize, "Div #" + index + " should be " + expectedSize ); @@ -238,61 +238,61 @@ test("css(String, Function) with incoming value", function() { test("css(Object) where values are Functions", function() { expect(3); - + var sizes = ["10px", "20px", "30px"]; - - jQuery("
" + - "
" + + + jQuery("
" + + "
" + "
") .appendTo("body"); var index = 0; - + jQuery("#cssFunctionTest div").css({fontSize: function() { var size = sizes[index]; index++; return size; }}); - + index = 0; - + jQuery("#cssFunctionTest div").each(function() { var computedSize = jQuery(this).css("font-size") var expectedSize = sizes[index] equals( computedSize, expectedSize, "Div #" + index + " should be " + expectedSize ); index++; }); - + jQuery("#cssFunctionTest").remove(); }); test("css(Object) where values are Functions with incoming values", function() { expect(3); - + var sizes = ["10px", "20px", "30px"]; - - jQuery("
" + - "
" + + + jQuery("
" + + "
" + "
") .appendTo("body"); var index = 0; - + jQuery("#cssFunctionTest div").css({fontSize: function() { var size = sizes[index]; index++; return size; }}); - + index = 0; - + jQuery("#cssFunctionTest div").css({"font-size": function(i, computedSize) { var expectedSize = sizes[index] equals( computedSize, expectedSize, "Div #" + index + " should be " + expectedSize ); index++; return computedSize; }}); - + jQuery("#cssFunctionTest").remove(); }); diff --git a/test/unit/data.js b/test/unit/data.js index 1a0f84c1f..310cd6bc4 100644 --- a/test/unit/data.js +++ b/test/unit/data.js @@ -78,6 +78,21 @@ test("jQuery.data", function() { ok( jQuery.data( window, "BAD" ), "Make sure that the value was set." ); }); +test("jQuery.hasData", function() { + expect(6); + + function testData(obj) { + equals( jQuery.hasData(obj), false, "No data exists" ); + jQuery.data( obj, "foo", "bar" ); + equals( jQuery.hasData(obj), true, "Data exists" ); + jQuery.removeData( obj, "foo" ); + equals( jQuery.hasData(obj), false, "Data was removed" ); + } + + testData(document.createElement('div')); + testData({}); +}); + test(".data()", function() { expect(5); @@ -180,7 +195,7 @@ test(".data(String) and .data(String, Object)", function() { equals( $elem.data('emptyString','').data('emptyString'), '', "Empty strings are preserved"); equals( $elem.data('false',false).data('false'), false, "false's are preserved"); equals( $elem.data('exists'), true, "Existing data is returned" ); - + // Clean up $elem.removeData(); ok( jQuery.isEmptyObject( $elem[0] ), "removeData clears the object" ); @@ -191,7 +206,7 @@ test("data-* attributes", function() { var div = jQuery("
"), child = jQuery("
"), dummy = jQuery("
"); - + equals( div.data("attr"), undefined, "Check for non-existing data-attr attribute" ); div.attr("data-attr", "exists"); @@ -199,10 +214,10 @@ test("data-* attributes", function() { div.attr("data-attr", "exists2"); equals( div.data("attr"), "exists", "Check that updates to data- don't update .data()" ); - + div.data("attr", "internal").attr("data-attr", "external"); equals( div.data("attr"), "internal", "Check for .data('attr') precedence (internal > external data-* attribute)" ); - + child.appendTo('#main'); equals( child.data("myobj"), "old data", "Value accessed from data-* attribute"); @@ -249,7 +264,7 @@ test("data-* attributes", function() { .attr("data-space", " ") .attr("data-null", "null") .attr("data-string", "test"); - + strictEqual( child.data('true'), true, "Primitive true read from attribute"); strictEqual( child.data('false'), false, "Primitive false read from attribute"); strictEqual( child.data('five'), 5, "Primitive number read from attribute"); @@ -265,7 +280,7 @@ test("data-* attributes", function() { strictEqual( child.data('string'), "test", "Typical string read from attribute"); child.remove(); - + // tests from metadata plugin function testData(index, elem) { switch (index) { @@ -289,10 +304,10 @@ test("data-* attributes", function() { ok(false, ["Assertion failed on index ", index, ", with data ", data].join('')); } } - + var metadata = '
  1. Some stuff
  2. Some stuff
  3. Some stuff
  4. Some stuff
', elem = jQuery(metadata).appendTo('#main'); - + elem.find("li").each(testData); elem.remove(); }); @@ -305,12 +320,12 @@ test(".data(Object)", function() { div.data({ "test": "in", "test2": "in2" }); equals( div.data("test"), "in", "Verify setting an object in data" ); equals( div.data("test2"), "in2", "Verify setting an object in data" ); - + var obj = {test:"unset"}, jqobj = jQuery(obj); jqobj.data({ "test": "in", "test2": "in2" }); equals( obj.test, "in", "Verify setting an object on an object extends the object" ); - equals( obj.test2, "in2", "Verify setting an object on an object extends the object" ); + equals( obj.test2, "in2", "Verify setting an object on an object extends the object" ); }); test("jQuery.removeData", function() { @@ -324,13 +339,13 @@ test("jQuery.removeData", function() { jQuery.removeData( div ); ok( !jQuery.data(div, "test2"), "Make sure that the data property no longer exists." ); ok( !div[ jQuery.expando ], "Make sure the expando no longer exists, as well." ); - + var obj = {}; jQuery.data(obj, "test", "testing"); equals( obj.test, "testing", "verify data on plain object"); jQuery.removeData(obj, "test"); equals( jQuery.data(obj, "test"), undefined, "Check removal of data on plain object" ); - equals( obj.test, undefined, "Check removal of data directly from plain object" ); + equals( obj.test, undefined, "Check removal of data directly from plain object" ); jQuery.data( window, "BAD", true ); jQuery.removeData( window, "BAD" ); diff --git a/test/unit/dimensions.js b/test/unit/dimensions.js index 8255bf325..b38e73bba 100644 --- a/test/unit/dimensions.js +++ b/test/unit/dimensions.js @@ -45,13 +45,13 @@ test("width() with function", function() { test("width() with function args", function() { expect( 2 ); - + var $div = jQuery("#nothiddendiv"); $div.width( 30 ).width(function(i, width) { equals( width, 30, "Make sure previous value is corrrect." ); return width + 1; }); - + equals( $div.width(), 31, "Make sure value was modified correctly." ); }); @@ -92,13 +92,13 @@ test("height() with function", function() { test("height() with function args", function() { expect( 2 ); - + var $div = jQuery("#nothiddendiv"); $div.height( 30 ).height(function(i, height) { equals( height, 30, "Make sure previous value is corrrect." ); return height + 1; }); - + equals( $div.height(), 31, "Make sure value was modified correctly." ); }); @@ -112,13 +112,13 @@ test("innerWidth()", function() { border: "2px solid #fff", width: 30 }); - + equals($div.innerWidth(), 30, "Test with margin and border"); $div.css("padding", "20px"); equals($div.innerWidth(), 70, "Test with margin, border and padding"); $div.hide(); equals($div.innerWidth(), 70, "Test hidden div"); - + // reset styles $div.css({ display: "", border: "", padding: "", width: "", height: "" }); @@ -130,7 +130,7 @@ test("innerWidth()", function() { test("innerHeight()", function() { expect(4); - + var $div = jQuery("#nothiddendiv"); // set styles $div.css({ @@ -138,13 +138,13 @@ test("innerHeight()", function() { border: "2px solid #fff", height: 30 }); - + equals($div.innerHeight(), 30, "Test with margin and border"); $div.css("padding", "20px"); equals($div.innerHeight(), 70, "Test with margin, border and padding"); $div.hide(); equals($div.innerHeight(), 70, "Test hidden div"); - + // reset styles $div.css({ display: "", border: "", padding: "", width: "", height: "" }); @@ -156,10 +156,10 @@ test("innerHeight()", function() { test("outerWidth()", function() { expect(7); - + var $div = jQuery("#nothiddendiv"); $div.css("width", 30); - + equals($div.outerWidth(), 30, "Test with only width set"); $div.css("padding", "20px"); equals($div.outerWidth(), 70, "Test with padding"); @@ -171,7 +171,7 @@ test("outerWidth()", function() { equals($div.outerWidth(true), 94, "Test with padding, border and margin with margin option"); $div.hide(); equals($div.outerWidth(true), 94, "Test hidden div with padding, border and margin with margin option"); - + // reset styles $div.css({ position: "", display: "", border: "", padding: "", width: "", height: "" }); @@ -183,10 +183,10 @@ test("outerWidth()", function() { test("outerHeight()", function() { expect(7); - + var $div = jQuery("#nothiddendiv"); $div.css("height", 30); - + equals($div.outerHeight(), 30, "Test with only width set"); $div.css("padding", "20px"); equals($div.outerHeight(), 70, "Test with padding"); @@ -197,7 +197,7 @@ test("outerHeight()", function() { equals($div.outerHeight(true), 94, "Test with padding, border and margin with margin option"); $div.hide(); equals($div.outerHeight(true), 94, "Test hidden div with padding, border and margin with margin option"); - + // reset styles $div.css({ display: "", border: "", padding: "", width: "", height: "" }); diff --git a/test/unit/effects.js b/test/unit/effects.js index 969079683..b7b60abbe 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -64,7 +64,7 @@ test("show()", function() { // #show-tests * is set display: none in CSS jQuery("#main").append('

'); - + var old = jQuery("#test-table").show().css("display") !== "table"; jQuery("#test-table").remove(); @@ -140,15 +140,15 @@ test("Persist correct display value", function() { // #show-tests * is set display: none in CSS jQuery("#main").append('
foo
'); - - var $span = jQuery("#show-tests span"), + + var $span = jQuery("#show-tests span"), displayNone = $span.css("display"), display = '', num = 0; - + $span.show(); - + display = $span.css("display"); - + $span.hide(); $span.fadeIn(100, function() { @@ -156,13 +156,13 @@ test("Persist correct display value", function() { equals($span.css("display"), display, "Expecting display: " + display); $span.fadeOut(100, function () { - + equals($span.css("display"), displayNone, "Expecting display: " + displayNone); - + $span.fadeIn(100, function() { - + equals($span.css("display"), display, "Expecting display: " + display); - + start(); }); }); @@ -194,7 +194,7 @@ test("animate block as inline width/height", function() { var span = jQuery("").css("display", "inline-block").appendTo("body"), expected = span.css("display"); - + span.remove(); if ( jQuery.support.inlineBlockNeedsLayout || expected === "inline-block" ) { @@ -220,7 +220,7 @@ test("animate native inline width/height", function() { var span = jQuery("").css("display", "inline-block").appendTo("body"), expected = span.css("display"); - + span.remove(); if ( jQuery.support.inlineBlockNeedsLayout || expected === "inline-block" ) { @@ -560,6 +560,23 @@ jQuery.checkOverflowDisplay = function(){ start(); } +test("support negative values < -10000 (bug #7193)", function () { + expect(1); + stop(); + + jQuery.extend(jQuery.fx.step, { + "marginBottom": function(fx) { + equals( fx.cur(), -11000, "Element has margin-bottom of -11000" ); + delete jQuery.fx.step.marginBottom; + } + }); + + jQuery("#main").css("marginBottom", "-11000px").animate({ marginBottom: "-11001px" }, { + duration: 1, + complete: start + }); +}); + test("JS Overflow and Display", function() { expect(2); stop(); @@ -889,7 +906,7 @@ test("hide hidden elements, with animation (bug #7141)", function() { expect(3); QUnit.reset(); stop(); - + var div = jQuery("
").appendTo("#main"); equals( div.css("display"), "none", "Element is hidden by default" ); div.hide(1, function () { diff --git a/test/unit/event.js b/test/unit/event.js index a647e5f3b..b4672a8b8 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -4,20 +4,20 @@ test("null or undefined handler", function() { expect(2); // Supports Fixes bug #7229 try { - + jQuery("#firstp").click(null); - + ok(true, "Passing a null handler will not throw an exception"); - } catch (e) {} + } catch (e) {} try { - + jQuery("#firstp").click(undefined); - + ok(true, "Passing an undefined handler will not throw an exception"); - } catch (e) {} + } catch (e) {} }); test("bind(), with data", function() { @@ -175,7 +175,7 @@ test("bind(), no data", function() { test("bind/one/unbind(Object)", function(){ expect(6); - + var clickCounter = 0, mouseoverCounter = 0; function handler(event) { if (event.type == "click") @@ -183,18 +183,18 @@ test("bind/one/unbind(Object)", function(){ else if (event.type == "mouseover") mouseoverCounter++; }; - + function handlerWithData(event) { if (event.type == "click") clickCounter += event.data; else if (event.type == "mouseover") mouseoverCounter += event.data; }; - + function trigger(){ $elem.trigger("click").trigger("mouseover"); } - + var $elem = jQuery("#firstp") // Regular bind .bind({ @@ -206,16 +206,16 @@ test("bind/one/unbind(Object)", function(){ click:handlerWithData, mouseover:handlerWithData }, 2 ); - + trigger(); - + equals( clickCounter, 3, "bind(Object)" ); equals( mouseoverCounter, 3, "bind(Object)" ); - + trigger(); equals( clickCounter, 4, "bind(Object)" ); equals( mouseoverCounter, 4, "bind(Object)" ); - + jQuery("#firstp").unbind({ click:handler, mouseover:handler @@ -228,10 +228,10 @@ test("bind/one/unbind(Object)", function(){ test("live/die(Object), delegate/undelegate(String, Object)", function() { expect(6); - + var clickCounter = 0, mouseoverCounter = 0, $p = jQuery("#firstp"), $a = $p.find("a:first"); - + var events = { click: function( event ) { clickCounter += ( event.data || 1 ); @@ -240,26 +240,26 @@ test("live/die(Object), delegate/undelegate(String, Object)", function() { mouseoverCounter += ( event.data || 1 ); } }; - + function trigger() { $a.trigger("click").trigger("mouseover"); } - + $a.live( events ); $p.delegate( "a", events, 2 ); - + trigger(); equals( clickCounter, 3, "live/delegate" ); equals( mouseoverCounter, 3, "live/delegate" ); - + $p.undelegate( "a", events ); - + trigger(); equals( clickCounter, 4, "undelegate" ); equals( mouseoverCounter, 4, "undelegate" ); - + $a.die( events ); - + trigger(); equals( clickCounter, 4, "die" ); equals( mouseoverCounter, 4, "die" ); @@ -267,12 +267,12 @@ test("live/die(Object), delegate/undelegate(String, Object)", function() { test("live/delegate immediate propagation", function() { expect(2); - + var $p = jQuery("#firstp"), $a = $p.find("a:first"), lastClick; - + lastClick = ""; - $a.live( "click", function(e) { - lastClick = "click1"; + $a.live( "click", function(e) { + lastClick = "click1"; e.stopImmediatePropagation(); }); $a.live( "click", function(e) { @@ -281,10 +281,10 @@ test("live/delegate immediate propagation", function() { $a.trigger( "click" ); equals( lastClick, "click1", "live stopImmediatePropagation" ); $a.die( "click" ); - + lastClick = ""; - $p.delegate( "a", "click", function(e) { - lastClick = "click1"; + $p.delegate( "a", "click", function(e) { + lastClick = "click1"; e.stopImmediatePropagation(); }); $p.delegate( "a", "click", function(e) { @@ -295,10 +295,45 @@ test("live/delegate immediate propagation", function() { $p.undelegate( "click" ); }); +test("bind/delegate bubbling, isDefaultPrevented (Bug #7793)", function() { + expect(2); + var $anchor2 = jQuery( "#anchor2" ), + $main = jQuery( "#main" ), + fakeClick = function($jq) { + // Use a native click so we don't get jQuery simulated bubbling + if ( document.createEvent ) { + var e = document.createEvent( "MouseEvents" ); + e.initEvent( "click", true, true ); + $jq[0].dispatchEvent(e); + } + else if ( $jq[0].click ) { + $jq[0].click(); // IE + } + }; + $anchor2.click(function(e) { + e.preventDefault(); + }); + $main.delegate("#foo", "click", function(e) { + equals( e.isDefaultPrevented(), true, "isDefaultPrevented true passed to bubbled event" ); + }); + fakeClick( $anchor2 ); + $anchor2.unbind( "click" ); + $main.undelegate( "click" ); + $anchor2.click(function(e) { + // Let the default action occur + }); + $main.delegate("#foo", "click", function(e) { + equals( e.isDefaultPrevented(), false, "isDefaultPrevented false passed to bubbled event" ); + }); + fakeClick( $anchor2 ); + $anchor2.unbind( "click" ); + $main.undelegate( "click" ); +}); + test("bind(), iframes", function() { // events don't work with iframes, see #939 - this test fails in IE because of contentDocument var doc = jQuery("#loadediframe").contents(); - + jQuery("div", doc).bind("click", function() { ok( true, "Binding to element inside iframe" ); }).click().unbind('click'); @@ -360,7 +395,7 @@ test("bind(), namespaced events, cloned events", function() { test("bind(), multi-namespaced events", function() { expect(6); - + var order = [ "click.test.abc", "click.test.abc", @@ -369,7 +404,7 @@ test("bind(), multi-namespaced events", function() { "click.test", "custom.test2" ]; - + function check(name, msg){ same(name, order.shift(), msg); } @@ -389,7 +424,7 @@ test("bind(), multi-namespaced events", function() { jQuery("#firstp").bind("click.test.abc",function(e){ check("click.test.abc", "Namespaced click triggered"); }); - + // Those would not trigger/unbind (#5303) jQuery("#firstp").trigger("click.a.test"); jQuery("#firstp").unbind("click.a.test"); @@ -453,7 +488,7 @@ test("bind(), make sure order is maintained", function() { elem.unbind("click"); }); - + test("bind(), with different this object", function() { expect(4); var thisObject = { myThis: true }, @@ -465,7 +500,7 @@ test("bind(), with different this object", function() { equals( this, thisObject, "bind() with different this object and data" ); equals( event.data, data, "bind() with different this object and data" ); }; - + jQuery("#firstp") .bind("click", jQuery.proxy(handler1, thisObject)).click().unbind("click", handler1) .bind("click", data, jQuery.proxy(handler2, thisObject)).click().unbind("click", handler2); @@ -531,29 +566,29 @@ test("bind()/trigger()/unbind() on plain object", function() { // Make sure it doesn't complain when no events are found jQuery(obj).unbind("test"); - + equals( obj.__events__, undefined, "Make sure events object is removed" ); }); test("unbind(type)", function() { expect( 0 ); - + var $elem = jQuery("#firstp"), message; function error(){ ok( false, message ); } - + message = "unbind passing function"; $elem.bind('error1', error).unbind('error1',error).triggerHandler('error1'); - + message = "unbind all from event"; $elem.bind('error1', error).unbind('error1').triggerHandler('error1'); - + message = "unbind all"; $elem.bind('error1', error).unbind().triggerHandler('error1'); - + message = "unbind many with function"; $elem.bind('error1 error2',error) .unbind('error1 error2', error ) @@ -563,7 +598,7 @@ test("unbind(type)", function() { $elem.bind('error1 error2',error) .unbind('error1 error2') .trigger('error1').triggerHandler('error2'); - + message = "unbind without a type or handler"; $elem.bind("error1 error2.test",error) .unbind() @@ -572,7 +607,7 @@ test("unbind(type)", function() { test("unbind(eventObject)", function() { expect(4); - + var $elem = jQuery("#firstp"), num; @@ -581,7 +616,7 @@ test("unbind(eventObject)", function() { $elem.trigger('foo').triggerHandler('bar'); equals( num, expected, "Check the right handlers are triggered" ); } - + $elem // This handler shouldn't be unbound .bind('foo', function(){ @@ -595,14 +630,14 @@ test("unbind(eventObject)", function() { .bind('bar', function(){ num += 4; }); - + assert( 7 ); assert( 5 ); - + $elem.unbind('bar'); assert( 1 ); - - $elem.unbind(); + + $elem.unbind(); assert( 0 ); }); @@ -632,25 +667,25 @@ test("trigger() shortcuts", function() { 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(); - + var counter = 0; jQuery('#firstp')[0].onclick = function(event) { counter++; }; jQuery('#firstp').click(); equals( counter, 1, "Check that click, triggers onclick event handler also" ); - + var clickCounter = 0; jQuery('#simon1')[0].onclick = function(event) { clickCounter++; }; jQuery('#simon1').click(); equals( clickCounter, 1, "Check that click, triggers onclick event handler on an a tag also" ); - + jQuery('').load(function(){ ok( true, "Trigger the load event, using the shortcut .load() (#2819)"); }).load(); @@ -728,7 +763,7 @@ test("trigger(type, [data], [fn])", function() { pass = false; } ok( pass, "Trigger focus on hidden element" ); - + pass = true; try { jQuery('table:first').bind('test:test', function(){}).trigger('test:test'); @@ -768,28 +803,28 @@ test("jQuery.Event.currentTarget", function(){ test("trigger(eventObject, [data], [fn])", function() { expect(25); - + var $parent = jQuery('
').hide().appendTo('body'), $child = jQuery('

foo

').appendTo( $parent ); - - var event = jQuery.Event("noNew"); + + var event = jQuery.Event("noNew"); ok( event != window, "Instantiate jQuery.Event without the 'new' keyword" ); equals( event.type, "noNew", "Verify its type" ); - + equals( event.isDefaultPrevented(), false, "Verify isDefaultPrevented" ); equals( event.isPropagationStopped(), false, "Verify isPropagationStopped" ); equals( event.isImmediatePropagationStopped(), false, "Verify isImmediatePropagationStopped" ); - + event.preventDefault(); equals( event.isDefaultPrevented(), true, "Verify isDefaultPrevented" ); event.stopPropagation(); equals( event.isPropagationStopped(), true, "Verify isPropagationStopped" ); - + event.isPropagationStopped = function(){ return false }; event.stopImmediatePropagation(); equals( event.isPropagationStopped(), true, "Verify isPropagationStopped" ); equals( event.isImmediatePropagationStopped(), true, "Verify isPropagationStopped" ); - + $parent.bind('foo',function(e){ // Tries bubbling equals( e.type, 'foo', 'Verify event type when passed passing an event object' ); @@ -797,72 +832,72 @@ test("trigger(eventObject, [data], [fn])", function() { equals( e.currentTarget.id, 'par', 'Verify event.target when passed passing an event object' ); equals( e.secret, 'boo!', 'Verify event object\'s custom attribute when passed passing an event object' ); }); - + // test with an event object event = new jQuery.Event("foo"); event.secret = 'boo!'; $child.trigger(event); - + // test with a literal object $child.trigger({type:'foo', secret:'boo!'}); - + $parent.unbind(); function error(){ ok( false, "This assertion shouldn't be reached"); } - + $parent.bind('foo', error ); - + $child.bind('foo',function(e, a, b, c ){ equals( arguments.length, 4, "Check arguments length"); equals( a, 1, "Check first custom argument"); equals( b, 2, "Check second custom argument"); equals( c, 3, "Check third custom argument"); - + equals( e.isDefaultPrevented(), false, "Verify isDefaultPrevented" ); equals( e.isPropagationStopped(), false, "Verify isPropagationStopped" ); equals( e.isImmediatePropagationStopped(), false, "Verify isImmediatePropagationStopped" ); - + // Skips both errors e.stopImmediatePropagation(); - + return "result"; }); - + // We should add this back in when we want to test the order // in which event handlers are iterated. //$child.bind('foo', error ); - + event = new jQuery.Event("foo"); $child.trigger( event, [1,2,3] ).unbind(); equals( event.result, "result", "Check event.result attribute"); - + // Will error if it bubbles $child.triggerHandler('foo'); - + $child.unbind(); $parent.unbind().remove(); }); test("jQuery.Event.currentTarget", function(){ expect(1); - + var counter = 0, $elem = jQuery('').click(function(e){ equals( e.currentTarget, this, "Check currentTarget on "+(counter++?"native":"fake") +" event" ); }); - + // Fake event $elem.trigger('click'); - + // Cleanup $elem.unbind(); }); test("toggle(Function, Function, ...)", function() { expect(16); - + var count = 0, fn1 = function(e) { count++; }, fn2 = function(e) { count--; }, @@ -885,7 +920,7 @@ test("toggle(Function, Function, ...)", function() { }); return false; }).click().click().click(); - + var turn = 0; var fns = [ function(){ @@ -898,7 +933,7 @@ test("toggle(Function, Function, ...)", function() { turn = 3; } ]; - + var $div = jQuery("
 
").toggle( fns[0], fns[1], fns[2] ); $div.click(); equals( turn, 1, "Trying toggle with 3 functions, attempt 1 yields 1"); @@ -910,7 +945,7 @@ test("toggle(Function, Function, ...)", function() { equals( turn, 1, "Trying toggle with 3 functions, attempt 4 yields 1"); $div.click(); equals( turn, 2, "Trying toggle with 3 functions, attempt 5 yields 2"); - + $div.unbind('click',fns[0]); var data = jQuery.data( $div[0], 'events' ); ok( !data, "Unbinding one function from toggle unbinds them all"); @@ -1052,7 +1087,7 @@ test(".live()/.die()", function() { // Test binding with different this object, event data, and trigger data jQuery("#foo").live("click", true, jQuery.proxy(function(e, data){ equals( e.data, true, "live with with different this object, event data, and trigger data" ); - equals( this.foo, "bar", "live with with different this object, event data, and trigger data" ); + equals( this.foo, "bar", "live with with different this object, event data, and trigger data" ); equals( data, true, "live with with different this object, event data, and trigger data") }, { foo: "bar" })); jQuery("#foo").trigger("click", true).die("click"); @@ -1113,25 +1148,25 @@ test(".live()/.die()", function() { // Cleanup jQuery("#nothiddendiv").die("foo", callback); - + // Make sure we don't loose the target by DOM modifications // after the bubble already reached the liveHandler var livec = 0, elemDiv = jQuery("#nothiddendivchild").html('').get(0); - + jQuery("#nothiddendivchild").live("click", function(e){ jQuery("#nothiddendivchild").html(''); }); jQuery("#nothiddendivchild").live("click", function(e){ if(e.target) {livec++;} }); - + jQuery("#nothiddendiv span").click(); equals( jQuery("#nothiddendiv span").length, 0, "Verify that first handler occurred and modified the DOM." ); equals( livec, 1, "Verify that second handler occurred even with nuked target." ); - + // Cleanup jQuery("#nothiddendivchild").die("click"); // Verify that .live() ocurs and cancel buble in the same order as // we would expect .bind() and .click() without delegation var lived = 0, livee = 0; - + // bind one pair in one order jQuery('span#liveSpan1 a').live('click', function(){ lived++; return false; }); jQuery('span#liveSpan1').live('click', function(){ livee++; }); @@ -1149,22 +1184,22 @@ test(".live()/.die()", function() { jQuery('span#liveSpan2 a').click(); equals( lived, 1, "Verify that only one first handler occurred." ); equals( livee, 0, "Verify that second handler doesn't." ); - + // Cleanup jQuery("span#liveSpan1 a").die("click") jQuery("span#liveSpan1").die("click"); jQuery("span#liveSpan2 a").die("click"); jQuery("span#liveSpan2").die("click"); - + // Test this, target and currentTarget are correct - jQuery('span#liveSpan1').live('click', function(e){ + jQuery('span#liveSpan1').live('click', function(e){ equals( this.id, 'liveSpan1', 'Check the this within a live handler' ); equals( e.currentTarget.id, 'liveSpan1', 'Check the event.currentTarget within a live handler' ); equals( e.target.nodeName.toUpperCase(), 'A', 'Check the event.target within a live handler' ); }); - + jQuery('span#liveSpan1 a').click(); - + jQuery('span#liveSpan1').die('click'); // Work with deep selectors @@ -1298,18 +1333,18 @@ test("live with change", function(){ expect(8); var selectChange = 0, checkboxChange = 0; - + var select = jQuery("select[name='S1']") select.live("change", function() { selectChange++; }); - - var checkbox = jQuery("#check2"), + + var checkbox = jQuery("#check2"), checkboxFunction = function(){ checkboxChange++; } checkbox.live("change", checkboxFunction); - + // test click on select // second click that changed it @@ -1317,17 +1352,17 @@ test("live with change", function(){ select[0].selectedIndex = select[0].selectedIndex ? 0 : 1; select.trigger("change"); equals( selectChange, 1, "Change on click." ); - + // test keys on select selectChange = 0; select[0].selectedIndex = select[0].selectedIndex ? 0 : 1; select.trigger("change"); equals( selectChange, 1, "Change on keyup." ); - + // test click on checkbox checkbox.trigger("change"); equals( checkboxChange, 1, "Change on checkbox." ); - + // test blur/focus on text var text = jQuery("#name"), textChange = 0, oldTextVal = text.val(); text.live("change", function() { @@ -1340,7 +1375,7 @@ test("live with change", function(){ text.val(oldTextVal); text.die("change"); - + // test blur/focus on password var password = jQuery("#name"), passwordChange = 0, oldPasswordVal = password.val(); password.live("change", function() { @@ -1353,9 +1388,9 @@ test("live with change", function(){ password.val(oldPasswordVal); password.die("change"); - + // make sure die works - + // die all changes selectChange = 0; select.die("change"); @@ -1367,7 +1402,7 @@ test("live with change", function(){ select[0].selectedIndex = select[0].selectedIndex ? 0 : 1; select.trigger("change"); equals( selectChange, 0, "Die on keyup works." ); - + // die specific checkbox checkbox.die("change", checkboxFunction); checkbox.trigger("change"); @@ -1376,7 +1411,7 @@ test("live with change", function(){ test("live with submit", function() { var count1 = 0, count2 = 0; - + jQuery("#testForm").live("submit", function(ev) { count1++; ev.preventDefault(); @@ -1390,7 +1425,7 @@ test("live with submit", function() { jQuery("#testForm input[name=sub1]").submit(); equals( count1, 1, "Verify form submit." ); equals( count2, 1, "Verify body submit." ); - + jQuery("#testForm").die("submit"); jQuery("body").die("submit"); }); @@ -1568,7 +1603,7 @@ test(".delegate()/.undelegate()", function() { // Test binding with different this object, event data, and trigger data jQuery("#body").delegate("#foo", "click", true, jQuery.proxy(function(e, data){ equals( e.data, true, "delegate with with different this object, event data, and trigger data" ); - equals( this.foo, "bar", "delegate with with different this object, event data, and trigger data" ); + equals( this.foo, "bar", "delegate with with different this object, event data, and trigger data" ); equals( data, true, "delegate with with different this object, event data, and trigger data") }, { foo: "bar" })); jQuery("#foo").trigger("click", true); @@ -1630,25 +1665,25 @@ test(".delegate()/.undelegate()", function() { // Cleanup jQuery("#body").undelegate("#nothiddendiv", "foo", callback); - + // Make sure we don't loose the target by DOM modifications // after the bubble already reached the liveHandler var livec = 0, elemDiv = jQuery("#nothiddendivchild").html('').get(0); - + 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(); equals( jQuery("#nothiddendiv span").length, 0, "Verify that first handler occurred and modified the DOM." ); equals( livec, 1, "Verify that second handler occurred even with nuked target." ); - + // Cleanup jQuery("#body").undelegate("#nothiddendivchild", "click"); // Verify that .live() ocurs and cancel buble in the same order as // we would expect .bind() and .click() without delegation var lived = 0, livee = 0; - + // bind one pair in one order jQuery("#body").delegate('span#liveSpan1 a', 'click', function(){ lived++; return false; }); jQuery("#body").delegate('span#liveSpan1', 'click', function(){ livee++; }); @@ -1666,19 +1701,19 @@ test(".delegate()/.undelegate()", function() { jQuery('span#liveSpan2 a').click(); equals( lived, 1, "Verify that only one first handler occurred." ); equals( livee, 0, "Verify that second handler doesn't." ); - + // Cleanup jQuery("#body").undelegate("click"); - + // Test this, target and currentTarget are correct - jQuery("#body").delegate('span#liveSpan1', 'click', function(e){ + jQuery("#body").delegate('span#liveSpan1', 'click', function(e){ equals( this.id, 'liveSpan1', 'Check the this within a delegate handler' ); equals( e.currentTarget.id, 'liveSpan1', 'Check the event.currentTarget within a delegate handler' ); equals( e.target.nodeName.toUpperCase(), 'A', 'Check the event.target within a delegate handler' ); }); - + jQuery('span#liveSpan1 a').click(); - + jQuery("#body").undelegate('span#liveSpan1', 'click'); // Work with deep selectors @@ -1754,18 +1789,18 @@ test("delegate with change", function(){ expect(8); var selectChange = 0, checkboxChange = 0; - + var select = jQuery("select[name='S1']"); jQuery("#body").delegate("select[name='S1']", "change", function() { selectChange++; }); - - var checkbox = jQuery("#check2"), + + var checkbox = jQuery("#check2"), checkboxFunction = function(){ checkboxChange++; } jQuery("#body").delegate("#check2", "change", checkboxFunction); - + // test click on select // second click that changed it @@ -1773,17 +1808,17 @@ test("delegate with change", function(){ select[0].selectedIndex = select[0].selectedIndex ? 0 : 1; select.trigger("change"); equals( selectChange, 1, "Change on click." ); - + // test keys on select selectChange = 0; select[0].selectedIndex = select[0].selectedIndex ? 0 : 1; select.trigger("change"); equals( selectChange, 1, "Change on keyup." ); - + // test click on checkbox checkbox.trigger("change"); equals( checkboxChange, 1, "Change on checkbox." ); - + // test blur/focus on text var text = jQuery("#name"), textChange = 0, oldTextVal = text.val(); jQuery("#body").delegate("#name", "change", function() { @@ -1796,7 +1831,7 @@ test("delegate with change", function(){ text.val(oldTextVal); jQuery("#body").die("change"); - + // test blur/focus on password var password = jQuery("#name"), passwordChange = 0, oldPasswordVal = password.val(); jQuery("#body").delegate("#name", "change", function() { @@ -1809,9 +1844,9 @@ test("delegate with change", function(){ password.val(oldPasswordVal); jQuery("#body").undelegate("#name", "change"); - + // make sure die works - + // die all changes selectChange = 0; jQuery("#body").undelegate("select[name='S1']", "change"); @@ -1823,7 +1858,7 @@ test("delegate with change", function(){ select[0].selectedIndex = select[0].selectedIndex ? 0 : 1; select.trigger("change"); equals( selectChange, 0, "Die on keyup works." ); - + // die specific checkbox jQuery("#body").undelegate("#check2", "change", checkboxFunction); checkbox.trigger("change"); @@ -1832,7 +1867,7 @@ test("delegate with change", function(){ test("delegate with submit", function() { var count1 = 0, count2 = 0; - + jQuery("#body").delegate("#testForm", "submit", function(ev) { count1++; ev.preventDefault(); @@ -1846,7 +1881,7 @@ test("delegate with submit", function() { jQuery("#testForm input[name=sub1]").submit(); equals( count1, 1, "Verify form submit." ); equals( count2, 1, "Verify body submit." ); - + jQuery("#body").undelegate(); jQuery(document).undelegate(); }); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index d49029eb8..559a076fa 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -1,5 +1,8 @@ module("manipulation"); +// Ensure that an extended Array prototype doesn't break jQuery +Array.prototype.arrayProtoFn = function(arg) { throw("arrayProtoFn should not be called"); }; + var bareObj = function(value) { return value; }; var functionReturningObj = function(value) { return (function() { return value; }); }; @@ -51,7 +54,7 @@ test("text(Function) with incoming value", function() { }); var testWrap = function(val) { - expect(18); + expect(19); var defaultText = 'Try them out:' var result = jQuery('#first').wrap(val( '
' )).text(); equals( defaultText, result, 'Check for wrapping of on-the-fly html' ); @@ -80,10 +83,20 @@ var testWrap = function(val) { equals( jQuery("#nonnodes > i").text(), j.text(), "Check node,textnode,comment wraps doesn't hurt text" ); // Try wrapping a disconnected node + var cacheLength = 0; + for (var i in jQuery.cache) { + cacheLength++; + } + j = jQuery("