projen
Version:
CDK for software projects
608 lines • 27.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var DOMImpl_1 = require("../dom/DOMImpl");
var infra_1 = require("@oozcitak/infra");
var util_1 = require("../util");
var DOMException_1 = require("../dom/DOMException");
var CreateAlgorithm_1 = require("./CreateAlgorithm");
var CustomElementAlgorithm_1 = require("./CustomElementAlgorithm");
var MutationObserverAlgorithm_1 = require("./MutationObserverAlgorithm");
var DOMAlgorithm_1 = require("./DOMAlgorithm");
var MutationAlgorithm_1 = require("./MutationAlgorithm");
var DocumentAlgorithm_1 = require("./DocumentAlgorithm");
/**
* Determines whether the element's attribute list contains the given
* attribute.
*
* @param attribute - an attribute node
* @param element - an element node
*/
function element_has(attribute, element) {
/**
* An element has an attribute A if its attribute list contains A.
*/
return element._attributeList._asArray().indexOf(attribute) !== -1;
}
exports.element_has = element_has;
/**
* Changes the value of an attribute node.
*
* @param attribute - an attribute node
* @param element - an element node
* @param value - attribute value
*/
function element_change(attribute, element, value) {
/**
* 1. Queue an attribute mutation record for element with attribute’s
* local name, attribute’s namespace, and attribute’s value.
*/
if (DOMImpl_1.dom.features.mutationObservers) {
MutationObserverAlgorithm_1.observer_queueAttributeMutationRecord(element, attribute._localName, attribute._namespace, attribute._value);
}
/**
* 2. If element is custom, then enqueue a custom element callback reaction
* with element, callback name "attributeChangedCallback", and an argument
* list containing attribute’s local name, attribute’s value, value, and
* attribute’s namespace.
*/
if (DOMImpl_1.dom.features.customElements) {
if (util_1.Guard.isCustomElementNode(element)) {
CustomElementAlgorithm_1.customElement_enqueueACustomElementCallbackReaction(element, "attributeChangedCallback", [attribute._localName, attribute._value, value, attribute._namespace]);
}
}
/**
* 3. Run the attribute change steps with element, attribute’s local name,
* attribute’s value, value, and attribute’s namespace.
* 4. Set attribute’s value to value.
*/
if (DOMImpl_1.dom.features.steps) {
DOMAlgorithm_1.dom_runAttributeChangeSteps(element, attribute._localName, attribute._value, value, attribute._namespace);
}
attribute._value = value;
}
exports.element_change = element_change;
/**
* Appends an attribute to an element node.
*
* @param attribute - an attribute
* @param element - an element to receive the attribute
*/
function element_append(attribute, element) {
/**
* 1. Queue an attribute mutation record for element with attribute’s
* local name, attribute’s namespace, and null.
*/
if (DOMImpl_1.dom.features.mutationObservers) {
MutationObserverAlgorithm_1.observer_queueAttributeMutationRecord(element, attribute._localName, attribute._namespace, null);
}
/**
* 2. If element is custom, then enqueue a custom element callback reaction
* with element, callback name "attributeChangedCallback", and an argument
* list containing attribute’s local name, null, attribute’s value, and
* attribute’s namespace.
*/
if (DOMImpl_1.dom.features.customElements) {
if (util_1.Guard.isCustomElementNode(element)) {
CustomElementAlgorithm_1.customElement_enqueueACustomElementCallbackReaction(element, "attributeChangedCallback", [attribute._localName, null, attribute._value, attribute._namespace]);
}
}
/**
* 3. Run the attribute change steps with element, attribute’s local name,
* null, attribute’s value, and attribute’s namespace.
*/
if (DOMImpl_1.dom.features.steps) {
DOMAlgorithm_1.dom_runAttributeChangeSteps(element, attribute._localName, null, attribute._value, attribute._namespace);
}
/**
* 4. Append attribute to element’s attribute list.
* 5. Set attribute’s element to element.
*/
element._attributeList._asArray().push(attribute);
attribute._element = element;
// mark that the document has namespaces
if (!element._nodeDocument._hasNamespaces && (attribute._namespace !== null ||
attribute._namespacePrefix !== null || attribute._localName === "xmlns")) {
element._nodeDocument._hasNamespaces = true;
}
}
exports.element_append = element_append;
/**
* Removes an attribute from an element node.
*
* @param attribute - an attribute
* @param element - an element to receive the attribute
*/
function element_remove(attribute, element) {
/**
* 1. Queue an attribute mutation record for element with attribute’s
* local name, attribute’s namespace, and attribute’s value.
*/
if (DOMImpl_1.dom.features.mutationObservers) {
MutationObserverAlgorithm_1.observer_queueAttributeMutationRecord(element, attribute._localName, attribute._namespace, attribute._value);
}
/**
* 2. If element is custom, then enqueue a custom element callback reaction
* with element, callback name "attributeChangedCallback", and an argument
* list containing attribute’s local name, attribute’s value, null,
* and attribute’s namespace.
*/
if (DOMImpl_1.dom.features.customElements) {
if (util_1.Guard.isCustomElementNode(element)) {
CustomElementAlgorithm_1.customElement_enqueueACustomElementCallbackReaction(element, "attributeChangedCallback", [attribute._localName, attribute._value, null, attribute._namespace]);
}
}
/**
* 3. Run the attribute change steps with element, attribute’s local name,
* attribute’s value, null, and attribute’s namespace.
*/
if (DOMImpl_1.dom.features.steps) {
DOMAlgorithm_1.dom_runAttributeChangeSteps(element, attribute._localName, attribute._value, null, attribute._namespace);
}
/**
* 3. Remove attribute from element’s attribute list.
* 5. Set attribute’s element to null.
*/
var index = element._attributeList._asArray().indexOf(attribute);
element._attributeList._asArray().splice(index, 1);
attribute._element = null;
}
exports.element_remove = element_remove;
/**
* Replaces an attribute with another of an element node.
*
* @param oldAttr - old attribute
* @param newAttr - new attribute
* @param element - an element to receive the attribute
*/
function element_replace(oldAttr, newAttr, element) {
/**
* 1. Queue an attribute mutation record for element with oldAttr’s
* local name, oldAttr’s namespace, and oldAttr’s value.
*/
if (DOMImpl_1.dom.features.mutationObservers) {
MutationObserverAlgorithm_1.observer_queueAttributeMutationRecord(element, oldAttr._localName, oldAttr._namespace, oldAttr._value);
}
/**
* 2. If element is custom, then enqueue a custom element callback reaction
* with element, callback name "attributeChangedCallback", and an argument
* list containing oldAttr’s local name, oldAttr’s value, newAttr’s value,
* and oldAttr’s namespace.
*/
if (DOMImpl_1.dom.features.customElements) {
if (util_1.Guard.isCustomElementNode(element)) {
CustomElementAlgorithm_1.customElement_enqueueACustomElementCallbackReaction(element, "attributeChangedCallback", [oldAttr._localName, oldAttr._value, newAttr._value, oldAttr._namespace]);
}
}
/**
* 3. Run the attribute change steps with element, oldAttr’s local name,
* oldAttr’s value, newAttr’s value, and oldAttr’s namespace.
*/
if (DOMImpl_1.dom.features.steps) {
DOMAlgorithm_1.dom_runAttributeChangeSteps(element, oldAttr._localName, oldAttr._value, newAttr._value, oldAttr._namespace);
}
/**
* 4. Replace oldAttr by newAttr in element’s attribute list.
* 5. Set oldAttr’s element to null.
* 6. Set newAttr’s element to element.
*/
var index = element._attributeList._asArray().indexOf(oldAttr);
if (index !== -1) {
element._attributeList._asArray()[index] = newAttr;
}
oldAttr._element = null;
newAttr._element = element;
// mark that the document has namespaces
if (!element._nodeDocument._hasNamespaces && (newAttr._namespace !== null ||
newAttr._namespacePrefix !== null || newAttr._localName === "xmlns")) {
element._nodeDocument._hasNamespaces = true;
}
}
exports.element_replace = element_replace;
/**
* Retrieves an attribute with the given name from an element node.
*
* @param qualifiedName - an attribute name
* @param element - an element to receive the attribute
*/
function element_getAnAttributeByName(qualifiedName, element) {
/**
* 1. If element is in the HTML namespace and its node document is an HTML
* document, then set qualifiedName to qualifiedName in ASCII lowercase.
* 2. Return the first attribute in element’s attribute list whose qualified
* name is qualifiedName, and null otherwise.
*/
if (element._namespace === infra_1.namespace.HTML && element._nodeDocument._type === "html") {
qualifiedName = qualifiedName.toLowerCase();
}
return element._attributeList._asArray().find(function (attr) { return attr._qualifiedName === qualifiedName; }) || null;
}
exports.element_getAnAttributeByName = element_getAnAttributeByName;
/**
* Retrieves an attribute with the given namespace and local name from an
* element node.
*
* @param namespace - an attribute namespace
* @param localName - an attribute local name
* @param element - an element to receive the attribute
*/
function element_getAnAttributeByNamespaceAndLocalName(namespace, localName, element) {
/**
* 1. If namespace is the empty string, set it to null.
* 2. Return the attribute in element’s attribute list whose namespace is
* namespace and local name is localName, if any, and null otherwise.
*/
var ns = namespace || null;
return element._attributeList._asArray().find(function (attr) { return attr._namespace === ns && attr._localName === localName; }) || null;
}
exports.element_getAnAttributeByNamespaceAndLocalName = element_getAnAttributeByNamespaceAndLocalName;
/**
* Retrieves an attribute's value with the given name namespace and local
* name from an element node.
*
* @param element - an element to receive the attribute
* @param localName - an attribute local name
* @param namespace - an attribute namespace
*/
function element_getAnAttributeValue(element, localName, namespace) {
if (namespace === void 0) { namespace = ''; }
/**
* 1. Let attr be the result of getting an attribute given namespace,
* localName, and element.
* 2. If attr is null, then return the empty string.
* 3. Return attr’s value.
*/
var attr = element_getAnAttributeByNamespaceAndLocalName(namespace, localName, element);
if (attr === null)
return '';
else
return attr._value;
}
exports.element_getAnAttributeValue = element_getAnAttributeValue;
/**
* Sets an attribute of an element node.
*
* @param attr - an attribute
* @param element - an element to receive the attribute
*/
function element_setAnAttribute(attr, element) {
/**
* 1. If attr’s element is neither null nor element, throw an
* "InUseAttributeError" DOMException.
* 2. Let oldAttr be the result of getting an attribute given attr’s
* namespace, attr’s local name, and element.
* 3. If oldAttr is attr, return attr.
* 4. If oldAttr is non-null, replace it by attr in element.
* 5. Otherwise, append attr to element.
* 6. Return oldAttr.
*/
if (attr._element !== null && attr._element !== element)
throw new DOMException_1.InUseAttributeError("This attribute already exists in the document: " + attr._qualifiedName + " as a child of " + attr._element._qualifiedName + ".");
var oldAttr = element_getAnAttributeByNamespaceAndLocalName(attr._namespace || '', attr._localName, element);
if (oldAttr === attr)
return attr;
if (oldAttr !== null) {
element_replace(oldAttr, attr, element);
}
else {
element_append(attr, element);
}
return oldAttr;
}
exports.element_setAnAttribute = element_setAnAttribute;
/**
* Sets an attribute's value of an element node.
*
* @param element - an element to receive the attribute
* @param localName - an attribute local name
* @param value - an attribute value
* @param prefix - an attribute prefix
* @param namespace - an attribute namespace
*/
function element_setAnAttributeValue(element, localName, value, prefix, namespace) {
if (prefix === void 0) { prefix = null; }
if (namespace === void 0) { namespace = null; }
/**
* 1. If prefix is not given, set it to null.
* 2. If namespace is not given, set it to null.
* 3. Let attribute be the result of getting an attribute given namespace,
* localName, and element.
* 4. If attribute is null, create an attribute whose namespace is
* namespace, namespace prefix is prefix, local name is localName, value
* is value, and node document is element’s node document, then append this
* attribute to element, and then return.
* 5. Change attribute from element to value.
*/
var attribute = element_getAnAttributeByNamespaceAndLocalName(namespace || '', localName, element);
if (attribute === null) {
var newAttr = CreateAlgorithm_1.create_attr(element._nodeDocument, localName);
newAttr._namespace = namespace;
newAttr._namespacePrefix = prefix;
newAttr._value = value;
element_append(newAttr, element);
return;
}
element_change(attribute, element, value);
}
exports.element_setAnAttributeValue = element_setAnAttributeValue;
/**
* Removes an attribute with the given name from an element node.
*
* @param qualifiedName - an attribute name
* @param element - an element to receive the attribute
*/
function element_removeAnAttributeByName(qualifiedName, element) {
/**
* 1. Let attr be the result of getting an attribute given qualifiedName
* and element.
* 2. If attr is non-null, remove it from element.
* 3. Return attr.
*/
var attr = element_getAnAttributeByName(qualifiedName, element);
if (attr !== null) {
element_remove(attr, element);
}
return attr;
}
exports.element_removeAnAttributeByName = element_removeAnAttributeByName;
/**
* Removes an attribute with the given namespace and local name from an
* element node.
*
* @param namespace - an attribute namespace
* @param localName - an attribute local name
* @param element - an element to receive the attribute
*/
function element_removeAnAttributeByNamespaceAndLocalName(namespace, localName, element) {
/**
* 1. Let attr be the result of getting an attribute given namespace, localName, and element.
* 2. If attr is non-null, remove it from element.
* 3. Return attr.
*/
var attr = element_getAnAttributeByNamespaceAndLocalName(namespace, localName, element);
if (attr !== null) {
element_remove(attr, element);
}
return attr;
}
exports.element_removeAnAttributeByNamespaceAndLocalName = element_removeAnAttributeByNamespaceAndLocalName;
/**
* Creates an element node.
* See: https://dom.spec.whatwg.org/#concept-create-element.
*
* @param document - the document owning the element
* @param localName - local name
* @param namespace - element namespace
* @param prefix - namespace prefix
* @param is - the "is" value
* @param synchronousCustomElementsFlag - synchronous custom elements flag
*/
function element_createAnElement(document, localName, namespace, prefix, is, synchronousCustomElementsFlag) {
if (prefix === void 0) { prefix = null; }
if (is === void 0) { is = null; }
if (synchronousCustomElementsFlag === void 0) { synchronousCustomElementsFlag = false; }
/**
* 1. If prefix was not given, let prefix be null.
* 2. If is was not given, let is be null.
* 3. Let result be null.
*/
var result = null;
if (!DOMImpl_1.dom.features.customElements) {
result = CreateAlgorithm_1.create_element(document, localName, namespace, prefix);
result._customElementState = "uncustomized";
result._customElementDefinition = null;
result._is = is;
return result;
}
/**
* 4. Let definition be the result of looking up a custom element definition
* given document, namespace, localName, and is.
*/
var definition = CustomElementAlgorithm_1.customElement_lookUpACustomElementDefinition(document, namespace, localName, is);
if (definition !== null && definition.name !== definition.localName) {
/**
* 5. If definition is non-null, and definition’s name is not equal to
* its local name (i.e., definition represents a customized built-in
* element), then:
* 5.1. Let interface be the element interface for localName and the HTML
* namespace.
* 5.2. Set result to a new element that implements interface, with no
* attributes, namespace set to the HTML namespace, namespace prefix
* set to prefix, local name set to localName, custom element state set
* to "undefined", custom element definition set to null, is value set
* to is, and node document set to document.
* 5.3. If the synchronous custom elements flag is set, upgrade element
* using definition.
* 5.4. Otherwise, enqueue a custom element upgrade reaction given result
* and definition.
*/
var elemenInterface = DocumentAlgorithm_1.document_elementInterface(localName, infra_1.namespace.HTML);
result = new elemenInterface();
result._localName = localName;
result._namespace = infra_1.namespace.HTML;
result._namespacePrefix = prefix;
result._customElementState = "undefined";
result._customElementDefinition = null;
result._is = is;
result._nodeDocument = document;
if (synchronousCustomElementsFlag) {
CustomElementAlgorithm_1.customElement_upgrade(definition, result);
}
else {
CustomElementAlgorithm_1.customElement_enqueueACustomElementUpgradeReaction(result, definition);
}
}
else if (definition !== null) {
/**
* 6. Otherwise, if definition is non-null, then:
*/
if (synchronousCustomElementsFlag) {
/**
* 6.1. If the synchronous custom elements flag is set, then run these
* steps while catching any exceptions:
*/
try {
/**
* 6.1.1. Let C be definition’s constructor.
* 6.1.2. Set result to the result of constructing C, with no arguments.
* 6.1.3. Assert: result’s custom element state and custom element definition
* are initialized.
* 6.1.4. Assert: result’s namespace is the HTML namespace.
* _Note:_ IDL enforces that result is an HTMLElement object, which all
* use the HTML namespace.
*/
var C = definition.constructor;
var result_1 = new C();
console.assert(result_1._customElementState !== undefined);
console.assert(result_1._customElementDefinition !== undefined);
console.assert(result_1._namespace === infra_1.namespace.HTML);
/**
* 6.1.5. If result’s attribute list is not empty, then throw a
* "NotSupportedError" DOMException.
* 6.1.6. If result has children, then throw a "NotSupportedError"
* DOMException.
* 6.1.7. If result’s parent is not null, then throw a
* "NotSupportedError" DOMException.
* 6.1.8. If result’s node document is not document, then throw a
* "NotSupportedError" DOMException.
* 6.1.9. If result’s local name is not equal to localName, then throw
* a "NotSupportedError" DOMException.
*/
if (result_1._attributeList.length !== 0)
throw new DOMException_1.NotSupportedError("Custom element already has attributes.");
if (result_1._children.size !== 0)
throw new DOMException_1.NotSupportedError("Custom element already has child nodes.");
if (result_1._parent !== null)
throw new DOMException_1.NotSupportedError("Custom element already has a parent node.");
if (result_1._nodeDocument !== document)
throw new DOMException_1.NotSupportedError("Custom element is already in a document.");
if (result_1._localName !== localName)
throw new DOMException_1.NotSupportedError("Custom element has a different local name.");
/**
* 6.1.10. Set result’s namespace prefix to prefix.
* 6.1.11. Set result’s is value to null.
*/
result_1._namespacePrefix = prefix;
result_1._is = null;
}
catch (e) {
/**
* If any of these steps threw an exception, then:
* - Report the exception.
* - Set result to a new element that implements the HTMLUnknownElement
* interface, with no attributes, namespace set to the HTML namespace,
* namespace prefix set to prefix, local name set to localName, custom
* element state set to "failed", custom element definition set to null,
* is value set to null, and node document set to document.
*/
// TODO: Report the exception
result = CreateAlgorithm_1.create_htmlUnknownElement(document, localName, infra_1.namespace.HTML, prefix);
result._customElementState = "failed";
result._customElementDefinition = null;
result._is = null;
}
}
else {
/**
* 6.2. Otherwise:
* 6.2.1. Set result to a new element that implements the HTMLElement
* interface, with no attributes, namespace set to the HTML namespace,
* namespace prefix set to prefix, local name set to localName, custom
* element state set to "undefined", custom element definition set to
* null, is value set to null, and node document set to document.
* 6.2.2. Enqueue a custom element upgrade reaction given result and
* definition.
*/
result = CreateAlgorithm_1.create_htmlElement(document, localName, infra_1.namespace.HTML, prefix);
result._customElementState = "undefined";
result._customElementDefinition = null;
result._is = null;
CustomElementAlgorithm_1.customElement_enqueueACustomElementUpgradeReaction(result, definition);
}
}
else {
/**
* 7. Otherwise:
* 7.1. Let interface be the element interface for localName and
* namespace.
* 7.2. Set result to a new element that implements interface, with no
* attributes, namespace set to namespace, namespace prefix set to prefix,
* local name set to localName, custom element state set to
* "uncustomized", custom element definition set to null, is value set to
* is, and node document set to document.
*/
var elementInterface = DocumentAlgorithm_1.document_elementInterface(localName, namespace);
result = new elementInterface();
result._localName = localName;
result._namespace = namespace;
result._namespacePrefix = prefix;
result._customElementState = "uncustomized";
result._customElementDefinition = null;
result._is = is;
result._nodeDocument = document;
/**
* 7.3. If namespace is the HTML namespace, and either localName is a
* valid custom element name or is is non-null, then set result’s
* custom element state to "undefined".
*/
if (namespace === infra_1.namespace.HTML && (is !== null ||
CustomElementAlgorithm_1.customElement_isValidCustomElementName(localName))) {
result._customElementState = "undefined";
}
}
/* istanbul ignore next */
if (result === null) {
throw new Error("Unable to create element.");
}
/**
* 8. Returns result
*/
return result;
}
exports.element_createAnElement = element_createAnElement;
/**
* Inserts a new node adjacent to this element.
*
* @param element - a reference element
* @param where - a string defining where to insert the element node.
* - `beforebegin` before this element itself.
* - `afterbegin` before the first child.
* - `beforeend` after the last child.
* - `afterend` after this element itself.
* @param node - node to insert
*/
function element_insertAdjacent(element, where, node) {
/**
* - "beforebegin"
* If element’s parent is null, return null.
* Return the result of pre-inserting node into element’s parent before
* element.
* - "afterbegin"
* Return the result of pre-inserting node into element before element’s
* first child.
* - "beforeend"
* Return the result of pre-inserting node into element before null.
* - "afterend"
* If element’s parent is null, return null.
* Return the result of pre-inserting node into element’s parent before element’s next sibling.
* - Otherwise
* Throw a "SyntaxError" DOMException.
*/
switch (where.toLowerCase()) {
case 'beforebegin':
if (element._parent === null)
return null;
return MutationAlgorithm_1.mutation_preInsert(node, element._parent, element);
case 'afterbegin':
return MutationAlgorithm_1.mutation_preInsert(node, element, element._firstChild);
case 'beforeend':
return MutationAlgorithm_1.mutation_preInsert(node, element, null);
case 'afterend':
if (element._parent === null)
return null;
return MutationAlgorithm_1.mutation_preInsert(node, element._parent, element._nextSibling);
default:
throw new DOMException_1.SyntaxError("Invalid 'where' argument. \"beforebegin\", \"afterbegin\", \"beforeend\" or \"afterend\" expected");
}
}
exports.element_insertAdjacent = element_insertAdjacent;
//# sourceMappingURL=ElementAlgorithm.js.map