ember-material-icons
Version:
Google Material icons for your ember-cli app
110 lines (86 loc) • 3.61 kB
text/typescript
import { Bounds, ConcreteBounds } from '../bounds';
import { moveNodesBefore, DOMChanges, DOMTreeConstruction } from '../dom/helper';
import { Option } from '@glimmer/util';
interface Wrapper {
depth: number;
before: string;
after: string;
}
let innerHTMLWrapper = {
colgroup: { depth: 2, before: '<table><colgroup>', after: '</colgroup></table>' },
table: { depth: 1, before: '<table>', after: '</table>' },
tbody: { depth: 2, before: '<table><tbody>', after: '</tbody></table>' },
tfoot: { depth: 2, before: '<table><tfoot>', after: '</tfoot></table>' },
thead: { depth: 2, before: '<table><thead>', after: '</thead></table>' },
tr: { depth: 3, before: '<table><tbody><tr>', after: '</tr></tbody></table>' }
};
// Patch: innerHTML Fix
// Browsers: IE9
// Reason: IE9 don't allow us to set innerHTML on col, colgroup, frameset,
// html, style, table, tbody, tfoot, thead, title, tr.
// Fix: Wrap the innerHTML we are about to set in its parents, apply the
// wrapped innerHTML on a div, then move the unwrapped nodes into the
// target position.
export function domChanges(document: Option<Document>, DOMChangesClass: typeof DOMChanges): typeof DOMChanges {
if (!document) return DOMChangesClass;
if (!shouldApplyFix(document)) {
return DOMChangesClass;
}
let div = document.createElement('div');
return class DOMChangesWithInnerHTMLFix extends DOMChangesClass {
insertHTMLBefore(parent: HTMLElement, nextSibling: Node, html: string): Bounds {
if (html === null || html === '') {
return super.insertHTMLBefore(parent, nextSibling, html);
}
let parentTag = parent.tagName.toLowerCase();
let wrapper = innerHTMLWrapper[parentTag];
if(wrapper === undefined) {
return super.insertHTMLBefore(parent, nextSibling, html);
}
return fixInnerHTML(parent, wrapper, div, html, nextSibling);
}
};
}
export function treeConstruction(document: Option<Document>, DOMTreeConstructionClass: typeof DOMTreeConstruction): typeof DOMTreeConstruction {
if (!document) return DOMTreeConstructionClass;
if (!shouldApplyFix(document)) {
return DOMTreeConstructionClass;
}
let div = document.createElement('div');
return class DOMTreeConstructionWithInnerHTMLFix extends DOMTreeConstructionClass {
insertHTMLBefore(parent: HTMLElement, html: string, reference: Node): Bounds {
if (html === null || html === '') {
return super.insertHTMLBefore(parent, html, reference);
}
let parentTag = parent.tagName.toLowerCase();
let wrapper = innerHTMLWrapper[parentTag];
if(wrapper === undefined) {
return super.insertHTMLBefore(parent, html, reference);
}
return fixInnerHTML(parent, wrapper, div, html, reference);
}
};
}
function fixInnerHTML(parent: HTMLElement, wrapper: Wrapper, div: HTMLElement, html: string, reference: Node): Bounds {
let wrappedHtml = wrapper.before + html + wrapper.after;
div.innerHTML = wrappedHtml;
let parentNode: Node = div;
for (let i=0; i<wrapper.depth; i++) {
parentNode = parentNode.childNodes[0];
}
let [first, last] = moveNodesBefore(parentNode, parent, reference);
return new ConcreteBounds(parent, first, last);
}
function shouldApplyFix(document: Document) {
let table = document.createElement('table');
try {
table.innerHTML = '<tbody></tbody>';
} catch (e) {
} finally {
if (table.childNodes.length !== 0) {
// It worked as expected, no fix required
return false;
}
}
return true;
}