PageSourceSearch

https://zan.kz/lib/jquery-mockjax/dist/jquery.mockjax.js

js zan.kz collected 2026-09-24 15:34:08 UTC 27,834 bytes, 854 lines download raw bytes

1/*! jQuery Mockjax
2 * A Plugin providing simple and flexible mocking of ajax requests and responses
3 * 
4 * Version: 2.1.1
5 * Home: https://github.com/jakerella/jquery-mockjax
6 * Copyright (c) 2016 Jordan Kasper, formerly appendTo;
7 * NOTE: This repository was taken over by Jordan Kasper (@jakerella) October, 2014
8 * 
9 * Dual licensed under the MIT or GPL licenses.
10 * http://opensource.org/licenses/MIT OR http://www.gnu.org/licenses/gpl-2.0.html
11 */
12(function(root, factory) {
13	'use strict';
14
15	// AMDJS module definition
16	if ( typeof define === 'function' && define.amd && define.amd.jQuery ) {
17		define(['jquery'], function($) {
18			return factory($, root);
19		});
20
21	// CommonJS module definition
22	} else if ( typeof exports === 'object') {
23
24		// NOTE: To use Mockjax as a Node module you MUST provide the factory with
25		// a valid version of jQuery and a window object (the global scope):
26		// var mockjax = require('jquery.mockjax')(jQuery, window);
27
28		module.exports = factory;
29
30	// Global jQuery in web browsers
31	} else {
32		return factory(root.jQuery || root.$, root);
33	}
34}(this, function($, window) {
35	'use strict';
36
37	var _ajax = $.ajax,
38		mockHandlers = [],
39		mockedAjaxCalls = [],
40		unmockedAjaxCalls = [],
41		CALLBACK_REGEX = /=\?(&|$)/,
42		jsc = (new Date()).getTime();
43
44
45	// Parse the given XML string.
46	function parseXML(xml) {
47		if ( window.DOMParser === undefined && window.ActiveXObject ) {
48			window.DOMParser = function() { };
49			DOMParser.prototype.parseFromString = function( xmlString ) {
50				var doc = new ActiveXObject('Microsoft.XMLDOM');
51				doc.async = 'false';
52				doc.loadXML( xmlString );
53				return doc;
54			};
55		}
56
57		try {
58			var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
59			if ( $.isXMLDoc( xmlDoc ) ) {
60				var err = $('parsererror', xmlDoc);
61				if ( err.length === 1 ) {
62					throw new Error('Error: ' + $(xmlDoc).text() );
63				}
64			} else {
65				throw new Error('Unable to parse XML');
66			}
67			return xmlDoc;
68		} catch( e ) {
69			var msg = ( e.name === undefined ? e : e.name + ': ' + e.message );
70			$(document).trigger('xmlParseError', [ msg ]);
71			return undefined;
72		}
73	}
74
75	// Check if the data field on the mock handler and the request match. This
76	// can be used to restrict a mock handler to being used only when a certain
77	// set of data is passed to it.
78	function isMockDataEqual( mock, live ) {
79		var identical = true;
80		// Test for situations where the data is a querystring (not an object)
81		if (typeof live === 'string') {
82			// Querystring may be a regex
83			if ($.isFunction( mock.test )) {
84				return mock.test(live);
85			} else if (typeof mock === 'object') {
86				live = getQueryParams(live);
87			} else {
88				return mock === live;
89			}
90		}
91		
92		$.each(mock, function(k) {
93			if ( live[k] === undefined ) {
94				identical = false;
95				return identical;
96			} else {
97				if ( typeof live[k] === 'object' && live[k] !== null ) {
98					if ( identical && $.isArray( live[k] ) ) {
99						identical = $.isArray( mock[k] ) && live[k].length === mock[k].length;
100					}
101					identical = identical && isMockDataEqual(mock[k], live[k]);
102				} else {
103					if ( mock[k] && $.isFunction( mock[k].test ) ) {
104						identical = identical && mock[k].test(live[k]);
105					} else {
106						identical = identical && ( mock[k] === live[k] );
107					}
108				}
109			}
110		});
111
112		return identical;
113	}
114	
115	function getQueryParams(queryString) {
116		var i, l, param, tmp,
117			paramsObj = {},
118			params = String(queryString).split(/&/);
119		
120		for (i=0, l=params.length; i<l; ++i) {
121			param = params[i];
122			try {
123				param = decodeURIComponent(param.replace(/\+/g, ' '));
124				param = param.split(/=/);
125			} catch(e) {
126				// Can't parse this one, so let it go?
127				continue;
128			}
129			
130			if (paramsObj[param[0]]) {
131				// this is an array query param (more than one entry in query)
132				if (!paramsObj[param[0]].splice) {
133					// if not already an array, make it one
134					tmp = paramsObj[param[0]];
135					paramsObj[param[0]] = [];
136					paramsObj[param[0]].push(tmp);
137				}
138				paramsObj[param[0]].push(param[1]);
139			} else {
140				paramsObj[param[0]] = param[1];
141			}
142		}
143		
144		return paramsObj;
145	}
146
147	// See if a mock handler property matches the default settings
148	function isDefaultSetting(handler, property) {
149		return handler[property] === $.mockjaxSettings[property];
150	}
151
152	// Check the given handler should mock the given request
153	function getMockForRequest( handler, requestSettings ) {
154		// If the mock was registered with a function, let the function decide if we
155		// want to mock this request
156		if ( $.isFunction(handler) ) {
157			return handler( requestSettings );
158		}
159
160		// Inspect the URL of the request and check if the mock handler's url
161		// matches the url for this ajax request
162		if ( $.isFunction(handler.url.test) ) {
163			// The user provided a regex for the url, test it
164			if ( !handler.url.test( requestSettings.url ) ) {
165				return null;
166			}
167		} else {
168
169			// Apply namespace prefix to the mock handler's url.
170			var namespace = handler.namespace || $.mockjaxSettings.namespace;
171			if (!!namespace) {
172				var namespacedUrl = [namespace, handler.url].join('/');
173				namespacedUrl = namespacedUrl.replace(/(\/+)/g, '/');
174				handler.url = namespacedUrl;
175			}
176
177			// Look for a simple wildcard '*' or a direct URL match
178			var star = handler.url.indexOf('*');
179			if (handler.url !== requestSettings.url && star === -1 ||
180					!new RegExp(handler.url.replace(/[-[\]{}()+?.,\\^$|#\s]/g, '\\$&').replace(/\*/g, '.+')).test(requestSettings.url)) {
181				return null;
182			}
183		}
184
185		// Inspect the request headers submitted
186		if ( handler.requestHeaders ) {
187			//No expectation for headers, do not mock this request
188			if (requestSettings.headers === undefined) {
189				return null;
190			} else {
191				var headersMismatch = false;
192				$.each(handler.requestHeaders, function(key, value) {
193					var v = requestSettings.headers[key];
194					if(v !== value) {
195						headersMismatch = true;
196						return false;
197					}
198				});
199				//Headers do not match, do not mock this request
200				if (headersMismatch) {
201					return null;
202				}
203			}
204		}
205
206		// Inspect the data submitted in the request (either POST body or GET query string)
207		if ( handler.data ) {
208			if ( !requestSettings.data || !isMockDataEqual(handler.data, requestSettings.data) ) {
209				// They're not identical, do not mock this request
210				return null;
211			}
212		}
213		// Inspect the request type
214		if ( handler && handler.type &&
215				handler.type.toLowerCase() !== requestSettings.type.toLowerCase() ) {
216			// The request type doesn't match (GET vs. POST)
217			return null;
218		}
219
220		return handler;
221	}
222
223	function isPosNum(value) {
224		return typeof value === 'number' && value >= 0;
225	}
226
227	function parseResponseTimeOpt(responseTime) {
228		if ($.isArray(responseTime) && responseTime.length === 2) {
229			var min = responseTime[0];
230			var max = responseTime[1];
231			if(isPosNum(min) && isPosNum(max)) {
232				return Math.floor(Math.random() * (max - min)) + min;
233			}
234		} else if(isPosNum(responseTime)) {
235			return responseTime;
236		}
237		return DEFAULT_RESPONSE_TIME;
238	}
239
240	// Process the xhr objects send operation
241	function _xhrSend(mockHandler, requestSettings, origSettings) {
242
243		// This is a substitute for < 1.4 which lacks $.proxy
244		var process = (function(that) {
245			return function() {
246				return (function() {
247					// The request has returned
248					this.status = mockHandler.status;
249					this.statusText = mockHandler.statusText;
250					this.readyState	= 1;
251
252					var finishRequest = function () {
253						this.readyState	= 4;
254
255						var onReady;
256						// Copy over our mock to our xhr object before passing control back to
257						// jQuery's onreadystatechange callback
258						if ( requestSettings.dataType === 'json' && ( typeof mockHandler.responseText === 'object' ) ) {
259							this.responseText = JSON.stringify(mockHandler.responseText);
260						} else if ( requestSettings.dataType === 'xml' ) {
261							if ( typeof mockHandler.responseXML === 'string' ) {
262								this.responseXML = parseXML(mockHandler.responseXML);
263								//in jQuery 1.9.1+, responseXML is processed differently and relies on responseText
264								this.responseText = mockHandler.responseXML;
265							} else {
266								this.responseXML = mockHandler.responseXML;
267							}
268						} else if (typeof mockHandler.responseText === 'object' && mockHandler.responseText !== null) {
269							// since jQuery 1.9 responseText type has to match contentType
270							mockHandler.contentType = 'application/json';
271							this.responseText = JSON.stringify(mockHandler.responseText);
272						} else {
273							this.responseText = mockHandler.responseText;
274						}
275						if( typeof mockHandler.status === 'number' || typeof mockHandler.status === 'string' ) {
276							this.status = mockHandler.status;
277						}
278						if( typeof mockHandler.statusText === 'string') {
279							this.statusText = mockHandler.statusText;
280						}
281						// jQuery 2.0 renamed onreadystatechange to onload
282						onReady = this.onload || this.onreadystatechange;
283
284						// jQuery < 1.4 doesn't have onreadystate change for xhr
285						if ( $.isFunction( onReady ) ) {
286							if( mockHandler.isTimeout) {
287								this.status = -1;
288							}
289							onReady.call( this, mockHandler.isTimeout ? 'timeout' : undefined );
290						} else if ( mockHandler.isTimeout ) {
291							// Fix for 1.3.2 timeout to keep success from firing.
292							this.status = -1;
293						}
294					};
295
296					// We have an executable function, call it to give
297					// the mock handler a chance to update it's data
298					if ( $.isFunction(mockHandler.response) ) {
299						// Wait for it to finish
300						if ( mockHandler.response.length === 2 ) {
301							mockHandler.response(origSettings, function () {
302								finishRequest.call(that);
303							});
304							return;
305						} else {
306							mockHandler.response(origSettings);
307						}
308					}
309
310					finishRequest.call(that);
311				}).apply(that);
312			};
313		})(this);
314
315		if ( mockHandler.proxy ) {
316			// We're proxying this request and loading in an external file instead
317			_ajax({
318				global: false,
319				url: mockHandler.proxy,
320				type: mockHandler.proxyType,
321				data: mockHandler.data,
322				async: requestSettings.async,
323				dataType: requestSettings.dataType === 'script' ? 'text/plain' : requestSettings.dataType,
324				complete: function(xhr) {
325					// Fix for bug #105
326					// jQuery will convert the text to XML for us, and if we use the actual responseXML here
327					// then some other things don't happen, resulting in no data given to the 'success' cb
328					mockHandler.responseXML = mockHandler.responseText = xhr.responseText;
329					
330					// Don't override the handler status/statusText if it's specified by the config
331					if (isDefaultSetting(mockHandler, 'status')) {
332						mockHandler.status = xhr.status;
333					}
334					if (isDefaultSetting(mockHandler, 'statusText')) {
335						mockHandler.statusText = xhr.statusText;
336					}
337
338					if ( requestSettings.async === false ) {
339						// TODO: Blocking delay
340						process();
341					} else {
342						this.responseTimer = setTimeout(process, parseResponseTimeOpt(mockHandler.responseTime));
343					}
344				}
345			});
346		} else {
347			// type === 'POST' || 'GET' || 'DELETE'
348			if ( requestSettings.async === false ) {
349				// TODO: Blocking delay
350				process();
351			} else {
352				this.responseTimer = setTimeout(process, parseResponseTimeOpt(mockHandler.responseTime));
353			}
354		}
355
356	}
357
358	// Construct a mocked XHR Object
359	function xhr(mockHandler, requestSettings, origSettings, origHandler) {
360		// Extend with our default mockjax settings
361		mockHandler = $.extend(true, {}, $.mockjaxSettings, mockHandler);
362
363		if (typeof mockHandler.headers === 'undefined') {
364			mockHandler.headers = {};
365		}
366		if (typeof requestSettings.headers === 'undefined') {
367			requestSettings.headers = {};
368		}
369		if ( mockHandler.contentType ) {
370			mockHandler.headers['content-type'] = mockHandler.contentType;
371		}
372
373		return {
374			status: mockHandler.status,
375			statusText: mockHandler.statusText,
376			readyState: 1,
377			open: function() { },
378			send: function() {
379				origHandler.fired = true;
vendor: 4,495 bytes, lines 380-490
380				_xhrSend.call(this, mockHandler, requestSettings, origSettings);
381			},
382			abort: function() {
383				clearTimeout(this.responseTimer);
384			},
385			setRequestHeader: function(header, value) {
386				requestSettings.headers[header] = value;
387			},
388			getResponseHeader: function(header) {
389				// 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
390				if ( mockHandler.headers && mockHandler.headers[header] ) {
391					// Return arbitrary headers
392					return mockHandler.headers[header];
393				} else if ( header.toLowerCase() === 'last-modified' ) {
394					return mockHandler.lastModified || (new Date()).toString();
395				} else if ( header.toLowerCase() === 'etag' ) {
396					return mockHandler.etag || '';
397				} else if ( header.toLowerCase() === 'content-type' ) {
398					return mockHandler.contentType || 'text/plain';
399				}
400			},
401			getAllResponseHeaders: function() {
402				var headers = '';
403				// since jQuery 1.9 responseText type has to match contentType
404				if (mockHandler.contentType) {
405					mockHandler.headers['Content-Type'] = mockHandler.contentType;
406				}
407				$.each(mockHandler.headers, function(k, v) {
408					headers += k + ': ' + v + '\n';
409				});
410				return headers;
411			}
412		};
413	}
414
415	// Process a JSONP mock request.
416	function processJsonpMock( requestSettings, mockHandler, origSettings ) {
417		// Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here
418		// because there isn't an easy hook for the cross domain script tag of jsonp
419
420		processJsonpUrl( requestSettings );
421
422		requestSettings.dataType = 'json';
423		if(requestSettings.data && CALLBACK_REGEX.test(requestSettings.data) || CALLBACK_REGEX.test(requestSettings.url)) {
424			createJsonpCallback(requestSettings, mockHandler, origSettings);
425
426			// We need to make sure
427			// that a JSONP style response is executed properly
428
429			var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
430				parts = rurl.exec( requestSettings.url ),
431				remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
432
433			requestSettings.dataType = 'script';
434			if(requestSettings.type.toUpperCase() === 'GET' && remote ) {
435				var newMockReturn = processJsonpRequest( requestSettings, mockHandler, origSettings );
436
437				// Check if we are supposed to return a Deferred back to the mock call, or just
438				// signal success
439				if(newMockReturn) {
440					return newMockReturn;
441				} else {
442					return true;
443				}
444			}
445		}
446		return null;
447	}
448
449	// Append the required callback parameter to the end of the request URL, for a JSONP request
450	function processJsonpUrl( requestSettings ) {
451		if ( requestSettings.type.toUpperCase() === 'GET' ) {
452			if ( !CALLBACK_REGEX.test( requestSettings.url ) ) {
453				requestSettings.url += (/\?/.test( requestSettings.url ) ? '&' : '?') +
454					(requestSettings.jsonp || 'callback') + '=?';
455			}
456		} else if ( !requestSettings.data || !CALLBACK_REGEX.test(requestSettings.data) ) {
457			requestSettings.data = (requestSettings.data ? requestSettings.data + '&' : '') + (requestSettings.jsonp || 'callback') + '=?';
458		}
459	}
460
461	// Process a JSONP request by evaluating the mocked response text
462	function processJsonpRequest( requestSettings, mockHandler, origSettings ) {
463		// Synthesize the mock request for adding a script tag
464		var callbackContext = origSettings && origSettings.context || requestSettings,
465			// If we are running under jQuery 1.5+, return a deferred object
466			newMock = ($.Deferred) ? (new $.Deferred()) : null;
467
468		// If the response handler on the moock is a function, call it
469		if ( mockHandler.response && $.isFunction(mockHandler.response) ) {
470
471			mockHandler.response(origSettings);
472
473
474		} else if ( typeof mockHandler.responseText === 'object' ) {
475			// Evaluate the responseText javascript in a global context
476			$.globalEval( '(' + JSON.stringify( mockHandler.responseText ) + ')');
477
478		} else if (mockHandler.proxy) {
479
480			// This handles the unique case where we have a remote URL, but want to proxy the JSONP
481			// response to another file (not the same URL as the mock matching)
482			_ajax({
483				global: false,
484				url: mockHandler.proxy,
485				type: mockHandler.proxyType,
486				data: mockHandler.data,
487				dataType: requestSettings.dataType === 'script' ? 'text/plain' : requestSettings.dataType,
488				complete: function(xhr) {
489					$.globalEval( '(' + xhr.responseText + ')');
490					completeJsonpCall( requestSettings, mockHandler, callbackContext, newM
490ock );
491				}
492			});
493
494			return newMock;
495
496		} else {
497			$.globalEval( '(' + 
498				((typeof mockHandler.responseText === 'string') ? 
499					('"' + mockHandler.responseText + '"') : mockHandler.responseText) +
500			')');
501		}
502
503		completeJsonpCall( requestSettings, mockHandler, callbackContext, newMock );
504
505		return newMock;
506	}
507
508	function completeJsonpCall( requestSettings, mockHandler, callbackContext, newMock ) {
509		var json;
510
511		// Successful response
512		setTimeout(function() {
513			jsonpSuccess( requestSettings, callbackContext, mockHandler );
514			jsonpComplete( requestSettings, callbackContext );
515		}, parseResponseTimeOpt( mockHandler.responseTime ));
516
517		if ( newMock ) {
518			try {
519				json = $.parseJSON( mockHandler.responseText );
520			} catch (err) { /* just checking... */ }
521
522			newMock.resolveWith( callbackContext, [json || mockHandler.responseText] );
523		}
524	}
525
526
527	// Create the required JSONP callback function for the request
528	function createJsonpCallback( requestSettings, mockHandler, origSettings ) {
529		var callbackContext = origSettings && origSettings.context || requestSettings;
530		var jsonp = (typeof requestSettings.jsonpCallback === 'string' && requestSettings.jsonpCallback) || ('jsonp' + jsc++);
531
532		// Replace the =? sequence both in the query string and the data
533		if ( requestSettings.data ) {
534			requestSettings.data = (requestSettings.data + '').replace(CALLBACK_REGEX, '=' + jsonp + '$1');
535		}
536
537		requestSettings.url = requestSettings.url.replace(CALLBACK_REGEX, '=' + jsonp + '$1');
538
539
540		// Handle JSONP-style loading
541		window[ jsonp ] = window[ jsonp ] || function() {
542			jsonpSuccess( requestSettings, callbackContext, mockHandler );
543			jsonpComplete( requestSettings, callbackContext );
544			// Garbage collect
545			window[ jsonp ] = undefined;
546
547			try {
548				delete window[ jsonp ];
549			} catch(e) {}
550		};
551		requestSettings.jsonpCallback = jsonp;
552	}
553
554	// The JSONP request was successful
555	function jsonpSuccess(requestSettings, callbackContext, mockHandler) {
556		// If a local callback was specified, fire it and pass it the data
557		if ( requestSettings.success ) {
558			requestSettings.success.call( callbackContext, mockHandler.responseText || '', 'success', {} );
559		}
560
561		// Fire the global callback
562		if ( requestSettings.global ) {
563			(requestSettings.context ? $(requestSettings.context) : $.event).trigger('ajaxSuccess', [{}, requestSettings]);
564		}
565	}
566
567	// The JSONP request was completed
568	function jsonpComplete(requestSettings, callbackContext) {
569		if ( requestSettings.complete ) {
570			requestSettings.complete.call( callbackContext, {
571				statusText: 'success',
572				status: 200
573			} , 'success' );
574		}
575
576		// The request was completed
577		if ( requestSettings.global ) {
578			(requestSettings.context ? $(requestSettings.context) : $.event).trigger('ajaxComplete', [{}, requestSettings]);
579		}
580
581		// Handle the global AJAX counter
582		if ( requestSettings.global && ! --$.active ) {
583			$.event.trigger( 'ajaxStop' );
584		}
585	}
586
587
588	// The core $.ajax replacement.
589	function handleAjax( url, origSettings ) {
590		var mockRequest, requestSettings, mockHandler, overrideCallback;
591
592		// If url is an object, simulate pre-1.5 signature
593		if ( typeof url === 'object' ) {
594			origSettings = url;
595			url = undefined;
596		} else {
597			// work around to support 1.5 signature
598			origSettings = origSettings || {};
599			origSettings.url = url || origSettings.url;
600		}
601
602		// Extend the original settings for the request
603		requestSettings = $.ajaxSetup({}, origSettings);
604		requestSettings.type = requestSettings.method = requestSettings.method || requestSettings.type;
605
606		// Generic function to override callback methods for use with
607		// callback options (onAfterSuccess, onAfterError, onAfterComplete)
608		overrideCallback = function(action, mockHandler) {
609			var origHandler = origSettings[action.toLowerCase()];
610			return function() {
611				if ( $.isFunction(origHandler) ) {
612					origHandler.apply(this, [].slice.call(arguments));
613				}
614				mockHandler['onAfter' + action]();
615			};
616		};
617
618		// Iterate over our mock handlers (in registration order) until we find
619		// one that is willing to intercept the request
620		for(var k = 0; k < mockHandlers.length; k++) {
621			if ( !mockHandlers[k] ) {
622				continue;
623			}
624
625			mockHandler = getMockForRequest( mockHandlers[k], requestSettings );
626			if(!mockHandler) {
627				// No valid mock found for this request
628				continue;
629			}
630
631			mockedAjaxCalls.push(requestSettings);
632
633			// If logging is enabled, log the mock to the console
634			$.mockjaxSettings.log( mockHandler, requestSettings );
635
636
637			if ( requestSettings.dataType && requestSettings.dataType.toUpperCase() === 'JSONP' ) {
638				if ((mockRequest = processJsonpMock( requestSettings, mockHandler, origSettings ))) {
639					// This mock will handle the JSONP request
640					return mockRequest;
641				}
642			}
643
644            // We are mocking, so there will be no cross domain request, however, jQuery
645            // aggressively pursues this if the domains don't match, so we need to 
646            // explicitly disallow it. (See #136) 
647            origSettings.crossDomain = false;            
648            
649			// Removed to fix #54 - keep the mocking data object intact
650			//mockHandler.data = requestSettings.data;
651
652			mockHandler.cache = requestSettings.cache;
653			mockHandler.timeout = requestSettings.timeout;
654			mockHandler.global = requestSettings.global;
655
656			// In the case of a timeout, we just need to ensure
657			// an actual jQuery timeout (That is, our reponse won't)
658			// return faster than the timeout setting.
659			if ( mockHandler.isTimeout ) {
660				if ( mockHandler.responseTime > 1 ) {
661					origSettings.timeout = mockHandler.responseTime - 1;
662				} else {
663					mockHandler.responseTime = 2;
664					origSettings.timeout = 1;
665				}
666			}
667
668			// Set up onAfter[X] callback functions
669			if ( $.isFunction( mockHandler.onAfterSuccess ) ) {
670				origSettings.success = overrideCallback('Success', mockHandler);
671			}
672			if ( $.isFunction( mockHandler.onAfterError ) ) {
673				origSettings.error = overrideCallback('Error', mockHandler);
674			}
675			if ( $.isFunction( mockHandler.onAfterComplete ) ) {
676				origSettings.complete = overrideCallback('Complete', mockHandler);
677			}
678
679			copyUrlParameters(mockHandler, origSettings);
680
681			/* jshint loopfunc:true */
682			(function(mockHandler, requestSettings, origSettings, origHandler) {
683
684				mockRequest = _ajax.call($, $.extend(true, {}, origSettings, {
685					// Mock the XHR object
686					xhr: function() { return xhr( mockHandler, requestSettings, origSettings, origHandler ); }
687				}));
688			})(mockHandler, requestSettings, origSettings, mockHandlers[k]);
689			/* jshint loopfunc:false */
690
691			return mockRequest;
692		}
693
694		// We don't have a mock request
695		unmockedAjaxCalls.push(origSettings);
696		if($.mockjaxSettings.throwUnmocked === true) {
697			throw new Error('AJAX not mocked: ' + origSettings.url);
698		}
699		else { // trigger a normal request
700			return _ajax.apply($, [origSettings]);
701		}
702	}
703
704	/**
705	* Copies URL parameter values if they were captured by a regular expression
706	* @param {Object} mockHandler
707	* @param {Object} origSettings
708	*/
709	function copyUrlParameters(mockHandler, origSettings) {
710		//parameters aren't captured if the URL isn't a RegExp
711		if (!(mockHandler.url instanceof RegExp)) {
712			return;
713		}
714		//if no URL params were defined on the handler, don't attempt a capture
715		if (!mockHandler.hasOwnProperty('urlParams')) {
716			return;
717		}
718		var captures = mockHandler.url.exec(origSettings.url);
719		//the whole RegExp match is always the first value in the capture results
720		if (captures.length === 1) {
721			return;
722		}
723		captures.shift();
724		//use handler params as keys and capture resuts as values
725		var i = 0,
726		capturesLength = captures.length,
727		paramsLength = mockHandler.urlParams.length,
728		//in case the number of params specified is less than actual captures
729		maxIterations = Math.min(capturesLength, paramsLength),
730		paramValues = {};
731		for (i; i < maxIterations; i++) {
732			var key = mockHandler.urlParams[i];
733			paramValues[key] = captures[i];
734		}
735		origSettings.urlParams = paramValues;
736	}
737
738	/**
739	 * Clears handlers that mock given url
740	 * @param url
741	 * @returns {Array}
742	 */
743	function clearByUrl(url) {
744		var i, len,
745			handler,
746			results = [],
747			match=url instanceof RegExp ?
748				function(testUrl) { return url.test(testUrl); } :
749				function(testUrl) { return url === testUrl; };
750		for (i=0, len=mockHandlers.length; i<len; i++) {
751			handler = mockHandlers[i];
752			if (!match(handler.url)) {
753				results.push(handler);
754			}
755		}
756		return results;
757	}
758
759
760	// Public
761
762	$.extend({
763		ajax: handleAjax
764	});
765
766	var DEFAULT_RESPONSE_TIME = 500;
767
768
769	$.mockjaxSettings = {
770		//url:  null,
771		//type: 'GET',
772		log: function( mockHandler, requestSettings ) {
773			if ( mockHandler.logging === false ||
774				 ( typeof mockHandler.logging === 'undefined' && $.mockjaxSettings.logging === false ) ) {
775				return;
776			}
777			if ( window.console && console.log ) {
778				var message = 'MOCK ' + requestSettings.type.toUpperCase() + ': ' + requestSettings.url;
779				var request = $.ajaxSetup({}, requestSettings);
780
781				if (typeof console.log === 'function') {
782					console.log(message, request);
783				} else {
784					try {
785						console.log( message + ' ' + JSON.stringify(request) );
786					} catch (e) {
787						console.log(message);
788					}
789				}
790			}
791		},
792		logging:       true,
793		namespace:     null,
794		status:        200,
795		statusText:    'OK',
796		responseTime:  DEFAULT_RESPONSE_TIME,
797		isTimeout:     false,
798		throwUnmocked: false,
799		contentType:   'text/plain',
800		response:      '',
801		responseText:  '',
802		responseXML:   '',
803		proxy:         '',
804		proxyType:     'GET',
805
806		lastModified:  null,
807		etag:          '',
808		headers: {
809			etag: 'IJF@H#@923uf8023hFO@I#H#',
810			'content-type' : 'text/plain'
811		}
812	};
813
814	$.mockjax = function(settings) {
815		var i = mockHandlers.length;
816		mockHandlers[i] = settings;
817		return i;
818	};
819	$.mockjax.clear = function(i) {
820		if ( typeof i === 'string' || i instanceof RegExp) {
821			mockHandlers = clearByUrl(i);
822		} else if ( i || i === 0 ) {
823			mockHandlers[i] = null;
824		} else {
825			mockHandlers = [];
826		}
827		mockedAjaxCalls = [];
828		unmockedAjaxCalls = [];
829	};
830	$.mockjax.handler = function(i) {
831		if ( arguments.length === 1 ) {
832			return mockHandlers[i];
833		}
834	};
835	$.mockjax.mockedAjaxCalls = function() {
836		return mockedAjaxCalls;
837	};
838	$.mockjax.unfiredHandlers = function() {
839		var results = [];
840		for (var i=0, len=mockHandlers.length; i<len; i++) {
841			var handler = mockHandlers[i];
842			if (handler !== null && !handler.fired) {
843				results.push(handler);
844			}
845		}
846		return results;
847	};
848	$.mockjax.unmockedAjaxCalls = function() {
849		return unmockedAjaxCalls;
850	};
851
852	return $.mockjax;
853
854}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.