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.