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 ;
127
+
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 ( ) ;
135
+
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 ) ;
238
+
239
+ if ( m . contentType ) {
240
+ m . headers [ 'content-type' ] = m . contentType ;
241
+ }
242
+
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 ) ;
282
+
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
+ } ) ;
345
+
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
+ } ;
369
+
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 ) ;
0 commit comments