Merge "Clean up SpecialFilepath."
[mediawiki.git] / resources / jquery / jquery.mockjax.js
blob5f6e130643ddc040ec9867c1f0e5392a53e29080
1 /*!
2  * MockJax - jQuery Plugin to Mock Ajax requests
3  *
4  * Version:  1.4.0
5  * Released: 2011-02-04
6  * Source:   http://github.com/appendto/jquery-mockjax
7  * Docs:     http://enterprisejquery.com/2010/07/mock-your-ajax-requests-with-mockjax-for-rapid-development
8  * Plugin:   mockjax
9  * Author:   Jonathan Sharp (http://jdsharp.com)
10  * License:  MIT,GPL
11  * 
12  * Copyright (c) 2010 appendTo LLC.
13  * Dual licensed under the MIT or GPL licenses.
14  * http://appendto.com/open-source-licenses
15  */
16 (function($) {
17         var _ajax = $.ajax,
18                 mockHandlers = [];
19         
20         function parseXML(xml) {
21                 if ( window['DOMParser'] == undefined && window.ActiveXObject ) {
22                         DOMParser = function() { };
23                         DOMParser.prototype.parseFromString = function( xmlString ) {
24                                 var doc = new ActiveXObject('Microsoft.XMLDOM');
25                         doc.async = 'false';
26                         doc.loadXML( xmlString );
27                                 return doc;
28                         };
29                 }
30                 
31                 try {
32                         var xmlDoc      = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
33                         if ( $.isXMLDoc( xmlDoc ) ) {
34                                 var err = $('parsererror', xmlDoc);
35                                 if ( err.length == 1 ) {
36                                         throw('Error: ' + $(xmlDoc).text() );
37                                 }
38                         } else {
39                                 throw('Unable to parse XML');
40                         }
41                 } catch( e ) {
42                         var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
43                         $(document).trigger('xmlParseError', [ msg ]);
44                         return undefined;
45                 }
46                 return xmlDoc;
47         }
48         
49         $.extend({
50                 ajax: function(origSettings) {
51                         var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings),
52                             mock = false;
53                         // Iterate over our mock handlers (in registration order) until we find
54                         // one that is willing to intercept the request
55                         $.each(mockHandlers, function(k, v) {
56                                 if ( !mockHandlers[k] ) {
57                                         return;
58                                 }
59                                 var m = null;
60                                 // If the mock was registered with a function, let the function decide if we 
61                                 // want to mock this request
62                                 if ( $.isFunction(mockHandlers[k]) ) {
63                                         m = mockHandlers[k](s);
64                                 } else {
65                                         m = mockHandlers[k];
66                                         // Inspect the URL of the request and check if the mock handler's url 
67                                         // matches the url for this ajax request
68                                         if ( $.isFunction(m.url.test) ) {
69                                                 // The user provided a regex for the url, test it
70                                                 if ( !m.url.test( s.url ) ) {
71                                                         m = null;
72                                                 }
73                                         } else {
74                                                 // Look for a simple wildcard '*' or a direct URL match
75                                                 var star = m.url.indexOf('*');
76                                                 if ( ( m.url != '*' && m.url != s.url && star == -1 ) ||
77                                                         ( star > -1 && m.url.substr(0, star) != s.url.substr(0, star) ) ) {
78                                                          // The url we tested did not match the wildcard *
79                                                          m = null;
80                                                 }
81                                         }
82                                         if ( m ) {
83                                                 // Inspect the data submitted in the request (either POST body or GET query string)
84                                                 if ( m.data && s.data ) {
85                                                         var identical = false;
86                                                         // Deep inspect the identity of the objects
87                                                         (function ident(mock, live) {
88                                                                 // Test for situations where the data is a querystring (not an object)
89                                                                 if (typeof live === 'string') {
90                                                                         // Querystring may be a regex
91                                                                         identical = $.isFunction( mock.test ) ? mock.test(live) : mock == live;
92                                                                         return identical;
93                                                                 }
94                                                                 $.each(mock, function(k, v) {
95                                                                         if ( live[k] === undefined ) {
96                                                                                 identical = false;
97                                                                                 return false;
98                                                                         } else {
99                                                                                 identical = true;
100                                                                                 if ( typeof live[k] == 'object' ) {
101                                                                                         return ident(mock[k], live[k]);
102                                                                                 } else {
103                                                                                         if ( $.isFunction( mock[k].test ) ) {
104                                                                                                 identical = mock[k].test(live[k]);
105                                                                                         } else {
106                                                                                                 identical = ( mock[k] == live[k] );
107                                                                                         }
108                                                                                         return identical;
109                                                                                 }
110                                                                         }
111                                                                 });
112                                                         })(m.data, s.data);
113                                                         // They're not identical, do not mock this request
114                                                         if ( identical == false ) {
115                                                                 m = null;
116                                                         }
117                                                 }
118                                                 // Inspect the request type
119                                                 if ( m && m.type && m.type != s.type ) {
120                                                         // The request type doesn't match (GET vs. POST)
121                                                         m = null;
122                                                 }
123                                         }
124                                 }
125                                 if ( m ) {
126                                         mock = true;
128                                         // Handle console logging
129                                         var c = $.extend({}, $.mockjaxSettings, m);
130                                         if ( c.log && $.isFunction(c.log) ) {
131                                                 c.log('MOCK ' + s.type.toUpperCase() + ': ' + s.url, $.extend({}, s));
132                                         }
133                                         
134                                         var jsre = /=\?(&|$)/, jsc = (new Date()).getTime();
136                                         // Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here
137                                         // because there isn't an easy hook for the cross domain script tag of jsonp
138                                         if ( s.dataType === "jsonp" ) {
139                                                 if ( s.type.toUpperCase() === "GET" ) {
140                                                         if ( !jsre.test( s.url ) ) {
141                                                                 s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
142                                                         }
143                                                 } else if ( !s.data || !jsre.test(s.data) ) {
144                                                         s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
145                                                 }
146                                                 s.dataType = "json";
147                                         }
148                         
149                                         // Build temporary JSONP function
150                                         if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
151                                                 jsonp = s.jsonpCallback || ("jsonp" + jsc++);
152                         
153                                                 // Replace the =? sequence both in the query string and the data
154                                                 if ( s.data ) {
155                                                         s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
156                                                 }
157                         
158                                                 s.url = s.url.replace(jsre, "=" + jsonp + "$1");
159                         
160                                                 // We need to make sure
161                                                 // that a JSONP style response is executed properly
162                                                 s.dataType = "script";
163                         
164                                                 // Handle JSONP-style loading
165                                                 window[ jsonp ] = window[ jsonp ] || function( tmp ) {
166                                                         data = tmp;
167                                                         success();
168                                                         complete();
169                                                         // Garbage collect
170                                                         window[ jsonp ] = undefined;
171                         
172                                                         try {
173                                                                 delete window[ jsonp ];
174                                                         } catch(e) {}
175                         
176                                                         if ( head ) {
177                                                                 head.removeChild( script );
178                                                         }
179                                                 };
180                                         }
181                                         
182                                         var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
183                                                 parts = rurl.exec( s.url ),
184                                                 remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
185                                         
186                                         // Test if we are going to create a script tag (if so, intercept & mock)
187                                         if ( s.dataType === "script" && s.type.toUpperCase() === "GET" && remote ) {
188                                                 // Synthesize the mock request for adding a script tag
189                                                 var callbackContext = origSettings && origSettings.context || s;
190                                                 
191                                                 function success() {
192                                                         // If a local callback was specified, fire it and pass it the data
193                                                         if ( s.success ) {
194                                                                 s.success.call( callbackContext, ( m.response ? m.response.toString() : m.responseText || ''), status, {} );
195                                                         }
196                                 
197                                                         // Fire the global callback
198                                                         if ( s.global ) {
199                                                                 trigger( "ajaxSuccess", [{}, s] );
200                                                         }
201                                                 }
202                                 
203                                                 function complete() {
204                                                         // Process result
205                                                         if ( s.complete ) {
206                                                                 s.complete.call( callbackContext, {} , status );
207                                                         }
208                                 
209                                                         // The request was completed
210                                                         if ( s.global ) {
211                                                                 trigger( "ajaxComplete", [{}, s] );
212                                                         }
213                                 
214                                                         // Handle the global AJAX counter
215                                                         if ( s.global && ! --jQuery.active ) {
216                                                                 jQuery.event.trigger( "ajaxStop" );
217                                                         }
218                                                 }
219                                                 
220                                                 function trigger(type, args) {
221                                                         (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
222                                                 }
223                                                 
224                                                 if ( m.response && $.isFunction(m.response) ) {
225                                                         m.response(origSettings);
226                                                 } else {
227                                                         $.globalEval(m.responseText);
228                                                 }
229                                                 success();
230                                                 complete();
231                                                 return false;
232                                         }
233                                         mock = _ajax.call($, $.extend(true, {}, origSettings, {
234                                                 // Mock the XHR object
235                                                 xhr: function() {
236                                                         // Extend with our default mockjax settings
237                                                         m = $.extend({}, $.mockjaxSettings, m);
239                                                         if ( m.contentType ) {
240                                                                 m.headers['content-type'] = m.contentType;
241                                                         }
243                                                         // Return our mock xhr object
244                                                         return {
245                                                                 status: m.status,
246                                                                 readyState: 1,
247                                                                 open: function() { },
248                                                                 send: function() {
249                                                                         // This is a substitute for < 1.4 which lacks $.proxy
250                                                                         var process = (function(that) {
251                                                                                 return function() {
252                                                                                         return (function() {
253                                                                                                 // The request has returned
254                                                                                                 this.status             = m.status;
255                                                                                                 this.readyState         = 4;
256                                                                                 
257                                                                                                 // We have an executable function, call it to give 
258                                                                                                 // the mock handler a chance to update it's data
259                                                                                                 if ( $.isFunction(m.response) ) {
260                                                                                                         m.response(origSettings);
261                                                                                                 }
262                                                                                                 // Copy over our mock to our xhr object before passing control back to 
263                                                                                                 // jQuery's onreadystatechange callback
264                                                                                                 if ( s.dataType == 'json' && ( typeof m.responseText == 'object' ) ) {
265                                                                                                         this.responseText = JSON.stringify(m.responseText);
266                                                                                                 } else if ( s.dataType == 'xml' ) {
267                                                                                                         if ( typeof m.responseXML == 'string' ) {
268                                                                                                                 this.responseXML = parseXML(m.responseXML);
269                                                                                                         } else {
270                                                                                                                 this.responseXML = m.responseXML;
271                                                                                                         }
272                                                                                                 } else {
273                                                                                                         this.responseText = m.responseText;
274                                                                                                 }
275                                                                                                 // jQuery < 1.4 doesn't have onreadystate change for xhr
276                                                                                                 if ( $.isFunction(this.onreadystatechange) ) {
277                                                                                                         this.onreadystatechange( m.isTimeout ? 'timeout' : undefined );
278                                                                                                 }
279                                                                                         }).apply(that);
280                                                                                 };
281                                                                         })(this);
283                                                                         if ( m.proxy ) {
284                                                                                 // We're proxying this request and loading in an external file instead
285                                                                                 _ajax({
286                                                                                         global: false,
287                                                                                         url: m.proxy,
288                                                                                         type: m.proxyType,
289                                                                                         data: m.data,
290                                                                                         dataType: s.dataType,
291                                                                                         complete: function(xhr, txt) {
292                                                                                                 m.responseXML = xhr.responseXML;
293                                                                                                 m.responseText = xhr.responseText;
294                                                                                                 this.responseTimer = setTimeout(process, m.responseTime || 0);
295                                                                                         }
296                                                                                 });
297                                                                         } else {
298                                                                                 // type == 'POST' || 'GET' || 'DELETE'
299                                                                                 if ( s.async === false ) {
300                                                                                         // TODO: Blocking delay
301                                                                                         process();
302                                                                                 } else {
303                                                                                         this.responseTimer = setTimeout(process, m.responseTime || 50);
304                                                                                 }
305                                                                         }
306                                                                 },
307                                                                 abort: function() {
308                                                                         clearTimeout(this.responseTimer);
309                                                                 },
310                                                                 setRequestHeader: function() { },
311                                                                 getResponseHeader: function(header) {
312                                                                         // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
313                                                                         if ( m.headers && m.headers[header] ) {
314                                                                                 // Return arbitrary headers
315                                                                                 return m.headers[header];
316                                                                         } else if ( header.toLowerCase() == 'last-modified' ) {
317                                                                                 return m.lastModified || (new Date()).toString();
318                                                                         } else if ( header.toLowerCase() == 'etag' ) {
319                                                                                 return m.etag || '';
320                                                                         } else if ( header.toLowerCase() == 'content-type' ) {
321                                                                                 return m.contentType || 'text/plain';
322                                                                         }
323                                                                 },
324                                                                 getAllResponseHeaders: function() {
325                                                                         var headers = '';
326                                                                         $.each(m.headers, function(k, v) {
327                                                                                 headers += k + ': ' + v + "\n";
328                                                                         });
329                                                                         return headers;
330                                                                 }
331                                                         };
332                                                 }
333                                         }));
334                                         return false;
335                                 }
336                         });
337                         // We don't have a mock request, trigger a normal request
338                         if ( !mock ) {
339                                 return _ajax.apply($, arguments);
340                         } else {
341                                 return mock;
342                         }
343                 }
344         });
346         $.mockjaxSettings = {
347                 //url:        null,
348                 //type:       'GET',
349                 log:          function(msg) {
350                                 window['console'] && window.console.log && window.console.log(msg);
351                               },
352                 status:       200,
353                 responseTime: 500,
354                 isTimeout:    false,
355                 contentType:  'text/plain',
356                 response:     '', 
357                 responseText: '',
358                 responseXML:  '',
359                 proxy:        '',
360                 proxyType:    'GET',
361                 
362                 lastModified: null,
363                 etag:         '',
364                 headers: {
365                         etag: 'IJF@H#@923uf8023hFO@I#H#',
366                         'content-type' : 'text/plain'
367                 }
368         };
370         $.mockjax = function(settings) {
371                 var i = mockHandlers.length;
372                 mockHandlers[i] = settings;
373                 return i;
374         };
375         $.mockjaxClear = function(i) {
376                 if ( arguments.length == 1 ) {
377                         mockHandlers[i] = null;
378                 } else {
379                         mockHandlers = [];
380                 }
381         };
382 })(jQuery);