@webcomponents/custom-elements
Version:
HTML Custom Elements Polyfill
127 lines • 5.19 kB
JavaScript
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt The complete set of authors may be found
* at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
* be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
* Google as part of the polymer project is also subject to an additional IP
* rights grant found at http://polymer.github.io/PATENTS.txt
*/
const reservedElementNameSet = new Set();
// IE11 does not support constructing a set using an iterable.
[
'annotation-xml',
'color-profile',
'font-face',
'font-face-src',
'font-face-uri',
'font-face-format',
'font-face-name',
'missing-glyph',
].forEach((item) => reservedElementNameSet.add(item));
export function isValidCustomElementName(localName) {
const reserved = reservedElementNameSet.has(localName);
const validForm = /^[a-z][.0-9_a-z]*-[-.0-9_a-z]*$/.test(localName);
return !reserved && validForm;
}
// Note, IE11 doesn't have `document.contains`.
const nativeContains = document.contains
? document.contains.bind(document)
: document.documentElement.contains.bind(document.documentElement);
export function isConnected(node) {
// Use `Node#isConnected`, if defined.
const nativeValue = node.isConnected;
if (nativeValue !== undefined) {
return nativeValue;
}
// Optimization: It's significantly faster here to try to use `contains`,
// especially on Edge/IE/
if (nativeContains(node)) {
return true;
}
let current = node;
while (current &&
!(current.__CE_isImportDocument || current instanceof Document)) {
current =
current.parentNode ||
(window.ShadowRoot && current instanceof ShadowRoot
? current.host
: undefined);
}
return !!(current &&
(current.__CE_isImportDocument || current instanceof Document));
}
export function childrenFromFragment(fragment) {
// Note, IE doesn't have `children` on document fragments.
const nativeChildren = fragment.children;
if (nativeChildren) {
return Array.prototype.slice.call(nativeChildren);
}
const children = [];
for (let n = fragment.firstChild; n; n = n.nextSibling) {
if (n.nodeType === Node.ELEMENT_NODE) {
children.push(n);
}
}
return children;
}
function nextSiblingOrAncestorSibling(root, start) {
let node = start;
while (node && node !== root && !node.nextSibling) {
node = node.parentNode;
}
return !node || node === root ? null : node.nextSibling;
}
function nextNode(root, start) {
return start.firstChild
? start.firstChild
: nextSiblingOrAncestorSibling(root, start);
}
export function walkDeepDescendantElements(root, callback, visitedImports) {
let node = root;
while (node) {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node;
callback(element);
const localName = element.localName;
if (localName === 'link' && element.getAttribute('rel') === 'import') {
// If this import (polyfilled or not) has its root node available,
// walk it.
const importNode = element.import;
if (visitedImports === undefined) {
visitedImports = new Set();
}
if (importNode instanceof Node && !visitedImports.has(importNode)) {
// Prevent multiple walks of the same import root.
visitedImports.add(importNode);
for (let child = importNode.firstChild; child; child = child.nextSibling) {
walkDeepDescendantElements(child, callback, visitedImports);
}
}
// Ignore descendants of import links to prevent attempting to walk the
// elements created by the HTML Imports polyfill that we just walked
// above.
node = nextSiblingOrAncestorSibling(root, element);
continue;
}
else if (localName === 'template') {
// Ignore descendants of templates. There shouldn't be any descendants
// because they will be moved into `.content` during construction in
// browsers that support template but, in case they exist and are still
// waiting to be moved by a polyfill, they will be ignored.
node = nextSiblingOrAncestorSibling(root, element);
continue;
}
// Walk shadow roots.
const shadowRoot = element.__CE_shadowRoot;
if (shadowRoot) {
for (let child = shadowRoot.firstChild; child; child = child.nextSibling) {
walkDeepDescendantElements(child, callback, visitedImports);
}
}
}
node = nextNode(root, node);
}
}
//# sourceMappingURL=Utilities.js.map