-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
77bbacf
commit d036b4d
Showing
16 changed files
with
484 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { ATTRIBUTE_NODE } from 'domconstants/constants'; | ||
|
||
import { escape } from 'html-escaper'; | ||
|
||
import Node from './node.js'; | ||
|
||
import { name, value, ownerElement, ownerDocument } from './symbols.js'; | ||
|
||
export default class Attribute extends Node { | ||
constructor(nodeName, nodeValue = '', owner = null) { | ||
super(ATTRIBUTE_NODE, owner?.[ownerDocument]); | ||
this[ownerElement] = owner; | ||
this[name] = nodeName; | ||
this.value = nodeValue; | ||
} | ||
get name() { | ||
return this[name]; | ||
} | ||
get localName() { | ||
return this[name]; | ||
} | ||
get nodeName() { | ||
return this[name]; | ||
} | ||
get value() { | ||
return this[value]; | ||
} | ||
set value(any) { | ||
this[value] = String(any); | ||
} | ||
get nodeValue() { | ||
return this[value]; | ||
} | ||
toString() { | ||
const { [name]: key, [value]: val } = this; | ||
return val === '' ? key : `${key}="${escape(val)}"`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import Node from './node.js'; | ||
import { nodeName, value } from './symbols.js'; | ||
|
||
export default class CharacterData extends Node { | ||
constructor(type, name, data, owner) { | ||
super(type, owner)[nodeName] = name; | ||
this.data = data; | ||
} | ||
get data() { | ||
return this[value]; | ||
} | ||
set data(any) { | ||
this[value] = String(any); | ||
} | ||
get nodeName() { | ||
return this[nodeName]; | ||
} | ||
get textContent() { | ||
return this.data; | ||
} | ||
set textContent(data) { | ||
this.data = data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { COMMENT_NODE } from 'domconstants/constants'; | ||
|
||
import CharacterData from './character-data.js'; | ||
import { value } from './symbols.js'; | ||
|
||
export default class Comment extends CharacterData { | ||
constructor(data = '', owner = null) { | ||
super(COMMENT_NODE, '#comment', data, owner); | ||
} | ||
toString() { | ||
return `<!--${this[value]}-->`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { DOCUMENT_TYPE_NODE } from 'domconstants/constants'; | ||
|
||
import Node from './node.js'; | ||
import { nodeName } from './symbols.js'; | ||
|
||
export default class DocumentType extends Node { | ||
constructor(name, owner = null) { | ||
super(DOCUMENT_TYPE_NODE, owner)[nodeName] = name; | ||
} | ||
get nodeName() { | ||
return this[nodeName]; | ||
} | ||
get name() { | ||
return this[nodeName]; | ||
} | ||
toString() { | ||
return `<!DOCTYPE ${this[nodeName]}>`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { DOCUMENT_NODE } from 'domconstants/constants'; | ||
|
||
import Element from './element.js'; | ||
import Attribute from './attribute.js'; | ||
import Text from './text.js'; | ||
import Comment from './text.js'; | ||
import DocumentType from './document-type.js'; | ||
import Parent from './parent.js'; | ||
import { setParentNode } from './utils.js'; | ||
|
||
import { childNodes, nodeName, ownerDocument } from './symbols.js'; | ||
|
||
const doctype = Symbol(); | ||
const documentElement = Symbol(); | ||
const head = Symbol(); | ||
const body = Symbol(); | ||
|
||
export default class Document extends Parent { | ||
constructor(type = 'html') { | ||
super(DOCUMENT_NODE, null)[nodeName] = '#document'; | ||
this[doctype] = setParentNode(new DocumentType(type), this); | ||
this[documentElement] = setParentNode(new Element('html', this), this); | ||
this[head] = setParentNode(new Element('head', this), this[documentElement]); | ||
this[body] = setParentNode(new Element('body', this), this[documentElement]); | ||
this[childNodes] = type ? | ||
[this[doctype], this[documentElement]] : | ||
[this[documentElement]] | ||
; | ||
this[documentElement][childNodes] = [this[head], this[body]]; | ||
} | ||
get doctype() { | ||
return this[doctype]; | ||
} | ||
get documentElement() { | ||
return this[documentElement]; | ||
} | ||
get head() { | ||
return this[head]; | ||
} | ||
get body() { | ||
return this[body]; | ||
} | ||
createAttribute(name) { | ||
const attribute = new Attribute(name); | ||
attribute[ownerDocument] = this; | ||
return attribute; | ||
} | ||
createElement(name, options = null) { | ||
const element = new Element(name, this); | ||
if (options?.is) element.setAttribute('is', options.is); | ||
return element; | ||
} | ||
createComment(data) { | ||
return new Comment(data, this); | ||
} | ||
createTextNode(data) { | ||
return new Text(data, this); | ||
} | ||
toString() { | ||
return this[childNodes].join(''); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { ELEMENT_NODE } from 'domconstants/constants'; | ||
import { VOID_ELEMENTS } from 'domconstants/re'; | ||
|
||
import Attribute from './attribute.js'; | ||
import Parent from './parent.js'; | ||
|
||
import namedNodeMap from './named-node-map.js'; | ||
|
||
import { attributes, localName, childNodes, nodeType, ownerElement, parentNode } from './symbols.js'; | ||
|
||
const upperName = ({ [localName]: name }) => name.toUpperCase(); | ||
|
||
const getAttributes = element => ( | ||
element[attributes] || (element[attributes] = new Map) | ||
); | ||
|
||
export default class Element extends Parent { | ||
constructor(name, owner = null) { | ||
super(ELEMENT_NODE, owner)[localName] = name; | ||
this[attributes] = null; | ||
} | ||
get attributes() { | ||
return namedNodeMap(getAttributes(this)); | ||
} | ||
get nodeName() { | ||
return this[localName].toUpperCase(); | ||
} | ||
get tagName() { | ||
return this[localName].toUpperCase(); | ||
} | ||
get innerHTML() { | ||
return this[childNodes].join(''); | ||
} | ||
get outerHTML() { | ||
return this.toString(); | ||
} | ||
getAttribute(name) { | ||
const attribute = this[attributes]?.get(name); | ||
return attribute ? attribute.value : null; | ||
} | ||
hasAttribute(name) { | ||
return !!this[attributes]?.has(name); | ||
} | ||
setAttribute(name, value) { | ||
const attributes = getAttributes(this); | ||
const attribute = attributes.get(name); | ||
if (attribute) | ||
attribute.value = value; | ||
else { | ||
const attribute = new Attribute(name, value, this); | ||
attributes.set(name, attribute); | ||
} | ||
} | ||
removeAttribute(name) { | ||
const attribute = this[attributes]?.get(name); | ||
if (attribute) { | ||
attribute[ownerElement] = null; | ||
this[attributes].delete(name); | ||
} | ||
} | ||
toString() { | ||
const { [localName]: name, [childNodes]: nodes, [attributes]: attrs } = this; | ||
const html = ['<', name]; | ||
if (attrs?.size) { | ||
for (const attribute of attrs.values()) | ||
html.push(' ', attribute); | ||
} | ||
html.push('>', ...nodes); | ||
if (!VOID_ELEMENTS.test(name)) | ||
html.push('</', name, '>'); | ||
return html.join(''); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import Document from './document.js'; | ||
|
||
export { Document }; | ||
|
||
const document = new Document; | ||
const element = document.createElement('test', { is: 'some-test' }); | ||
console.log(element.isConnected, 'connected'); | ||
document.body.appendChild(element); | ||
console.log(element.isConnected, 'connected'); | ||
element.setAttribute('hidden', ''); | ||
console.log(element.attributes.hidden.isConnected, 'connected'); | ||
const p = element.appendChild(document.createElement('p')); | ||
p.appendChild(document.createTextNode('Hello & World')); | ||
const s = element.appendChild(document.createElement('script')); | ||
s.appendChild(document.createTextNode('alert("Hello & World")')); | ||
console.log(document.toString()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const { from } = Array; | ||
const { iterator } = Symbol; | ||
|
||
const asString = (_, i) => String(i); | ||
const isIndex = ({ size }, name) => /^\d+$/.test(name) && name < size; | ||
|
||
const namedNodeMapHandler = { | ||
get: (map, name) => { | ||
if (name === 'length') return map.size; | ||
if (name === iterator) return yieldAttributes.bind(map.values()); | ||
return map.get(name) || ( | ||
isIndex(map, name) ? | ||
[...map.values()][name] : | ||
void 0 | ||
); | ||
}, | ||
|
||
has: (map, name) => ( | ||
name === 'length' || | ||
name === iterator || | ||
map.has(name) || | ||
isIndex(map, name) | ||
), | ||
|
||
ownKeys: map => [ | ||
...from({ length: map.size }, asString), | ||
...map.keys(), | ||
], | ||
}; | ||
|
||
function* yieldAttributes() { | ||
for (const attribute of this) | ||
yield attribute; | ||
} | ||
|
||
export default attributes => new Proxy(attributes, namedNodeMapHandler); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { | ||
ELEMENT_NODE, | ||
ATTRIBUTE_NODE, | ||
TEXT_NODE, | ||
COMMENT_NODE, | ||
DOCUMENT_NODE, | ||
DOCUMENT_FRAGMENT_NODE, | ||
} from 'domconstants/constants'; | ||
|
||
import { nodeType, ownerDocument, parentNode } from './symbols.js'; | ||
import { changeParentNode } from './utils.js'; | ||
|
||
export default class Node { | ||
static { | ||
this.ELEMENT_NODE = ELEMENT_NODE; | ||
this.ATTRIBUTE_NODE = ATTRIBUTE_NODE; | ||
this.TEXT_NODE = TEXT_NODE; | ||
this.COMMENT_NODE = COMMENT_NODE; | ||
this.DOCUMENT_NODE = DOCUMENT_NODE; | ||
this.DOCUMENT_FRAGMENT_NODE = DOCUMENT_FRAGMENT_NODE; | ||
} | ||
constructor(type, owner) { | ||
this[parentNode] = null; | ||
this[nodeType] = type; | ||
this[ownerDocument] = owner; | ||
} | ||
get parentNode() { | ||
return this[parentNode]; | ||
} | ||
get nodeType() { | ||
return this[nodeType]; | ||
} | ||
get ownerDocument() { | ||
return this[ownerDocument]; | ||
} | ||
get isConnected() { | ||
let { [parentNode]: parent, [ownerDocument]: owner } = this; | ||
while (parent && parent !== owner) | ||
parent = parent[parentNode]; | ||
return parent === owner; | ||
} | ||
get parentElement() { | ||
const { [parentNode]: parent } = this; | ||
return parent?.[nodeType] === ELEMENT_NODE ? parent : null; | ||
} | ||
remove() { | ||
changeParentNode(this, null); | ||
} | ||
} |
Oops, something went wrong.