ember-material-icons
Version:
Google Material icons for your ember-cli app
108 lines (83 loc) • 3.65 kB
text/typescript
import { Bounds } from '../bounds';
import { DOMChanges, DOMTreeConstruction } from '../dom/helper';
import { Option } from '@glimmer/util';
// Patch: Adjacent text node merging fix
// Browsers: IE, Edge, Firefox w/o inspector open
// Reason: These browsers will merge adjacent text nodes. For exmaple given
// <div>Hello</div> with div.insertAdjacentHTML(' world') browsers
// with proper behavior will populate div.childNodes with two items.
// These browsers will populate it with one merged node instead.
// Fix: Add these nodes to a wrapper element, then iterate the childNodes
// of that wrapper and move the nodes to their target location. Note
// that potential SVG bugs will have been handled before this fix.
// Note that this fix must only apply to the previous text node, as
// the base implementation of `insertHTMLBefore` already handles
// following text nodes correctly.
export function domChanges(document: Option<Document>, DOMChangesClass: typeof DOMChanges): typeof DOMChanges {
if (!document) return DOMChangesClass;
if (!shouldApplyFix(document)) {
return DOMChangesClass;
}
return class DOMChangesWithTextNodeMergingFix extends DOMChangesClass {
private uselessComment: Comment;
constructor(document: Document) {
super(document);
this.uselessComment = document.createComment('');
}
insertHTMLBefore(parent: HTMLElement, nextSibling: Node, html: string): Bounds {
if (html === null) {
return super.insertHTMLBefore(parent, nextSibling, html);
}
let didSetUselessComment = false;
let nextPrevious = nextSibling ? nextSibling.previousSibling : parent.lastChild;
if (nextPrevious && nextPrevious instanceof Text) {
didSetUselessComment = true;
parent.insertBefore(this.uselessComment, nextSibling);
}
let bounds = super.insertHTMLBefore(parent, nextSibling, html);
if (didSetUselessComment) {
parent.removeChild(this.uselessComment);
}
return bounds;
}
};
}
export function treeConstruction(document: Option<Document>, TreeConstructionClass: typeof DOMTreeConstruction): typeof DOMTreeConstruction {
if (!document) return TreeConstructionClass;
if (!shouldApplyFix(document)) {
return TreeConstructionClass;
}
return class TreeConstructionWithTextNodeMergingFix extends TreeConstructionClass {
private uselessComment: Comment;
constructor(document: Document) {
super(document);
this.uselessComment = this.createComment('') as Comment;
}
insertHTMLBefore(parent: HTMLElement, html: string, reference: Node): Bounds {
if (html === null) {
return super.insertHTMLBefore(parent, html, reference);
}
let didSetUselessComment = false;
let nextPrevious = reference ? reference.previousSibling : parent.lastChild;
if (nextPrevious && nextPrevious instanceof Text) {
didSetUselessComment = true;
parent.insertBefore(this.uselessComment, reference);
}
let bounds = super.insertHTMLBefore(parent, html, reference);
if (didSetUselessComment) {
parent.removeChild(this.uselessComment);
}
return bounds;
}
};
}
function shouldApplyFix(document: Document) {
let mergingTextDiv: HTMLDivElement = document.createElement('div');
mergingTextDiv.innerHTML = 'first';
mergingTextDiv.insertAdjacentHTML('beforeEnd', 'second');
if (mergingTextDiv.childNodes.length === 2) {
// It worked as expected, no fix required
return false;
}
return true;
}