aboutsummaryrefslogtreecommitdiff
path: root/node_modules/jsdom/lib/jsdom/living/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/jsdom/lib/jsdom/living/helpers')
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js15
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/binary-data.js9
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/create-element.js320
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js188
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/custom-elements.js270
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/dates-and-times.js270
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/details.js15
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/document-base-url.js54
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/events.js24
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/focusing.js104
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/form-controls.js306
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/html-constructor.js78
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/http-request.js254
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/internal-constants.js12
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/iterable-weak-set.js48
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/json.js12
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/mutation-observers.js198
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/namespaces.js15
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/node.js68
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/number-and-date-inputs.js195
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/ordered-set.js104
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js76
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/selectors.js47
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/shadow-dom.js285
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/strings.js148
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js114
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/stylesheets.js113
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/svg/basic-types.js41
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/svg/render.js46
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/text.js19
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/traversal.js72
-rw-r--r--node_modules/jsdom/lib/jsdom/living/helpers/validate-names.js75
32 files changed, 3595 insertions, 0 deletions
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js b/node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
new file mode 100644
index 0000000..4af6a24
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/agent-factory.js
@@ -0,0 +1,15 @@
+"use strict";
+const http = require("http");
+const https = require("https");
+const { parse: parseURLToNodeOptions } = require("url");
+const HttpProxyAgent = require("http-proxy-agent");
+const HttpsProxyAgent = require("https-proxy-agent");
+
+module.exports = function agentFactory(proxy, rejectUnauthorized) {
+ const agentOpts = { keepAlive: true, rejectUnauthorized };
+ if (proxy) {
+ const proxyOpts = { ...parseURLToNodeOptions(proxy), ...agentOpts };
+ return { https: new HttpsProxyAgent(proxyOpts), http: new HttpProxyAgent(proxyOpts) };
+ }
+ return { http: new http.Agent(agentOpts), https: new https.Agent(agentOpts) };
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/binary-data.js b/node_modules/jsdom/lib/jsdom/living/helpers/binary-data.js
new file mode 100644
index 0000000..dc5909c
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/binary-data.js
@@ -0,0 +1,9 @@
+"use strict";
+
+// See https://github.com/jsdom/jsdom/pull/2743#issuecomment-562991955 for background.
+exports.copyToArrayBufferInNewRealm = (nodejsBuffer, newRealm) => {
+ const newAB = new newRealm.ArrayBuffer(nodejsBuffer.byteLength);
+ const view = new Uint8Array(newAB);
+ view.set(nodejsBuffer);
+ return newAB;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/create-element.js b/node_modules/jsdom/lib/jsdom/living/helpers/create-element.js
new file mode 100644
index 0000000..0a330ec
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/create-element.js
@@ -0,0 +1,320 @@
+"use strict";
+
+const DOMException = require("domexception/webidl2js-wrapper");
+
+const interfaces = require("../interfaces");
+
+const { implForWrapper } = require("../generated/utils");
+
+const { HTML_NS, SVG_NS } = require("./namespaces");
+const { domSymbolTree } = require("./internal-constants");
+const { validateAndExtract } = require("./validate-names");
+const reportException = require("./runtime-script-errors");
+const {
+ isValidCustomElementName, upgradeElement, lookupCEDefinition, enqueueCEUpgradeReaction
+} = require("./custom-elements");
+
+const INTERFACE_TAG_MAPPING = {
+ // https://html.spec.whatwg.org/multipage/dom.html#elements-in-the-dom%3Aelement-interface
+ // https://html.spec.whatwg.org/multipage/indices.html#elements-3
+ [HTML_NS]: {
+ HTMLElement: [
+ "abbr", "address", "article", "aside", "b", "bdi", "bdo", "cite", "code", "dd", "dfn", "dt", "em", "figcaption",
+ "figure", "footer", "header", "hgroup", "i", "kbd", "main", "mark", "nav", "noscript", "rp", "rt", "ruby", "s",
+ "samp", "section", "small", "strong", "sub", "summary", "sup", "u", "var", "wbr"
+ ],
+ HTMLAnchorElement: ["a"],
+ HTMLAreaElement: ["area"],
+ HTMLAudioElement: ["audio"],
+ HTMLBaseElement: ["base"],
+ HTMLBodyElement: ["body"],
+ HTMLBRElement: ["br"],
+ HTMLButtonElement: ["button"],
+ HTMLCanvasElement: ["canvas"],
+ HTMLDataElement: ["data"],
+ HTMLDataListElement: ["datalist"],
+ HTMLDetailsElement: ["details"],
+ HTMLDialogElement: ["dialog"],
+ HTMLDirectoryElement: ["dir"],
+ HTMLDivElement: ["div"],
+ HTMLDListElement: ["dl"],
+ HTMLEmbedElement: ["embed"],
+ HTMLFieldSetElement: ["fieldset"],
+ HTMLFontElement: ["font"],
+ HTMLFormElement: ["form"],
+ HTMLFrameElement: ["frame"],
+ HTMLFrameSetElement: ["frameset"],
+ HTMLHeadingElement: ["h1", "h2", "h3", "h4", "h5", "h6"],
+ HTMLHeadElement: ["head"],
+ HTMLHRElement: ["hr"],
+ HTMLHtmlElement: ["html"],
+ HTMLIFrameElement: ["iframe"],
+ HTMLImageElement: ["img"],
+ HTMLInputElement: ["input"],
+ HTMLLabelElement: ["label"],
+ HTMLLegendElement: ["legend"],
+ HTMLLIElement: ["li"],
+ HTMLLinkElement: ["link"],
+ HTMLMapElement: ["map"],
+ HTMLMarqueeElement: ["marquee"],
+ HTMLMediaElement: [],
+ HTMLMenuElement: ["menu"],
+ HTMLMetaElement: ["meta"],
+ HTMLMeterElement: ["meter"],
+ HTMLModElement: ["del", "ins"],
+ HTMLObjectElement: ["object"],
+ HTMLOListElement: ["ol"],
+ HTMLOptGroupElement: ["optgroup"],
+ HTMLOptionElement: ["option"],
+ HTMLOutputElement: ["output"],
+ HTMLParagraphElement: ["p"],
+ HTMLParamElement: ["param"],
+ HTMLPictureElement: ["picture"],
+ HTMLPreElement: ["listing", "pre", "xmp"],
+ HTMLProgressElement: ["progress"],
+ HTMLQuoteElement: ["blockquote", "q"],
+ HTMLScriptElement: ["script"],
+ HTMLSelectElement: ["select"],
+ HTMLSlotElement: ["slot"],
+ HTMLSourceElement: ["source"],
+ HTMLSpanElement: ["span"],
+ HTMLStyleElement: ["style"],
+ HTMLTableCaptionElement: ["caption"],
+ HTMLTableCellElement: ["th", "td"],
+ HTMLTableColElement: ["col", "colgroup"],
+ HTMLTableElement: ["table"],
+ HTMLTimeElement: ["time"],
+ HTMLTitleElement: ["title"],
+ HTMLTableRowElement: ["tr"],
+ HTMLTableSectionElement: ["thead", "tbody", "tfoot"],
+ HTMLTemplateElement: ["template"],
+ HTMLTextAreaElement: ["textarea"],
+ HTMLTrackElement: ["track"],
+ HTMLUListElement: ["ul"],
+ HTMLUnknownElement: [],
+ HTMLVideoElement: ["video"]
+ },
+ [SVG_NS]: {
+ SVGElement: [],
+ SVGGraphicsElement: [],
+ SVGSVGElement: ["svg"],
+ SVGTitleElement: ["title"]
+ }
+};
+
+const TAG_INTERFACE_LOOKUP = {};
+
+for (const namespace of [HTML_NS, SVG_NS]) {
+ TAG_INTERFACE_LOOKUP[namespace] = {};
+
+ const interfaceNames = Object.keys(INTERFACE_TAG_MAPPING[namespace]);
+ for (const interfaceName of interfaceNames) {
+ const tagNames = INTERFACE_TAG_MAPPING[namespace][interfaceName];
+
+ for (const tagName of tagNames) {
+ TAG_INTERFACE_LOOKUP[namespace][tagName] = interfaceName;
+ }
+ }
+}
+
+const UNKNOWN_HTML_ELEMENTS_NAMES = ["applet", "bgsound", "blink", "isindex", "keygen", "multicol", "nextid", "spacer"];
+const HTML_ELEMENTS_NAMES = [
+ "acronym", "basefont", "big", "center", "nobr", "noembed", "noframes", "plaintext", "rb", "rtc",
+ "strike", "tt"
+];
+
+// https://html.spec.whatwg.org/multipage/dom.html#elements-in-the-dom:element-interface
+function getHTMLElementInterface(name) {
+ if (UNKNOWN_HTML_ELEMENTS_NAMES.includes(name)) {
+ return interfaces.getInterfaceWrapper("HTMLUnknownElement");
+ }
+
+ if (HTML_ELEMENTS_NAMES.includes(name)) {
+ return interfaces.getInterfaceWrapper("HTMLElement");
+ }
+
+ const specDefinedInterface = TAG_INTERFACE_LOOKUP[HTML_NS][name];
+ if (specDefinedInterface !== undefined) {
+ return interfaces.getInterfaceWrapper(specDefinedInterface);
+ }
+
+ if (isValidCustomElementName(name)) {
+ return interfaces.getInterfaceWrapper("HTMLElement");
+ }
+
+ return interfaces.getInterfaceWrapper("HTMLUnknownElement");
+}
+
+// https://svgwg.org/svg2-draft/types.html#ElementsInTheSVGDOM
+function getSVGInterface(name) {
+ const specDefinedInterface = TAG_INTERFACE_LOOKUP[SVG_NS][name];
+ if (specDefinedInterface !== undefined) {
+ return interfaces.getInterfaceWrapper(specDefinedInterface);
+ }
+
+ return interfaces.getInterfaceWrapper("SVGElement");
+}
+
+// Returns the list of valid tag names that can bo associated with a element given its namespace and name.
+function getValidTagNames(namespace, name) {
+ if (INTERFACE_TAG_MAPPING[namespace] && INTERFACE_TAG_MAPPING[namespace][name]) {
+ return INTERFACE_TAG_MAPPING[namespace][name];
+ }
+
+ return [];
+}
+
+// https://dom.spec.whatwg.org/#concept-create-element
+function createElement(
+ document,
+ localName,
+ namespace,
+ prefix = null,
+ isValue = null,
+ synchronousCE = false
+) {
+ let result = null;
+
+ const { _globalObject } = document;
+ const definition = lookupCEDefinition(document, namespace, localName, isValue);
+
+ if (definition !== null && definition.name !== localName) {
+ const elementInterface = getHTMLElementInterface(localName);
+
+ result = elementInterface.createImpl(_globalObject, [], {
+ ownerDocument: document,
+ localName,
+ namespace: HTML_NS,
+ prefix,
+ ceState: "undefined",
+ ceDefinition: null,
+ isValue
+ });
+
+ if (synchronousCE) {
+ upgradeElement(definition, result);
+ } else {
+ enqueueCEUpgradeReaction(result, definition);
+ }
+ } else if (definition !== null) {
+ if (synchronousCE) {
+ try {
+ const C = definition.constructor;
+
+ const resultWrapper = C.construct();
+ result = implForWrapper(resultWrapper);
+
+ if (!result._ceState || !result._ceDefinition || result._namespaceURI !== HTML_NS) {
+ throw new TypeError("Internal error: Invalid custom element.");
+ }
+
+ if (result._attributeList.length !== 0) {
+ throw DOMException.create(_globalObject, ["Unexpected attributes.", "NotSupportedError"]);
+ }
+ if (domSymbolTree.hasChildren(result)) {
+ throw DOMException.create(_globalObject, ["Unexpected child nodes.", "NotSupportedError"]);
+ }
+ if (domSymbolTree.parent(result)) {
+ throw DOMException.create(_globalObject, ["Unexpected element parent.", "NotSupportedError"]);
+ }
+ if (result._ownerDocument !== document) {
+ throw DOMException.create(_globalObject, ["Unexpected element owner document.", "NotSupportedError"]);
+ }
+ if (result._namespaceURI !== namespace) {
+ throw DOMException.create(_globalObject, ["Unexpected element namespace URI.", "NotSupportedError"]);
+ }
+ if (result._localName !== localName) {
+ throw DOMException.create(_globalObject, ["Unexpected element local name.", "NotSupportedError"]);
+ }
+
+ result._prefix = prefix;
+ result._isValue = isValue;
+ } catch (error) {
+ reportException(document._defaultView, error);
+
+ const interfaceWrapper = interfaces.getInterfaceWrapper("HTMLUnknownElement");
+ result = interfaceWrapper.createImpl(_globalObject, [], {
+ ownerDocument: document,
+ localName,
+ namespace: HTML_NS,
+ prefix,
+ ceState: "failed",
+ ceDefinition: null,
+ isValue: null
+ });
+ }
+ } else {
+ const interfaceWrapper = interfaces.getInterfaceWrapper("HTMLElement");
+ result = interfaceWrapper.createImpl(_globalObject, [], {
+ ownerDocument: document,
+ localName,
+ namespace: HTML_NS,
+ prefix,
+ ceState: "undefined",
+ ceDefinition: null,
+ isValue: null
+ });
+
+ enqueueCEUpgradeReaction(result, definition);
+ }
+ } else {
+ let elementInterface;
+
+ switch (namespace) {
+ case HTML_NS:
+ elementInterface = getHTMLElementInterface(localName);
+ break;
+
+ case SVG_NS:
+ elementInterface = getSVGInterface(localName);
+ break;
+
+ default:
+ elementInterface = interfaces.getInterfaceWrapper("Element");
+ break;
+ }
+
+ result = elementInterface.createImpl(_globalObject, [], {
+ ownerDocument: document,
+ localName,
+ namespace,
+ prefix,
+ ceState: "uncustomized",
+ ceDefinition: null,
+ isValue
+ });
+
+ if (namespace === HTML_NS && (isValidCustomElementName(localName) || isValue !== null)) {
+ result._ceState = "undefined";
+ }
+ }
+
+ return result;
+}
+
+// https://dom.spec.whatwg.org/#internal-createelementns-steps
+function internalCreateElementNSSteps(document, namespace, qualifiedName, options) {
+ const extracted = validateAndExtract(document._globalObject, namespace, qualifiedName);
+
+ let isValue = null;
+ if (options && options.is !== undefined) {
+ isValue = options.is;
+ }
+
+ return createElement(
+ document,
+ extracted.localName,
+ extracted.namespace,
+ extracted.prefix,
+ isValue,
+ true
+ );
+}
+
+module.exports = {
+ createElement,
+ internalCreateElementNSSteps,
+
+ getValidTagNames,
+ getHTMLElementInterface
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js b/node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js
new file mode 100644
index 0000000..b46e2ae
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/create-event-accessor.js
@@ -0,0 +1,188 @@
+"use strict";
+
+const idlUtils = require("../generated/utils");
+const ErrorEvent = require("../generated/ErrorEvent");
+const EventHandlerNonNull = require("../generated/EventHandlerNonNull.js");
+const OnBeforeUnloadEventHandlerNonNull = require("../generated/OnBeforeUnloadEventHandlerNonNull.js");
+const OnErrorEventHandlerNonNull = require("../generated/OnErrorEventHandlerNonNull.js");
+const reportException = require("./runtime-script-errors");
+
+exports.appendHandler = (el, eventName) => {
+ // tryImplForWrapper() is currently required due to use in Window.js
+ idlUtils.tryImplForWrapper(el).addEventListener(eventName, event => {
+ // https://html.spec.whatwg.org/#the-event-handler-processing-algorithm
+ const callback = exports.getCurrentEventHandlerValue(el, eventName);
+ if (callback === null) {
+ return;
+ }
+
+ const specialError = ErrorEvent.isImpl(event) && event.type === "error" &&
+ event.currentTarget.constructor.name === "Window";
+
+ let returnValue = null;
+ // https://heycam.github.io/webidl/#es-invoking-callback-functions
+ if (typeof callback === "function") {
+ if (specialError) {
+ returnValue = callback.call(
+ event.currentTarget,
+ event.message,
+ event.filename,
+ event.lineno,
+ event.colno,
+ event.error
+ );
+ } else {
+ returnValue = callback.call(event.currentTarget, event);
+ }
+ }
+
+ // TODO: we don't implement BeforeUnloadEvent so we can't brand-check here
+ if (event.type === "beforeunload") {
+ if (returnValue !== null) {
+ event._canceledFlag = true;
+ if (event.returnValue === "") {
+ event.returnValue = returnValue;
+ }
+ }
+ } else if (specialError) {
+ if (returnValue === true) {
+ event._canceledFlag = true;
+ }
+ } else if (returnValue === false) {
+ event._canceledFlag = true;
+ }
+ });
+};
+
+// "Simple" in this case means "no content attributes involved"
+exports.setupForSimpleEventAccessors = (prototype, events) => {
+ prototype._getEventHandlerFor = function (event) {
+ return this._eventHandlers ? this._eventHandlers[event] : undefined;
+ };
+
+ prototype._setEventHandlerFor = function (event, handler) {
+ if (!this._registeredHandlers) {
+ this._registeredHandlers = new Set();
+ this._eventHandlers = Object.create(null);
+ }
+
+ if (!this._registeredHandlers.has(event) && handler !== null) {
+ this._registeredHandlers.add(event);
+ exports.appendHandler(this, event);
+ }
+ this._eventHandlers[event] = handler;
+ };
+
+ for (const event of events) {
+ exports.createEventAccessor(prototype, event);
+ }
+};
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler
+exports.getCurrentEventHandlerValue = (target, event) => {
+ const value = target._getEventHandlerFor(event);
+ if (!value) {
+ return null;
+ }
+
+ if (value.body !== undefined) {
+ let element, document, fn;
+ if (target.constructor.name === "Window") {
+ element = null;
+ document = idlUtils.implForWrapper(target.document);
+ } else {
+ element = target;
+ document = element.ownerDocument;
+ }
+ const { body } = value;
+
+ const formOwner = element !== null && element.form ? element.form : null;
+ const window = target.constructor.name === "Window" && target._document ? target : document.defaultView;
+
+ try {
+ // eslint-disable-next-line no-new-func
+ Function(body); // properly error out on syntax errors
+ // Note: this won't execute body; that would require `Function(body)()`.
+ } catch (e) {
+ if (window) {
+ reportException(window, e);
+ }
+ target._setEventHandlerFor(event, null);
+ return null;
+ }
+
+ // Note: the with (window) { } is not necessary in Node, but is necessary in a browserified environment.
+
+ const createFunction = document.defaultView.Function;
+ if (event === "error" && element === null) {
+ const sourceURL = document ? `\n//# sourceURL=${document.URL}` : "";
+
+ fn = createFunction(`\
+with (arguments[0]) { return function onerror(event, source, lineno, colno, error) {
+${body}
+}; }${sourceURL}`)(window);
+
+ fn = OnErrorEventHandlerNonNull.convert(fn);
+ } else {
+ const calls = [];
+ if (element !== null) {
+ calls.push(idlUtils.wrapperForImpl(document));
+ }
+
+ if (formOwner !== null) {
+ calls.push(idlUtils.wrapperForImpl(formOwner));
+ }
+
+ if (element !== null) {
+ calls.push(idlUtils.wrapperForImpl(element));
+ }
+
+ let wrapperBody = `\
+with (arguments[0]) { return function on${event}(event) {
+${body}
+}; }`;
+
+ // eslint-disable-next-line no-unused-vars
+ for (const call of calls) {
+ wrapperBody = `\
+with (arguments[0]) { return function () {
+${wrapperBody}
+}; }`;
+ }
+
+ if (document) {
+ wrapperBody += `\n//# sourceURL=${document.URL}`;
+ }
+
+ fn = createFunction(wrapperBody)(window);
+ for (const call of calls) {
+ fn = fn(call);
+ }
+
+ if (event === "beforeunload") {
+ fn = OnBeforeUnloadEventHandlerNonNull.convert(fn);
+ } else {
+ fn = EventHandlerNonNull.convert(fn);
+ }
+ }
+
+ target._setEventHandlerFor(event, fn);
+ }
+
+ return target._getEventHandlerFor(event);
+};
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-idl-attributes
+// TODO: Consider replacing this with `[ReflectEvent]`
+exports.createEventAccessor = (obj, event) => {
+ Object.defineProperty(obj, "on" + event, {
+ configurable: true,
+ enumerable: true,
+ get() {
+ return exports.getCurrentEventHandlerValue(this, event);
+ },
+ set(val) {
+ this._setEventHandlerFor(event, val);
+ }
+ });
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/custom-elements.js b/node_modules/jsdom/lib/jsdom/living/helpers/custom-elements.js
new file mode 100644
index 0000000..1dbd773
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/custom-elements.js
@@ -0,0 +1,270 @@
+"use strict";
+
+const DOMException = require("domexception/webidl2js-wrapper");
+const isPotentialCustomElementName = require("is-potential-custom-element-name");
+
+const NODE_TYPE = require("../node-type");
+const { HTML_NS } = require("./namespaces");
+const { shadowIncludingRoot } = require("./shadow-dom");
+const reportException = require("./runtime-script-errors");
+
+const { implForWrapper, wrapperForImpl } = require("../generated/utils");
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-reactions-stack
+class CEReactionsStack {
+ constructor() {
+ this._stack = [];
+
+ // https://html.spec.whatwg.org/multipage/custom-elements.html#backup-element-queue
+ this.backupElementQueue = [];
+
+ // https://html.spec.whatwg.org/multipage/custom-elements.html#processing-the-backup-element-queue
+ this.processingBackupElementQueue = false;
+ }
+
+ push(elementQueue) {
+ this._stack.push(elementQueue);
+ }
+
+ pop() {
+ return this._stack.pop();
+ }
+
+ get currentElementQueue() {
+ const { _stack } = this;
+ return _stack[_stack.length - 1];
+ }
+
+ isEmpty() {
+ return this._stack.length === 0;
+ }
+}
+
+// In theory separate cross-origin Windows created by separate JSDOM instances could have separate stacks. But, we would
+// need to implement the whole agent architecture. Which is kind of questionable given that we don't run our Windows in
+// their own separate threads, which is what agents are meant to represent.
+const customElementReactionsStack = new CEReactionsStack();
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#cereactions
+function ceReactionsPreSteps() {
+ customElementReactionsStack.push([]);
+}
+function ceReactionsPostSteps() {
+ const queue = customElementReactionsStack.pop();
+ invokeCEReactions(queue);
+}
+
+const RESTRICTED_CUSTOM_ELEMENT_NAME = new Set([
+ "annotation-xml",
+ "color-profile",
+ "font-face",
+ "font-face-src",
+ "font-face-uri",
+ "font-face-format",
+ "font-face-name",
+ "missing-glyph"
+]);
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
+function isValidCustomElementName(name) {
+ if (RESTRICTED_CUSTOM_ELEMENT_NAME.has(name)) {
+ return false;
+ }
+
+ return isPotentialCustomElementName(name);
+}
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#concept-upgrade-an-element
+function upgradeElement(definition, element) {
+ if (element._ceState !== "undefined" || element._ceState === "uncustomized") {
+ return;
+ }
+
+ element._ceDefinition = definition;
+ element._ceState = "failed";
+
+ for (const attribute of element._attributeList) {
+ const { _localName, _namespace, _value } = attribute;
+ enqueueCECallbackReaction(element, "attributeChangedCallback", [_localName, null, _value, _namespace]);
+ }
+
+ if (shadowIncludingRoot(element).nodeType === NODE_TYPE.DOCUMENT_NODE) {
+ enqueueCECallbackReaction(element, "connectedCallback", []);
+ }
+
+ definition.constructionStack.push(element);
+
+ const { constructionStack, constructor: C } = definition;
+
+ let constructionError;
+ try {
+ if (definition.disableShadow === true && element._shadowRoot !== null) {
+ throw DOMException.create(element._globalObject, [
+ "Can't upgrade a custom element with a shadow root if shadow is disabled",
+ "NotSupportedError"
+ ]);
+ }
+
+ const constructionResult = C.construct();
+ const constructionResultImpl = implForWrapper(constructionResult);
+
+ if (constructionResultImpl !== element) {
+ throw new TypeError("Invalid custom element constructor return value");
+ }
+ } catch (error) {
+ constructionError = error;
+ }
+
+ constructionStack.pop();
+
+ if (constructionError !== undefined) {
+ element._ceDefinition = null;
+ element._ceReactionQueue = [];
+
+ throw constructionError;
+ }
+
+ element._ceState = "custom";
+}
+
+// https://html.spec.whatwg.org/#concept-try-upgrade
+function tryUpgradeElement(element) {
+ const { _ownerDocument, _namespaceURI, _localName, _isValue } = element;
+ const definition = lookupCEDefinition(_ownerDocument, _namespaceURI, _localName, _isValue);
+
+ if (definition !== null) {
+ enqueueCEUpgradeReaction(element, definition);
+ }
+}
+
+// https://html.spec.whatwg.org/#look-up-a-custom-element-definition
+function lookupCEDefinition(document, namespace, localName, isValue) {
+ const definition = null;
+
+ if (namespace !== HTML_NS) {
+ return definition;
+ }
+
+ if (!document._defaultView) {
+ return definition;
+ }
+
+ const registry = implForWrapper(document._globalObject.customElements);
+
+ const definitionByName = registry._customElementDefinitions.find(def => {
+ return def.name === def.localName && def.localName === localName;
+ });
+ if (definitionByName !== undefined) {
+ return definitionByName;
+ }
+
+ const definitionByIs = registry._customElementDefinitions.find(def => {
+ return def.name === isValue && def.localName === localName;
+ });
+ if (definitionByIs !== undefined) {
+ return definitionByIs;
+ }
+
+ return definition;
+}
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#invoke-custom-element-reactions
+function invokeCEReactions(elementQueue) {
+ while (elementQueue.length > 0) {
+ const element = elementQueue.shift();
+
+ const reactions = element._ceReactionQueue;
+
+ try {
+ while (reactions.length > 0) {
+ const reaction = reactions.shift();
+
+ switch (reaction.type) {
+ case "upgrade":
+ upgradeElement(reaction.definition, element);
+ break;
+
+ case "callback":
+ reaction.callback.apply(wrapperForImpl(element), reaction.args);
+ break;
+ }
+ }
+ } catch (error) {
+ reportException(element._globalObject, error);
+ }
+ }
+}
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#enqueue-an-element-on-the-appropriate-element-queue
+function enqueueElementOnAppropriateElementQueue(element) {
+ if (customElementReactionsStack.isEmpty()) {
+ customElementReactionsStack.backupElementQueue.push(element);
+
+ if (customElementReactionsStack.processingBackupElementQueue) {
+ return;
+ }
+
+ customElementReactionsStack.processingBackupElementQueue = true;
+
+ Promise.resolve().then(() => {
+ const elementQueue = customElementReactionsStack.backupElementQueue;
+ invokeCEReactions(elementQueue);
+
+ customElementReactionsStack.processingBackupElementQueue = false;
+ });
+ } else {
+ customElementReactionsStack.currentElementQueue.push(element);
+ }
+}
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#enqueue-a-custom-element-callback-reaction
+function enqueueCECallbackReaction(element, callbackName, args) {
+ const { _ceDefinition: { lifecycleCallbacks, observedAttributes } } = element;
+
+ const callback = lifecycleCallbacks[callbackName];
+ if (callback === null) {
+ return;
+ }
+
+ if (callbackName === "attributeChangedCallback") {
+ const attributeName = args[0];
+ if (!observedAttributes.includes(attributeName)) {
+ return;
+ }
+ }
+
+ element._ceReactionQueue.push({
+ type: "callback",
+ callback,
+ args
+ });
+
+ enqueueElementOnAppropriateElementQueue(element);
+}
+
+// https://html.spec.whatwg.org/#enqueue-a-custom-element-upgrade-reaction
+function enqueueCEUpgradeReaction(element, definition) {
+ element._ceReactionQueue.push({
+ type: "upgrade",
+ definition
+ });
+
+ enqueueElementOnAppropriateElementQueue(element);
+}
+
+module.exports = {
+ customElementReactionsStack,
+
+ ceReactionsPreSteps,
+ ceReactionsPostSteps,
+
+ isValidCustomElementName,
+
+ upgradeElement,
+ tryUpgradeElement,
+
+ lookupCEDefinition,
+ enqueueCEUpgradeReaction,
+ enqueueCECallbackReaction,
+ invokeCEReactions
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/dates-and-times.js b/node_modules/jsdom/lib/jsdom/living/helpers/dates-and-times.js
new file mode 100644
index 0000000..15d920b
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/dates-and-times.js
@@ -0,0 +1,270 @@
+"use strict";
+
+function isLeapYear(year) {
+ return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0);
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#number-of-days-in-month-month-of-year-year
+const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+function numberOfDaysInMonthOfYear(month, year) {
+ if (month === 2 && isLeapYear(year)) {
+ return 29;
+ }
+ return daysInMonth[month - 1];
+}
+
+const monthRe = /^([0-9]{4,})-([0-9]{2})$/;
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-month-string
+function parseMonthString(str) {
+ const matches = monthRe.exec(str);
+ if (!matches) {
+ return null;
+ }
+ const year = Number(matches[1]);
+ if (year <= 0) {
+ return null;
+ }
+ const month = Number(matches[2]);
+ if (month < 1 || month > 12) {
+ return null;
+ }
+ return { year, month };
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-month-string
+function isValidMonthString(str) {
+ return parseMonthString(str) !== null;
+}
+function serializeMonth({ year, month }) {
+ const yearStr = `${year}`.padStart(4, "0");
+ const monthStr = `${month}`.padStart(2, "0");
+ return `${yearStr}-${monthStr}`;
+}
+
+const dateRe = /^([0-9]{4,})-([0-9]{2})-([0-9]{2})$/;
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-date-string
+function parseDateString(str) {
+ const matches = dateRe.exec(str);
+ if (!matches) {
+ return null;
+ }
+ const year = Number(matches[1]);
+ if (year <= 0) {
+ return null;
+ }
+ const month = Number(matches[2]);
+ if (month < 1 || month > 12) {
+ return null;
+ }
+ const day = Number(matches[3]);
+ if (day < 1 || day > numberOfDaysInMonthOfYear(month, year)) {
+ return null;
+ }
+ return { year, month, day };
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
+function isValidDateString(str) {
+ return parseDateString(str) !== null;
+}
+function serializeDate(date) {
+ const dayStr = `${date.day}`.padStart(2, "0");
+ return `${serializeMonth(date)}-${dayStr}`;
+}
+
+const yearlessDateRe = /^(?:--)?([0-9]{2})-([0-9]{2})$/;
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-yearless-date-string
+function parseYearlessDateString(str) {
+ const matches = yearlessDateRe.exec(str);
+ if (!matches) {
+ return null;
+ }
+ const month = Number(matches[1]);
+ if (month < 1 || month > 12) {
+ return null;
+ }
+ const day = Number(matches[2]);
+ if (day < 1 || day > numberOfDaysInMonthOfYear(month, 4)) {
+ return null;
+ }
+ return { month, day };
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-yearless-date-string
+function isValidYearlessDateString(str) {
+ return parseYearlessDateString(str) !== null;
+}
+function serializeYearlessDate({ month, day }) {
+ const monthStr = `${month}`.padStart(2, "0");
+ const dayStr = `${day}`.padStart(2, "0");
+ return `${monthStr}-${dayStr}`;
+}
+
+const timeRe = /^([0-9]{2}):([0-9]{2})(?::([0-9]{2}(?:\.([0-9]{1,3}))?))?$/;
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-time-string
+function parseTimeString(str) {
+ const matches = timeRe.exec(str);
+ if (!matches) {
+ return null;
+ }
+ const hour = Number(matches[1]);
+ if (hour < 0 || hour > 23) {
+ return null;
+ }
+ const minute = Number(matches[2]);
+ if (minute < 0 || minute > 59) {
+ return null;
+ }
+ const second = matches[3] !== undefined ? Math.trunc(Number(matches[3])) : 0;
+ if (second < 0 || second >= 60) {
+ return null;
+ }
+ const millisecond = matches[4] !== undefined ? Number(matches[4]) : 0;
+ return { hour, minute, second, millisecond };
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string
+function isValidTimeString(str) {
+ return parseTimeString(str) !== null;
+}
+
+function serializeTime({ hour, minute, second, millisecond }) {
+ const hourStr = `${hour}`.padStart(2, "0");
+ const minuteStr = `${minute}`.padStart(2, "0");
+ if (second === 0 && millisecond === 0) {
+ return `${hourStr}:${minuteStr}`;
+ }
+ const secondStr = `${second}`.padStart(2, "0");
+ const millisecondStr = `${millisecond}`.padStart(3, "0");
+ return `${hourStr}:${minuteStr}:${secondStr}.${millisecondStr}`;
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-local-date-and-time-string
+function parseLocalDateAndTimeString(str, normalized = false) {
+ let separatorIdx = str.indexOf("T");
+ if (separatorIdx < 0 && !normalized) {
+ separatorIdx = str.indexOf(" ");
+ }
+ if (separatorIdx < 0) {
+ return null;
+ }
+ const date = parseDateString(str.slice(0, separatorIdx));
+ if (date === null) {
+ return null;
+ }
+ const time = parseTimeString(str.slice(separatorIdx + 1));
+ if (time === null) {
+ return null;
+ }
+ return { date, time };
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-local-date-and-time-string
+function isValidLocalDateAndTimeString(str) {
+ return parseLocalDateAndTimeString(str) !== null;
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-normalised-local-date-and-time-string
+function isValidNormalizedLocalDateAndTimeString(str) {
+ return parseLocalDateAndTimeString(str, true) !== null;
+}
+function serializeNormalizedDateAndTime({ date, time }) {
+ return `${serializeDate(date)}T${serializeTime(time)}`;
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#week-number-of-the-last-day
+// https://stackoverflow.com/a/18538272/1937836
+function weekNumberOfLastDay(year) {
+ const jan1 = new Date(year, 0);
+ return jan1.getDay() === 4 || (isLeapYear(year) && jan1.getDay() === 3) ? 53 : 52;
+}
+
+const weekRe = /^([0-9]{4,5})-W([0-9]{2})$/;
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-week-string
+function parseWeekString(str) {
+ const matches = weekRe.exec(str);
+ if (!matches) {
+ return null;
+ }
+ const year = Number(matches[1]);
+ if (year <= 0) {
+ return null;
+ }
+ const week = Number(matches[2]);
+ if (week < 1 || week > weekNumberOfLastDay(year)) {
+ return null;
+ }
+ return { year, week };
+}
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-week-string
+function isValidWeekString(str) {
+ return parseWeekString(str) !== null;
+}
+function serializeWeek({ year, week }) {
+ const yearStr = `${year}`.padStart(4, "0");
+ const weekStr = `${week}`.padStart(2, "0");
+ return `${yearStr}-W${weekStr}`;
+}
+
+// https://stackoverflow.com/a/6117889
+function parseDateAsWeek(originalDate) {
+ const dayInSeconds = 86400000;
+ // Copy date so don't modify original
+ const date = new Date(Date.UTC(originalDate.getUTCFullYear(), originalDate.getUTCMonth(), originalDate.getUTCDate()));
+ // Set to nearest Thursday: current date + 4 - current day number
+ // Make Sunday's day number 7
+ date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay() || 7));
+ // Get first day of year
+ const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
+ // Calculate full weeks to nearest Thursday
+ const week = Math.ceil((((date - yearStart) / dayInSeconds) + 1) / 7);
+
+ return { year: date.getUTCFullYear(), week };
+}
+
+function isDate(obj) {
+ try {
+ Date.prototype.valueOf.call(obj);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+module.exports = {
+ isDate,
+ numberOfDaysInMonthOfYear,
+
+ parseMonthString,
+ isValidMonthString,
+ serializeMonth,
+
+ parseDateString,
+ isValidDateString,
+ serializeDate,
+
+ parseYearlessDateString,
+ isValidYearlessDateString,
+ serializeYearlessDate,
+
+ parseTimeString,
+ isValidTimeString,
+ serializeTime,
+
+ parseLocalDateAndTimeString,
+ isValidLocalDateAndTimeString,
+ isValidNormalizedLocalDateAndTimeString,
+ serializeNormalizedDateAndTime,
+
+ parseDateAsWeek,
+ weekNumberOfLastDay,
+ parseWeekString,
+ isValidWeekString,
+ serializeWeek
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/details.js b/node_modules/jsdom/lib/jsdom/living/helpers/details.js
new file mode 100644
index 0000000..25c5387
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/details.js
@@ -0,0 +1,15 @@
+"use strict";
+const { firstChildWithLocalName } = require("./traversal");
+const { HTML_NS } = require("./namespaces");
+
+// https://html.spec.whatwg.org/multipage/interactive-elements.html#summary-for-its-parent-details
+exports.isSummaryForParentDetails = summaryElement => {
+ const parent = summaryElement.parentNode;
+ if (parent === null) {
+ return false;
+ }
+ if (parent._localName !== "details" || parent._namespaceURI !== HTML_NS) {
+ return false;
+ }
+ return firstChildWithLocalName(parent, "summary") === summaryElement;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/document-base-url.js b/node_modules/jsdom/lib/jsdom/living/helpers/document-base-url.js
new file mode 100644
index 0000000..f69e061
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/document-base-url.js
@@ -0,0 +1,54 @@
+"use strict";
+const whatwgURL = require("whatwg-url");
+const { implForWrapper } = require("../generated/utils");
+
+exports.documentBaseURL = document => {
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#document-base-url
+
+ const firstBase = document.querySelector("base[href]");
+ const fallbackBaseURL = exports.fallbackBaseURL(document);
+
+ if (firstBase === null) {
+ return fallbackBaseURL;
+ }
+
+ return frozenBaseURL(firstBase, fallbackBaseURL);
+};
+
+exports.documentBaseURLSerialized = document => {
+ return whatwgURL.serializeURL(exports.documentBaseURL(document));
+};
+
+exports.fallbackBaseURL = document => {
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#fallback-base-url
+
+ // Unimplemented: <iframe srcdoc>
+
+ if (document.URL === "about:blank" && document._defaultView &&
+ document._defaultView._parent !== document._defaultView) {
+ const parentDocument = implForWrapper(document._defaultView._parent._document);
+ return exports.documentBaseURL(parentDocument);
+ }
+
+ return document._URL;
+};
+
+exports.parseURLToResultingURLRecord = (url, document) => {
+ // https://html.spec.whatwg.org/#resolve-a-url
+
+ // Encoding stuff ignored; always UTF-8 for us, for now.
+
+ const baseURL = exports.documentBaseURL(document);
+
+ return whatwgURL.parseURL(url, { baseURL });
+ // This returns the resulting URL record; to get the resulting URL string, just serialize it.
+};
+
+function frozenBaseURL(baseElement, fallbackBaseURL) {
+ // https://html.spec.whatwg.org/multipage/semantics.html#frozen-base-url
+ // The spec is eager (setting the frozen base URL when things change); we are lazy (getting it when we need to)
+
+ const baseHrefAttribute = baseElement.getAttributeNS(null, "href");
+ const result = whatwgURL.parseURL(baseHrefAttribute, { baseURL: fallbackBaseURL });
+ return result === null ? fallbackBaseURL : result;
+}
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/events.js b/node_modules/jsdom/lib/jsdom/living/helpers/events.js
new file mode 100644
index 0000000..cd65a38
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/events.js
@@ -0,0 +1,24 @@
+"use strict";
+
+const Event = require("../generated/Event");
+const { tryImplForWrapper } = require("../generated/utils");
+
+function createAnEvent(e, globalObject, eventInterface = Event, attributes = {}) {
+ return eventInterface.createImpl(
+ globalObject,
+ [e, attributes],
+ { isTrusted: attributes.isTrusted !== false }
+ );
+}
+
+function fireAnEvent(e, target, eventInterface, attributes, legacyTargetOverrideFlag) {
+ const event = createAnEvent(e, target._globalObject, eventInterface, attributes);
+
+ // tryImplForWrapper() is currently required due to use in Window.js
+ return tryImplForWrapper(target)._dispatch(event, legacyTargetOverrideFlag);
+}
+
+module.exports = {
+ createAnEvent,
+ fireAnEvent
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/focusing.js b/node_modules/jsdom/lib/jsdom/living/helpers/focusing.js
new file mode 100644
index 0000000..7d1a38c
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/focusing.js
@@ -0,0 +1,104 @@
+"use strict";
+const FocusEvent = require("../generated/FocusEvent.js");
+const idlUtils = require("../generated/utils.js");
+const { isDisabled } = require("./form-controls.js");
+const { firstChildWithLocalName } = require("./traversal");
+const { createAnEvent } = require("./events");
+const { HTML_NS, SVG_NS } = require("./namespaces");
+const { isRenderedElement } = require("./svg/render");
+
+const focusableFormElements = new Set(["input", "select", "textarea", "button"]);
+
+// https://html.spec.whatwg.org/multipage/interaction.html#focusable-area, but also some of
+// https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps and some of
+// https://svgwg.org/svg2-draft/interact.html#TermFocusable
+exports.isFocusableAreaElement = elImpl => {
+ // We implemented most of the suggested focusable elements found here:
+ // https://html.spec.whatwg.org/multipage/interaction.html#tabindex-value
+ // However, some suggested elements are not focusable in web browsers, as detailed here:
+ // https://github.com/whatwg/html/issues/5490
+ if (elImpl._namespaceURI === HTML_NS) {
+ if (!elImpl._ownerDocument._defaultView) {
+ return false;
+ }
+
+ if (!elImpl.isConnected) {
+ return false;
+ }
+
+ if (!Number.isNaN(parseInt(elImpl.getAttributeNS(null, "tabindex")))) {
+ return true;
+ }
+
+ if (elImpl._localName === "iframe") {
+ return true;
+ }
+
+ if (elImpl._localName === "a" && elImpl.hasAttributeNS(null, "href")) {
+ return true;
+ }
+
+ if (elImpl._localName === "summary" && elImpl.parentNode &&
+ elImpl.parentNode._localName === "details" &&
+ elImpl === firstChildWithLocalName(elImpl.parentNode, "summary")) {
+ return true;
+ }
+
+ if (focusableFormElements.has(elImpl._localName) && !isDisabled(elImpl)) {
+ if (elImpl._localName === "input" && elImpl.type === "hidden") {
+ return false;
+ }
+
+ return true;
+ }
+
+ if (elImpl.hasAttributeNS(null, "contenteditable")) {
+ return true;
+ }
+
+ return false;
+
+ // This does not check for a designMode Document as specified in
+ // https://html.spec.whatwg.org/multipage/interaction.html#editing-host because the designMode
+ // attribute is not implemented.
+ }
+
+ if (elImpl._namespaceURI === SVG_NS) {
+ if (!Number.isNaN(parseInt(elImpl.getAttributeNS(null, "tabindex"))) && isRenderedElement(elImpl)) {
+ return true;
+ }
+
+ if (elImpl._localName === "a" && elImpl.hasAttributeNS(null, "href")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+};
+
+// https://html.spec.whatwg.org/multipage/interaction.html#fire-a-focus-event plus the steps of
+// https://html.spec.whatwg.org/multipage/interaction.html#focus-update-steps that adjust Documents to Windows
+// It's extended with the bubbles option to also handle focusin/focusout, which are "defined" in
+// https://w3c.github.io/uievents/#event-type-focusin. See https://github.com/whatwg/html/issues/3514.
+exports.fireFocusEventWithTargetAdjustment = (name, target, relatedTarget, { bubbles = false } = {}) => {
+ if (target === null) {
+ // E.g. firing blur with nothing previously focused.
+ return;
+ }
+
+ const event = createAnEvent(name, target._globalObject, FocusEvent, {
+ bubbles,
+ composed: true,
+ relatedTarget,
+ view: target._ownerDocument._defaultView,
+ detail: 0
+ });
+
+ if (target._defaultView) {
+ target = idlUtils.implForWrapper(target._defaultView);
+ }
+
+ target._dispatch(event);
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/form-controls.js b/node_modules/jsdom/lib/jsdom/living/helpers/form-controls.js
new file mode 100644
index 0000000..bd4a799
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/form-controls.js
@@ -0,0 +1,306 @@
+"use strict";
+
+const {
+ isValidFloatingPointNumber,
+ isValidSimpleColor,
+ parseFloatingPointNumber,
+ stripLeadingAndTrailingASCIIWhitespace,
+ stripNewlines,
+ splitOnCommas
+} = require("./strings");
+const {
+ isValidDateString,
+ isValidMonthString,
+ isValidTimeString,
+ isValidWeekString,
+ parseLocalDateAndTimeString,
+ serializeNormalizedDateAndTime
+} = require("./dates-and-times");
+const whatwgURL = require("whatwg-url");
+
+const NodeList = require("../generated/NodeList");
+const { domSymbolTree } = require("./internal-constants");
+const { closest, firstChildWithLocalName } = require("./traversal");
+const NODE_TYPE = require("../node-type");
+const { HTML_NS } = require("./namespaces");
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-disabled
+exports.isDisabled = formControl => {
+ if (formControl.localName === "button" || formControl.localName === "input" || formControl.localName === "select" ||
+ formControl.localName === "textarea") {
+ if (formControl.hasAttributeNS(null, "disabled")) {
+ return true;
+ }
+ }
+
+ let e = formControl.parentNode;
+ while (e) {
+ if (e.localName === "fieldset" && e.hasAttributeNS(null, "disabled")) {
+ const firstLegendElementChild = firstChildWithLocalName(e, "legend");
+ if (!firstLegendElementChild || !firstLegendElementChild.contains(formControl)) {
+ return true;
+ }
+ }
+ e = e.parentNode;
+ }
+
+ return false;
+};
+
+// https://html.spec.whatwg.org/multipage/forms.html#category-listed
+const listedElements = new Set(["button", "fieldset", "input", "object", "output", "select", "textarea"]);
+exports.isListed = formControl => listedElements.has(formControl._localName) && formControl.namespaceURI === HTML_NS;
+
+// https://html.spec.whatwg.org/multipage/forms.html#category-submit
+const submittableElements = new Set(["button", "input", "object", "select", "textarea"]);
+exports.isSubmittable = formControl => {
+ return submittableElements.has(formControl._localName) && formControl.namespaceURI === HTML_NS;
+};
+
+// https://html.spec.whatwg.org/multipage/forms.html#concept-submit-button
+const submitButtonInputTypes = new Set(["submit", "image"]);
+exports.isSubmitButton = formControl => {
+ return ((formControl._localName === "input" && submitButtonInputTypes.has(formControl.type)) ||
+ (formControl._localName === "button" && formControl.type === "submit")) &&
+ formControl.namespaceURI === HTML_NS;
+};
+
+// https://html.spec.whatwg.org/multipage/forms.html#concept-button
+const buttonInputTypes = new Set([...submitButtonInputTypes, "reset", "button"]);
+exports.isButton = formControl => {
+ return ((formControl._localName === "input" && buttonInputTypes.has(formControl.type)) ||
+ formControl._localName === "button") &&
+ formControl.namespaceURI === HTML_NS;
+};
+
+// https://html.spec.whatwg.org/multipage/dom.html#interactive-content-2
+exports.isInteractiveContent = node => {
+ if (node.nodeType !== NODE_TYPE.ELEMENT_NODE) {
+ return false;
+ }
+ if (node.namespaceURI !== HTML_NS) {
+ return false;
+ }
+ if (node.hasAttributeNS(null, "tabindex")) {
+ return true;
+ }
+ switch (node.localName) {
+ case "a":
+ return node.hasAttributeNS(null, "href");
+
+ case "audio":
+ case "video":
+ return node.hasAttributeNS(null, "controls");
+
+ case "img":
+ case "object":
+ return node.hasAttributeNS(null, "usemap");
+
+ case "input":
+ return node.type !== "hidden";
+
+ case "button":
+ case "details":
+ case "embed":
+ case "iframe":
+ case "label":
+ case "select":
+ case "textarea":
+ return true;
+ }
+
+ return false;
+};
+
+// https://html.spec.whatwg.org/multipage/forms.html#category-label
+exports.isLabelable = node => {
+ if (node.nodeType !== NODE_TYPE.ELEMENT_NODE) {
+ return false;
+ }
+ if (node.namespaceURI !== HTML_NS) {
+ return false;
+ }
+ switch (node.localName) {
+ case "button":
+ case "meter":
+ case "output":
+ case "progress":
+ case "select":
+ case "textarea":
+ return true;
+
+ case "input":
+ return node.type !== "hidden";
+ }
+
+ return false;
+};
+
+exports.getLabelsForLabelable = labelable => {
+ if (!exports.isLabelable(labelable)) {
+ return null;
+ }
+ if (!labelable._labels) {
+ const root = labelable.getRootNode({});
+ labelable._labels = NodeList.create(root._globalObject, [], {
+ element: root,
+ query: () => {
+ const nodes = [];
+ for (const descendant of domSymbolTree.treeIterator(root)) {
+ if (descendant.control === labelable) {
+ nodes.push(descendant);
+ }
+ }
+ return nodes;
+ }
+ });
+ }
+ return labelable._labels;
+};
+
+// https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address
+exports.isValidEmailAddress = (emailAddress, multiple = false) => {
+ const emailAddressRegExp = new RegExp("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9]" +
+ "(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}" +
+ "[a-zA-Z0-9])?)*$");
+ // A valid e-mail address list is a set of comma-separated tokens, where each token is itself
+ // a valid e - mail address.To obtain the list of tokens from a valid e - mail address list,
+ // an implementation must split the string on commas.
+ if (multiple) {
+ return splitOnCommas(emailAddress).every(value => emailAddressRegExp.test(value));
+ }
+ return emailAddressRegExp.test(emailAddress);
+};
+
+exports.isValidAbsoluteURL = url => {
+ return whatwgURL.parseURL(url) !== null;
+};
+
+exports.sanitizeValueByType = (input, val) => {
+ switch (input.type.toLowerCase()) {
+ case "password":
+ case "search":
+ case "tel":
+ case "text":
+ val = stripNewlines(val);
+ break;
+
+ case "color":
+ // https://html.spec.whatwg.org/multipage/forms.html#color-state-(type=color):value-sanitization-algorithm
+ val = isValidSimpleColor(val) ? val.toLowerCase() : "#000000";
+ break;
+
+ case "date":
+ // https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):value-sanitization-algorithm
+ if (!isValidDateString(val)) {
+ val = "";
+ }
+ break;
+
+ case "datetime-local": {
+ // https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):value-sanitization-algorithm
+ const dateAndTime = parseLocalDateAndTimeString(val);
+ val = dateAndTime !== null ? serializeNormalizedDateAndTime(dateAndTime) : "";
+ break;
+ }
+
+ case "email":
+ // https://html.spec.whatwg.org/multipage/forms.html#e-mail-state-(type=email):value-sanitization-algorithm
+ // https://html.spec.whatwg.org/multipage/forms.html#e-mail-state-(type=email):value-sanitization-algorithm-2
+ if (input.hasAttributeNS(null, "multiple")) {
+ val = val.split(",").map(token => stripLeadingAndTrailingASCIIWhitespace(token)).join(",");
+ } else {
+ val = stripNewlines(val);
+ val = stripLeadingAndTrailingASCIIWhitespace(val);
+ }
+ break;
+
+ case "month":
+ // https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):value-sanitization-algorithm
+ if (!isValidMonthString(val)) {
+ val = "";
+ }
+ break;
+
+ case "number":
+ // https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):value-sanitization-algorithm
+ // TODO: using parseFloatingPointNumber in addition to isValidFloatingPointNumber to pass number.html WPT.
+ // Possible spec bug.
+ if (!isValidFloatingPointNumber(val) || parseFloatingPointNumber(val) === null) {
+ val = "";
+ }
+ break;
+
+ case "range":
+ // https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
+ // TODO: using parseFloatingPointNumber in addition to isValidFloatingPointNumber to pass number.html WPT.
+ // Possible spec bug.
+ if (!isValidFloatingPointNumber(val) || parseFloatingPointNumber(val) === null) {
+ const minimum = input._minimum;
+ const maximum = input._maximum;
+ const defaultValue = maximum < minimum ? minimum : (minimum + maximum) / 2;
+ val = `${defaultValue}`;
+ } else if (val < input._minimum) {
+ val = `${input._minimum}`;
+ } else if (val > input._maximum) {
+ val = `${input._maximum}`;
+ }
+ break;
+
+ case "time":
+ // https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):value-sanitization-algorithm
+ if (!isValidTimeString(val)) {
+ val = "";
+ }
+ break;
+
+ case "url":
+ // https://html.spec.whatwg.org/multipage/forms.html#url-state-(type=url):value-sanitization-algorithm
+ val = stripNewlines(val);
+ val = stripLeadingAndTrailingASCIIWhitespace(val);
+ break;
+
+ case "week":
+ // https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):value-sanitization-algorithm
+ if (!isValidWeekString(val)) {
+ val = "";
+ }
+ }
+
+ return val;
+};
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-owner
+// TODO: The spec describes an imperative process for assigning/resetting an element's form
+// owner based on activities involving form-associated elements. This simpler implementation
+// instead calculates the current form owner only when the property is accessed. This is not
+// sufficient to pass all the web platform tests, but is good enough for most purposes. We
+// should eventually update it to use the correct version, though. See
+// https://github.com/whatwg/html/issues/4050 for some discussion.
+
+exports.formOwner = formControl => {
+ const formAttr = formControl.getAttributeNS(null, "form");
+ if (formAttr === "") {
+ return null;
+ }
+ if (formAttr === null) {
+ return closest(formControl, "form");
+ }
+
+ const root = formControl.getRootNode({});
+ let firstElementWithId;
+ for (const descendant of domSymbolTree.treeIterator(root)) {
+ if (descendant.nodeType === NODE_TYPE.ELEMENT_NODE &&
+ descendant.getAttributeNS(null, "id") === formAttr) {
+ firstElementWithId = descendant;
+ break;
+ }
+ }
+
+ if (firstElementWithId &&
+ firstElementWithId.namespaceURI === HTML_NS &&
+ firstElementWithId.localName === "form") {
+ return firstElementWithId;
+ }
+ return null;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/html-constructor.js b/node_modules/jsdom/lib/jsdom/living/helpers/html-constructor.js
new file mode 100644
index 0000000..ffaf377
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/html-constructor.js
@@ -0,0 +1,78 @@
+"use strict";
+
+const { HTML_NS } = require("./namespaces");
+const { createElement, getValidTagNames } = require("./create-element");
+
+const { implForWrapper, wrapperForImpl } = require("../generated/utils");
+
+// https://html.spec.whatwg.org/multipage/custom-elements.html#concept-already-constructed-marker
+const ALREADY_CONSTRUCTED_MARKER = Symbol("already-constructed-marker");
+
+// https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
+function HTMLConstructor(globalObject, constructorName, newTarget) {
+ const registry = implForWrapper(globalObject.customElements);
+ if (newTarget === HTMLConstructor) {
+ throw new TypeError("Invalid constructor");
+ }
+
+ const definition = registry._customElementDefinitions.find(entry => entry.objectReference === newTarget);
+ if (definition === undefined) {
+ throw new TypeError("Invalid constructor, the constructor is not part of the custom element registry");
+ }
+
+ let isValue = null;
+
+ if (definition.localName === definition.name) {
+ if (constructorName !== "HTMLElement") {
+ throw new TypeError("Invalid constructor, autonomous custom element should extend from HTMLElement");
+ }
+ } else {
+ const validLocalNames = getValidTagNames(HTML_NS, constructorName);
+ if (!validLocalNames.includes(definition.localName)) {
+ throw new TypeError(`${definition.localName} is not valid local name for ${constructorName}`);
+ }
+
+ isValue = definition.name;
+ }
+
+ let { prototype } = newTarget;
+
+ if (prototype === null || typeof prototype !== "object") {
+ // The following line deviates from the specification. The HTMLElement prototype should be retrieved from the realm
+ // associated with the "new.target". Because it is impossible to get such information in jsdom, we fallback to the
+ // HTMLElement prototype associated with the current object.
+ prototype = globalObject.HTMLElement.prototype;
+ }
+
+ if (definition.constructionStack.length === 0) {
+ const documentImpl = implForWrapper(globalObject.document);
+
+ const elementImpl = createElement(documentImpl, definition.localName, HTML_NS);
+
+ const element = wrapperForImpl(elementImpl);
+ Object.setPrototypeOf(element, prototype);
+
+ elementImpl._ceState = "custom";
+ elementImpl._ceDefinition = definition;
+ elementImpl._isValue = isValue;
+
+ return element;
+ }
+
+ const elementImpl = definition.constructionStack[definition.constructionStack.length - 1];
+ const element = wrapperForImpl(elementImpl);
+
+ if (elementImpl === ALREADY_CONSTRUCTED_MARKER) {
+ throw new TypeError("This instance is already constructed");
+ }
+
+ Object.setPrototypeOf(element, prototype);
+
+ definition.constructionStack[definition.constructionStack.length - 1] = ALREADY_CONSTRUCTED_MARKER;
+
+ return element;
+}
+
+module.exports = {
+ HTMLConstructor
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/http-request.js b/node_modules/jsdom/lib/jsdom/living/helpers/http-request.js
new file mode 100644
index 0000000..616a806
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/http-request.js
@@ -0,0 +1,254 @@
+"use strict";
+const http = require("http");
+const https = require("https");
+const { Writable } = require("stream");
+const zlib = require("zlib");
+
+const ver = process.version.replace("v", "").split(".");
+const majorNodeVersion = Number.parseInt(ver[0]);
+
+function abortRequest(clientRequest) {
+ // clientRequest.destroy breaks the test suite for versions 10 and 12,
+ // hence the version check
+ if (majorNodeVersion > 13) {
+ clientRequest.destroy();
+ } else {
+ clientRequest.abort();
+ }
+ clientRequest.removeAllListeners();
+ clientRequest.on("error", () => {});
+}
+
+module.exports = class Request extends Writable {
+ constructor(url, clientOptions, requestOptions) {
+ super();
+ Object.assign(this, clientOptions);
+ this.currentURL = url;
+ this._requestOptions = requestOptions;
+ this.headers = requestOptions.headers;
+ this._ended = false;
+ this._redirectCount = 0;
+ this._requestBodyBuffers = [];
+ this._bufferIndex = 0;
+ this._performRequest();
+ }
+
+ abort() {
+ abortRequest(this._currentRequest);
+ this.emit("abort");
+ this.removeAllListeners();
+ }
+
+ pipeRequest(form) {
+ form.pipe(this._currentRequest);
+ }
+
+ write(data, encoding) {
+ if (data.length > 0) {
+ this._requestBodyBuffers.push({ data, encoding });
+ this._currentRequest.write(data, encoding);
+ }
+ }
+
+ end() {
+ this.emit("request", this._currentRequest);
+ this._ended = true;
+ this._currentRequest.end();
+ }
+
+ setHeader(name, value) {
+ this.headers[name] = value;
+ this._currentRequest.setHeader(name, value);
+ }
+
+ removeHeader(name) {
+ delete this.headers[name];
+ this._currentRequest.removeHeader(name);
+ }
+
+ // Without this method, the test send-redirect-infinite-sync will halt the test suite
+ // TODO: investigate this further and ideally remove
+ toJSON() {
+ const { method, headers } = this._requestOptions;
+ return { uri: new URL(this.currentURL), method, headers };
+ }
+
+ _writeNext(error) {
+ if (this._currentRequest) {
+ if (error) {
+ this.emit("error", error);
+ } else if (this._bufferIndex < this._requestBodyBuffers.length) {
+ const buffer = this._requestBodyBuffers[this._bufferIndex++];
+ if (!this._currentRequest.writableEnded) {
+ this._currentRequest.write(
+ buffer.data,
+ buffer.encoding,
+ this._writeNext.bind(this)
+ );
+ }
+ } else if (this._ended) {
+ this._currentRequest.end();
+ }
+ }
+ }
+
+ _performRequest() {
+ const urlOptions = new URL(this.currentURL);
+ const scheme = urlOptions.protocol;
+ this._requestOptions.agent = this.agents[scheme.substring(0, scheme.length - 1)];
+ const { request } = scheme === "https:" ? https : http;
+ this._currentRequest = request(this.currentURL, this._requestOptions, response => {
+ this._processResponse(response);
+ });
+
+ let cookies;
+ if (this._redirectCount === 0) {
+ this.originalCookieHeader = this.getHeader("Cookie");
+ }
+ if (this.cookieJar) {
+ cookies = this.cookieJar.getCookieStringSync(this.currentURL);
+ }
+ if (cookies && cookies.length) {
+ if (this.originalCookieHeader) {
+ this.setHeader("Cookie", this.originalCookieHeader + "; " + cookies);
+ } else {
+ this.setHeader("Cookie", cookies);
+ }
+ }
+
+ for (const event of ["connect", "error", "socket", "timeout"]) {
+ this._currentRequest.on(event, (...args) => {
+ this.emit(event, ...args);
+ });
+ }
+ if (this._isRedirect) {
+ this._bufferIndex = 0;
+ this._writeNext();
+ }
+ }
+
+ _processResponse(response) {
+ const cookies = response.headers["set-cookie"];
+ if (this.cookieJar && Array.isArray(cookies)) {
+ try {
+ cookies.forEach(cookie => {
+ this.cookieJar.setCookieSync(cookie, this.currentURL, { ignoreError: true });
+ });
+ } catch (e) {
+ this.emit("error", e);
+ }
+ }
+
+ const { statusCode } = response;
+ const { location } = response.headers;
+ // In Node v15, aborting a message with remaining data causes an error to be thrown,
+ // hence the version check
+ const catchResErrors = err => {
+ if (!(majorNodeVersion >= 15 && err.message === "aborted")) {
+ this.emit("error", err);
+ }
+ };
+ response.on("error", catchResErrors);
+ let redirectAddress = null;
+ let resendWithAuth = false;
+ if (typeof location === "string" &&
+ location.length &&
+ this.followRedirects &&
+ statusCode >= 300 &&
+ statusCode < 400) {
+ redirectAddress = location;
+ } else if (statusCode === 401 &&
+ /^Basic /i.test(response.headers["www-authenticate"] || "") &&
+ (this.user && this.user.length)) {
+ this._requestOptions.auth = `${this.user}:${this.pass}`;
+ resendWithAuth = true;
+ }
+ if (redirectAddress || resendWithAuth) {
+ if (++this._redirectCount > 21) {
+ const redirectError = new Error("Maximum number of redirects exceeded");
+ redirectError.code = "ERR_TOO_MANY_REDIRECTS";
+ this.emit("error", redirectError);
+ return;
+ }
+ abortRequest(this._currentRequest);
+ response.destroy();
+ this._isRedirect = true;
+ if (((statusCode === 301 || statusCode === 302) && this._requestOptions.method === "POST") ||
+ (statusCode === 303 && !/^(?:GET|HEAD)$/.test(this._requestOptions.method))) {
+ this._requestOptions.method = "GET";
+ this._requestBodyBuffers = [];
+ }
+ let previousHostName = this._removeMatchingHeaders(/^host$/i);
+ if (!previousHostName) {
+ previousHostName = new URL(this.currentURL).hostname;
+ }
+ const previousURL = this.currentURL;
+ if (!resendWithAuth) {
+ const nextURL = redirectAddress.startsWith("https:") ?
+ new URL(redirectAddress) :
+ new URL(redirectAddress, this.currentURL);
+ if (nextURL.hostname !== previousHostName) {
+ this._removeMatchingHeaders(/^authorization$/i);
+ }
+ this.currentURL = nextURL.toString();
+ }
+ this.headers.Referer = previousURL;
+ this.emit("redirect", response, this.headers, this.currentURL);
+ try {
+ this._performRequest();
+ } catch (cause) {
+ this.emit("error", cause);
+ }
+ } else {
+ let pipeline = response;
+ const acceptEncoding = this.headers["Accept-Encoding"];
+ const requestCompressed = typeof acceptEncoding === "string" &&
+ (acceptEncoding.includes("gzip") || acceptEncoding.includes("deflate"));
+ if (
+ requestCompressed &&
+ this._requestOptions.method !== "HEAD" &&
+ statusCode >= 200 &&
+ statusCode !== 204 &&
+ statusCode !== 304
+ ) {
+ const zlibOptions = {
+ flush: zlib.constants.Z_SYNC_FLUSH,
+ finishFlush: zlib.constants.Z_SYNC_FLUSH
+ };
+ const contentEncoding = (response.headers["content-encoding"] || "identity").trim().toLowerCase();
+ if (contentEncoding === "gzip") {
+ pipeline = zlib.createGunzip(zlibOptions);
+ response.pipe(pipeline);
+ } else if (contentEncoding === "deflate") {
+ pipeline = zlib.createInflate(zlibOptions);
+ response.pipe(pipeline);
+ }
+ }
+ pipeline.removeAllListeners("error");
+ this.emit("response", response, this.currentURL);
+ pipeline.on("data", bytes => this.emit("data", bytes));
+ pipeline.once("end", bytes => this.emit("end", bytes));
+ pipeline.on("error", catchResErrors);
+ pipeline.on("close", () => this.emit("close"));
+ this._requestBodyBuffers = [];
+ }
+ }
+
+ getHeader(key, value) {
+ if (this._currentRequest) {
+ return this._currentRequest.getHeader(key, value);
+ }
+ return null;
+ }
+
+ _removeMatchingHeaders(regex) {
+ let lastValue;
+ for (const header in this.headers) {
+ if (regex.test(header)) {
+ lastValue = this.headers[header];
+ delete this.headers[header];
+ }
+ }
+ return lastValue;
+ }
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/internal-constants.js b/node_modules/jsdom/lib/jsdom/living/helpers/internal-constants.js
new file mode 100644
index 0000000..707add9
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/internal-constants.js
@@ -0,0 +1,12 @@
+"use strict";
+const SymbolTree = require("symbol-tree");
+
+exports.cloningSteps = Symbol("cloning steps");
+
+// TODO: the many underscore-prefixed hooks should move here
+// E.g. _attrModified (which maybe should be split into its per-spec variants)
+
+/**
+ * This SymbolTree is used to build the tree for all Node in a document
+ */
+exports.domSymbolTree = new SymbolTree("DOM SymbolTree");
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/iterable-weak-set.js b/node_modules/jsdom/lib/jsdom/living/helpers/iterable-weak-set.js
new file mode 100644
index 0000000..9d5e167
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/iterable-weak-set.js
@@ -0,0 +1,48 @@
+"use strict";
+
+// An iterable WeakSet implementation inspired by the iterable WeakMap example code in the WeakRefs specification:
+// https://github.com/tc39/proposal-weakrefs#iterable-weakmaps
+module.exports = class IterableWeakSet {
+ constructor() {
+ this._refSet = new Set();
+ this._refMap = new WeakMap();
+ this._finalizationRegistry = new FinalizationRegistry(({ ref, set }) => set.delete(ref));
+ }
+
+ add(value) {
+ if (!this._refMap.has(value)) {
+ const ref = new WeakRef(value);
+ this._refMap.set(value, ref);
+ this._refSet.add(ref);
+ this._finalizationRegistry.register(value, { ref, set: this._refSet }, ref);
+ }
+
+ return this;
+ }
+
+ delete(value) {
+ const ref = this._refMap.get(value);
+ if (!ref) {
+ return false;
+ }
+
+ this._refMap.delete(value);
+ this._refSet.delete(ref);
+ this._finalizationRegistry.unregister(ref);
+ return true;
+ }
+
+ has(value) {
+ return this._refMap.has(value);
+ }
+
+ * [Symbol.iterator]() {
+ for (const ref of this._refSet) {
+ const value = ref.deref();
+ if (value === undefined) {
+ continue;
+ }
+ yield value;
+ }
+ }
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/json.js b/node_modules/jsdom/lib/jsdom/living/helpers/json.js
new file mode 100644
index 0000000..6920bc2
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/json.js
@@ -0,0 +1,12 @@
+"use strict";
+
+// https://infra.spec.whatwg.org/#parse-json-from-bytes
+exports.parseJSONFromBytes = bytes => {
+ // https://encoding.spec.whatwg.org/#utf-8-decode
+ if (bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) {
+ bytes = bytes.subarray(3);
+ }
+ const jsonText = bytes.toString("utf-8");
+
+ return JSON.parse(jsonText);
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/mutation-observers.js b/node_modules/jsdom/lib/jsdom/living/helpers/mutation-observers.js
new file mode 100644
index 0000000..c1c9209
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/mutation-observers.js
@@ -0,0 +1,198 @@
+"use strict";
+
+const { domSymbolTree } = require("./internal-constants");
+const reportException = require("./runtime-script-errors");
+
+const Event = require("../generated/Event");
+const idlUtils = require("../generated/utils");
+const MutationRecord = require("../generated/MutationRecord");
+
+const MUTATION_TYPE = {
+ ATTRIBUTES: "attributes",
+ CHARACTER_DATA: "characterData",
+ CHILD_LIST: "childList"
+};
+
+// Note:
+// Since jsdom doesn't currently implement the concept of "unit of related similar-origin browsing contexts"
+// (https://html.spec.whatwg.org/multipage/browsers.html#unit-of-related-similar-origin-browsing-contexts)
+// we will approximate that the following properties are global for now.
+
+// https://dom.spec.whatwg.org/#mutation-observer-compound-microtask-queued-flag
+let mutationObserverMicrotaskQueueFlag = false;
+
+// Non-spec compliant: List of all the mutation observers with mutation records enqueued. It's a replacement for
+// mutation observer list (https://dom.spec.whatwg.org/#mutation-observer-list) but without leaking since it's empty
+// before notifying the mutation observers.
+const activeMutationObservers = new Set();
+
+// https://dom.spec.whatwg.org/#signal-slot-list
+const signalSlotList = [];
+
+// https://dom.spec.whatwg.org/#queue-a-mutation-record
+function queueMutationRecord(
+ type,
+ target,
+ name,
+ namespace,
+ oldValue,
+ addedNodes,
+ removedNodes,
+ previousSibling,
+ nextSibling
+) {
+ const interestedObservers = new Map();
+
+ const nodes = domSymbolTree.ancestorsToArray(target);
+
+ for (const node of nodes) {
+ for (const registered of node._registeredObserverList) {
+ const { options, observer: mo } = registered;
+
+ if (
+ !(node !== target && options.subtree === false) &&
+ !(type === MUTATION_TYPE.ATTRIBUTES && options.attributes !== true) &&
+ !(type === MUTATION_TYPE.ATTRIBUTES && options.attributeFilter &&
+ !options.attributeFilter.some(value => value === name || value === namespace)) &&
+ !(type === MUTATION_TYPE.CHARACTER_DATA && options.characterData !== true) &&
+ !(type === MUTATION_TYPE.CHILD_LIST && options.childList === false)
+ ) {
+ if (!interestedObservers.has(mo)) {
+ interestedObservers.set(mo, null);
+ }
+
+ if (
+ (type === MUTATION_TYPE.ATTRIBUTES && options.attributeOldValue === true) ||
+ (type === MUTATION_TYPE.CHARACTER_DATA && options.characterDataOldValue === true)
+ ) {
+ interestedObservers.set(mo, oldValue);
+ }
+ }
+ }
+ }
+
+ for (const [observer, mappedOldValue] of interestedObservers.entries()) {
+ const record = MutationRecord.createImpl(target._globalObject, [], {
+ type,
+ target,
+ attributeName: name,
+ attributeNamespace: namespace,
+ oldValue: mappedOldValue,
+ addedNodes,
+ removedNodes,
+ previousSibling,
+ nextSibling
+ });
+
+ observer._recordQueue.push(record);
+ activeMutationObservers.add(observer);
+ }
+
+ queueMutationObserverMicrotask();
+}
+
+// https://dom.spec.whatwg.org/#queue-a-tree-mutation-record
+function queueTreeMutationRecord(target, addedNodes, removedNodes, previousSibling, nextSibling) {
+ queueMutationRecord(
+ MUTATION_TYPE.CHILD_LIST,
+ target,
+ null,
+ null,
+ null,
+ addedNodes,
+ removedNodes,
+ previousSibling,
+ nextSibling
+ );
+}
+
+// https://dom.spec.whatwg.org/#queue-an-attribute-mutation-record
+function queueAttributeMutationRecord(target, name, namespace, oldValue) {
+ queueMutationRecord(
+ MUTATION_TYPE.ATTRIBUTES,
+ target,
+ name,
+ namespace,
+ oldValue,
+ [],
+ [],
+ null,
+ null
+ );
+}
+
+// https://dom.spec.whatwg.org/#queue-a-mutation-observer-compound-microtask
+function queueMutationObserverMicrotask() {
+ if (mutationObserverMicrotaskQueueFlag) {
+ return;
+ }
+
+ mutationObserverMicrotaskQueueFlag = true;
+
+ Promise.resolve().then(() => {
+ notifyMutationObservers();
+ });
+}
+
+// https://dom.spec.whatwg.org/#notify-mutation-observers
+function notifyMutationObservers() {
+ mutationObserverMicrotaskQueueFlag = false;
+
+ const notifyList = [...activeMutationObservers].sort((a, b) => a._id - b._id);
+ activeMutationObservers.clear();
+
+ const signalList = [...signalSlotList];
+ signalSlotList.splice(0, signalSlotList.length);
+
+ for (const mo of notifyList) {
+ const records = [...mo._recordQueue];
+ mo._recordQueue = [];
+
+ for (const node of mo._nodeList) {
+ node._registeredObserverList = node._registeredObserverList.filter(registeredObserver => {
+ return registeredObserver.source !== mo;
+ });
+ }
+
+ if (records.length > 0) {
+ try {
+ const moWrapper = idlUtils.wrapperForImpl(mo);
+ mo._callback.call(
+ moWrapper,
+ records.map(idlUtils.wrapperForImpl),
+ moWrapper
+ );
+ } catch (e) {
+ const { target } = records[0];
+ const window = target._ownerDocument._defaultView;
+
+ reportException(window, e);
+ }
+ }
+ }
+
+ for (const slot of signalList) {
+ const slotChangeEvent = Event.createImpl(
+ slot._globalObject,
+ [
+ "slotchange",
+ { bubbles: true }
+ ],
+ { isTrusted: true }
+ );
+
+ slot._dispatch(slotChangeEvent);
+ }
+}
+
+module.exports = {
+ MUTATION_TYPE,
+
+ queueMutationRecord,
+ queueTreeMutationRecord,
+ queueAttributeMutationRecord,
+
+ queueMutationObserverMicrotask,
+
+ signalSlotList
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/namespaces.js b/node_modules/jsdom/lib/jsdom/living/helpers/namespaces.js
new file mode 100644
index 0000000..ec8eccc
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/namespaces.js
@@ -0,0 +1,15 @@
+"use strict";
+
+// https://infra.spec.whatwg.org/#namespaces
+
+exports.HTML_NS = "http://www.w3.org/1999/xhtml";
+
+exports.MATHML_NS = "http://www.w3.org/1998/Math/MathML";
+
+exports.SVG_NS = "http://www.w3.org/2000/svg";
+
+exports.XLINK_NS = "http://www.w3.org/1999/xlink";
+
+exports.XML_NS = "http://www.w3.org/XML/1998/namespace";
+
+exports.XMLNS_NS = "http://www.w3.org/2000/xmlns/";
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/node.js b/node_modules/jsdom/lib/jsdom/living/helpers/node.js
new file mode 100644
index 0000000..40e12bc
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/node.js
@@ -0,0 +1,68 @@
+"use strict";
+
+const NODE_TYPE = require("../node-type");
+const { domSymbolTree } = require("./internal-constants");
+
+// https://dom.spec.whatwg.org/#concept-node-length
+function nodeLength(node) {
+ switch (node.nodeType) {
+ case NODE_TYPE.DOCUMENT_TYPE_NODE:
+ return 0;
+
+ case NODE_TYPE.TEXT_NODE:
+ case NODE_TYPE.PROCESSING_INSTRUCTION_NODE:
+ case NODE_TYPE.COMMENT_NODE:
+ return node.data.length;
+
+ default:
+ return domSymbolTree.childrenCount(node);
+ }
+}
+
+// https://dom.spec.whatwg.org/#concept-tree-root
+function nodeRoot(node) {
+ while (domSymbolTree.parent(node)) {
+ node = domSymbolTree.parent(node);
+ }
+
+ return node;
+}
+
+// https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
+function isInclusiveAncestor(ancestorNode, node) {
+ while (node) {
+ if (ancestorNode === node) {
+ return true;
+ }
+
+ node = domSymbolTree.parent(node);
+ }
+
+ return false;
+}
+
+// https://dom.spec.whatwg.org/#concept-tree-following
+function isFollowing(nodeA, nodeB) {
+ if (nodeA === nodeB) {
+ return false;
+ }
+
+ let current = nodeB;
+ while (current) {
+ if (current === nodeA) {
+ return true;
+ }
+
+ current = domSymbolTree.following(current);
+ }
+
+ return false;
+}
+
+module.exports = {
+ nodeLength,
+ nodeRoot,
+
+ isInclusiveAncestor,
+ isFollowing
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/number-and-date-inputs.js b/node_modules/jsdom/lib/jsdom/living/helpers/number-and-date-inputs.js
new file mode 100644
index 0000000..e29bc74
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/number-and-date-inputs.js
@@ -0,0 +1,195 @@
+"use strict";
+const { parseFloatingPointNumber } = require("./strings");
+const {
+ parseDateString,
+ parseLocalDateAndTimeString,
+ parseMonthString,
+ parseTimeString,
+ parseWeekString,
+
+ serializeDate,
+ serializeMonth,
+ serializeNormalizedDateAndTime,
+ serializeTime,
+ serializeWeek,
+ parseDateAsWeek
+} = require("./dates-and-times");
+
+// Necessary because Date.UTC() treats year within [0, 99] as [1900, 1999].
+function getUTCMs(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, millisecond = 0) {
+ if (year > 99 || year < 0) {
+ return Date.UTC(year, month - 1, day, hour, minute, second, millisecond);
+ }
+ const d = new Date(0);
+ d.setUTCFullYear(year);
+ d.setUTCMonth(month - 1);
+ d.setUTCDate(day);
+ d.setUTCHours(hour);
+ d.setUTCMinutes(minute);
+ d.setUTCSeconds(second, millisecond);
+ return d.valueOf();
+}
+
+const dayOfWeekRelMondayLUT = [-1, 0, 1, 2, 3, -3, -2];
+
+exports.convertStringToNumberByType = {
+ // https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-string-number
+ date(input) {
+ const date = parseDateString(input);
+ if (date === null) {
+ return null;
+ }
+ return getUTCMs(date.year, date.month, date.day);
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-value-string-number
+ month(input) {
+ const date = parseMonthString(input);
+ if (date === null) {
+ return null;
+ }
+ return (date.year - 1970) * 12 + (date.month - 1);
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):concept-input-value-string-number
+ week(input) {
+ const date = parseWeekString(input);
+ if (date === null) {
+ return null;
+ }
+ const dateObj = new Date(getUTCMs(date.year));
+ // An HTML week starts on Monday, while 0 represents Sunday. Account for such.
+ const dayOfWeekRelMonday = dayOfWeekRelMondayLUT[dateObj.getUTCDay()];
+ return dateObj.setUTCDate(1 + 7 * (date.week - 1) - dayOfWeekRelMonday);
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):concept-input-value-string-number
+ time(input) {
+ const time = parseTimeString(input);
+ if (time === null) {
+ return null;
+ }
+ return ((time.hour * 60 + time.minute) * 60 + time.second) * 1000 + time.millisecond;
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):concept-input-value-string-number
+ "datetime-local"(input) {
+ const dateAndTime = parseLocalDateAndTimeString(input);
+ if (dateAndTime === null) {
+ return null;
+ }
+ const { date: { year, month, day }, time: { hour, minute, second, millisecond } } = dateAndTime;
+ // Doesn't quite matter whether or not UTC is used, since the offset from 1970-01-01 local time is returned.
+ return getUTCMs(year, month, day, hour, minute, second, millisecond);
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):concept-input-value-string-number
+ number: parseFloatingPointNumber,
+ // https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):concept-input-value-string-number
+ range: parseFloatingPointNumber
+};
+
+exports.convertStringToDateByType = {
+ date(input) {
+ const parsedInput = exports.convertStringToNumberByType.date(input);
+ return parsedInput === null ? null : new Date(parsedInput);
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-value-string-number
+ month(input) {
+ const parsedMonthString = parseMonthString(input);
+ if (parsedMonthString === null) {
+ return null;
+ }
+
+ const date = new Date(0);
+ date.setUTCFullYear(parsedMonthString.year);
+ date.setUTCMonth(parsedMonthString.month - 1);
+ return date;
+ },
+ week(input) {
+ const parsedInput = exports.convertStringToNumberByType.week(input);
+ return parsedInput === null ? null : new Date(parsedInput);
+ },
+ time(input) {
+ const parsedInput = exports.convertStringToNumberByType.time(input);
+ return parsedInput === null ? null : new Date(parsedInput);
+ },
+ "datetime-local"(input) {
+ const parsedInput = exports.convertStringToNumberByType["datetime-local"](input);
+ return parsedInput === null ? null : new Date(parsedInput);
+ }
+};
+
+exports.serializeDateByType = {
+ date(input) {
+ return serializeDate({
+ year: input.getUTCFullYear(),
+ month: input.getUTCMonth() + 1,
+ day: input.getUTCDate()
+ });
+ },
+ month(input) {
+ return serializeMonth({
+ year: input.getUTCFullYear(),
+ month: input.getUTCMonth() + 1
+ });
+ },
+ week(input) {
+ return serializeWeek(parseDateAsWeek(input));
+ },
+ time(input) {
+ return serializeTime({
+ hour: input.getUTCHours(),
+ minute: input.getUTCMinutes(),
+ second: input.getUTCSeconds(),
+ millisecond: input.getUTCMilliseconds()
+ });
+ },
+ "datetime-local"(input) {
+ return serializeNormalizedDateAndTime({
+ date: {
+ year: input.getUTCFullYear(),
+ month: input.getUTCMonth() + 1,
+ day: input.getUTCDate()
+ },
+ time: {
+ hour: input.getUTCHours(),
+ minute: input.getUTCMinutes(),
+ second: input.getUTCSeconds(),
+ millisecond: input.getUTCMilliseconds()
+ }
+ });
+ }
+};
+
+exports.convertNumberToStringByType = {
+ // https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-string-number
+ date(input) {
+ return exports.serializeDateByType.date(new Date(input));
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-value-string-date
+ month(input) {
+ const year = 1970 + Math.floor(input / 12);
+ const month = input % 12;
+ const date = new Date(0);
+ date.setUTCFullYear(year);
+ date.setUTCMonth(month);
+
+ return exports.serializeDateByType.month(date);
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):concept-input-value-string-date
+ week(input) {
+ return exports.serializeDateByType.week(new Date(input));
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):concept-input-value-string-date
+ time(input) {
+ return exports.serializeDateByType.time(new Date(input));
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):concept-input-value-number-string
+ "datetime-local"(input) {
+ return exports.serializeDateByType["datetime-local"](new Date(input));
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):concept-input-value-number-string
+ number(input) {
+ return input.toString();
+ },
+ // https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):concept-input-value-number-string
+ range(input) {
+ return input.toString();
+ }
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/ordered-set.js b/node_modules/jsdom/lib/jsdom/living/helpers/ordered-set.js
new file mode 100644
index 0000000..d3e1932
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/ordered-set.js
@@ -0,0 +1,104 @@
+"use strict";
+
+// https://infra.spec.whatwg.org/#sets
+//
+// Only use this class if a Set cannot be used, e.g. when "replace" operation is needed, since there's no way to replace
+// an element while keep the relative order using a Set, only remove and then add something at the end.
+
+module.exports = class OrderedSet {
+ constructor() {
+ this._items = [];
+ }
+
+ append(item) {
+ if (!this.contains(item)) {
+ this._items.push(item);
+ }
+ }
+
+ prepend(item) {
+ if (!this.contains(item)) {
+ this._items.unshift(item);
+ }
+ }
+
+ replace(item, replacement) {
+ let seen = false;
+ for (let i = 0; i < this._items.length;) {
+ const isInstance = this._items[i] === item || this._items[i] === replacement;
+ if (seen && isInstance) {
+ this._items.splice(i, 1);
+ } else {
+ if (isInstance) {
+ this._items[i] = replacement;
+ seen = true;
+ }
+ i++;
+ }
+ }
+ }
+
+ remove(...items) {
+ this.removePredicate(item => items.includes(item));
+ }
+
+ removePredicate(predicate) {
+ for (let i = 0; i < this._items.length;) {
+ if (predicate(this._items[i])) {
+ this._items.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ empty() {
+ this._items.length = 0;
+ }
+
+ contains(item) {
+ return this._items.includes(item);
+ }
+
+ get size() {
+ return this._items.length;
+ }
+
+ isEmpty() {
+ return this._items.length === 0;
+ }
+
+ // Useful for other parts of jsdom
+
+ [Symbol.iterator]() {
+ return this._items[Symbol.iterator]();
+ }
+
+ keys() {
+ return this._items.keys();
+ }
+
+ get(index) {
+ return this._items[index];
+ }
+
+ some(func) {
+ return this._items.some(func);
+ }
+
+ // https://dom.spec.whatwg.org/#concept-ordered-set-parser
+ static parse(input) {
+ const tokens = new OrderedSet();
+ for (const token of input.split(/[\t\n\f\r ]+/)) {
+ if (token) {
+ tokens.append(token);
+ }
+ }
+ return tokens;
+ }
+
+ // https://dom.spec.whatwg.org/#concept-ordered-set-serializer
+ serialize() {
+ return this._items.join(" ");
+ }
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js b/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js
new file mode 100644
index 0000000..41982b0
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js
@@ -0,0 +1,76 @@
+"use strict";
+const util = require("util");
+const idlUtils = require("../generated/utils");
+const ErrorEvent = require("../generated/ErrorEvent");
+const { createAnEvent } = require("../helpers/events");
+
+const errorReportingMode = Symbol("error reporting mode");
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#report-the-error
+// Omits script parameter and any check for muted errors.
+// Takes target as an EventTarget impl.
+// Takes error object, message, and location as params, unlike the spec.
+// Returns whether the event was handled or not.
+function reportAnError(line, col, target, errorObject, message, location) {
+ if (target[errorReportingMode]) {
+ return false;
+ }
+
+ target[errorReportingMode] = true;
+
+ if (typeof message !== "string") {
+ message = "uncaught exception: " + util.inspect(errorObject);
+ }
+
+ const event = createAnEvent("error", target._globalObject, ErrorEvent, {
+ cancelable: true,
+ message,
+ filename: location,
+ lineno: line,
+ colno: col,
+ error: errorObject
+ });
+
+ try {
+ target._dispatch(event);
+ } finally {
+ target[errorReportingMode] = false;
+ return event.defaultPrevented;
+ }
+}
+
+module.exports = function reportException(window, error, filenameHint) {
+ // This function will give good results on real Error objects with stacks; poor ones otherwise
+
+ const stack = error && error.stack;
+ const lines = stack && stack.split("\n");
+
+ // Find the first line that matches; important for multi-line messages
+ let pieces;
+ if (lines) {
+ for (let i = 1; i < lines.length && !pieces; ++i) {
+ pieces = lines[i].match(/at (?:(.+)\s+)?\(?(?:(.+?):(\d+):(\d+)|([^)]+))\)?/);
+ }
+ }
+
+ const fileName = (pieces && pieces[2]) || filenameHint || window._document.URL;
+ const lineNumber = (pieces && parseInt(pieces[3])) || 0;
+ const columnNumber = (pieces && parseInt(pieces[4])) || 0;
+
+ const windowImpl = idlUtils.implForWrapper(window);
+
+ const handled = reportAnError(lineNumber, columnNumber, windowImpl, error, error && error.message, fileName);
+
+ if (!handled) {
+ const errorString = shouldBeDisplayedAsError(error) ? `[${error.name}: ${error.message}]` : util.inspect(error);
+ const jsdomError = new Error(`Uncaught ${errorString}`);
+ jsdomError.detail = error;
+ jsdomError.type = "unhandled exception";
+
+ window._virtualConsole.emit("jsdomError", jsdomError);
+ }
+};
+
+function shouldBeDisplayedAsError(x) {
+ return x && x.name && x.message !== undefined && x.stack;
+}
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/selectors.js b/node_modules/jsdom/lib/jsdom/living/helpers/selectors.js
new file mode 100644
index 0000000..8f320ab
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/selectors.js
@@ -0,0 +1,47 @@
+"use strict";
+
+const nwsapi = require("nwsapi");
+
+const idlUtils = require("../generated/utils");
+
+function initNwsapi(node) {
+ const { _globalObject, _ownerDocument } = node;
+
+ return nwsapi({
+ document: _ownerDocument,
+ DOMException: _globalObject.DOMException
+ });
+}
+
+exports.matchesDontThrow = (elImpl, selector) => {
+ const document = elImpl._ownerDocument;
+
+ if (!document._nwsapiDontThrow) {
+ document._nwsapiDontThrow = initNwsapi(elImpl);
+ document._nwsapiDontThrow.configure({
+ LOGERRORS: false,
+ VERBOSITY: false,
+ IDS_DUPES: true,
+ MIXEDCASE: true
+ });
+ }
+
+ return document._nwsapiDontThrow.match(selector, idlUtils.wrapperForImpl(elImpl));
+};
+
+// nwsapi gets `document.documentElement` at creation-time, so we have to initialize lazily, since in the initial
+// stages of Document initialization, there is no documentElement present yet.
+exports.addNwsapi = parentNode => {
+ const document = parentNode._ownerDocument;
+
+ if (!document._nwsapi) {
+ document._nwsapi = initNwsapi(parentNode);
+ document._nwsapi.configure({
+ LOGERRORS: false,
+ IDS_DUPES: true,
+ MIXEDCASE: true
+ });
+ }
+
+ return document._nwsapi;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/shadow-dom.js b/node_modules/jsdom/lib/jsdom/living/helpers/shadow-dom.js
new file mode 100644
index 0000000..a88a33e
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/shadow-dom.js
@@ -0,0 +1,285 @@
+"use strict";
+
+const NODE_TYPE = require("../node-type");
+
+const { nodeRoot } = require("./node");
+const { HTML_NS } = require("./namespaces");
+const { domSymbolTree } = require("./internal-constants");
+const { signalSlotList, queueMutationObserverMicrotask } = require("./mutation-observers");
+
+// Valid host element for ShadowRoot.
+// Defined in: https://dom.spec.whatwg.org/#dom-element-attachshadow
+const VALID_HOST_ELEMENT_NAME = new Set([
+ "article",
+ "aside",
+ "blockquote",
+ "body",
+ "div",
+ "footer",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "header",
+ "main",
+ "nav",
+ "p",
+ "section",
+ "span"
+]);
+
+function isValidHostElementName(name) {
+ return VALID_HOST_ELEMENT_NAME.has(name);
+}
+
+// Use an approximation by checking the presence of nodeType instead of instead of using the isImpl from
+// "../generated/Node" to avoid introduction of circular dependencies.
+function isNode(nodeImpl) {
+ return Boolean(nodeImpl && "nodeType" in nodeImpl);
+}
+
+// Use an approximation by checking the value of nodeType and presence of nodeType host instead of instead
+// of using the isImpl from "../generated/ShadowRoot" to avoid introduction of circular dependencies.
+function isShadowRoot(nodeImpl) {
+ return Boolean(nodeImpl && nodeImpl.nodeType === NODE_TYPE.DOCUMENT_FRAGMENT_NODE && "host" in nodeImpl);
+}
+
+// https://dom.spec.whatwg.org/#concept-slotable
+function isSlotable(nodeImpl) {
+ return nodeImpl && (nodeImpl.nodeType === NODE_TYPE.ELEMENT_NODE || nodeImpl.nodeType === NODE_TYPE.TEXT_NODE);
+}
+
+function isSlot(nodeImpl) {
+ return nodeImpl && nodeImpl.localName === "slot" && nodeImpl._namespaceURI === HTML_NS;
+}
+
+// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
+function isShadowInclusiveAncestor(ancestor, node) {
+ while (isNode(node)) {
+ if (node === ancestor) {
+ return true;
+ }
+
+ if (isShadowRoot(node)) {
+ node = node.host;
+ } else {
+ node = domSymbolTree.parent(node);
+ }
+ }
+
+ return false;
+}
+
+// https://dom.spec.whatwg.org/#retarget
+function retarget(a, b) {
+ while (true) {
+ if (!isNode(a)) {
+ return a;
+ }
+
+ const aRoot = nodeRoot(a);
+ if (
+ !isShadowRoot(aRoot) ||
+ (isNode(b) && isShadowInclusiveAncestor(aRoot, b))
+ ) {
+ return a;
+ }
+
+ a = nodeRoot(a).host;
+ }
+}
+
+// https://dom.spec.whatwg.org/#get-the-parent
+function getEventTargetParent(eventTarget, event) {
+ // _getTheParent will be missing for Window, since it doesn't have an impl class and we don't want to pollute the
+ // user-visible global scope with a _getTheParent value. TODO: remove this entire function and use _getTheParent
+ // directly, once Window gets split into impl/wrapper.
+ return eventTarget._getTheParent ? eventTarget._getTheParent(event) : null;
+}
+
+// https://dom.spec.whatwg.org/#concept-shadow-including-root
+function shadowIncludingRoot(node) {
+ const root = nodeRoot(node);
+ return isShadowRoot(root) ? shadowIncludingRoot(root.host) : root;
+}
+
+// https://dom.spec.whatwg.org/#assign-a-slot
+function assignSlot(slotable) {
+ const slot = findSlot(slotable);
+
+ if (slot) {
+ assignSlotable(slot);
+ }
+}
+
+// https://dom.spec.whatwg.org/#assign-slotables
+function assignSlotable(slot) {
+ const slotables = findSlotable(slot);
+
+ let shouldFireSlotChange = false;
+
+ if (slotables.length !== slot._assignedNodes.length) {
+ shouldFireSlotChange = true;
+ } else {
+ for (let i = 0; i < slotables.length; i++) {
+ if (slotables[i] !== slot._assignedNodes[i]) {
+ shouldFireSlotChange = true;
+ break;
+ }
+ }
+ }
+
+ if (shouldFireSlotChange) {
+ signalSlotChange(slot);
+ }
+
+ slot._assignedNodes = slotables;
+
+ for (const slotable of slotables) {
+ slotable._assignedSlot = slot;
+ }
+}
+
+// https://dom.spec.whatwg.org/#assign-slotables-for-a-tree
+function assignSlotableForTree(root) {
+ for (const slot of domSymbolTree.treeIterator(root)) {
+ if (isSlot(slot)) {
+ assignSlotable(slot);
+ }
+ }
+}
+
+// https://dom.spec.whatwg.org/#find-slotables
+function findSlotable(slot) {
+ const result = [];
+
+ const root = nodeRoot(slot);
+ if (!isShadowRoot(root)) {
+ return result;
+ }
+
+ for (const slotable of domSymbolTree.treeIterator(root.host)) {
+ const foundSlot = findSlot(slotable);
+
+ if (foundSlot === slot) {
+ result.push(slotable);
+ }
+ }
+
+ return result;
+}
+
+// https://dom.spec.whatwg.org/#find-flattened-slotables
+function findFlattenedSlotables(slot) {
+ const result = [];
+
+ const root = nodeRoot(slot);
+ if (!isShadowRoot(root)) {
+ return result;
+ }
+
+ const slotables = findSlotable(slot);
+
+ if (slotables.length === 0) {
+ for (const child of domSymbolTree.childrenIterator(slot)) {
+ if (isSlotable(child)) {
+ slotables.push(child);
+ }
+ }
+ }
+
+ for (const node of slotables) {
+ if (isSlot(node) && isShadowRoot(nodeRoot(node))) {
+ const temporaryResult = findFlattenedSlotables(node);
+ result.push(...temporaryResult);
+ } else {
+ result.push(node);
+ }
+ }
+
+ return result;
+}
+
+// https://dom.spec.whatwg.org/#find-a-slot
+function findSlot(slotable, openFlag) {
+ const { parentNode: parent } = slotable;
+
+ if (!parent) {
+ return null;
+ }
+
+ const shadow = parent._shadowRoot;
+
+ if (!shadow || (openFlag && shadow.mode !== "open")) {
+ return null;
+ }
+
+ for (const child of domSymbolTree.treeIterator(shadow)) {
+ if (isSlot(child) && child.name === slotable._slotableName) {
+ return child;
+ }
+ }
+
+ return null;
+}
+
+// https://dom.spec.whatwg.org/#signal-a-slot-change
+function signalSlotChange(slot) {
+ if (!signalSlotList.some(entry => entry === slot)) {
+ signalSlotList.push(slot);
+ }
+
+ queueMutationObserverMicrotask();
+}
+
+// https://dom.spec.whatwg.org/#concept-shadow-including-descendant
+function* shadowIncludingInclusiveDescendantsIterator(node) {
+ yield node;
+
+ if (node._shadowRoot) {
+ yield* shadowIncludingInclusiveDescendantsIterator(node._shadowRoot);
+ }
+
+ for (const child of domSymbolTree.childrenIterator(node)) {
+ yield* shadowIncludingInclusiveDescendantsIterator(child);
+ }
+}
+
+// https://dom.spec.whatwg.org/#concept-shadow-including-descendant
+function* shadowIncludingDescendantsIterator(node) {
+ if (node._shadowRoot) {
+ yield* shadowIncludingInclusiveDescendantsIterator(node._shadowRoot);
+ }
+
+ for (const child of domSymbolTree.childrenIterator(node)) {
+ yield* shadowIncludingInclusiveDescendantsIterator(child);
+ }
+}
+
+module.exports = {
+ isValidHostElementName,
+
+ isNode,
+ isSlotable,
+ isSlot,
+ isShadowRoot,
+
+ isShadowInclusiveAncestor,
+ retarget,
+ getEventTargetParent,
+ shadowIncludingRoot,
+
+ assignSlot,
+ assignSlotable,
+ assignSlotableForTree,
+
+ findSlot,
+ findFlattenedSlotables,
+
+ signalSlotChange,
+
+ shadowIncludingInclusiveDescendantsIterator,
+ shadowIncludingDescendantsIterator
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/strings.js b/node_modules/jsdom/lib/jsdom/living/helpers/strings.js
new file mode 100644
index 0000000..1579251
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/strings.js
@@ -0,0 +1,148 @@
+"use strict";
+
+// https://infra.spec.whatwg.org/#ascii-whitespace
+const asciiWhitespaceRe = /^[\t\n\f\r ]$/;
+exports.asciiWhitespaceRe = asciiWhitespaceRe;
+
+// https://infra.spec.whatwg.org/#ascii-lowercase
+exports.asciiLowercase = s => {
+ return s.replace(/[A-Z]/g, l => l.toLowerCase());
+};
+
+// https://infra.spec.whatwg.org/#ascii-uppercase
+exports.asciiUppercase = s => {
+ return s.replace(/[a-z]/g, l => l.toUpperCase());
+};
+
+// https://infra.spec.whatwg.org/#strip-newlines
+exports.stripNewlines = s => {
+ return s.replace(/[\n\r]+/g, "");
+};
+
+// https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
+exports.stripLeadingAndTrailingASCIIWhitespace = s => {
+ return s.replace(/^[ \t\n\f\r]+/, "").replace(/[ \t\n\f\r]+$/, "");
+};
+
+// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
+exports.stripAndCollapseASCIIWhitespace = s => {
+ return s.replace(/[ \t\n\f\r]+/g, " ").replace(/^[ \t\n\f\r]+/, "").replace(/[ \t\n\f\r]+$/, "");
+};
+
+// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-simple-colour
+exports.isValidSimpleColor = s => {
+ return /^#[a-fA-F\d]{6}$/.test(s);
+};
+
+// https://infra.spec.whatwg.org/#ascii-case-insensitive
+exports.asciiCaseInsensitiveMatch = (a, b) => {
+ if (a.length !== b.length) {
+ return false;
+ }
+
+ for (let i = 0; i < a.length; ++i) {
+ if ((a.charCodeAt(i) | 32) !== (b.charCodeAt(i) | 32)) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers
+// Error is represented as null.
+const parseInteger = exports.parseInteger = input => {
+ // The implementation here is slightly different from the spec's. We want to use parseInt(), but parseInt() trims
+ // Unicode whitespace in addition to just ASCII ones, so we make sure that the trimmed prefix contains only ASCII
+ // whitespace ourselves.
+ const numWhitespace = input.length - input.trimStart().length;
+ if (/[^\t\n\f\r ]/.test(input.slice(0, numWhitespace))) {
+ return null;
+ }
+ // We don't allow hexadecimal numbers here.
+ // eslint-disable-next-line radix
+ const value = parseInt(input, 10);
+ if (Number.isNaN(value)) {
+ return null;
+ }
+ // parseInt() returns -0 for "-0". Normalize that here.
+ return value === 0 ? 0 : value;
+};
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-non-negative-integers
+// Error is represented as null.
+exports.parseNonNegativeInteger = input => {
+ const value = parseInteger(input);
+ if (value === null) {
+ return null;
+ }
+ if (value < 0) {
+ return null;
+ }
+ return value;
+};
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-floating-point-number
+const floatingPointNumRe = /^-?(?:\d+|\d*\.\d+)(?:[eE][-+]?\d+)?$/;
+exports.isValidFloatingPointNumber = str => floatingPointNumRe.test(str);
+
+// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values
+// Error is represented as null.
+exports.parseFloatingPointNumber = str => {
+ // The implementation here is slightly different from the spec's. We need to use parseFloat() in order to retain
+ // accuracy, but parseFloat() trims Unicode whitespace in addition to just ASCII ones, so we make sure that the
+ // trimmed prefix contains only ASCII whitespace ourselves.
+ const numWhitespace = str.length - str.trimStart().length;
+ if (/[^\t\n\f\r ]/.test(str.slice(0, numWhitespace))) {
+ return null;
+ }
+ const parsed = parseFloat(str);
+ return isFinite(parsed) ? parsed : null;
+};
+
+// https://infra.spec.whatwg.org/#split-on-ascii-whitespace
+exports.splitOnASCIIWhitespace = str => {
+ let position = 0;
+ const tokens = [];
+ while (position < str.length && asciiWhitespaceRe.test(str[position])) {
+ position++;
+ }
+ if (position === str.length) {
+ return tokens;
+ }
+ while (position < str.length) {
+ const start = position;
+ while (position < str.length && !asciiWhitespaceRe.test(str[position])) {
+ position++;
+ }
+ tokens.push(str.slice(start, position));
+ while (position < str.length && asciiWhitespaceRe.test(str[position])) {
+ position++;
+ }
+ }
+ return tokens;
+};
+
+// https://infra.spec.whatwg.org/#split-on-commas
+exports.splitOnCommas = str => {
+ let position = 0;
+ const tokens = [];
+ while (position < str.length) {
+ let start = position;
+ while (position < str.length && str[position] !== ",") {
+ position++;
+ }
+ let end = position;
+ while (start < str.length && asciiWhitespaceRe.test(str[start])) {
+ start++;
+ }
+ while (end > start && asciiWhitespaceRe.test(str[end - 1])) {
+ end--;
+ }
+ tokens.push(str.slice(start, end));
+ if (position < str.length) {
+ position++;
+ }
+ }
+ return tokens;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js b/node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js
new file mode 100644
index 0000000..63e749d
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/style-rules.js
@@ -0,0 +1,114 @@
+"use strict";
+const cssom = require("cssom");
+const defaultStyleSheet = require("../../browser/default-stylesheet");
+const { matchesDontThrow } = require("./selectors");
+
+const { forEach, indexOf } = Array.prototype;
+
+let parsedDefaultStyleSheet;
+
+// Properties for which getResolvedValue is implemented. This is less than
+// every supported property.
+// https://drafts.csswg.org/indexes/#properties
+exports.propertiesWithResolvedValueImplemented = {
+ __proto__: null,
+
+ // https://drafts.csswg.org/css2/visufx.html#visibility
+ visibility: {
+ inherited: true,
+ initial: "visible",
+ computedValue: "as-specified"
+ }
+};
+
+exports.forEachMatchingSheetRuleOfElement = (elementImpl, handleRule) => {
+ function handleSheet(sheet) {
+ forEach.call(sheet.cssRules, rule => {
+ if (rule.media) {
+ if (indexOf.call(rule.media, "screen") !== -1) {
+ forEach.call(rule.cssRules, innerRule => {
+ if (matches(innerRule, elementImpl)) {
+ handleRule(innerRule);
+ }
+ });
+ }
+ } else if (matches(rule, elementImpl)) {
+ handleRule(rule);
+ }
+ });
+ }
+
+ if (!parsedDefaultStyleSheet) {
+ parsedDefaultStyleSheet = cssom.parse(defaultStyleSheet);
+ }
+
+ handleSheet(parsedDefaultStyleSheet);
+ forEach.call(elementImpl._ownerDocument.styleSheets._list, handleSheet);
+};
+
+function matches(rule, element) {
+ return matchesDontThrow(element, rule.selectorText);
+}
+
+// Naive implementation of https://drafts.csswg.org/css-cascade-4/#cascading
+// based on the previous jsdom implementation of getComputedStyle.
+// Does not implement https://drafts.csswg.org/css-cascade-4/#cascade-specificity,
+// or rather specificity is only implemented by the order in which the matching
+// rules appear. The last rule is the most specific while the first rule is
+// the least specific.
+function getCascadedPropertyValue(element, property) {
+ let value = "";
+
+ exports.forEachMatchingSheetRuleOfElement(element, rule => {
+ const propertyValue = rule.style.getPropertyValue(property);
+ // getPropertyValue returns "" if the property is not found
+ if (propertyValue !== "") {
+ value = propertyValue;
+ }
+ });
+
+ const inlineValue = element.style.getPropertyValue(property);
+ if (inlineValue !== "" && inlineValue !== null) {
+ value = inlineValue;
+ }
+
+ return value;
+}
+
+// https://drafts.csswg.org/css-cascade-4/#specified-value
+function getSpecifiedValue(element, property) {
+ const cascade = getCascadedPropertyValue(element, property);
+
+ if (cascade !== "") {
+ return cascade;
+ }
+
+ // Defaulting
+ const { initial, inherited } = exports.propertiesWithResolvedValueImplemented[property];
+ if (inherited && element.parentElement !== null) {
+ return getComputedValue(element.parentElement, property);
+ }
+
+ // root element without parent element or inherited property
+ return initial;
+}
+
+// https://drafts.csswg.org/css-cascade-4/#computed-value
+function getComputedValue(element, property) {
+ const { computedValue } = exports.propertiesWithResolvedValueImplemented[property];
+ if (computedValue === "as-specified") {
+ return getSpecifiedValue(element, property);
+ }
+
+ throw new TypeError(`Internal error: unrecognized computed value instruction '${computedValue}'`);
+}
+
+// https://drafts.csswg.org/cssom/#resolved-value
+// Only implements `visibility`
+exports.getResolvedValue = (element, property) => {
+ // Determined for special case properties, none of which are implemented here.
+ // So we skip to "any other property: The resolved value is the computed value."
+ return getComputedValue(element, property);
+};
+
+exports.SHADOW_DOM_PSEUDO_REGEXP = /^::(?:part|slotted)\(/i;
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/stylesheets.js b/node_modules/jsdom/lib/jsdom/living/helpers/stylesheets.js
new file mode 100644
index 0000000..7138599
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/stylesheets.js
@@ -0,0 +1,113 @@
+"use strict";
+const cssom = require("cssom");
+const whatwgEncoding = require("whatwg-encoding");
+const whatwgURL = require("whatwg-url");
+
+// TODO: this should really implement https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
+// It (and the things it calls) is nowhere close right now.
+exports.fetchStylesheet = (elementImpl, urlString) => {
+ const parsedURL = whatwgURL.parseURL(urlString);
+ return fetchStylesheetInternal(elementImpl, urlString, parsedURL);
+};
+
+// https://drafts.csswg.org/cssom/#remove-a-css-style-sheet
+exports.removeStylesheet = (sheet, elementImpl) => {
+ const { styleSheets } = elementImpl._ownerDocument;
+ styleSheets._remove(sheet);
+
+ // Remove the association explicitly; in the spec it's implicit so this step doesn't exist.
+ elementImpl.sheet = null;
+
+ // TODO: "Set the CSS style sheet’s parent CSS style sheet, owner node and owner CSS rule to null."
+ // Probably when we have a real CSSOM implementation.
+};
+
+// https://drafts.csswg.org/cssom/#create-a-css-style-sheet kinda:
+// - Parsing failures are not handled gracefully like they should be
+// - The import rules stuff seems out of place, and probably should affect the load event...
+exports.createStylesheet = (sheetText, elementImpl, baseURL) => {
+ let sheet;
+ try {
+ sheet = cssom.parse(sheetText);
+ } catch (e) {
+ if (elementImpl._ownerDocument._defaultView) {
+ const error = new Error("Could not parse CSS stylesheet");
+ error.detail = sheetText;
+ error.type = "css parsing";
+
+ elementImpl._ownerDocument._defaultView._virtualConsole.emit("jsdomError", error);
+ }
+ return;
+ }
+
+ scanForImportRules(elementImpl, sheet.cssRules, baseURL);
+
+ addStylesheet(sheet, elementImpl);
+};
+
+// https://drafts.csswg.org/cssom/#add-a-css-style-sheet
+function addStylesheet(sheet, elementImpl) {
+ elementImpl._ownerDocument.styleSheets._add(sheet);
+
+ // Set the association explicitly; in the spec it's implicit.
+ elementImpl.sheet = sheet;
+
+ // TODO: title and disabled stuff
+}
+
+function fetchStylesheetInternal(elementImpl, urlString, parsedURL) {
+ const document = elementImpl._ownerDocument;
+ let defaultEncoding = document._encoding;
+ const resourceLoader = document._resourceLoader;
+
+ if (elementImpl.localName === "link" && elementImpl.hasAttributeNS(null, "charset")) {
+ defaultEncoding = whatwgEncoding.labelToName(elementImpl.getAttributeNS(null, "charset"));
+ }
+
+ function onStylesheetLoad(data) {
+ const css = whatwgEncoding.decode(data, defaultEncoding);
+
+ // TODO: MIME type checking?
+ if (elementImpl.sheet) {
+ exports.removeStylesheet(elementImpl.sheet, elementImpl);
+ }
+ exports.createStylesheet(css, elementImpl, parsedURL);
+ }
+
+ resourceLoader.fetch(urlString, {
+ element: elementImpl,
+ onLoad: onStylesheetLoad
+ });
+}
+
+// TODO this is actually really messed up and overwrites the sheet on elementImpl
+// Tracking in https://github.com/jsdom/jsdom/issues/2124
+function scanForImportRules(elementImpl, cssRules, baseURL) {
+ if (!cssRules) {
+ return;
+ }
+
+ for (let i = 0; i < cssRules.length; ++i) {
+ if (cssRules[i].cssRules) {
+ // @media rule: keep searching inside it.
+ scanForImportRules(elementImpl, cssRules[i].cssRules, baseURL);
+ } else if (cssRules[i].href) {
+ // @import rule: fetch the resource and evaluate it.
+ // See http://dev.w3.org/csswg/cssom/#css-import-rule
+ // If loading of the style sheet fails its cssRules list is simply
+ // empty. I.e. an @import rule always has an associated style sheet.
+ const parsed = whatwgURL.parseURL(cssRules[i].href, { baseURL });
+ if (parsed === null) {
+ const window = elementImpl._ownerDocument._defaultView;
+ if (window) {
+ const error = new Error(`Could not parse CSS @import URL ${cssRules[i].href} relative to base URL ` +
+ `"${whatwgURL.serializeURL(baseURL)}"`);
+ error.type = "css @import URL parsing";
+ window._virtualConsole.emit("jsdomError", error);
+ }
+ } else {
+ fetchStylesheetInternal(elementImpl, whatwgURL.serializeURL(parsed), parsed);
+ }
+ }
+ }
+}
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/svg/basic-types.js b/node_modules/jsdom/lib/jsdom/living/helpers/svg/basic-types.js
new file mode 100644
index 0000000..16d0dc1
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/svg/basic-types.js
@@ -0,0 +1,41 @@
+"use strict";
+
+// https://svgwg.org/svg2-draft/types.html#TermDetach
+function detach(value) {
+ if (typeof value === "string") {
+ return;
+ }
+
+ throw new TypeError(`jsdom internal error: detaching object of wrong type ${value}`);
+}
+exports.detach = detach;
+
+// https://svgwg.org/svg2-draft/types.html#TermAttach
+// listObject corresponds to the parameter taken by the algorithm in the spec, but is currently unused because only
+// DOMString type is supported by jsdom (and this function) right now.
+// eslint-disable-next-line no-unused-vars
+function attach(value, listObject) {
+ if (typeof value === "string") {
+ return;
+ }
+
+ throw new TypeError(`jsdom internal error: attaching object of wrong type ${value}`);
+}
+exports.attach = attach;
+
+// https://svgwg.org/svg2-draft/types.html#TermReserialize for DOMString.
+function reserializeSpaceSeparatedTokens(elements) {
+ return elements.join(" ");
+}
+exports.reserializeSpaceSeparatedTokens = reserializeSpaceSeparatedTokens;
+
+// Used for systemLanguage attribute, whose value is a set of comma-separated tokens:
+// https://svgwg.org/svg2-draft/struct.html#SystemLanguageAttribute
+// SVG 2 spec (https://svgwg.org/svg2-draft/types.html#TermReserialize) says any SVGStringList should reserialize the
+// same way, as space-separated tokens, but doing so for systemLanguage is illogical and contradicts the Firefox
+// behavior.
+// I cannot find a description of reserialization of SVGStringList in the SVG 1.1 spec.
+function reserializeCommaSeparatedTokens(elements) {
+ return elements.join(", ");
+}
+exports.reserializeCommaSeparatedTokens = reserializeCommaSeparatedTokens;
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/svg/render.js b/node_modules/jsdom/lib/jsdom/living/helpers/svg/render.js
new file mode 100644
index 0000000..651568d
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/svg/render.js
@@ -0,0 +1,46 @@
+"use strict";
+const { SVG_NS } = require("../namespaces");
+
+// https://svgwg.org/svg2-draft/render.html#TermNeverRenderedElement
+const neverRenderedElements = new Set([
+ "clipPath",
+ "defs",
+ "desc",
+ "linearGradient",
+ "marker",
+ "mask",
+ "metadata",
+ "pattern",
+ "radialGradient",
+ "script",
+ "style",
+ "title",
+ "symbol"
+]);
+
+// https://svgwg.org/svg2-draft/render.html#Rendered-vs-NonRendered
+exports.isRenderedElement = elImpl => {
+ if (neverRenderedElements.has(elImpl._localName)) {
+ return false;
+ }
+
+ // This does not check for elements excluded because of conditional processing attributes or ‘switch’ structures,
+ // because conditional processing is not implemented.
+ // https://svgwg.org/svg2-draft/struct.html#ConditionalProcessing
+
+ // This does not check for computed style of display being none, since that is not yet implemented for HTML
+ // focusability either (and there are no tests yet).
+
+ if (!elImpl.isConnected) {
+ return false;
+ }
+
+ // The spec is unclear about how to deal with non-SVG parents, so we only perform this check for SVG-namespace
+ // parents.
+ if (elImpl.parentElement && elImpl.parentElement._namespaceURI === SVG_NS &&
+ !exports.isRenderedElement(elImpl.parentNode)) {
+ return false;
+ }
+
+ return true;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/text.js b/node_modules/jsdom/lib/jsdom/living/helpers/text.js
new file mode 100644
index 0000000..632c0e5
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/text.js
@@ -0,0 +1,19 @@
+"use strict";
+const { domSymbolTree } = require("./internal-constants");
+const { CDATA_SECTION_NODE, TEXT_NODE } = require("../node-type");
+
+//
+// https://dom.spec.whatwg.org/#concept-child-text-content
+//
+exports.childTextContent = node => {
+ let result = "";
+ const iterator = domSymbolTree.childrenIterator(node);
+ for (const child of iterator) {
+ if (child.nodeType === TEXT_NODE ||
+ // The CDataSection extends Text.
+ child.nodeType === CDATA_SECTION_NODE) {
+ result += child.data;
+ }
+ }
+ return result;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/traversal.js b/node_modules/jsdom/lib/jsdom/living/helpers/traversal.js
new file mode 100644
index 0000000..91f7148
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/traversal.js
@@ -0,0 +1,72 @@
+"use strict";
+const { domSymbolTree } = require("./internal-constants");
+const { HTML_NS } = require("./namespaces");
+
+// All these operate on and return impls, not wrappers!
+
+exports.closest = (e, localName, namespace = HTML_NS) => {
+ while (e) {
+ if (e.localName === localName && e.namespaceURI === namespace) {
+ return e;
+ }
+ e = domSymbolTree.parent(e);
+ }
+
+ return null;
+};
+
+exports.childrenByLocalName = (parent, localName, namespace = HTML_NS) => {
+ return domSymbolTree.childrenToArray(parent, { filter(node) {
+ return node._localName === localName && node._namespaceURI === namespace;
+ } });
+};
+
+exports.descendantsByLocalName = (parent, localName, namespace = HTML_NS) => {
+ return domSymbolTree.treeToArray(parent, { filter(node) {
+ return node._localName === localName && node._namespaceURI === namespace && node !== parent;
+ } });
+};
+
+exports.childrenByLocalNames = (parent, localNamesSet, namespace = HTML_NS) => {
+ return domSymbolTree.childrenToArray(parent, { filter(node) {
+ return localNamesSet.has(node._localName) && node._namespaceURI === namespace;
+ } });
+};
+
+exports.descendantsByLocalNames = (parent, localNamesSet, namespace = HTML_NS) => {
+ return domSymbolTree.treeToArray(parent, { filter(node) {
+ return localNamesSet.has(node._localName) &&
+ node._namespaceURI === namespace &&
+ node !== parent;
+ } });
+};
+
+exports.firstChildWithLocalName = (parent, localName, namespace = HTML_NS) => {
+ const iterator = domSymbolTree.childrenIterator(parent);
+ for (const child of iterator) {
+ if (child._localName === localName && child._namespaceURI === namespace) {
+ return child;
+ }
+ }
+ return null;
+};
+
+exports.firstChildWithLocalNames = (parent, localNamesSet, namespace = HTML_NS) => {
+ const iterator = domSymbolTree.childrenIterator(parent);
+ for (const child of iterator) {
+ if (localNamesSet.has(child._localName) && child._namespaceURI === namespace) {
+ return child;
+ }
+ }
+ return null;
+};
+
+exports.firstDescendantWithLocalName = (parent, localName, namespace = HTML_NS) => {
+ const iterator = domSymbolTree.treeIterator(parent);
+ for (const descendant of iterator) {
+ if (descendant._localName === localName && descendant._namespaceURI === namespace) {
+ return descendant;
+ }
+ }
+ return null;
+};
diff --git a/node_modules/jsdom/lib/jsdom/living/helpers/validate-names.js b/node_modules/jsdom/lib/jsdom/living/helpers/validate-names.js
new file mode 100644
index 0000000..d341dbd
--- /dev/null
+++ b/node_modules/jsdom/lib/jsdom/living/helpers/validate-names.js
@@ -0,0 +1,75 @@
+"use strict";
+const xnv = require("xml-name-validator");
+const DOMException = require("domexception/webidl2js-wrapper");
+const { XML_NS, XMLNS_NS } = require("../helpers/namespaces");
+
+// https://dom.spec.whatwg.org/#validate
+
+exports.name = function (globalObject, name) {
+ const result = xnv.name(name);
+ if (!result.success) {
+ throw DOMException.create(globalObject, [
+ `"${name}" did not match the Name production: ${result.error}`,
+ "InvalidCharacterError"
+ ]);
+ }
+};
+
+exports.qname = function (globalObject, qname) {
+ exports.name(globalObject, qname);
+
+ const result = xnv.qname(qname);
+ if (!result.success) {
+ throw DOMException.create(globalObject, [
+ `"${qname}" did not match the QName production: ${result.error}`,
+ "InvalidCharacterError"
+ ]);
+ }
+};
+
+exports.validateAndExtract = function (globalObject, namespace, qualifiedName) {
+ if (namespace === "") {
+ namespace = null;
+ }
+
+ exports.qname(globalObject, qualifiedName);
+
+ let prefix = null;
+ let localName = qualifiedName;
+
+ const colonIndex = qualifiedName.indexOf(":");
+ if (colonIndex !== -1) {
+ prefix = qualifiedName.substring(0, colonIndex);
+ localName = qualifiedName.substring(colonIndex + 1);
+ }
+
+ if (prefix !== null && namespace === null) {
+ throw DOMException.create(globalObject, [
+ "A namespace was given but a prefix was also extracted from the qualifiedName",
+ "NamespaceError"
+ ]);
+ }
+
+ if (prefix === "xml" && namespace !== XML_NS) {
+ throw DOMException.create(globalObject, [
+ "A prefix of \"xml\" was given but the namespace was not the XML namespace",
+ "NamespaceError"
+ ]);
+ }
+
+ if ((qualifiedName === "xmlns" || prefix === "xmlns") && namespace !== XMLNS_NS) {
+ throw DOMException.create(globalObject, [
+ "A prefix or qualifiedName of \"xmlns\" was given but the namespace was not the XMLNS namespace",
+ "NamespaceError"
+ ]);
+ }
+
+ if (namespace === XMLNS_NS && qualifiedName !== "xmlns" && prefix !== "xmlns") {
+ throw DOMException.create(globalObject, [
+ "The XMLNS namespace was given but neither the prefix nor qualifiedName was \"xmlns\"",
+ "NamespaceError"
+ ]);
+ }
+
+ return { namespace, prefix, localName };
+};