UNPKG

d2-ui

Version:
1,502 lines (1,279 loc) 75.2 kB
/** * Sinon.JS 1.17.4, 2016/05/02 * * @author Christian Johansen (christian@cjohansen.no) * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS * * (The BSD License) * * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Christian Johansen nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Sinon core utilities. For internal use only. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */ var sinon = (function () { "use strict"; // eslint-disable-line no-unused-vars var sinonModule; var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; function loadDependencies(require, exports, module) { sinonModule = module.exports = require("./sinon/util/core"); require("./sinon/extend"); require("./sinon/walk"); require("./sinon/typeOf"); require("./sinon/times_in_words"); require("./sinon/spy"); require("./sinon/call"); require("./sinon/behavior"); require("./sinon/stub"); require("./sinon/mock"); require("./sinon/collection"); require("./sinon/assert"); require("./sinon/sandbox"); require("./sinon/test"); require("./sinon/test_case"); require("./sinon/match"); require("./sinon/format"); require("./sinon/log_error"); } if (isAMD) { define(loadDependencies); } else if (isNode) { loadDependencies(require, module.exports, module); sinonModule = module.exports; } else { sinonModule = {}; } return sinonModule; }()); /** * @depend ../../sinon.js */ /** * Sinon core utilities. For internal use only. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */ (function (sinonGlobal) { var div = typeof document !== "undefined" && document.createElement("div"); var hasOwn = Object.prototype.hasOwnProperty; function isDOMNode(obj) { var success = false; try { obj.appendChild(div); success = div.parentNode === obj; } catch (e) { return false; } finally { try { obj.removeChild(div); } catch (e) { // Remove failed, not much we can do about that } } return success; } function isElement(obj) { return div && obj && obj.nodeType === 1 && isDOMNode(obj); } function isFunction(obj) { return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); } function isReallyNaN(val) { return typeof val === "number" && isNaN(val); } function mirrorProperties(target, source) { for (var prop in source) { if (!hasOwn.call(target, prop)) { target[prop] = source[prop]; } } } function isRestorable(obj) { return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; } // Cheap way to detect if we have ES5 support. var hasES5Support = "keys" in Object; function makeApi(sinon) { sinon.wrapMethod = function wrapMethod(object, property, method) { if (!object) { throw new TypeError("Should wrap property of object"); } if (typeof method !== "function" && typeof method !== "object") { throw new TypeError("Method wrapper should be a function or a property descriptor"); } function checkWrappedMethod(wrappedMethod) { var error; if (!isFunction(wrappedMethod)) { error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + property + " as function"); } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) { error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); } else if (wrappedMethod.calledBefore) { var verb = wrappedMethod.returns ? "stubbed" : "spied on"; error = new TypeError("Attempted to wrap " + property + " which is already " + verb); } if (error) { if (wrappedMethod && wrappedMethod.stackTrace) { error.stack += "\n--------------\n" + wrappedMethod.stackTrace; } throw error; } } var error, wrappedMethod, i; // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem // when using hasOwn.call on objects from other frames. var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); if (hasES5Support) { var methodDesc = (typeof method === "function") ? {value: method} : method; var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property); if (!wrappedMethodDesc) { error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + property + " as function"); } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) { error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); } if (error) { if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) { error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace; } throw error; } var types = sinon.objectKeys(methodDesc); for (i = 0; i < types.length; i++) { wrappedMethod = wrappedMethodDesc[types[i]]; checkWrappedMethod(wrappedMethod); } mirrorProperties(methodDesc, wrappedMethodDesc); for (i = 0; i < types.length; i++) { mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]); } Object.defineProperty(object, property, methodDesc); } else { wrappedMethod = object[property]; checkWrappedMethod(wrappedMethod); object[property] = method; method.displayName = property; } method.displayName = property; // Set up a stack trace which can be used later to find what line of // code the original method was created on. method.stackTrace = (new Error("Stack Trace for original")).stack; method.restore = function () { // For prototype properties try to reset by delete first. // If this fails (ex: localStorage on mobile safari) then force a reset // via direct assignment. if (!owned) { // In some cases `delete` may throw an error try { delete object[property]; } catch (e) {} // eslint-disable-line no-empty // For native code functions `delete` fails without throwing an error // on Chrome < 43, PhantomJS, etc. } else if (hasES5Support) { Object.defineProperty(object, property, wrappedMethodDesc); } // Use strict equality comparison to check failures then force a reset // via direct assignment. if (object[property] === method) { object[property] = wrappedMethod; } }; method.restore.sinon = true; if (!hasES5Support) { mirrorProperties(method, wrappedMethod); } return method; }; sinon.create = function create(proto) { var F = function () {}; F.prototype = proto; return new F(); }; sinon.deepEqual = function deepEqual(a, b) { if (sinon.match && sinon.match.isMatcher(a)) { return a.test(b); } if (typeof a !== "object" || typeof b !== "object") { return isReallyNaN(a) && isReallyNaN(b) || a === b; } if (isElement(a) || isElement(b)) { return a === b; } if (a === b) { return true; } if ((a === null && b !== null) || (a !== null && b === null)) { return false; } if (a instanceof RegExp && b instanceof RegExp) { return (a.source === b.source) && (a.global === b.global) && (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); } var aString = Object.prototype.toString.call(a); if (aString !== Object.prototype.toString.call(b)) { return false; } if (aString === "[object Date]") { return a.valueOf() === b.valueOf(); } var prop; var aLength = 0; var bLength = 0; if (aString === "[object Array]" && a.length !== b.length) { return false; } for (prop in a) { if (a.hasOwnProperty(prop)) { aLength += 1; if (!(prop in b)) { return false; } if (!deepEqual(a[prop], b[prop])) { return false; } } } for (prop in b) { if (b.hasOwnProperty(prop)) { bLength += 1; } } return aLength === bLength; }; sinon.functionName = function functionName(func) { var name = func.displayName || func.name; // Use function decomposition as a last resort to get function // name. Does not rely on function decomposition to work - if it // doesn't debugging will be slightly less informative // (i.e. toString will say 'spy' rather than 'myFunc'). if (!name) { var matches = func.toString().match(/function ([^\s\(]+)/); name = matches && matches[1]; } return name; }; sinon.functionToString = function toString() { if (this.getCall && this.callCount) { var thisValue, prop; var i = this.callCount; while (i--) { thisValue = this.getCall(i).thisValue; for (prop in thisValue) { if (thisValue[prop] === this) { return prop; } } } } return this.displayName || "sinon fake"; }; sinon.objectKeys = function objectKeys(obj) { if (obj !== Object(obj)) { throw new TypeError("sinon.objectKeys called on a non-object"); } var keys = []; var key; for (key in obj) { if (hasOwn.call(obj, key)) { keys.push(key); } } return keys; }; sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) { var proto = object; var descriptor; while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) { proto = Object.getPrototypeOf(proto); } return descriptor; }; sinon.getConfig = function (custom) { var config = {}; custom = custom || {}; var defaults = sinon.defaultConfig; for (var prop in defaults) { if (defaults.hasOwnProperty(prop)) { config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; } } return config; }; sinon.defaultConfig = { injectIntoThis: true, injectInto: null, properties: ["spy", "stub", "mock", "clock", "server", "requests"], useFakeTimers: true, useFakeServer: true }; sinon.timesInWords = function timesInWords(count) { return count === 1 && "once" || count === 2 && "twice" || count === 3 && "thrice" || (count || 0) + " times"; }; sinon.calledInOrder = function (spies) { for (var i = 1, l = spies.length; i < l; i++) { if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { return false; } } return true; }; sinon.orderByFirstCall = function (spies) { return spies.sort(function (a, b) { // uuid, won't ever be equal var aCall = a.getCall(0); var bCall = b.getCall(0); var aId = aCall && aCall.callId || -1; var bId = bCall && bCall.callId || -1; return aId < bId ? -1 : 1; }); }; sinon.createStubInstance = function (constructor) { if (typeof constructor !== "function") { throw new TypeError("The constructor should be a function."); } return sinon.stub(sinon.create(constructor.prototype)); }; sinon.restore = function (object) { if (object !== null && typeof object === "object") { for (var prop in object) { if (isRestorable(object[prop])) { object[prop].restore(); } } } else if (isRestorable(object)) { object.restore(); } }; return sinon; } var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; function loadDependencies(require, exports) { makeApi(exports); } if (isAMD) { define(loadDependencies); return; } if (isNode) { loadDependencies(require, module.exports, module); return; } if (sinonGlobal) { makeApi(sinonGlobal); } }( typeof sinon === "object" && sinon // eslint-disable-line no-undef )); /** * @depend util/core.js */ (function (sinonGlobal) { function makeApi(sinon) { // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug var hasDontEnumBug = (function () { var obj = { constructor: function () { return "0"; }, toString: function () { return "1"; }, valueOf: function () { return "2"; }, toLocaleString: function () { return "3"; }, prototype: function () { return "4"; }, isPrototypeOf: function () { return "5"; }, propertyIsEnumerable: function () { return "6"; }, hasOwnProperty: function () { return "7"; }, length: function () { return "8"; }, unique: function () { return "9"; } }; var result = []; for (var prop in obj) { if (obj.hasOwnProperty(prop)) { result.push(obj[prop]()); } } return result.join("") !== "0123456789"; })(); /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will * override properties in previous sources. * * target - The Object to extend * sources - Objects to copy properties from. * * Returns the extended target */ function extend(target /*, sources */) { var sources = Array.prototype.slice.call(arguments, 1); var source, i, prop; for (i = 0; i < sources.length; i++) { source = sources[i]; for (prop in source) { if (source.hasOwnProperty(prop)) { target[prop] = source[prop]; } } // Make sure we copy (own) toString method even when in JScript with DontEnum bug // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) { target.toString = source.toString; } } return target; } sinon.extend = extend; return sinon.extend; } function loadDependencies(require, exports, module) { var sinon = require("./util/core"); module.exports = makeApi(sinon); } var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; if (isAMD) { define(loadDependencies); return; } if (isNode) { loadDependencies(require, module.exports, module); return; } if (sinonGlobal) { makeApi(sinonGlobal); } }( typeof sinon === "object" && sinon // eslint-disable-line no-undef )); /** * Minimal Event interface implementation * * Original implementation by Sven Fuchs: https://gist.github.com/995028 * Modifications and tests by Christian Johansen. * * @author Sven Fuchs (svenfuchs@artweb-design.de) * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2011 Sven Fuchs, Christian Johansen */ if (typeof sinon === "undefined") { this.sinon = {}; } (function () { var push = [].push; function makeApi(sinon) { sinon.Event = function Event(type, bubbles, cancelable, target) { this.initEvent(type, bubbles, cancelable, target); }; sinon.Event.prototype = { initEvent: function (type, bubbles, cancelable, target) { this.type = type; this.bubbles = bubbles; this.cancelable = cancelable; this.target = target; }, stopPropagation: function () {}, preventDefault: function () { this.defaultPrevented = true; } }; sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { this.initEvent(type, false, false, target); this.loaded = typeof progressEventRaw.loaded === "number" ? progressEventRaw.loaded : null; this.total = typeof progressEventRaw.total === "number" ? progressEventRaw.total : null; this.lengthComputable = !!progressEventRaw.total; }; sinon.ProgressEvent.prototype = new sinon.Event(); sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent; sinon.CustomEvent = function CustomEvent(type, customData, target) { this.initEvent(type, false, false, target); this.detail = customData.detail || null; }; sinon.CustomEvent.prototype = new sinon.Event(); sinon.CustomEvent.prototype.constructor = sinon.CustomEvent; sinon.EventTarget = { addEventListener: function addEventListener(event, listener) { this.eventListeners = this.eventListeners || {}; this.eventListeners[event] = this.eventListeners[event] || []; push.call(this.eventListeners[event], listener); }, removeEventListener: function removeEventListener(event, listener) { var listeners = this.eventListeners && this.eventListeners[event] || []; for (var i = 0, l = listeners.length; i < l; ++i) { if (listeners[i] === listener) { return listeners.splice(i, 1); } } }, dispatchEvent: function dispatchEvent(event) { var type = event.type; var listeners = this.eventListeners && this.eventListeners[type] || []; for (var i = 0; i < listeners.length; i++) { if (typeof listeners[i] === "function") { listeners[i].call(this, event); } else { listeners[i].handleEvent(event); } } return !!event.defaultPrevented; } }; } var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; function loadDependencies(require) { var sinon = require("./core"); makeApi(sinon); } if (isAMD) { define(loadDependencies); } else if (isNode) { loadDependencies(require); } else { makeApi(sinon); // eslint-disable-line no-undef } }()); /** * @depend util/core.js */ /** * Logs errors * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2014 Christian Johansen */ (function (sinonGlobal) { // cache a reference to setTimeout, so that our reference won't be stubbed out // when using fake timers and errors will still get logged // https://github.com/cjohansen/Sinon.JS/issues/381 var realSetTimeout = setTimeout; function makeApi(sinon) { function log() {} function logError(label, err) { var msg = label + " threw exception: "; function throwLoggedError() { err.message = msg + err.message; throw err; } sinon.log(msg + "[" + err.name + "] " + err.message); if (err.stack) { sinon.log(err.stack); } if (logError.useImmediateExceptions) { throwLoggedError(); } else { logError.setTimeout(throwLoggedError, 0); } } // When set to true, any errors logged will be thrown immediately; // If set to false, the errors will be thrown in separate execution frame. logError.useImmediateExceptions = false; // wrap realSetTimeout with something we can stub in tests logError.setTimeout = function (func, timeout) { realSetTimeout(func, timeout); }; var exports = {}; exports.log = sinon.log = log; exports.logError = sinon.logError = logError; return exports; } function loadDependencies(require, exports, module) { var sinon = require("./util/core"); module.exports = makeApi(sinon); } var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; if (isAMD) { define(loadDependencies); return; } if (isNode) { loadDependencies(require, module.exports, module); return; } if (sinonGlobal) { makeApi(sinonGlobal); } }( typeof sinon === "object" && sinon // eslint-disable-line no-undef )); /** * @depend core.js * @depend ../extend.js * @depend event.js * @depend ../log_error.js */ /** * Fake XDomainRequest object */ /** * Returns the global to prevent assigning values to 'this' when this is undefined. * This can occur when files are interpreted by node in strict mode. * @private */ function getGlobal() { return typeof window !== "undefined" ? window : global; } if (typeof sinon === "undefined") { if (typeof this === "undefined") { getGlobal().sinon = {}; } else { this.sinon = {}; } } // wrapper for global (function (global) { var xdr = { XDomainRequest: global.XDomainRequest }; xdr.GlobalXDomainRequest = global.XDomainRequest; xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== "undefined"; xdr.workingXDR = xdr.supportsXDR ? xdr.GlobalXDomainRequest : false; function makeApi(sinon) { sinon.xdr = xdr; function FakeXDomainRequest() { this.readyState = FakeXDomainRequest.UNSENT; this.requestBody = null; this.requestHeaders = {}; this.status = 0; this.timeout = null; if (typeof FakeXDomainRequest.onCreate === "function") { FakeXDomainRequest.onCreate(this); } } function verifyState(x) { if (x.readyState !== FakeXDomainRequest.OPENED) { throw new Error("INVALID_STATE_ERR"); } if (x.sendFlag) { throw new Error("INVALID_STATE_ERR"); } } function verifyRequestSent(x) { if (x.readyState === FakeXDomainRequest.UNSENT) { throw new Error("Request not sent"); } if (x.readyState === FakeXDomainRequest.DONE) { throw new Error("Request done"); } } function verifyResponseBodyType(body) { if (typeof body !== "string") { var error = new Error("Attempted to respond to fake XDomainRequest with " + body + ", which is not a string."); error.name = "InvalidBodyException"; throw error; } } sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, { open: function open(method, url) { this.method = method; this.url = url; this.responseText = null; this.sendFlag = false; this.readyStateChange(FakeXDomainRequest.OPENED); }, readyStateChange: function readyStateChange(state) { this.readyState = state; var eventName = ""; switch (this.readyState) { case FakeXDomainRequest.UNSENT: break; case FakeXDomainRequest.OPENED: break; case FakeXDomainRequest.LOADING: if (this.sendFlag) { //raise the progress event eventName = "onprogress"; } break; case FakeXDomainRequest.DONE: if (this.isTimeout) { eventName = "ontimeout"; } else if (this.errorFlag || (this.status < 200 || this.status > 299)) { eventName = "onerror"; } else { eventName = "onload"; } break; } // raising event (if defined) if (eventName) { if (typeof this[eventName] === "function") { try { this[eventName](); } catch (e) { sinon.logError("Fake XHR " + eventName + " handler", e); } } } }, send: function send(data) { verifyState(this); if (!/^(get|head)$/i.test(this.method)) { this.requestBody = data; } this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; this.errorFlag = false; this.sendFlag = true; this.readyStateChange(FakeXDomainRequest.OPENED); if (typeof this.onSend === "function") { this.onSend(this); } }, abort: function abort() { this.aborted = true; this.responseText = null; this.errorFlag = true; if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) { this.readyStateChange(sinon.FakeXDomainRequest.DONE); this.sendFlag = false; } }, setResponseBody: function setResponseBody(body) { verifyRequestSent(this); verifyResponseBodyType(body); var chunkSize = this.chunkSize || 10; var index = 0; this.responseText = ""; do { this.readyStateChange(FakeXDomainRequest.LOADING); this.responseText += body.substring(index, index + chunkSize); index += chunkSize; } while (index < body.length); this.readyStateChange(FakeXDomainRequest.DONE); }, respond: function respond(status, contentType, body) { // content-type ignored, since XDomainRequest does not carry this // we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease // test integration across browsers this.status = typeof status === "number" ? status : 200; this.setResponseBody(body || ""); }, simulatetimeout: function simulatetimeout() { this.status = 0; this.isTimeout = true; // Access to this should actually throw an error this.responseText = undefined; this.readyStateChange(FakeXDomainRequest.DONE); } }); sinon.extend(FakeXDomainRequest, { UNSENT: 0, OPENED: 1, LOADING: 3, DONE: 4 }); sinon.useFakeXDomainRequest = function useFakeXDomainRequest() { sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) { if (xdr.supportsXDR) { global.XDomainRequest = xdr.GlobalXDomainRequest; } delete sinon.FakeXDomainRequest.restore; if (keepOnCreate !== true) { delete sinon.FakeXDomainRequest.onCreate; } }; if (xdr.supportsXDR) { global.XDomainRequest = sinon.FakeXDomainRequest; } return sinon.FakeXDomainRequest; }; sinon.FakeXDomainRequest = FakeXDomainRequest; } var isNode = typeof module !== "undefined" && module.exports && typeof require === "function"; var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd; function loadDependencies(require, exports, module) { var sinon = require("./core"); require("../extend"); require("./event"); require("../log_error"); makeApi(sinon); module.exports = sinon; } if (isAMD) { define(loadDependencies); } else if (isNode) { loadDependencies(require, module.exports, module); } else { makeApi(sinon); // eslint-disable-line no-undef } })(typeof global !== "undefined" ? global : self); /** * @depend core.js * @depend ../extend.js * @depend event.js * @depend ../log_error.js */ /** * Fake XMLHttpRequest object * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */ (function (sinonGlobal, global) { function getWorkingXHR(globalScope) { var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined"; if (supportsXHR) { return globalScope.XMLHttpRequest; } var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined"; if (supportsActiveX) { return function () { return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0"); }; } return false; } var supportsProgress = typeof ProgressEvent !== "undefined"; var supportsCustomEvent = typeof CustomEvent !== "undefined"; var supportsFormData = typeof FormData !== "undefined"; var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; var supportsBlob = (function () { try { return !!new Blob(); } catch (e) { return false; } })(); var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest }; sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest; sinonXhr.GlobalActiveXObject = global.ActiveXObject; sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined"; sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined"; sinonXhr.workingXHR = getWorkingXHR(global); sinonXhr.supportsCORS = sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()); var unsafeHeaders = { "Accept-Charset": true, "Accept-Encoding": true, Connection: true, "Content-Length": true, Cookie: true, Cookie2: true, "Content-Transfer-Encoding": true, Date: true, Expect: true, Host: true, "Keep-Alive": true, Referer: true, TE: true, Trailer: true, "Transfer-Encoding": true, Upgrade: true, "User-Agent": true, Via: true }; // An upload object is created for each // FakeXMLHttpRequest and allows upload // events to be simulated using uploadProgress // and uploadError. function UploadProgress() { this.eventListeners = { abort: [], error: [], load: [], loadend: [], progress: [] }; } UploadProgress.prototype.addEventListener = function addEventListener(event, listener) { this.eventListeners[event].push(listener); }; UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) { var listeners = this.eventListeners[event] || []; for (var i = 0, l = listeners.length; i < l; ++i) { if (listeners[i] === listener) { return listeners.splice(i, 1); } } }; UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) { var listeners = this.eventListeners[event.type] || []; for (var i = 0, listener; (listener = listeners[i]) != null; i++) { listener(event); } }; // Note that for FakeXMLHttpRequest to work pre ES5 // we lose some of the alignment with the spec. // To ensure as close a match as possible, // set responseType before calling open, send or respond; function FakeXMLHttpRequest() { this.readyState = FakeXMLHttpRequest.UNSENT; this.requestHeaders = {}; this.requestBody = null; this.status = 0; this.statusText = ""; this.upload = new UploadProgress(); this.responseType = ""; this.response = ""; if (sinonXhr.supportsCORS) { this.withCredentials = false; } var xhr = this; var events = ["loadstart", "load", "abort", "loadend"]; function addEventListener(eventName) { xhr.addEventListener(eventName, function (event) { var listener = xhr["on" + eventName]; if (listener && typeof listener === "function") { listener.call(this, event); } }); } for (var i = events.length - 1; i >= 0; i--) { addEventListener(events[i]); } if (typeof FakeXMLHttpRequest.onCreate === "function") { FakeXMLHttpRequest.onCreate(this); } } function verifyState(xhr) { if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { throw new Error("INVALID_STATE_ERR"); } if (xhr.sendFlag) { throw new Error("INVALID_STATE_ERR"); } } function getHeader(headers, header) { header = header.toLowerCase(); for (var h in headers) { if (h.toLowerCase() === header) { return h; } } return null; } // filtering to enable a white-list version of Sinon FakeXhr, // where whitelisted requests are passed through to real XHR function each(collection, callback) { if (!collection) { return; } for (var i = 0, l = collection.length; i < l; i += 1) { callback(collection[i]); } } function some(collection, callback) { for (var index = 0; index < collection.length; index++) { if (callback(collection[index]) === true) { return true; } } return false; } // largest arity in XHR is 5 - XHR#open var apply = function (obj, method, args) { switch (args.length) { case 0: return obj[method](); case 1: return obj[method](args[0]); case 2: return obj[method](args[0], args[1]); case 3: return obj[method](args[0], args[1], args[2]); case 4: return obj[method](args[0], args[1], args[2], args[3]); case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]); } }; FakeXMLHttpRequest.filters = []; FakeXMLHttpRequest.addFilter = function addFilter(fn) { this.filters.push(fn); }; var IE6Re = /MSIE 6/; FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) { var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap each([ "open", "setRequestHeader", "send", "abort", "getResponseHeader", "getAllResponseHeaders", "addEventListener", "overrideMimeType", "removeEventListener" ], function (method) { fakeXhr[method] = function () { return apply(xhr, method, arguments); }; }); var copyAttrs = function (args) { each(args, function (attr) { try { fakeXhr[attr] = xhr[attr]; } catch (e) { if (!IE6Re.test(navigator.userAgent)) { throw e; } } }); }; var stateChange = function stateChange() { fakeXhr.readyState = xhr.readyState; if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { copyAttrs(["status", "statusText"]); } if (xhr.readyState >= FakeXMLHttpRequest.LOADING) { copyAttrs(["responseText", "response"]); } if (xhr.readyState === FakeXMLHttpRequest.DONE) { copyAttrs(["responseXML"]); } if (fakeXhr.onreadystatechange) { fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); } }; if (xhr.addEventListener) { for (var event in fakeXhr.eventListeners) { if (fakeXhr.eventListeners.hasOwnProperty(event)) { /*eslint-disable no-loop-func*/ each(fakeXhr.eventListeners[event], function (handler) { xhr.addEventListener(event, handler); }); /*eslint-enable no-loop-func*/ } } xhr.addEventListener("readystatechange", stateChange); } else { xhr.onreadystatechange = stateChange; } apply(xhr, "open", xhrArgs); }; FakeXMLHttpRequest.useFilters = false; function verifyRequestOpened(xhr) { if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { throw new Error("INVALID_STATE_ERR - " + xhr.readyState); } } function verifyRequestSent(xhr) { if (xhr.readyState === FakeXMLHttpRequest.DONE) { throw new Error("Request done"); } } function verifyHeadersReceived(xhr) { if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) { throw new Error("No headers received"); } } function verifyResponseBodyType(body) { if (typeof body !== "string") { var error = new Error("Attempted to respond to fake XMLHttpRequest with " + body + ", which is not a string."); error.name = "InvalidBodyException"; throw error; } } function convertToArrayBuffer(body) { var buffer = new ArrayBuffer(body.length); var view = new Uint8Array(buffer); for (var i = 0; i < body.length; i++) { var charCode = body.charCodeAt(i); if (charCode >= 256) { throw new TypeError("arraybuffer or blob responseTypes require binary string, " + "invalid character " + body[i] + " found."); } view[i] = charCode; } return buffer; } function isXmlContentType(contentType) { return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType); } function convertResponseBody(responseType, contentType, body) { if (responseType === "" || responseType === "text") { return body; } else if (supportsArrayBuffer && responseType === "arraybuffer") { return convertToArrayBuffer(body); } else if (responseType === "json") { try { return JSON.parse(body); } catch (e) { // Return parsing failure as null return null; } } else if (supportsBlob && responseType === "blob") { var blobOptions = {}; if (contentType) { blobOptions.type = contentType; } return new Blob([convertToArrayBuffer(body)], blobOptions); } else if (responseType === "document") { if (isXmlContentType(contentType)) { return FakeXMLHttpRequest.parseXML(body); } return null; } throw new Error("Invalid responseType " + responseType); } function clearResponse(xhr) { if (xhr.responseType === "" || xhr.responseType === "text") { xhr.response = xhr.responseText = ""; } else { xhr.response = xhr.responseText = null; } xhr.responseXML = null; } FakeXMLHttpRequest.parseXML = function parseXML(text) { // Treat empty string as parsing failure if (text !== "") { try { if (typeof DOMParser !== "undefined") { var parser = new DOMParser(); return parser.parseFromString(text, "text/xml"); } var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = "false"; xmlDoc.loadXML(text); return xmlDoc; } catch (e) { // Unable to parse XML - no biggie } } return null; }; FakeXMLHttpRequest.statusCodes = { 100: "Continue", 101: "Switching Protocols", 200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status", 300: "Multiple Choice", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 307: "Temporary Redirect", 400: "Bad Request", 401: "Unauthorized", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Request Entity Too Large", 414: "Request-URI Too Long", 415: "Unsupported Media Type", 416: "Requested Range Not Satisfiable", 417: "Expectation Failed", 422: "Unprocessable Entity", 500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported" }; function makeApi(sinon) { sinon.xhr = sinonXhr; sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { async: true, open: function open(method, url, async, username, password) { this.method = method; this.url = url; this.async = typeof async === "boolean" ? async : true; this.username = username; this.password = password; clearResponse(this); this.requestHeaders = {}; this.sendFlag = false; if (FakeXMLHttpRequest.useFilters === true) { var xhrArgs = arguments; var defake = some(FakeXMLHttpRequest.filters, function (filter) { return filter.apply(this, xhrArgs); }); if (defake) { return FakeXMLHttpRequest.defake(this, arguments); } } this.readyStateChange(FakeXMLHttpRequest.OPENED); }, readyStateChange: function readyStateChange(state) { this.readyState = state; var readyStateChangeEvent = new sinon.Event("readystatechange", false, false, this); var event, progress; if (typeof this.onreadystatechange === "function") { try { this.onreadystatechange(readyStateChangeEvent); } catch (e) { sinon.logError("Fake XHR onreadystatechange handler", e); } } if (this.readyState === FakeXMLHttpRequest.DONE) { if (this.status < 200 || this.status > 299) { progress = {loaded: 0, total: 0}; event = this.aborted ? "abort" : "error"; } else { progress = {loaded: 100, total: 100}; event = "load"; } if (supportsProgress) { this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progress, this)); this.upload.dispatchEvent(new sinon.ProgressEvent(event, progress, this)); this.upload.dispatchEvent(new sinon.ProgressEve