1import { ElementType } from "domelementtype"; 2 3const nodeTypes = new Map<ElementType, number>([ 4 [ElementType.Tag, 1], 5 [ElementType.Script, 1], 6 [ElementType.Style, 1], 7 [ElementType.Directive, 1], 8 [ElementType.Text, 3], 9 [ElementType.CDATA, 4], 10 [ElementType.Comment, 8] 11]); 12 13// This object will be used as the prototype for Nodes when creating a 14// DOM-Level-1-compliant structure. 15export class Node { 16 /** Parent of the node */ 17 parent: NodeWithChildren | null = null; 18 19 /** Previous sibling */ 20 prev: Node | null = null; 21 22 /** Next sibling */ 23 next: Node | null = null; 24 25 /** The start index of the node. Requires `withStartIndices` on the handler to be `true. */ 26 startIndex: number | null = null; 27 28 /** The end index of the node. Requires `withEndIndices` on the handler to be `true. */ 29 endIndex: number | null = null; 30 31 /** 32 * 33 * @param type The type of the node. 34 */ 35 constructor(public type: ElementType) {} 36 37 // Read-only aliases 38 get nodeType(): number { 39 return nodeTypes.get(this.type) || 1; 40 } 41 42 // Read-write aliases for properties 43 get parentNode(): NodeWithChildren | null { 44 return this.parent || null; 45 } 46 47 set parentNode(parent: NodeWithChildren | null) { 48 this.parent = parent; 49 } 50 51 get previousSibling(): Node | null { 52 return this.prev || null; 53 } 54 55 set previousSibling(prev: Node | null) { 56 this.prev = prev; 57 } 58 59 get nextSibling(): Node | null { 60 return this.next || null; 61 } 62 63 set nextSibling(next: Node | null) { 64 this.next = next; 65 } 66} 67 68export class DataNode extends Node { 69 /** 70 * 71 * @param type The type of the node 72 * @param data The content of the data node 73 */ 74 constructor( 75 type: ElementType.Comment | ElementType.Text | ElementType.Directive, 76 public data: string 77 ) { 78 super(type); 79 } 80 81 get nodeValue(): string { 82 return this.data; 83 } 84 85 set nodeValue(data: string) { 86 this.data = data; 87 } 88} 89 90export class ProcessingInstruction extends DataNode { 91 constructor(public name: string, data: string) { 92 super(ElementType.Directive, data); 93 } 94} 95 96export class NodeWithChildren extends Node { 97 /** 98 * 99 * @param type Type of the node. 100 * @param children Children of the node. Only certain node types can have children. 101 */ 102 constructor( 103 type: 104 | ElementType.CDATA 105 | ElementType.Script 106 | ElementType.Style 107 | ElementType.Tag, 108 public children: Node[] 109 ) { 110 super(type); 111 } 112 113 // Aliases 114 get firstChild(): Node | null { 115 return this.children[0] || null; 116 } 117 118 get lastChild(): Node | null { 119 return this.children[this.children.length - 1] || null; 120 } 121 122 get childNodes(): Node[] { 123 return this.children; 124 } 125 126 set childNodes(children: Node[]) { 127 this.children = children; 128 } 129} 130 131export class Element extends NodeWithChildren { 132 /** 133 * 134 * @param name Name of the tag, eg. `div`, `span` 135 * @param attribs Object mapping attribute names to attribute values 136 */ 137 constructor( 138 public name: string, 139 public attribs: { [name: string]: string } 140 ) { 141 super( 142 name === "script" 143 ? ElementType.Script 144 : name === "style" 145 ? ElementType.Style 146 : ElementType.Tag, 147 [] 148 ); 149 this.attribs = attribs; 150 } 151 152 // DOM Level 1 aliases 153 get tagName(): string { 154 return this.name; 155 } 156 157 set tagName(name: string) { 158 this.name = name; 159 } 160} 161