ember-legacy-class-transform
Version:
The default blueprint for ember-cli addons.
177 lines • 7.82 kB
JavaScript
import { ConcreteBounds, SingleNodeBounds } from '../bounds';
import { domChanges as domChangesTableElementFix, treeConstruction as treeConstructionTableElementFix } from '../compat/inner-html-fix';
import { domChanges as domChangesSvgElementFix, treeConstruction as treeConstructionSvgElementFix } from '../compat/svg-inner-html-fix';
import { domChanges as domChangesNodeMergingFix, treeConstruction as treeConstructionNodeMergingFix } from '../compat/text-node-merging-fix';
export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
// http://www.w3.org/TR/html/syntax.html#html-integration-point
const SVG_INTEGRATION_POINTS = { foreignObject: 1, desc: 1, title: 1 };
// http://www.w3.org/TR/html/syntax.html#adjust-svg-attributes
// TODO: Adjust SVG attributes
// http://www.w3.org/TR/html/syntax.html#parsing-main-inforeign
// TODO: Adjust SVG elements
// http://www.w3.org/TR/html/syntax.html#parsing-main-inforeign
export const BLACKLIST_TABLE = Object.create(null);
["b", "big", "blockquote", "body", "br", "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing", "main", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small", "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul", "var"].forEach(tag => BLACKLIST_TABLE[tag] = 1);
const WHITESPACE = /[\t-\r \xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
let doc = typeof document === 'undefined' ? null : document;
export function isWhitespace(string) {
return WHITESPACE.test(string);
}
export function moveNodesBefore(source, target, nextSibling) {
let first = source.firstChild;
let last = null;
let current = first;
while (current) {
last = current;
current = current.nextSibling;
target.insertBefore(last, nextSibling);
}
return [first, last];
}
export class DOMOperations {
constructor(document) {
this.document = document;
this.setupUselessElement();
}
// split into seperate method so that NodeDOMTreeConstruction
// can override it.
setupUselessElement() {
this.uselessElement = this.document.createElement('div');
}
createElement(tag, context) {
let isElementInSVGNamespace, isHTMLIntegrationPoint;
if (context) {
isElementInSVGNamespace = context.namespaceURI === SVG_NAMESPACE || tag === 'svg';
isHTMLIntegrationPoint = SVG_INTEGRATION_POINTS[context.tagName];
} else {
isElementInSVGNamespace = tag === 'svg';
isHTMLIntegrationPoint = false;
}
if (isElementInSVGNamespace && !isHTMLIntegrationPoint) {
// FIXME: This does not properly handle <font> with color, face, or
// size attributes, which is also disallowed by the spec. We should fix
// this.
if (BLACKLIST_TABLE[tag]) {
throw new Error(`Cannot create a ${tag} inside an SVG context`);
}
return this.document.createElementNS(SVG_NAMESPACE, tag);
} else {
return this.document.createElement(tag);
}
}
insertBefore(parent, node, reference) {
parent.insertBefore(node, reference);
}
insertHTMLBefore(_parent, nextSibling, html) {
return insertHTMLBefore(this.uselessElement, _parent, nextSibling, html);
}
createTextNode(text) {
return this.document.createTextNode(text);
}
createComment(data) {
return this.document.createComment(data);
}
}
export var DOM;
(function (DOM) {
class TreeConstruction extends DOMOperations {
createElementNS(namespace, tag) {
return this.document.createElementNS(namespace, tag);
}
setAttribute(element, name, value, namespace) {
if (namespace) {
element.setAttributeNS(namespace, name, value);
} else {
element.setAttribute(name, value);
}
}
}
DOM.TreeConstruction = TreeConstruction;
let appliedTreeContruction = TreeConstruction;
appliedTreeContruction = treeConstructionNodeMergingFix(doc, appliedTreeContruction);
appliedTreeContruction = treeConstructionTableElementFix(doc, appliedTreeContruction);
appliedTreeContruction = treeConstructionSvgElementFix(doc, appliedTreeContruction, SVG_NAMESPACE);
DOM.DOMTreeConstruction = appliedTreeContruction;
})(DOM || (DOM = {}));
export class DOMChanges extends DOMOperations {
constructor(document) {
super(document);
this.document = document;
this.namespace = null;
}
setAttribute(element, name, value) {
element.setAttribute(name, value);
}
setAttributeNS(element, namespace, name, value) {
element.setAttributeNS(namespace, name, value);
}
removeAttribute(element, name) {
element.removeAttribute(name);
}
removeAttributeNS(element, namespace, name) {
element.removeAttributeNS(namespace, name);
}
insertNodeBefore(parent, node, reference) {
if (isDocumentFragment(node)) {
let { firstChild, lastChild } = node;
this.insertBefore(parent, node, reference);
return new ConcreteBounds(parent, firstChild, lastChild);
} else {
this.insertBefore(parent, node, reference);
return new SingleNodeBounds(parent, node);
}
}
insertTextBefore(parent, nextSibling, text) {
let textNode = this.createTextNode(text);
this.insertBefore(parent, textNode, nextSibling);
return textNode;
}
insertBefore(element, node, reference) {
element.insertBefore(node, reference);
}
insertAfter(element, node, reference) {
this.insertBefore(element, node, reference.nextSibling);
}
}
export function insertHTMLBefore(_useless, _parent, _nextSibling, html) {
// TypeScript vendored an old version of the DOM spec where `insertAdjacentHTML`
// only exists on `HTMLElement` but not on `Element`. We actually work with the
// newer version of the DOM API here (and monkey-patch this method in `./compat`
// when we detect older browsers). This is a hack to work around this limitation.
let parent = _parent;
let useless = _useless;
let nextSibling = _nextSibling;
let prev = nextSibling ? nextSibling.previousSibling : parent.lastChild;
let last;
if (html === null || html === '') {
return new ConcreteBounds(parent, null, null);
}
if (nextSibling === null) {
parent.insertAdjacentHTML('beforeend', html);
last = parent.lastChild;
} else if (nextSibling instanceof HTMLElement) {
nextSibling.insertAdjacentHTML('beforebegin', html);
last = nextSibling.previousSibling;
} else {
// Non-element nodes do not support insertAdjacentHTML, so add an
// element and call it on that element. Then remove the element.
//
// This also protects Edge, IE and Firefox w/o the inspector open
// from merging adjacent text nodes. See ./compat/text-node-merging-fix.ts
parent.insertBefore(useless, nextSibling);
useless.insertAdjacentHTML('beforebegin', html);
last = useless.previousSibling;
parent.removeChild(useless);
}
let first = prev ? prev.nextSibling : parent.firstChild;
return new ConcreteBounds(parent, first, last);
}
function isDocumentFragment(node) {
return node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
}
let helper = DOMChanges;
helper = domChangesNodeMergingFix(doc, helper);
helper = domChangesTableElementFix(doc, helper);
helper = domChangesSvgElementFix(doc, helper, SVG_NAMESPACE);
export default helper;
export const DOMTreeConstruction = DOM.DOMTreeConstruction;