diff options
author | scottjehl <scott@scottjehl.com> | 2011-01-13 14:14:04 -0500 |
---|---|---|
committer | scottjehl <scott@scottjehl.com> | 2011-01-13 14:14:04 -0500 |
commit | 610ab137da38106f8c464f099a304ae3795c2231 (patch) | |
tree | e55254f034702e0213c51e062a3d4aba5291f4e9 /src | |
parent | 18fa1fd9da12fd6a259b422f91b663d9fbdb181e (diff) | |
parent | a8fa5f2ec1030bceb9a65d0237f0c92ae4e014dd (diff) | |
download | jquery-610ab137da38106f8c464f099a304ae3795c2231.tar.gz jquery-610ab137da38106f8c464f099a304ae3795c2231.zip |
Merge branch 'master' of https://github.com/jquery/jquery
Diffstat (limited to 'src')
-rw-r--r-- | src/ajax.js | 826 | ||||
-rw-r--r-- | src/ajax/jsonp.js | 83 | ||||
-rw-r--r-- | src/ajax/script.js (renamed from src/transports/script.js) | 63 | ||||
-rw-r--r-- | src/ajax/xhr.js (renamed from src/transports/xhr.js) | 182 | ||||
-rw-r--r-- | src/attributes.js | 2 | ||||
-rw-r--r-- | src/core.js | 268 | ||||
-rw-r--r-- | src/css.js | 17 | ||||
-rw-r--r-- | src/data.js | 15 | ||||
-rw-r--r-- | src/dimensions.js | 2 | ||||
-rw-r--r-- | src/effects.js | 4 | ||||
-rw-r--r-- | src/event.js | 75 | ||||
-rw-r--r-- | src/intro.js | 2 | ||||
-rw-r--r-- | src/manipulation.js | 49 | ||||
-rw-r--r-- | src/offset.js | 10 | ||||
-rw-r--r-- | src/support.js | 2 | ||||
-rw-r--r-- | src/transports/jsonp.js | 89 | ||||
-rw-r--r-- | src/traversing.js | 14 | ||||
-rw-r--r-- | src/xhr.js | 909 |
18 files changed, 1220 insertions, 1392 deletions
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\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/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\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/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("<div>") // 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; + } + + // 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 jQuery.xhr().open( s ? s.type : undefined , url ).send( undefined , s ); - + + 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 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 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 <object> or <embed> 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 = "<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>"; + div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>"; 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 ); |