| ajax.js |
| 1 | define( ".core", ".varrnotwhite", ".ajaxvarnonce", ".ajaxvarrquery", ".coreinit", ".ajaxparseJSON", ".ajaxparseXML", ".deferred" , function( jQuery, rnotwhite, nonce, rquery ) { var rhash = #.*$, rts = (?&)_=^&*, rheaders = ^(.*?): \t*(^\r *)$mg, #7653, #8125, #8152: local protocol detection rlocalProtocol = ^(?:aboutappapp-storage.+-extensionfilereswidget):$, rnoContent = ^(?:GETHEAD)$, rprotocol = ^\\, rurl = ^(\w.+-+:)(?:\\(?:^\?#*@)(^\?#:*)(?::(\d+))), * Prefilters * 1) They are useful to introduce custom dataTypes (see ajaxjsonp.js 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) 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 = {}, Avoid comment-prolog char sequence (#10098); must appease lint and evade compression allTypes = "*".concat( "*" ), Document location ajaxLocation = location.href, Segment location into parts ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) ; Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport function addToPrefiltersOrTransports( structure ) { dataTypeExpression is optional and defaults to "*" return function( dataTypeExpression, func ) { if ( typeof dataTypeExpression !== "string" ) { func = dataTypeExpression; dataTypeExpression = "*"; } var dataType, i = 0, dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) ; if ( jQuery.isFunction( func ) ) { For each dataType in the dataTypeExpression while ( (dataType = dataTypesi++) ) { Prepend if requested if ( dataType0 === "+" ) { dataType = dataType.slice( 1 ) "*"; (structure dataType = structure dataType ).unshift( func ); Otherwise append } else { (structure dataType = structure dataType ).push( func ); } } } }; } Base inspection function for prefilters and transports function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { var inspected = {}, seekingTransport = ( structure === transports ); function inspect( dataType ) { var selected; inspected dataType = true; jQuery.each( structure dataType , function( _, prefilterOrFactory ) { var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected dataTypeOrTransport ) { options.dataTypes.unshift( dataTypeOrTransport ); inspect( dataTypeOrTransport ); return false; } else if ( seekingTransport ) { return !( selected = dataTypeOrTransport ); } }); return selected; } return inspect( options.dataTypes 0 ) !inspected "*" && inspect( "*" ); } A special extend for ajax options that takes "flat" options (not to be deep extended) Fixes #9887 function ajaxExtend( target, src ) { var key, deep, flatOptions = jQuery.ajaxSettings.flatOptions {}; for ( key in src ) { if ( src key !== undefined ) { ( flatOptions key ? |
| 2 | target : ( deep (deep = {}) ) ) key = src key ; } } if ( deep ) { jQuery.extend( true, target, deep ); } return target; } * Handles responses to an ajax request: * - finds the right dataType (mediates between content-type and expected dataType) * - returns the corresponding response * function ajaxHandleResponses( s, jqXHR, responses ) { var ct, type, finalDataType, firstDataType, contents = s.contents, dataTypes = s.dataTypes; Remove auto dataType and get content-type in the process while ( dataTypes 0 === "*" ) { dataTypes.shift(); if ( ct === undefined ) { ct = s.mimeType jqXHR.getResponseHeader("Content-Type"); } } Check if we're dealing with a known content-type if ( ct ) { for ( type in contents ) { if ( contents type && contents type .test( ct ) ) { dataTypes.unshift( type ); break; } } } Check to see if we have a response for the expected dataType if ( dataTypes 0 in responses ) { finalDataType = dataTypes 0 ; } else { Try convertible dataTypes for ( type in responses ) { if ( !dataTypes 0 s.converters type + " " + dataTypes0 ) { finalDataType = type; break; } if ( !firstDataType ) { firstDataType = type; } } Or just use first one finalDataType = finalDataType firstDataType; } If we found a dataType We add the dataType to the list if needed and return the corresponding response if ( finalDataType ) { if ( finalDataType !== dataTypes 0 ) { dataTypes.unshift( finalDataType ); } return responses finalDataType ; } } * Chain conversions given the request and the original response * Also sets the responseXXX fields on the jqXHR instance * function ajaxConvert( s, response, jqXHR, isSuccess ) { var conv2, current, conv, tmp, prev, converters = {}, Work with a copy of dataTypes in case we need to modify it for conversion dataTypes = s.dataTypes.slice(); Create converters map with lowercased keys if ( dataTypes 1 ) { for ( conv in s.converters ) { converters conv.toLowerCase() = s.converters conv ; } } current = dataTypes.shift(); Convert to each sequential dataType while ( current ) { if ( s.responseFields current ) { jqXHR s.responseFields current = response; } Apply the dataFilter if provided if ( !prev && isSuccess && s.dataFilter ) { response = s.dataFilter( response, s.dataType ); } prev = current; current = dataTypes.shift(); if ( current ) { There's only work to do if current dataType is non-auto if ( current === "*" ) { current = prev; Convert response if prev dataType is non-auto and differs from current } else if ( prev !== "*" && prev !== current ) { Seek a direct converter conv = converters prev + " " + current converters "* " + current ; If none found, seek a pair if ( !conv ) { for ( conv2 in converters ) { If conv2 outputs current tmp = conv2.split( " " ); if ( tmp 1 === current ) { If prev can be converted to accepted input conv = converters prev + " " + tmp 0 converters "* " + tmp 0 ; if ( conv ) { Condense equivalence converters if ( conv === true ) { conv = converters conv2 ; Otherwise, insert the intermediate dataType } else if ( converters conv2 !== true ) { current = tmp 0 ; dataTypes.unshift( tmp 1 ); } break; } } } } Apply converter (if not an equivalence) if ( conv !== true ) { Unless errors are allowed to bubble, catch and return them if ( conv && s "throws" ) { response = conv( response ); } else { try { response = conv( response ); } catch ( e ) { return { state: "parsererror", error: conv ? |
| 3 | e : "No conversion from " + prev + " to " + current }; } } } } } } return { state: "success", data: response }; } jQuery.extend({ Counter for holding the number of active queries active: 0, Last-Modified header cache for next request lastModified: {}, etag: {}, ajaxSettings: { url: ajaxLocation, type: "GET", isLocal: rlocalProtocol.test( ajaxLocParts 1 ), global: true, processData: true, async: true, contentType: "applicationx-www-form-urlencoded; charset=UTF-8", * timeout: 0, data: null, dataType: null, username: null, password: null, cache: null, throws: false, traditional: false, headers: {}, * accepts: { "*": allTypes, text: "textplain", html: "texthtml", xml: "applicationxml, textxml", json: "applicationjson, textjavascript" }, contents: { xml: xml, html: html, json: json }, responseFields: { xml: "responseXML", text: "responseText", json: "responseJSON" }, Data converters Keys separate source (or catchall "*") and destination types with a single space converters: { Convert anything to text "* text": String, Text to html (true = no transformation) "text html": true, Evaluate text as a json expression "text json": jQuery.parseJSON, Parse text as xml "text xml": jQuery.parseXML }, For options that shouldn't be deep extended: you can add your own custom options here if and when you create one that shouldn't be deep extended (see ajaxExtend) flatOptions: { url: true, context: true } }, Creates a full fledged settings object into target with both ajaxSettings and settings fields. |
| 4 | If target is omitted, writes into ajaxSettings. ajaxSetup: function( target, settings ) { return settings ? Building a settings object ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : Extending ajaxSettings ajaxExtend( jQuery.ajaxSettings, target ); }, ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), ajaxTransport: addToPrefiltersOrTransports( transports ), Main method ajax: function( url, options ) { If url is an object, simulate pre-1.5 signature if ( typeof url === "object" ) { options = url; url = undefined; } Force options to be an object options = options {}; var transport, URL without anti-cache param cacheURL, Response headers responseHeadersString, responseHeaders, timeout handle timeoutTimer, Cross-domain detection vars parts, To know if global events are to be dispatched fireGlobals, Loop variable i, Create the final options object s = jQuery.ajaxSetup( {}, options ), Callbacks context callbackContext = s.context s, Context for global events is callbackContext if it is a DOM node or jQuery collection globalEventContext = s.context && ( callbackContext.nodeType callbackContext.jquery ) ? |
| 5 | jQuery( callbackContext ) : jQuery.event, Deferreds deferred = jQuery.Deferred(), completeDeferred = jQuery.Callbacks("once memory"), Status-dependent callbacks statusCode = s.statusCode {}, Headers (they are sent all at once) requestHeaders = {}, requestHeadersNames = {}, The jqXHR state state = 0, Default abort message strAbort = "canceled", Fake xhr jqXHR = { readyState: 0, Builds headers hashtable if needed getResponseHeader: function( key ) { var match; if ( state === 2 ) { if ( !responseHeaders ) { responseHeaders = {}; while ( (match = rheaders.exec( responseHeadersString )) ) { responseHeaders match1.toLowerCase() = match 2 ; } } match = responseHeaders key.toLowerCase() ; } return match == null ? |
| 6 | null : match; }, Raw string getAllResponseHeaders: function() { return state === 2 ? responseHeadersString : null; }, Caches the header setRequestHeader: function( name, value ) { var lname = name.toLowerCase(); if ( !state ) { name = requestHeadersNames lname = requestHeadersNames lname name; requestHeaders name = value; } return this; }, Overrides response content-type header overrideMimeType: function( type ) { if ( !state ) { s.mimeType = type; } return this; }, Status-dependent callbacks statusCode: function( map ) { var code; if ( map ) { if ( state < 2 ) { for ( code in map ) { Lazy-add the new callback in a way that preserves old ones statusCode code = statusCode code , map code ; } } else { Execute the appropriate callbacks jqXHR.always( map jqXHR.status ); } } return this; }, Cancel the request abort: function( statusText ) { var finalText = statusText strAbort; if ( transport ) { transport.abort( finalText ); } done( 0, finalText ); return this; } }; Attach deferreds deferred.promise( jqXHR ).complete = completeDeferred.add; jqXHR.success = jqXHR.done; jqXHR.error = jqXHR.fail; Remove hash character (#7531: and string promotion) Add protocol if not provided (prefilters might expect it) Handle falsy url in the settings object (#10093: consistency with old signature) We also use the url parameter if available s.url = ( ( url s.url ajaxLocation ) + "" ).replace( rhash, "" ) .replace( rprotocol, ajaxLocParts 1 + "" ); Alias method option to type as per ticket #12004 s.type = options.method options.type s.method s.type; Extract dataTypes list s.dataTypes = jQuery.trim( s.dataType "*" ).toLowerCase().match( rnotwhite ) "" ; A cross-domain request is in order when we have a protocol:host:port mismatch if ( s.crossDomain == null ) { parts = rurl.exec( s.url.toLowerCase() ); s.crossDomain = !!( parts && ( parts 1 !== ajaxLocParts 1 parts 2 !== ajaxLocParts 2 ( parts 3 ( parts 1 === "http:" ? |
| 7 | "80" : "443" ) ) !== ( ajaxLocParts 3 ( ajaxLocParts 1 === "http:" ? "80" : "443" ) ) ) ); } Convert data if not already a string if ( s.data && s.processData && typeof s.data !== "string" ) { s.data = jQuery.param( s.data, s.traditional ); } Apply prefilters inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); If request was aborted inside a prefilter, stop there if ( state === 2 ) { return jqXHR; } We can fire global events as of now if asked to Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) fireGlobals = jQuery.event && s.global; Watch for a new set of requests if ( fireGlobals && jQuery.active++ === 0 ) { jQuery.event.trigger("ajaxStart"); } Uppercase the type s.type = s.type.toUpperCase(); Determine if request has content s.hasContent = !rnoContent.test( s.type ); Save the URL in case we're toying with the If-Modified-Since andor If-None-Match header later on cacheURL = s.url; More options handling for requests with no content if ( !s.hasContent ) { If data is available, append data to url if ( s.data ) { cacheURL = ( s.url += ( rquery.test( cacheURL ) ? |
| 8 | "&" : "?" ) + s.data ); #9682: remove data so that it's not used in an eventual retry delete s.data; } Add anti-cache in url if needed if ( s.cache === false ) { s.url = rts.test( cacheURL ) ? If there is already a '_' parameter, set its value cacheURL.replace( rts, "$1_=" + nonce++ ) : Otherwise add one to the end cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++; } } Set the If-Modified-Since andor If-None-Match header, if in ifModified mode. |
| 9 | if ( s.ifModified ) { if ( jQuery.lastModified cacheURL ) { jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified cacheURL ); } if ( jQuery.etag cacheURL ) { jqXHR.setRequestHeader( "If-None-Match", jQuery.etag cacheURL ); } } Set the correct header, if data is being sent if ( s.data && s.hasContent && s.contentType !== false options.contentType ) { jqXHR.setRequestHeader( "Content-Type", s.contentType ); } Set the Accepts header for the server, depending on the dataType jqXHR.setRequestHeader( "Accept", s.dataTypes 0 && s.accepts s.dataTypes0 ? s.accepts s.dataTypes0 + ( s.dataTypes 0 !== "*" ? |
| 10 | ", " + allTypes + "; q=0.01" : "" ) : s.accepts "*" ); Check for headers option for ( i in s.headers ) { jqXHR.setRequestHeader( i, s.headers i ); } Allow custom headersmimetypes and early abort if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false state === 2 ) ) { Abort if not done already and return return jqXHR.abort(); } Aborting is no longer a cancellation strAbort = "abort"; Install callbacks on deferreds for ( i in { success: 1, error: 1, complete: 1 } ) { jqXHR i ( s i ); } Get transport transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); If no transport, we auto-abort if ( !transport ) { done( -1, "No Transport" ); } else { jqXHR.readyState = 1; Send global event if ( fireGlobals ) { globalEventContext.trigger( "ajaxSend", jqXHR, s ); } Timeout if ( s.async && s.timeout > 0 ) { timeoutTimer = setTimeout(function() { jqXHR.abort("timeout"); }, s.timeout ); } try { state = 1; transport.send( requestHeaders, done ); } catch ( e ) { Propagate exception as error if not done if ( state < 2 ) { done( -1, e ); Simply rethrow otherwise } else { throw e; } } } Callback for when everything is done function done( status, nativeStatusText, responses, headers ) { var isSuccess, success, error, response, modified, statusText = nativeStatusText; Called once if ( state === 2 ) { return; } State is "done" now state = 2; Clear timeout if it exists if ( timeoutTimer ) { clearTimeout( timeoutTimer ); } Dereference transport for early garbage collection (no matter how long the jqXHR object will be used) transport = undefined; Cache response headers responseHeadersString = headers ""; Set readyState jqXHR.readyState = status > 0 ? |
| … |
Комментарии