Bug Tracker

Ticket #5059: ajax.txt

File ajax.txt, 15.5 KB (added by ooktay, 10 years ago)
Line 
1var jsc = now(),
2        rscript = /<script(.|\s)*?\/script>/g,
3        rselectTextarea = /select|textarea/i,
4        rinput = /text|hidden|password|search/i,
5        jsre = /=\?(&|$)/,
6        rquery = /\?/,
7        rts = /(\?|&)_=.*?(&|$)/,
8        rurl = /^(\w+:)?\/\/([^\/?#]+)/,
9        r20 = /%20/g;
10
11jQuery.fn.extend({
12        // Keep a copy of the old load
13        _load: jQuery.fn.load,
14
15        load: function( url, params, callback ) {
16                if ( typeof url !== "string" ) {
17                        return this._load( url );
18                }
19
20                var off = url.indexOf(" ");
21                if ( off >= 0 ) {
22                        var selector = url.slice(off, url.length);
23                        url = url.slice(0, off);
24                }
25
26                // Default to a GET request
27                var type = "GET";
28
29                // If the second parameter was provided
30                if ( params ) {
31                        // If it's a function
32                        if ( jQuery.isFunction( params ) ) {
33                                // We assume that it's the callback
34                                callback = params;
35                                params = null;
36
37                        // Otherwise, build a param string
38                        } else if ( typeof params === "object" ) {
39                                params = jQuery.param( params );
40                                type = "POST";
41                        }
42                }
43
44                var self = this;
45
46                // Request the remote document
47                jQuery.ajax({
48                        url: url,
49                        type: type,
50                        dataType: "html",
51                        data: params,
52                        complete: function(res, status){
53                                // If successful, inject the HTML into all the matched elements
54                                if ( status === "success" || status === "notmodified" ) {
55                                        // See if a selector was specified
56                                        self.html( selector ?
57                                                // Create a dummy div to hold the results
58                                                jQuery("<div/>")
59                                                        // inject the contents of the document in, removing the scripts
60                                                        // to avoid any 'Permission Denied' errors in IE
61                                                        .append(res.responseText.replace(rscript, ""))
62
63                                                        // Locate the specified elements
64                                                        .find(selector) :
65
66                                                // If not, just inject the full result
67                                                res.responseText );
68                                }
69
70                                if ( callback ) {
71                                        self.each( callback, [res.responseText, status, res] );
72                                }
73                        }
74                });
75
76                return this;
77        },
78
79        serialize: function() {
80                return jQuery.param(this.serializeArray());
81        },
82        serializeArray: function() {
83                return this.map(function(){
84                        return this.elements ? jQuery.makeArray(this.elements) : this;
85                })
86                .filter(function(){
87                        return this.name && !this.disabled &&
88                                (this.checked || rselectTextarea.test(this.nodeName) ||
89                                        rinput.test(this.type));
90                })
91                .map(function(i, elem){
92                        var val = jQuery(this).val();
93
94                        return val == null ?
95                                null :
96                                jQuery.isArray(val) ?
97                                        jQuery.map( val, function(val, i){
98                                                return {name: elem.name, value: val};
99                                        }) :
100                                        {name: elem.name, value: val};
101                }).get();
102        }
103});
104
105// Attach a bunch of functions for handling common AJAX events
106jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){
107        jQuery.fn[o] = function(f){
108                return this.bind(o, f);
109        };
110});
111
112jQuery.extend({
113
114        get: function( url, data, callback, type ) {
115                // shift arguments if data argument was ommited
116                if ( jQuery.isFunction( data ) ) {
117                        callback = data;
118                        data = null;
119                }
120
121                return jQuery.ajax({
122                        type: "GET",
123                        url: url,
124                        data: data,
125                        success: callback,
126                        dataType: type
127                });
128        },
129
130        getScript: function( url, callback ) {
131                return jQuery.get(url, null, callback, "script");
132        },
133
134        getJSON: function( url, data, callback ) {
135                return jQuery.get(url, data, callback, "json");
136        },
137
138        post: function( url, data, callback, type ) {
139                if ( jQuery.isFunction( data ) ) {
140                        callback = data;
141                        data = {};
142                }
143
144                return jQuery.ajax({
145                        type: "POST",
146                        url: url,
147                        data: data,
148                        success: callback,
149                        dataType: type
150                });
151        },
152
153        ajaxSetup: function( settings ) {
154                jQuery.extend( jQuery.ajaxSettings, settings );
155        },
156
157        ajaxSettings: {
158                url: location.href,
159                global: true,
160                type: "GET",
161                contentType: "application/x-www-form-urlencoded",
162                processData: true,
163                async: true,
164                /*
165                timeout: 0,
166                data: null,
167                username: null,
168                password: null,
169                */
170                // Create the request object; Microsoft failed to properly
171                // implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available
172                // This function can be overriden by calling jQuery.ajaxSetup
173                xhr: function(){
174                        return window.ActiveXObject ?
175                                new ActiveXObject("Microsoft.XMLHTTP") :
176                                new XMLHttpRequest();
177                },
178                accepts: {
179                        xml: "application/xml, text/xml",
180                        html: "text/html",
181                        script: "text/javascript, application/javascript",
182                        json: "application/json, text/javascript",
183                        text: "text/plain",
184                        _default: "*/*"
185                }
186        },
187
188        // Last-Modified header cache for next request
189        lastModified: {},
190        etag: {},
191
192        ajax: function( s ) {
193                // Extend the settings, but re-extend 's' so that it can be
194                // checked again later (in the test suite, specifically)
195                s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
196
197                var jsonp, status, data,
198                        type = s.type.toUpperCase();
199
200                // convert data if not already a string
201                if ( s.data && s.processData && typeof s.data !== "string" ) {
202                        s.data = jQuery.param(s.data);
203                }
204
205                // Handle JSONP Parameter Callbacks
206                if ( s.dataType === "jsonp" ) {
207                        if ( type === "GET" ) {
208                                if ( !jsre.test( s.url ) ) {
209                                        s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
210                                }
211                        } else if ( !s.data || !jsre.test(s.data) ) {
212                                s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
213                        }
214                        s.dataType = "json";
215                }
216
217                // Build temporary JSONP function
218                if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
219                        jsonp = "jsonp" + jsc++;
220
221                        // Replace the =? sequence both in the query string and the data
222                        if ( s.data ) {
223                                s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
224                        }
225
226                        s.url = s.url.replace(jsre, "=" + jsonp + "$1");
227
228                        // We need to make sure
229                        // that a JSONP style response is executed properly
230                        s.dataType = "script";
231
232                        // Handle JSONP-style loading
233                        window[ jsonp ] = function(tmp){
234                                data = tmp;
235                                success();
236                                complete();
237                                // Garbage collect
238                                window[ jsonp ] = undefined;
239                                try{ delete window[ jsonp ]; } catch(e){}
240                                if ( head ) {
241                                        head.removeChild( script );
242                                }
243                        };
244                }
245
246                if ( s.dataType === "script" && s.cache === null ) {
247                        s.cache = false;
248                }
249
250                if ( s.cache === false && type === "GET" ) {
251                        var ts = now();
252
253                        // try replacing _= if it is there
254                        var ret = s.url.replace(rts, "$1_=" + ts + "$2");
255
256                        // if nothing was replaced, add timestamp to the end
257                        s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
258                }
259
260                // If data is available, append data to url for get requests
261                if ( s.data && type === "GET" ) {
262                        s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
263                }
264
265                // Watch for a new set of requests
266                if ( s.global && ! jQuery.active++ ) {
267                        jQuery.event.trigger( "ajaxStart" );
268                }
269
270                // Matches an absolute URL, and saves the domain
271                var parts = rurl.exec( s.url );
272
273                // If we're requesting a remote document
274                // and trying to load JSON or Script with a GET
275                if ( s.dataType === "script" && type === "GET" && parts
276                        && ( parts[1] && parts[1] !== location.protocol || parts[2] !== location.host )) {
277
278                        var head = document.getElementsByTagName("head")[0];
279                        var script = document.createElement("script");
280                        script.src = s.url;
281                        if ( s.scriptCharset ) {
282                                script.charset = s.scriptCharset;
283                        }
284                       
285                        // ooktay: Error handling on firefox (DOM standard)
286                        script.onerror = function(){
287                                jQuery.handleError(s, null, "error");
288                                complete();
289                        };
290                       
291                        // Handle Script loading
292                        if ( !jsonp ) {
293                                var done = false;
294
295                                // Attach handlers for all browsers
296                                script.onload = script.onreadystatechange = function(){
297                                        if ( !done && (!this.readyState ||
298                                                        this.readyState === "loaded" || this.readyState === "complete") ) {
299                                                done = true;
300                                                success();
301                                                complete();
302
303                                                // Handle memory leak in IE
304                                                script.onload = script.onreadystatechange = null;
305                                                if ( head && script.parentNode ) {
306                                                        head.removeChild( script );
307                                                }
308                                        }
309                                };
310                        }
311
312                        // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
313                        // This arises when a base node is used (#2709 and #4378).
314                        head.insertBefore( script, head.firstChild );
315
316                        // We handle everything using the script element injection
317                        return undefined;
318                }
319
320                var requestDone = false;
321
322                // Create the request object
323                var xhr = s.xhr();
324
325                // Open the socket
326                // Passing null username, generates a login popup on Opera (#2865)
327                if ( s.username ) {
328                        xhr.open(type, s.url, s.async, s.username, s.password);
329                } else {
330                        xhr.open(type, s.url, s.async);
331                }
332
333                // Need an extra try/catch for cross domain requests in Firefox 3
334                try {
335                        // Set the correct header, if data is being sent
336                        if ( s.data ) {
337                                xhr.setRequestHeader("Content-Type", s.contentType);
338                        }
339
340                                // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
341                                if ( s.ifModified ) {
342                                        if ( jQuery.lastModified[s.url] ) {
343                                                xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
344                                        }
345
346                                        if ( jQuery.etag[s.url] ) {
347                                                xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
348                                        }
349                                }
350
351                        // Set header so the called script knows that it's an XMLHttpRequest
352                        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
353
354                        // Set the Accepts header for the server, depending on the dataType
355                        xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
356                                s.accepts[ s.dataType ] + ", */*" :
357                                s.accepts._default );
358                } catch(e){}
359
360                // Allow custom headers/mimetypes and early abort
361                if ( s.beforeSend && s.beforeSend(xhr, s) === false ) {
362                        // Handle the global AJAX counter
363                        if ( s.global && ! --jQuery.active ) {
364                                jQuery.event.trigger( "ajaxStop" );
365                        }
366
367                        // close opended socket
368                        xhr.abort();
369                        return false;
370                }
371
372                if ( s.global ) {
373                        jQuery.event.trigger("ajaxSend", [xhr, s]);
374                }
375
376                // Wait for a response to come back
377                var onreadystatechange = function(isTimeout){
378                        // The request was aborted, clear the interval and decrement jQuery.active
379                        if ( xhr.readyState === 0 ) {
380                                if ( ival ) {
381                                        // clear poll interval
382                                        clearInterval( ival );
383                                        ival = null;
384
385                                        // Handle the global AJAX counter
386                                        if ( s.global && ! --jQuery.active ) {
387                                                jQuery.event.trigger( "ajaxStop" );
388                                        }
389                                }
390
391                        // The transfer is complete and the data is available, or the request timed out
392                        } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
393                                requestDone = true;
394
395                                // clear poll interval
396                                if (ival) {
397                                        clearInterval(ival);
398                                        ival = null;
399                                }
400
401                                status = isTimeout === "timeout" ?
402                                        "timeout" :
403                                        !jQuery.httpSuccess( xhr ) ?
404                                                "error" :
405                                                s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
406                                                        "notmodified" :
407                                                        "success";
408
409                                if ( status === "success" ) {
410                                        // Watch for, and catch, XML document parse errors
411                                        try {
412                                                // process the data (runs the xml through httpData regardless of callback)
413                                                data = jQuery.httpData( xhr, s.dataType, s );
414                                        } catch(e) {
415                                                status = "parsererror";
416                                        }
417                                }
418
419                                // Make sure that the request was successful or notmodified
420                                if ( status === "success" || status === "notmodified" ) {
421                                        // JSONP handles its own success callback
422                                        if ( !jsonp ) {
423                                                success();
424                                        }
425                                } else {
426                                        jQuery.handleError(s, xhr, status);
427                                }
428
429                                // Fire the complete handlers
430                                complete();
431
432                                if ( isTimeout ) {
433                                        xhr.abort();
434                                }
435
436                                // Stop memory leaks
437                                if ( s.async ) {
438                                        xhr = null;
439                                }
440                        }
441                };
442
443                if ( s.async ) {
444                        // don't attach the handler to the request, just poll it instead
445                        var ival = setInterval(onreadystatechange, 13);
446
447                        // Timeout checker
448                        if ( s.timeout > 0 ) {
449                                setTimeout(function(){
450                                        // Check to see if the request is still happening
451                                        if ( xhr && !requestDone ) {
452                                                onreadystatechange( "timeout" );
453                                        }
454                                }, s.timeout);
455                        }
456                }
457
458                // Send the data
459                try {
460                        xhr.send( type === "POST" || type === "PUT" ? s.data : null );
461                } catch(e) {
462                        jQuery.handleError(s, xhr, null, e);
463                }
464
465                // firefox 1.5 doesn't fire statechange for sync requests
466                if ( !s.async ) {
467                        onreadystatechange();
468                }
469
470                function success(){
471                        // If a local callback was specified, fire it and pass it the data
472                        if ( s.success ) {
473                                s.success( data, status );
474                        }
475
476                        // Fire the global callback
477                        if ( s.global ) {
478                                jQuery.event.trigger( "ajaxSuccess", [xhr, s] );
479                        }
480                }
481
482                function complete(){
483                        // Process result
484                        if ( s.complete ) {
485                                s.complete(xhr, status);
486                        }
487
488                        // The request was completed
489                        if ( s.global ) {
490                                jQuery.event.trigger( "ajaxComplete", [xhr, s] );
491                        }
492
493                        // Handle the global AJAX counter
494                        if ( s.global && ! --jQuery.active ) {
495                                jQuery.event.trigger( "ajaxStop" );
496                        }
497                }
498
499                // return XMLHttpRequest to allow aborting the request etc.
500                return xhr;
501        },
502
503        handleError: function( s, xhr, status, e ) {
504                // If a local callback was specified, fire it
505                if ( s.error ) {
506                        s.error( xhr, status, e );
507                }
508
509                // Fire the global callback
510                if ( s.global ) {
511                        jQuery.event.trigger( "ajaxError", [xhr, s, e] );
512                }
513        },
514
515        // Counter for holding the number of active queries
516        active: 0,
517
518        // Determines if an XMLHttpRequest was successful or not
519        httpSuccess: function( xhr ) {
520                try {
521                        // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
522                        return !xhr.status && location.protocol === "file:" ||
523                                // Opera returns 0 when status is 304
524                                ( xhr.status >= 200 && xhr.status < 300 ) ||
525                                xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
526                } catch(e){}
527
528                return false;
529        },
530
531        // Determines if an XMLHttpRequest returns NotModified
532        httpNotModified: function( xhr, url ) {
533                var last_modified = xhr.getResponseHeader("Last-Modified"),
534                        etag = xhr.getResponseHeader("Etag");
535
536                if ( last_modified ) {
537                        jQuery.lastModified[url] = last_modified;
538                }
539
540                if ( etag ) {
541                        jQuery.etag[url] = etag;
542                }
543
544                // Opera returns 0 when status is 304
545                return xhr.status === 304 || xhr.status === 0;
546        },
547
548        httpData: function( xhr, type, s ) {
549                var ct = xhr.getResponseHeader("content-type"),
550                        xml = type === "xml" || !type && ct && ct.indexOf("xml") >= 0,
551                        data = xml ? xhr.responseXML : xhr.responseText;
552
553                if ( xml && data.documentElement.nodeName === "parsererror" ) {
554                        throw "parsererror";
555                }
556
557                // Allow a pre-filtering function to sanitize the response
558                // s is checked to keep backwards compatibility
559                if ( s && s.dataFilter ) {
560                        data = s.dataFilter( data, type );
561                }
562
563                // The filter can actually parse the response
564                if ( typeof data === "string" ) {
565
566                        // If the type is "script", eval it in global context
567                        if ( type === "script" ) {
568                                jQuery.globalEval( data );
569                        }
570
571                        // Get the JavaScript object, if JSON is used.
572                        if ( type === "json" ) {
573                                if ( typeof JSON === "object" && JSON.parse ) {
574                                        data = JSON.parse( data );
575                                } else {
576                                        data = (new Function("return " + data))();
577                                }
578                        }
579                }
580
581                return data;
582        },
583
584        // Serialize an array of form elements or a set of
585        // key/values into a query string
586        param: function( a ) {
587                var s = [];
588
589                function add( key, value ){
590                        s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value);
591                }
592
593                // If an array was passed in, assume that it is an array
594                // of form elements
595                if ( jQuery.isArray(a) || a.jquery ) {
596                        // Serialize the form elements
597                        jQuery.each( a, function(){
598                                add( this.name, this.value );
599                        });
600
601                // Otherwise, assume that it's an object of key/value pairs
602                } else {
603                        // Serialize the key/values
604                        for ( var j in a ) {
605                                // If the value is an array then the key names need to be repeated
606                                if ( jQuery.isArray(a[j]) ) {
607                                        jQuery.each( a[j], function(){
608                                                add( j, this );
609                                        });
610                                } else {
611                                        add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] );
612                                }
613                        }
614                }
615
616                // Return the resulting serialization
617                return s.join("&").replace(r20, "+");
618        }
619
620});