Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. "use strict";
  2. var GlobalTextEncoder =
  3. typeof TextEncoder !== "undefined"
  4. ? TextEncoder
  5. : require("@sinonjs/text-encoding").TextEncoder;
  6. var globalObject = require("@sinonjs/commons").global;
  7. var configureLogError = require("../configure-logger");
  8. var sinonEvent = require("../event");
  9. var extend = require("just-extend");
  10. var supportsProgress = typeof ProgressEvent !== "undefined";
  11. var supportsCustomEvent = typeof CustomEvent !== "undefined";
  12. var supportsFormData = typeof FormData !== "undefined";
  13. var supportsArrayBuffer = typeof ArrayBuffer !== "undefined";
  14. var supportsBlob = require("./blob").isSupported;
  15. function getWorkingXHR(globalScope) {
  16. var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined";
  17. if (supportsXHR) {
  18. return globalScope.XMLHttpRequest;
  19. }
  20. var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined";
  21. if (supportsActiveX) {
  22. return function() {
  23. return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0");
  24. };
  25. }
  26. return false;
  27. }
  28. // Ref: https://fetch.spec.whatwg.org/#forbidden-header-name
  29. var unsafeHeaders = {
  30. "Accept-Charset": true,
  31. "Access-Control-Request-Headers": true,
  32. "Access-Control-Request-Method": true,
  33. "Accept-Encoding": true,
  34. Connection: true,
  35. "Content-Length": true,
  36. Cookie: true,
  37. Cookie2: true,
  38. "Content-Transfer-Encoding": true,
  39. Date: true,
  40. DNT: true,
  41. Expect: true,
  42. Host: true,
  43. "Keep-Alive": true,
  44. Origin: true,
  45. Referer: true,
  46. TE: true,
  47. Trailer: true,
  48. "Transfer-Encoding": true,
  49. Upgrade: true,
  50. "User-Agent": true,
  51. Via: true
  52. };
  53. function EventTargetHandler() {
  54. var self = this;
  55. var events = [
  56. "loadstart",
  57. "progress",
  58. "abort",
  59. "error",
  60. "load",
  61. "timeout",
  62. "loadend"
  63. ];
  64. function addEventListener(eventName) {
  65. self.addEventListener(eventName, function(event) {
  66. var listener = self[`on${eventName}`];
  67. if (listener && typeof listener === "function") {
  68. listener.call(this, event);
  69. }
  70. });
  71. }
  72. events.forEach(addEventListener);
  73. }
  74. EventTargetHandler.prototype = sinonEvent.EventTarget;
  75. function normalizeHeaderValue(value) {
  76. // Ref: https://fetch.spec.whatwg.org/#http-whitespace-bytes
  77. /*eslint no-control-regex: "off"*/
  78. return value.replace(/^[\x09\x0A\x0D\x20]+|[\x09\x0A\x0D\x20]+$/g, "");
  79. }
  80. function getHeader(headers, header) {
  81. var foundHeader = Object.keys(headers).filter(function(h) {
  82. return h.toLowerCase() === header.toLowerCase();
  83. });
  84. return foundHeader[0] || null;
  85. }
  86. function excludeSetCookie2Header(header) {
  87. return !/^Set-Cookie2?$/i.test(header);
  88. }
  89. function verifyResponseBodyType(body, responseType) {
  90. var error = null;
  91. var isString = typeof body === "string";
  92. if (responseType === "arraybuffer") {
  93. if (!isString && !(body instanceof ArrayBuffer)) {
  94. error = new Error(
  95. `Attempted to respond to fake XMLHttpRequest with ${body}, which is not a string or ArrayBuffer.`
  96. );
  97. error.name = "InvalidBodyException";
  98. }
  99. } else if (!isString) {
  100. error = new Error(
  101. `Attempted to respond to fake XMLHttpRequest with ${body}, which is not a string.`
  102. );
  103. error.name = "InvalidBodyException";
  104. }
  105. if (error) {
  106. throw error;
  107. }
  108. }
  109. function convertToArrayBuffer(body, encoding) {
  110. if (body instanceof ArrayBuffer) {
  111. return body;
  112. }
  113. return new GlobalTextEncoder(encoding || "utf-8").encode(body).buffer;
  114. }
  115. function isXmlContentType(contentType) {
  116. return (
  117. !contentType ||
  118. /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType)
  119. );
  120. }
  121. function clearResponse(xhr) {
  122. if (xhr.responseType === "" || xhr.responseType === "text") {
  123. xhr.response = xhr.responseText = "";
  124. } else {
  125. xhr.response = xhr.responseText = null;
  126. }
  127. xhr.responseXML = null;
  128. }
  129. function fakeXMLHttpRequestFor(globalScope) {
  130. var isReactNative =
  131. globalScope.navigator &&
  132. globalScope.navigator.product === "ReactNative";
  133. var sinonXhr = { XMLHttpRequest: globalScope.XMLHttpRequest };
  134. sinonXhr.GlobalXMLHttpRequest = globalScope.XMLHttpRequest;
  135. sinonXhr.GlobalActiveXObject = globalScope.ActiveXObject;
  136. sinonXhr.supportsActiveX =
  137. typeof sinonXhr.GlobalActiveXObject !== "undefined";
  138. sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined";
  139. sinonXhr.workingXHR = getWorkingXHR(globalScope);
  140. sinonXhr.supportsTimeout =
  141. sinonXhr.supportsXHR &&
  142. "timeout" in new sinonXhr.GlobalXMLHttpRequest();
  143. sinonXhr.supportsCORS =
  144. isReactNative ||
  145. (sinonXhr.supportsXHR &&
  146. "withCredentials" in new sinonXhr.GlobalXMLHttpRequest());
  147. // Note that for FakeXMLHttpRequest to work pre ES5
  148. // we lose some of the alignment with the spec.
  149. // To ensure as close a match as possible,
  150. // set responseType before calling open, send or respond;
  151. function FakeXMLHttpRequest(config) {
  152. EventTargetHandler.call(this);
  153. this.readyState = FakeXMLHttpRequest.UNSENT;
  154. this.requestHeaders = {};
  155. this.requestBody = null;
  156. this.status = 0;
  157. this.statusText = "";
  158. this.upload = new EventTargetHandler();
  159. this.responseType = "";
  160. this.response = "";
  161. this.logError = configureLogError(config);
  162. if (sinonXhr.supportsTimeout) {
  163. this.timeout = 0;
  164. }
  165. if (sinonXhr.supportsCORS) {
  166. this.withCredentials = false;
  167. }
  168. if (typeof FakeXMLHttpRequest.onCreate === "function") {
  169. FakeXMLHttpRequest.onCreate(this);
  170. }
  171. }
  172. function verifyState(xhr) {
  173. if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
  174. throw new Error("INVALID_STATE_ERR");
  175. }
  176. if (xhr.sendFlag) {
  177. throw new Error("INVALID_STATE_ERR");
  178. }
  179. }
  180. // largest arity in XHR is 5 - XHR#open
  181. var apply = function(obj, method, args) {
  182. switch (args.length) {
  183. case 0:
  184. return obj[method]();
  185. case 1:
  186. return obj[method](args[0]);
  187. case 2:
  188. return obj[method](args[0], args[1]);
  189. case 3:
  190. return obj[method](args[0], args[1], args[2]);
  191. case 4:
  192. return obj[method](args[0], args[1], args[2], args[3]);
  193. case 5:
  194. return obj[method](args[0], args[1], args[2], args[3], args[4]);
  195. default:
  196. throw new Error("Unhandled case");
  197. }
  198. };
  199. FakeXMLHttpRequest.filters = [];
  200. FakeXMLHttpRequest.addFilter = function addFilter(fn) {
  201. this.filters.push(fn);
  202. };
  203. FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) {
  204. var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap
  205. [
  206. "open",
  207. "setRequestHeader",
  208. "abort",
  209. "getResponseHeader",
  210. "getAllResponseHeaders",
  211. "addEventListener",
  212. "overrideMimeType",
  213. "removeEventListener"
  214. ].forEach(function(method) {
  215. fakeXhr[method] = function() {
  216. return apply(xhr, method, arguments);
  217. };
  218. });
  219. fakeXhr.send = function() {
  220. // Ref: https://xhr.spec.whatwg.org/#the-responsetype-attribute
  221. if (xhr.responseType !== fakeXhr.responseType) {
  222. xhr.responseType = fakeXhr.responseType;
  223. }
  224. return apply(xhr, "send", arguments);
  225. };
  226. var copyAttrs = function(args) {
  227. args.forEach(function(attr) {
  228. fakeXhr[attr] = xhr[attr];
  229. });
  230. };
  231. var stateChangeStart = function() {
  232. fakeXhr.readyState = xhr.readyState;
  233. if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
  234. copyAttrs(["status", "statusText"]);
  235. }
  236. if (xhr.readyState >= FakeXMLHttpRequest.LOADING) {
  237. copyAttrs(["response"]);
  238. if (xhr.responseType === "" || xhr.responseType === "text") {
  239. copyAttrs(["responseText"]);
  240. }
  241. }
  242. if (
  243. xhr.readyState === FakeXMLHttpRequest.DONE &&
  244. (xhr.responseType === "" || xhr.responseType === "document")
  245. ) {
  246. copyAttrs(["responseXML"]);
  247. }
  248. };
  249. var stateChangeEnd = function() {
  250. if (fakeXhr.onreadystatechange) {
  251. // eslint-disable-next-line no-useless-call
  252. fakeXhr.onreadystatechange.call(fakeXhr, {
  253. target: fakeXhr,
  254. currentTarget: fakeXhr
  255. });
  256. }
  257. };
  258. var stateChange = function stateChange() {
  259. stateChangeStart();
  260. stateChangeEnd();
  261. };
  262. if (xhr.addEventListener) {
  263. xhr.addEventListener("readystatechange", stateChangeStart);
  264. Object.keys(fakeXhr.eventListeners).forEach(function(event) {
  265. /*eslint-disable no-loop-func*/
  266. fakeXhr.eventListeners[event].forEach(function(handler) {
  267. xhr.addEventListener(event, handler.listener, {
  268. capture: handler.capture,
  269. once: handler.once
  270. });
  271. });
  272. /*eslint-enable no-loop-func*/
  273. });
  274. xhr.addEventListener("readystatechange", stateChangeEnd);
  275. } else {
  276. xhr.onreadystatechange = stateChange;
  277. }
  278. apply(xhr, "open", xhrArgs);
  279. };
  280. FakeXMLHttpRequest.useFilters = false;
  281. function verifyRequestOpened(xhr) {
  282. if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
  283. throw new Error(`INVALID_STATE_ERR - ${xhr.readyState}`);
  284. }
  285. }
  286. function verifyRequestSent(xhr) {
  287. if (xhr.readyState === FakeXMLHttpRequest.DONE) {
  288. throw new Error("Request done");
  289. }
  290. }
  291. function verifyHeadersReceived(xhr) {
  292. if (
  293. xhr.async &&
  294. xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED
  295. ) {
  296. throw new Error("No headers received");
  297. }
  298. }
  299. function convertResponseBody(responseType, contentType, body) {
  300. if (responseType === "" || responseType === "text") {
  301. return body;
  302. } else if (supportsArrayBuffer && responseType === "arraybuffer") {
  303. return convertToArrayBuffer(body);
  304. } else if (responseType === "json") {
  305. try {
  306. return JSON.parse(body);
  307. } catch (e) {
  308. // Return parsing failure as null
  309. return null;
  310. }
  311. } else if (supportsBlob && responseType === "blob") {
  312. var blobOptions = {};
  313. if (contentType) {
  314. blobOptions.type = contentType;
  315. }
  316. return new Blob([convertToArrayBuffer(body)], blobOptions);
  317. } else if (responseType === "document") {
  318. if (isXmlContentType(contentType)) {
  319. return FakeXMLHttpRequest.parseXML(body);
  320. }
  321. return null;
  322. }
  323. throw new Error(`Invalid responseType ${responseType}`);
  324. }
  325. /**
  326. * Steps to follow when there is an error, according to:
  327. * https://xhr.spec.whatwg.org/#request-error-steps
  328. */
  329. function requestErrorSteps(xhr) {
  330. clearResponse(xhr);
  331. xhr.errorFlag = true;
  332. xhr.requestHeaders = {};
  333. xhr.responseHeaders = {};
  334. if (
  335. xhr.readyState !== FakeXMLHttpRequest.UNSENT &&
  336. xhr.sendFlag &&
  337. xhr.readyState !== FakeXMLHttpRequest.DONE
  338. ) {
  339. xhr.readyStateChange(FakeXMLHttpRequest.DONE);
  340. xhr.sendFlag = false;
  341. }
  342. }
  343. FakeXMLHttpRequest.parseXML = function parseXML(text) {
  344. // Treat empty string as parsing failure
  345. if (text !== "") {
  346. try {
  347. if (typeof DOMParser !== "undefined") {
  348. var parser = new DOMParser();
  349. var parsererrorNS = "";
  350. try {
  351. var parsererrors = parser
  352. .parseFromString("INVALID", "text/xml")
  353. .getElementsByTagName("parsererror");
  354. if (parsererrors.length) {
  355. parsererrorNS = parsererrors[0].namespaceURI;
  356. }
  357. } catch (e) {
  358. // passing invalid XML makes IE11 throw
  359. // so no namespace needs to be determined
  360. }
  361. var result;
  362. try {
  363. result = parser.parseFromString(text, "text/xml");
  364. } catch (err) {
  365. return null;
  366. }
  367. return result.getElementsByTagNameNS(
  368. parsererrorNS,
  369. "parsererror"
  370. ).length
  371. ? null
  372. : result;
  373. }
  374. var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
  375. xmlDoc.async = "false";
  376. xmlDoc.loadXML(text);
  377. return xmlDoc.parseError.errorCode !== 0 ? null : xmlDoc;
  378. } catch (e) {
  379. // Unable to parse XML - no biggie
  380. }
  381. }
  382. return null;
  383. };
  384. FakeXMLHttpRequest.statusCodes = {
  385. 100: "Continue",
  386. 101: "Switching Protocols",
  387. 200: "OK",
  388. 201: "Created",
  389. 202: "Accepted",
  390. 203: "Non-Authoritative Information",
  391. 204: "No Content",
  392. 205: "Reset Content",
  393. 206: "Partial Content",
  394. 207: "Multi-Status",
  395. 300: "Multiple Choice",
  396. 301: "Moved Permanently",
  397. 302: "Found",
  398. 303: "See Other",
  399. 304: "Not Modified",
  400. 305: "Use Proxy",
  401. 307: "Temporary Redirect",
  402. 400: "Bad Request",
  403. 401: "Unauthorized",
  404. 402: "Payment Required",
  405. 403: "Forbidden",
  406. 404: "Not Found",
  407. 405: "Method Not Allowed",
  408. 406: "Not Acceptable",
  409. 407: "Proxy Authentication Required",
  410. 408: "Request Timeout",
  411. 409: "Conflict",
  412. 410: "Gone",
  413. 411: "Length Required",
  414. 412: "Precondition Failed",
  415. 413: "Request Entity Too Large",
  416. 414: "Request-URI Too Long",
  417. 415: "Unsupported Media Type",
  418. 416: "Requested Range Not Satisfiable",
  419. 417: "Expectation Failed",
  420. 422: "Unprocessable Entity",
  421. 500: "Internal Server Error",
  422. 501: "Not Implemented",
  423. 502: "Bad Gateway",
  424. 503: "Service Unavailable",
  425. 504: "Gateway Timeout",
  426. 505: "HTTP Version Not Supported"
  427. };
  428. extend(FakeXMLHttpRequest.prototype, sinonEvent.EventTarget, {
  429. async: true,
  430. open: function open(method, url, async, username, password) {
  431. this.method = method;
  432. this.url = url;
  433. this.async = typeof async === "boolean" ? async : true;
  434. this.username = username;
  435. this.password = password;
  436. clearResponse(this);
  437. this.requestHeaders = {};
  438. this.sendFlag = false;
  439. if (FakeXMLHttpRequest.useFilters === true) {
  440. var xhrArgs = arguments;
  441. var defake = FakeXMLHttpRequest.filters.some(function(filter) {
  442. return filter.apply(this, xhrArgs);
  443. });
  444. if (defake) {
  445. FakeXMLHttpRequest.defake(this, arguments);
  446. return;
  447. }
  448. }
  449. this.readyStateChange(FakeXMLHttpRequest.OPENED);
  450. },
  451. readyStateChange: function readyStateChange(state) {
  452. this.readyState = state;
  453. var readyStateChangeEvent = new sinonEvent.Event(
  454. "readystatechange",
  455. false,
  456. false,
  457. this
  458. );
  459. if (typeof this.onreadystatechange === "function") {
  460. try {
  461. this.onreadystatechange(readyStateChangeEvent);
  462. } catch (e) {
  463. this.logError("Fake XHR onreadystatechange handler", e);
  464. }
  465. }
  466. if (this.readyState !== FakeXMLHttpRequest.DONE) {
  467. this.dispatchEvent(readyStateChangeEvent);
  468. } else {
  469. var event, progress;
  470. if (this.timedOut || this.aborted || this.status === 0) {
  471. progress = { loaded: 0, total: 0 };
  472. event =
  473. (this.timedOut && "timeout") ||
  474. (this.aborted && "abort") ||
  475. "error";
  476. } else {
  477. progress = { loaded: 100, total: 100 };
  478. event = "load";
  479. }
  480. if (supportsProgress) {
  481. this.upload.dispatchEvent(
  482. new sinonEvent.ProgressEvent("progress", progress, this)
  483. );
  484. this.upload.dispatchEvent(
  485. new sinonEvent.ProgressEvent(event, progress, this)
  486. );
  487. this.upload.dispatchEvent(
  488. new sinonEvent.ProgressEvent("loadend", progress, this)
  489. );
  490. }
  491. this.dispatchEvent(
  492. new sinonEvent.ProgressEvent("progress", progress, this)
  493. );
  494. this.dispatchEvent(
  495. new sinonEvent.ProgressEvent(event, progress, this)
  496. );
  497. this.dispatchEvent(readyStateChangeEvent);
  498. this.dispatchEvent(
  499. new sinonEvent.ProgressEvent("loadend", progress, this)
  500. );
  501. }
  502. },
  503. // Ref https://xhr.spec.whatwg.org/#the-setrequestheader()-method
  504. setRequestHeader: function setRequestHeader(header, value) {
  505. if (typeof value !== "string") {
  506. throw new TypeError(
  507. `By RFC7230, section 3.2.4, header values should be strings. Got ${typeof value}`
  508. );
  509. }
  510. verifyState(this);
  511. var checkUnsafeHeaders = true;
  512. if (typeof this.unsafeHeadersEnabled === "function") {
  513. checkUnsafeHeaders = this.unsafeHeadersEnabled();
  514. }
  515. if (
  516. checkUnsafeHeaders &&
  517. (getHeader(unsafeHeaders, header) !== null ||
  518. /^(Sec-|Proxy-)/i.test(header))
  519. ) {
  520. throw new Error(
  521. // eslint-disable-next-line quotes
  522. `Refused to set unsafe header "${header}"`
  523. );
  524. }
  525. // eslint-disable-next-line no-param-reassign
  526. value = normalizeHeaderValue(value);
  527. var existingHeader = getHeader(this.requestHeaders, header);
  528. if (existingHeader) {
  529. this.requestHeaders[existingHeader] += `, ${value}`;
  530. } else {
  531. this.requestHeaders[header] = value;
  532. }
  533. },
  534. setStatus: function setStatus(status) {
  535. var sanitizedStatus = typeof status === "number" ? status : 200;
  536. verifyRequestOpened(this);
  537. this.status = sanitizedStatus;
  538. this.statusText = FakeXMLHttpRequest.statusCodes[sanitizedStatus];
  539. },
  540. // Helps testing
  541. setResponseHeaders: function setResponseHeaders(headers) {
  542. verifyRequestOpened(this);
  543. var responseHeaders = (this.responseHeaders = {});
  544. Object.keys(headers).forEach(function(header) {
  545. responseHeaders[header] = headers[header];
  546. });
  547. if (this.async) {
  548. this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
  549. } else {
  550. this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
  551. }
  552. },
  553. // Currently treats ALL data as a DOMString (i.e. no Document)
  554. send: function send(data) {
  555. verifyState(this);
  556. if (!/^(head)$/i.test(this.method)) {
  557. var contentType = getHeader(
  558. this.requestHeaders,
  559. "Content-Type"
  560. );
  561. if (this.requestHeaders[contentType]) {
  562. var value = this.requestHeaders[contentType].split(";");
  563. this.requestHeaders[
  564. contentType
  565. ] = `${value[0]};charset=utf-8`;
  566. } else if (supportsFormData && !(data instanceof FormData)) {
  567. this.requestHeaders["Content-Type"] =
  568. "text/plain;charset=utf-8";
  569. }
  570. this.requestBody = data;
  571. }
  572. this.errorFlag = false;
  573. this.sendFlag = this.async;
  574. clearResponse(this);
  575. if (typeof this.onSend === "function") {
  576. this.onSend(this);
  577. }
  578. // Only listen if setInterval and Date are a stubbed.
  579. if (
  580. sinonXhr.supportsTimeout &&
  581. typeof setInterval.clock === "object" &&
  582. typeof Date.clock === "object"
  583. ) {
  584. var initiatedTime = Date.now();
  585. var self = this;
  586. // Listen to any possible tick by fake timers and check to see if timeout has
  587. // been exceeded. It's important to note that timeout can be changed while a request
  588. // is in flight, so we must check anytime the end user forces a clock tick to make
  589. // sure timeout hasn't changed.
  590. // https://xhr.spec.whatwg.org/#dfnReturnLink-2
  591. var clearIntervalId = setInterval(function() {
  592. // Check if the readyState has been reset or is done. If this is the case, there
  593. // should be no timeout. This will also prevent aborted requests and
  594. // fakeServerWithClock from triggering unnecessary responses.
  595. if (
  596. self.readyState === FakeXMLHttpRequest.UNSENT ||
  597. self.readyState === FakeXMLHttpRequest.DONE
  598. ) {
  599. clearInterval(clearIntervalId);
  600. } else if (
  601. typeof self.timeout === "number" &&
  602. self.timeout > 0
  603. ) {
  604. if (Date.now() >= initiatedTime + self.timeout) {
  605. self.triggerTimeout();
  606. clearInterval(clearIntervalId);
  607. }
  608. }
  609. }, 1);
  610. }
  611. this.dispatchEvent(
  612. new sinonEvent.Event("loadstart", false, false, this)
  613. );
  614. },
  615. abort: function abort() {
  616. this.aborted = true;
  617. requestErrorSteps(this);
  618. this.readyState = FakeXMLHttpRequest.UNSENT;
  619. },
  620. error: function() {
  621. clearResponse(this);
  622. this.errorFlag = true;
  623. this.requestHeaders = {};
  624. this.responseHeaders = {};
  625. this.readyStateChange(FakeXMLHttpRequest.DONE);
  626. },
  627. triggerTimeout: function triggerTimeout() {
  628. if (sinonXhr.supportsTimeout) {
  629. this.timedOut = true;
  630. requestErrorSteps(this);
  631. }
  632. },
  633. getResponseHeader: function getResponseHeader(header) {
  634. if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
  635. return null;
  636. }
  637. if (/^Set-Cookie2?$/i.test(header)) {
  638. return null;
  639. }
  640. // eslint-disable-next-line no-param-reassign
  641. header = getHeader(this.responseHeaders, header);
  642. return this.responseHeaders[header] || null;
  643. },
  644. getAllResponseHeaders: function getAllResponseHeaders() {
  645. if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
  646. return "";
  647. }
  648. var responseHeaders = this.responseHeaders;
  649. var headers = Object.keys(responseHeaders)
  650. .filter(excludeSetCookie2Header)
  651. .reduce(function(prev, header) {
  652. var value = responseHeaders[header];
  653. return `${prev}${header}: ${value}\r\n`;
  654. }, "");
  655. return headers;
  656. },
  657. setResponseBody: function setResponseBody(body) {
  658. verifyRequestSent(this);
  659. verifyHeadersReceived(this);
  660. verifyResponseBodyType(body, this.responseType);
  661. var contentType =
  662. this.overriddenMimeType ||
  663. this.getResponseHeader("Content-Type");
  664. var isTextResponse =
  665. this.responseType === "" || this.responseType === "text";
  666. clearResponse(this);
  667. if (this.async) {
  668. var chunkSize = this.chunkSize || 10;
  669. var index = 0;
  670. do {
  671. this.readyStateChange(FakeXMLHttpRequest.LOADING);
  672. if (isTextResponse) {
  673. this.responseText = this.response += body.substring(
  674. index,
  675. index + chunkSize
  676. );
  677. }
  678. index += chunkSize;
  679. } while (index < body.length);
  680. }
  681. this.response = convertResponseBody(
  682. this.responseType,
  683. contentType,
  684. body
  685. );
  686. if (isTextResponse) {
  687. this.responseText = this.response;
  688. }
  689. if (this.responseType === "document") {
  690. this.responseXML = this.response;
  691. } else if (
  692. this.responseType === "" &&
  693. isXmlContentType(contentType)
  694. ) {
  695. this.responseXML = FakeXMLHttpRequest.parseXML(
  696. this.responseText
  697. );
  698. }
  699. this.readyStateChange(FakeXMLHttpRequest.DONE);
  700. },
  701. respond: function respond(status, headers, body) {
  702. this.responseURL = this.url;
  703. this.setStatus(status);
  704. this.setResponseHeaders(headers || {});
  705. this.setResponseBody(body || "");
  706. },
  707. uploadProgress: function uploadProgress(progressEventRaw) {
  708. if (supportsProgress) {
  709. this.upload.dispatchEvent(
  710. new sinonEvent.ProgressEvent(
  711. "progress",
  712. progressEventRaw,
  713. this.upload
  714. )
  715. );
  716. }
  717. },
  718. downloadProgress: function downloadProgress(progressEventRaw) {
  719. if (supportsProgress) {
  720. this.dispatchEvent(
  721. new sinonEvent.ProgressEvent(
  722. "progress",
  723. progressEventRaw,
  724. this
  725. )
  726. );
  727. }
  728. },
  729. uploadError: function uploadError(error) {
  730. if (supportsCustomEvent) {
  731. this.upload.dispatchEvent(
  732. new sinonEvent.CustomEvent("error", { detail: error })
  733. );
  734. }
  735. },
  736. overrideMimeType: function overrideMimeType(type) {
  737. if (this.readyState >= FakeXMLHttpRequest.LOADING) {
  738. throw new Error("INVALID_STATE_ERR");
  739. }
  740. this.overriddenMimeType = type;
  741. }
  742. });
  743. var states = {
  744. UNSENT: 0,
  745. OPENED: 1,
  746. HEADERS_RECEIVED: 2,
  747. LOADING: 3,
  748. DONE: 4
  749. };
  750. extend(FakeXMLHttpRequest, states);
  751. extend(FakeXMLHttpRequest.prototype, states);
  752. function useFakeXMLHttpRequest() {
  753. FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
  754. if (sinonXhr.supportsXHR) {
  755. globalScope.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest;
  756. }
  757. if (sinonXhr.supportsActiveX) {
  758. globalScope.ActiveXObject = sinonXhr.GlobalActiveXObject;
  759. }
  760. delete FakeXMLHttpRequest.restore;
  761. if (keepOnCreate !== true) {
  762. delete FakeXMLHttpRequest.onCreate;
  763. }
  764. };
  765. if (sinonXhr.supportsXHR) {
  766. globalScope.XMLHttpRequest = FakeXMLHttpRequest;
  767. }
  768. if (sinonXhr.supportsActiveX) {
  769. globalScope.ActiveXObject = function ActiveXObject(objId) {
  770. if (
  771. objId === "Microsoft.XMLHTTP" ||
  772. /^Msxml2\.XMLHTTP/i.test(objId)
  773. ) {
  774. return new FakeXMLHttpRequest();
  775. }
  776. return new sinonXhr.GlobalActiveXObject(objId);
  777. };
  778. }
  779. return FakeXMLHttpRequest;
  780. }
  781. return {
  782. xhr: sinonXhr,
  783. FakeXMLHttpRequest: FakeXMLHttpRequest,
  784. useFakeXMLHttpRequest: useFakeXMLHttpRequest
  785. };
  786. }
  787. module.exports = extend(fakeXMLHttpRequestFor(globalObject), {
  788. fakeXMLHttpRequestFor: fakeXMLHttpRequestFor
  789. });