als-document 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser-tests/node.js +9 -9
- package/browser-tests/parser.js +21 -21
- package/docs/#.md +7 -10
- package/docs/2.node.md +22 -1
- package/docs/5. Document.md +8 -25
- package/document.js +6 -6
- package/index.js +6 -6
- package/index.mjs +6 -6
- package/lib/node/document.js +39 -7
- package/lib/node/find.js +1 -0
- package/lib/node/node.js +19 -5
- package/lib/node/single-node.js +2 -2
- package/lib/parse/cache.js +1 -1
- package/lib/query/check-element.js +2 -2
- package/package.json +1 -1
- package/readme.md +31 -29
- package/tests/class-list.test.js +67 -0
- package/tests/document.test.js +90 -0
- package/tests/node.test.js +9 -9
- package/tests/parser.test.js +19 -19
package/document.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const alsDocument = (function(){
|
|
2
2
|
class Query{
|
|
3
3
|
static get(query){
|
|
4
4
|
let q=new Query(query)
|
|
5
5
|
return q.selectors
|
|
6
6
|
}
|
|
7
7
|
constructor(query){
|
|
8
8
|
this.query=query
|
|
9
9
|
this.selectors=[]
|
|
10
10
|
this.stringValues=[];
|
|
11
11
|
this.parseSelectors(query.split(','))
|
|
12
12
|
}
|
|
13
13
|
parseSelectors(selectors){
|
|
14
14
|
selectors.forEach(selector=>{
|
|
15
15
|
let originalSelector=selector.trim()
|
|
16
16
|
selector=this.removeSpaces(selector)
|
|
17
17
|
this.stringValues=[]
|
|
18
18
|
selector=selector.replace(/\[.*?\]/g,(value)=>{
|
|
19
19
|
this.stringValues.push(value)
|
|
20
20
|
return `[${this.stringValues.length-1}]`
|
|
21
21
|
})
|
|
22
22
|
let [element,ancestors]=this.splitAndCutLast(selector,' ')
|
|
23
23
|
element=this.getFamily(element)
|
|
24
24
|
if (ancestors.length>0)
|
|
25
25
|
element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
|
|
26
26
|
element.group=originalSelector
|
|
27
27
|
this.selectors.push(element)
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
splitAndCutLast(string,splitBy){
|
|
31
31
|
const array=string.split(splitBy);
|
|
32
32
|
const last=array.pop();
|
|
33
33
|
return [last,array];
|
|
34
34
|
}
|
|
35
35
|
getFamily(group,element,prev,prevAny,sign){
|
|
36
36
|
if (group.match(/\~|\+/)!==null){
|
|
37
37
|
let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
|
|
38
38
|
let signs=group.replace(last,'')
|
|
39
39
|
prevBrothers.forEach(el=>signs=signs.replace(el,''))
|
|
40
40
|
signs=signs.match(/\~|\+/g)
|
|
41
41
|
if (signs.length==1){
|
|
42
42
|
sign=signs[0]
|
|
43
43
|
} else if (signs.length>1){
|
|
44
44
|
sign=signs.splice(signs.length-1,signs.length-1)[0]
|
|
45
45
|
prevBrothers[0]=prevBrothers.map((b,i)=>{
|
|
46
46
|
if (i< prevBrothers.length-1) b+=signs[i]
|
|
47
47
|
return b
|
|
48
48
|
}).join('')
|
|
49
49
|
prevBrothers[0]=this.getFamily(prevBrothers[0])
|
|
50
50
|
}
|
|
51
51
|
if (sign=='~') prevAny=prevBrothers[0]
|
|
52
52
|
else if (sign=='+') prev=prevBrothers[0]
|
|
53
53
|
element=last
|
|
54
54
|
} else element=group
|
|
55
55
|
let family
|
|
56
56
|
if (prev || prevAny){
|
|
57
57
|
family=this.getParents(element)
|
|
58
58
|
if (prev) family.prev=this.getParents(prev)
|
|
59
59
|
if (prevAny) family.prevAny=this.getParents(prevAny)
|
|
60
60
|
} else family=this.getParents(element)
|
|
61
61
|
if (family.query!==group) family.group=group
|
|
62
62
|
return family
|
|
63
63
|
}
|
|
64
64
|
getParents(selector){
|
|
65
65
|
if (typeof selector=='string'){
|
|
66
66
|
let [element,parents]=this.splitAndCutLast(selector,'>')
|
|
67
67
|
element=this.buildElement(element)
|
|
68
68
|
parents=parents.map(parent=>this.buildElement(parent))
|
|
69
69
|
if (parents.length>0) element.parents=parents
|
|
70
70
|
return element
|
|
71
71
|
} else return selector
|
|
72
72
|
}
|
|
73
73
|
buildElement(element,id=null,tag=null,classList=[]){
|
|
74
74
|
let query=element
|
|
75
75
|
element=element.replace(/\#(\w-?)*/,$id=>{
|
|
76
76
|
id=$id.replace(/^\#/,''); return ''
|
|
77
77
|
})
|
|
78
78
|
element=element.replace(/\.(\w-?)*/,$class=>{
|
|
79
79
|
classList.push($class.replace(/^\./,'')); return ''
|
|
80
80
|
})
|
|
81
81
|
element=element.replace(/(\w\:?-?)*/,$tag=>{
|
|
82
82
|
tag=$tag=='' ? null : $tag; return ''
|
|
83
83
|
})
|
|
84
84
|
let attribs=this.getAttributes(element)
|
|
85
85
|
element={ query }
|
|
86
86
|
if (id) element.id=id
|
|
87
87
|
if (tag) element.tag=tag
|
|
88
88
|
if (classList.length>0) element.classList=classList
|
|
89
89
|
if (attribs.length>0) element.attribs=attribs
|
|
90
90
|
return element
|
|
91
91
|
}
|
|
92
92
|
getAttributes(element){
|
|
93
93
|
let attribs=this.stringValues.filter((value,index)=>{
|
|
94
94
|
let searchValue=`[${index}]`
|
|
95
95
|
if (element.match(searchValue)) return true
|
|
96
96
|
else return false
|
|
97
97
|
})
|
|
98
98
|
attribs=attribs.map(attrib=>{
|
|
99
99
|
let query=attrib
|
|
100
100
|
attrib=attrib.replace('[','').replace(']','')
|
|
101
101
|
let [name,...values]=attrib.split('=')
|
|
102
102
|
const value=values.join('=').trim().replace(/^\"/,'').replace(/\"$/,'')
|
|
103
103
|
let sign
|
|
104
104
|
attrib={query,name}
|
|
105
105
|
if(value){
|
|
106
106
|
sign='='
|
|
107
107
|
attrib.name=attrib.name.replace(/[\~\|\^\$\*]$/,(match=>{
|
|
108
108
|
sign=match+sign
|
|
109
109
|
return ''
|
|
110
110
|
}))
|
|
111
111
|
attrib.value=value
|
|
112
112
|
}
|
|
113
113
|
if (sign){
|
|
114
114
|
attrib.sign=sign
|
|
115
115
|
attrib.check=this.getAttribFn(sign).bind(attrib)
|
|
116
116
|
}
|
|
117
117
|
return attrib
|
|
118
118
|
});
|
|
119
119
|
return attribs
|
|
120
120
|
}
|
|
121
121
|
getAttribFn(sign){
|
|
122
122
|
if (sign=='=') return function (value){ return value===this.value }
|
|
123
123
|
if (sign=='*=') return function (value){ return value.includes(this.value) }
|
|
124
124
|
if (sign=='^=') return function (value){ return value.startsWith(this.value) }
|
|
125
125
|
if (sign=='$=') return function (value){ return value.endsWith(this.value) }
|
|
126
126
|
if (sign=='|=') return function (value){
|
|
127
127
|
return value.trim().split(' ').length==1
|
|
128
128
|
&& (value.startsWith(this.value) || value.startsWith(this.value+'-'))
|
|
129
129
|
? true : false
|
|
130
130
|
}
|
|
131
131
|
if (sign=='~=') return function (value){
|
|
132
132
|
return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
removeSpaces(selector){
|
|
136
136
|
selector=selector.replace(/\s{2}/g,' ')
|
|
137
137
|
selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
|
|
138
138
|
selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
|
|
139
139
|
return selector
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
-
function checkElement(el,selector){
|
|
143
142
|
if(selector==undefined) return true
|
|
144
143
|
if(el==null) return false
|
|
145
144
|
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
146
145
|
if(typeof el==='string') return false
|
|
147
146
|
if(id!==undefined && el.id===null) return false
|
|
148
147
|
if(id && id!==el.id) return false
|
|
149
148
|
if(tag && el.tagName===undefined) return false
|
|
150
149
|
else if(tag && tag!==el.tagName) return false
|
|
151
150
|
const clas=el.attributes.class
|
|
152
151
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
153
152
|
else if(classList!==undefined){
|
|
154
153
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
155
154
|
}
|
|
156
155
|
if(checkattributes(attributes,el)===false) return false
|
|
157
156
|
if(checkElement(el.prev,prev)===false) return false
|
|
158
157
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
159
158
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
160
159
|
if(el.parent){
|
|
161
160
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
162
161
|
}
|
|
163
162
|
return true
|
|
163
|
+
function checkElement(el,selector){
|
|
164
164
|
if(selector==undefined) return true
|
|
165
165
|
if(el==null) return false
|
|
166
166
|
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
167
167
|
if(typeof el==='string') return false
|
|
168
168
|
if(id!==undefined && el.id===null) return false
|
|
169
169
|
if(id && id!==el.id) return false
|
|
170
170
|
if(tag && el._tagName===undefined) return false
|
|
171
171
|
else if(tag && tag!==el._tagName) return false
|
|
172
172
|
const clas=el.attributes.class
|
|
173
173
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
174
174
|
else if(classList!==undefined){
|
|
175
175
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
176
176
|
}
|
|
177
177
|
if(checkattributes(attributes,el)===false) return false
|
|
178
178
|
if(checkElement(el.prev,prev)===false) return false
|
|
179
179
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
180
180
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
181
181
|
if(el.parent){
|
|
182
182
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
183
183
|
}
|
|
184
184
|
return true
|
|
185
185
|
}
|
|
186
186
|
function checkattributes(attributes=[],el){
|
|
187
187
|
let elattributes=el.attributes
|
|
188
188
|
let names=Object.keys(elattributes)
|
|
189
189
|
let passedTests=0
|
|
190
190
|
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
191
191
|
let{name,value,check}=attributes[i]
|
|
192
192
|
if(name=='inner' && value!==undefined && check && el.inner){
|
|
193
193
|
if(check(el.inner)) passedTests++
|
|
194
194
|
}
|
|
195
195
|
if(!names.includes(name)) continue
|
|
196
196
|
else if(value==undefined) passedTests++
|
|
197
197
|
else if(value && elattributes[name]){
|
|
198
198
|
if(check(elattributes[name])==false) continue
|
|
199
199
|
else passedTests++
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
if(passedTests==attributes.length) return true
|
|
203
203
|
else return false
|
|
204
204
|
}
|
|
205
205
|
function checkPrevAny(children=[],index,prevAny){
|
|
206
206
|
let size=children.length
|
|
207
207
|
if((size==0 || index==0) && prevAny) return false
|
|
208
208
|
for(let i=index; i>=0; i--){
|
|
209
209
|
if(checkElement(children[i],prevAny)) return true
|
|
210
210
|
}
|
|
211
211
|
return false
|
|
212
212
|
}
|
|
213
213
|
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
214
214
|
let count=0
|
|
215
215
|
if(selectorAncestors.length==0) return true
|
|
216
216
|
let endIndex=ancestors.length-1
|
|
217
217
|
let selectorIndex=selectorAncestors.length-1
|
|
218
218
|
while(selectorIndex>=0){
|
|
219
219
|
for(let i=endIndex; i>=0; i--){
|
|
220
220
|
endIndex=i-1
|
|
221
221
|
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
222
222
|
count++
|
|
223
223
|
break
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
selectorIndex--
|
|
227
227
|
}
|
|
228
228
|
if(count==selectorAncestors.length) return true
|
|
229
229
|
else return false
|
|
@@ -11,7 +11,7 @@ const getDataName=prop=>'data-'+prop.toLowerCase()
|
|
|
11
11
|
function getDataset(element){
|
|
12
12
|
return new Proxy(element.attributes,{
|
|
13
13
|
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
14
14
|
const dataAttr=getDataName(prop)
|
|
15
15
|
if (dataAttr in target){
|
|
16
16
|
delete target[dataAttr];
|
|
17
17
|
return true;
|
|
18
18
|
}
|
|
19
19
|
return false;
|
|
20
20
|
}
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function find(selectors,element,collection,first=false,firstTime=true){
|
|
25
24
|
for(let selector of selectors){
|
|
26
25
|
if(checkElement(element,selector)) collection.add(element)
|
|
27
26
|
}
|
|
28
27
|
if(element.children)
|
|
29
28
|
element.children.forEach(child=>{
|
|
30
29
|
if(first && collection.size>0) return
|
|
31
30
|
find(selectors,child,collection,first,false)
|
|
32
31
|
})
|
|
33
32
|
return firstTime ? [...collection] : collection
|
|
33
|
+
function find(selectors,element,collection,first=false,firstTime=true){
|
|
34
34
|
if(!firstTime)
|
|
35
35
|
for(let selector of selectors){
|
|
36
36
|
if(checkElement(element,selector)) collection.add(element)
|
|
37
37
|
}
|
|
38
38
|
if(element.children)
|
|
39
39
|
element.children.forEach(child=>{
|
|
40
40
|
if(first && collection.size>0) return
|
|
41
41
|
find(selectors,child,collection,first,false)
|
|
42
42
|
})
|
|
43
43
|
return firstTime ? [...collection] : collection
|
|
44
44
|
}
|
|
45
45
|
class TextNode{
|
|
46
46
|
constructor(data){
|
|
47
47
|
this.nodeName='#text';
|
|
48
48
|
this.parent=null;
|
|
49
49
|
this.textContent=data;
|
|
50
50
|
}
|
|
51
51
|
get nodeValue(){return this.textContent}
|
|
52
52
|
get parentNode(){return this.parent}
|
|
53
53
|
}
|
|
@@ -22,21 +22,21 @@ function buildStyle(attributes){
|
|
|
22
22
|
const styles=attributes.style || "";
|
|
23
23
|
con
|
|
24
24
|
class NodeClassList{
|
|
25
25
|
constructor(node){ this.node=node }
|
|
26
26
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
27
27
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
28
28
|
contains(className){ return this.classes.includes(className) }
|
|
29
29
|
add(className){
|
|
30
30
|
const currentClasses=this.classes;
|
|
31
31
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
32
32
|
}
|
|
33
33
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
34
34
|
toggle(className){
|
|
35
35
|
if (this.classes.includes(className)) this.remove(className);
|
|
36
36
|
else this.add(className);
|
|
37
37
|
}
|
|
38
38
|
replace(oldClass,newClass){
|
|
39
39
|
if (this.classes.includes(oldClass)){
|
|
40
40
|
this.remove(oldClass);
|
|
41
41
|
this.add(newClass);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
function insertBefore(arr,index,newItem){
|
|
46
46
|
const existingIndex=arr.indexOf(newItem);
|
|
47
47
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
48
48
|
arr.splice(index,0,newItem);
|
|
49
|
-
}
|
|
50
49
|
class Node{
|
|
51
50
|
constructor(tagName,attributes={},parent=null){
|
|
52
51
|
this.isSingle=false;
|
|
53
52
|
this.tagName=tagName;
|
|
54
53
|
this.attributes=attributes;
|
|
55
54
|
this.childNodes=[];
|
|
56
55
|
if (parent!==null) parent.childNodes.push(this)
|
|
57
56
|
this.parent=parent;
|
|
58
57
|
this._classList=null;
|
|
59
58
|
this.__style=null;
|
|
60
59
|
this._dataset=null
|
|
61
60
|
}
|
|
62
61
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
63
62
|
set id(newValue){ this.attributes.id=newValue; }
|
|
64
63
|
get className(){ return this.attributes.class || null }
|
|
65
64
|
get parentNode(){ return this.parent }
|
|
66
65
|
get ancestors(){
|
|
67
66
|
if (!this.parent) return []
|
|
68
67
|
const ancestors=[]
|
|
69
68
|
let element=this.parent
|
|
70
69
|
while (element.tagName!=='ROOT'){
|
|
71
70
|
ancestors.push(element)
|
|
72
71
|
element=element.parent
|
|
73
72
|
}
|
|
74
73
|
return ancestors.reverse()
|
|
75
74
|
}
|
|
76
75
|
get childNodeIndex(){
|
|
77
76
|
if (!this.parent) return null
|
|
78
77
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
79
78
|
}
|
|
80
79
|
get childIndex(){
|
|
81
80
|
if (!this.parent) return null
|
|
82
81
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
83
82
|
}
|
|
84
83
|
get previousElementSibling(){ return this.prev }
|
|
85
84
|
get prev(){
|
|
86
85
|
if (!this.childIndex) return null
|
|
87
86
|
return this.parent.children[this.childIndex-1]
|
|
88
87
|
}
|
|
89
88
|
get nextElementSibling(){ return this.next }
|
|
90
89
|
get next(){
|
|
91
90
|
if (!this.childIndex) return null
|
|
92
91
|
return this.parent.children[this.childIndex+1] || null
|
|
93
92
|
}
|
|
94
93
|
get dataset(){
|
|
95
94
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
96
95
|
return this._dataset;
|
|
97
96
|
}
|
|
98
97
|
get classList(){
|
|
99
98
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
100
99
|
return this._classList;
|
|
101
100
|
}
|
|
102
101
|
get style(){
|
|
103
102
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
104
103
|
return this.__style
|
|
105
104
|
}
|
|
106
105
|
get outerHTML(){
|
|
107
106
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
108
107
|
return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
|
|
109
108
|
}
|
|
110
109
|
getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
|
|
111
110
|
setAttribute(attrName,value=''){ this.attributes[attrName]=value }
|
|
112
111
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
113
112
|
remove(){
|
|
114
113
|
if (!this.parent) return
|
|
115
114
|
const index=this.childNodeIndex;
|
|
116
115
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
117
116
|
}
|
|
118
117
|
get innerHTML(){
|
|
119
118
|
return this.childNodes.map(child=>{
|
|
120
119
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
121
120
|
else if (child instanceof TextNode) return child.textContent;
|
|
122
121
|
else return child
|
|
123
122
|
}).join("");
|
|
124
123
|
}
|
|
125
124
|
$$(query){ return this.querySelectorAll(query) }
|
|
126
125
|
querySelectorAll(query){
|
|
127
126
|
const selectors=Query.get(query)
|
|
128
127
|
return find(selectors,this,new Set())
|
|
129
128
|
}
|
|
130
129
|
$(query){ return this.querySelector(query) }
|
|
131
130
|
querySelector(query){
|
|
132
131
|
const selectors=Query.get(query)
|
|
133
132
|
return find(selectors,this,new Set(),true)[0] || null
|
|
134
133
|
}
|
|
135
134
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
136
135
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
137
136
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
138
137
|
get children(){
|
|
139
138
|
return this.childNodes.filter(child=>{
|
|
140
139
|
if (!(child instanceof Node)) return false
|
|
141
140
|
if (child.tagName==='#comment') return false
|
|
142
141
|
return true
|
|
143
142
|
});
|
|
144
143
|
}
|
|
145
144
|
insertAdjacentElement(position,newElement){
|
|
146
145
|
if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
147
146
|
const pos=position.toLowerCase();
|
|
148
147
|
if(newElement.parentNode){
|
|
149
148
|
newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
|
|
150
149
|
}
|
|
151
150
|
if (pos==='afterbegin' || pos==='beforeend'){
|
|
152
151
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
153
152
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
154
153
|
newElement.parent=this
|
|
155
154
|
return newElement
|
|
156
155
|
}
|
|
157
156
|
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
158
157
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
159
158
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
160
159
|
newElement.parent=this.parent
|
|
161
160
|
return newElement
|
|
162
161
|
}
|
|
163
162
|
insertAdjacentHTML(position,html){
|
|
164
163
|
const newNode=parseHTML(html);
|
|
165
164
|
newNode.childNodes.forEach(node=>{
|
|
166
165
|
this.insertAdjacentElement(position,node);
|
|
167
166
|
});
|
|
168
167
|
return newNode
|
|
169
168
|
}
|
|
170
169
|
insertAdjacentText(position,text){
|
|
171
170
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
172
171
|
}
|
|
173
172
|
insert(position,element){
|
|
174
173
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
175
174
|
if (positions[position]) position=positions[position]
|
|
176
175
|
if (typeof element==='string'){
|
|
177
176
|
element=element.trim()
|
|
178
177
|
if (element.startsWith('<') && element.endsWith('>')){
|
|
179
178
|
return this.insertAdjacentHTML(position,element)
|
|
180
179
|
}
|
|
181
180
|
return this.insertAdjacentText(position,element)
|
|
182
181
|
}
|
|
183
182
|
return this.insertAdjacentElement(position,element)
|
|
184
183
|
}
|
|
185
184
|
set innerHTML(html){
|
|
186
185
|
this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
|
|
187
186
|
}
|
|
188
187
|
set outerHTML(html){
|
|
189
188
|
const parsed=parseHTML(html);
|
|
190
189
|
if (!this.parent) throw new Error('element has no parent node')
|
|
191
190
|
const index=this.childIndex
|
|
192
191
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
193
192
|
}
|
|
194
193
|
appendChild(newChild){
|
|
195
194
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
196
195
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
197
196
|
} else if (typeof newChild==='string') newChild=new TextNode(newChild)
|
|
198
197
|
else return newChild
|
|
199
198
|
this.childNodes.push(newChild);
|
|
200
199
|
newChild.parent=this;
|
|
201
200
|
return newChild;
|
|
202
201
|
}
|
|
203
202
|
get textContent(){
|
|
204
203
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
205
204
|
return this.childNodes.map(child=>{
|
|
206
205
|
if (child instanceof SingleNode) return ''
|
|
207
206
|
if (child instanceof TextNode) return child.nodeValue
|
|
208
207
|
if (child instanceof Node) return child.textContent;
|
|
209
208
|
else return child;
|
|
210
209
|
}).join('');
|
|
211
210
|
}
|
|
212
211
|
set textContent(value){
|
|
213
212
|
this.childNodes=[];
|
|
214
213
|
if (value!==null && value!==undefined){
|
|
215
214
|
this.childNodes.push(value.toString());
|
|
216
215
|
}
|
|
217
216
|
}
|
|
217
|
+
}
|
|
218
218
|
class Node{
|
|
219
219
|
constructor(tagName,attributes={},parent=null){
|
|
220
220
|
this.isSingle=false;
|
|
221
221
|
this._tagName=tagName.toLowerCase();
|
|
222
222
|
this.tagName=tagName.toUpperCase();
|
|
223
223
|
this.attributes=attributes;
|
|
224
224
|
this.childNodes=[];
|
|
225
225
|
if (parent!==null) parent.childNodes.push(this)
|
|
226
226
|
this.parent=parent;
|
|
227
227
|
this._classList=null;
|
|
228
228
|
this.__style=null;
|
|
229
229
|
this._dataset=null
|
|
230
230
|
}
|
|
231
231
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
232
232
|
set id(newValue){ this.attributes.id=newValue; }
|
|
233
233
|
get className(){ return this.attributes.class || null }
|
|
234
234
|
get parentNode(){ return this.parent }
|
|
235
235
|
get ancestors(){
|
|
236
236
|
if (!this.parent) return []
|
|
237
237
|
const ancestors=[]
|
|
238
238
|
let element=this.parent
|
|
239
239
|
while (element.tagName!=='ROOT'){
|
|
240
240
|
ancestors.push(element)
|
|
241
241
|
element=element.parent
|
|
242
242
|
}
|
|
243
243
|
return ancestors.reverse()
|
|
244
244
|
}
|
|
245
245
|
get childNodeIndex(){
|
|
246
246
|
if (!this.parent) return null
|
|
247
247
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
248
248
|
}
|
|
249
249
|
get childIndex(){
|
|
250
250
|
if (!this.parent) return null
|
|
251
251
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
252
252
|
}
|
|
253
253
|
get previousElementSibling(){ return this.prev }
|
|
254
254
|
get prev(){
|
|
255
255
|
if (this.childIndex===null) return null
|
|
256
256
|
return this.parent.children[this.childIndex-1]
|
|
257
257
|
}
|
|
258
258
|
get nextElementSibling(){ return this.next }
|
|
259
259
|
get next(){
|
|
260
260
|
if (this.childIndex===null) return null
|
|
261
261
|
return this.parent.children[this.childIndex+1] || null
|
|
262
262
|
}
|
|
263
263
|
get dataset(){
|
|
264
264
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
265
265
|
return this._dataset;
|
|
266
266
|
}
|
|
267
267
|
get classList(){
|
|
268
268
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
269
269
|
return this._classList;
|
|
270
270
|
}
|
|
271
271
|
get style(){
|
|
272
272
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
273
273
|
return this.__style
|
|
274
274
|
}
|
|
275
275
|
get outerHTML(){
|
|
276
276
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
277
277
|
return `<${this._tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this._tagName}>`;
|
|
278
278
|
}
|
|
279
279
|
getAttribute(attrName){ return this.attributes[attrName]!==undefined ? this.attributes[attrName] : null }
|
|
280
280
|
setAttribute(attrName,value=''){ this.attributes[attrName]=value }
|
|
281
281
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
282
282
|
remove(){
|
|
283
283
|
if (!this.parent) return
|
|
284
284
|
const index=this.childNodeIndex;
|
|
285
285
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
286
286
|
}
|
|
287
287
|
get innerHTML(){
|
|
288
288
|
return this.childNodes.map(child=>{
|
|
289
289
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
290
290
|
else if (child instanceof TextNode) return child.textContent;
|
|
291
291
|
else return child
|
|
292
292
|
}).join("");
|
|
293
293
|
}
|
|
294
294
|
get innerText(){
|
|
295
295
|
return this.childNodes.map(child=>{
|
|
296
296
|
if (child instanceof Node || child instanceof SingleNode) return child.innerText;
|
|
297
297
|
else if (child instanceof TextNode) return child.textContent;
|
|
298
298
|
else return child
|
|
299
299
|
}).join("");
|
|
300
300
|
}
|
|
301
301
|
set innerText(value){
|
|
302
302
|
this.childNodes=[new TextNode(value)]
|
|
303
303
|
return value
|
|
304
304
|
}
|
|
305
305
|
$$(query){ return this.querySelectorAll(query) }
|
|
306
306
|
querySelectorAll(query){
|
|
307
307
|
const selectors=Query.get(query)
|
|
308
308
|
return find(selectors,this,new Set())
|
|
309
309
|
}
|
|
310
310
|
$(query){ return this.querySelector(query) }
|
|
311
311
|
querySelector(query){
|
|
312
312
|
const selectors=Query.get(query)
|
|
313
313
|
return find(selectors,this,new Set(),true)[0] || null
|
|
314
314
|
}
|
|
315
315
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
316
316
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
317
317
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
318
318
|
get children(){
|
|
319
319
|
return this.childNodes.filter(child=>{
|
|
320
320
|
if (!(child instanceof Node)) return false
|
|
321
321
|
if (child._tagName==='#comment') return false
|
|
322
322
|
return true
|
|
323
323
|
});
|
|
324
324
|
}
|
|
325
325
|
insertAdjacentElement(position,newElement){
|
|
326
326
|
if (newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
327
327
|
const pos=position.toLowerCase();
|
|
328
328
|
if(newElement.parentNode){
|
|
329
329
|
newElement.parentNode.childNodes=newElement.parentNode.childNodes.filter(el=>el!==newElement)
|
|
330
330
|
}
|
|
331
331
|
if (pos==='afterbegin' || pos==='beforeend'){
|
|
332
332
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
333
333
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
334
334
|
newElement.parent=this
|
|
335
335
|
return newElement
|
|
336
336
|
}
|
|
337
337
|
if (!this.parent) throw new Error("Can't insert element to element without parent")
|
|
338
338
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
339
339
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
340
340
|
newElement.parent=this.parent
|
|
341
341
|
return newElement
|
|
342
342
|
}
|
|
343
343
|
insertAdjacentHTML(position,html){
|
|
344
344
|
const newNode=parseHTML(html);
|
|
345
345
|
newNode.childNodes.forEach(node=>{
|
|
346
346
|
this.insertAdjacentElement(position,node);
|
|
347
347
|
});
|
|
348
348
|
return newNode
|
|
349
349
|
}
|
|
350
350
|
insertAdjacentText(position,text){
|
|
351
351
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
352
352
|
}
|
|
353
353
|
insert(position,element){
|
|
354
354
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
355
355
|
if (positions[position]) position=positions[position]
|
|
356
356
|
if (typeof element==='string'){
|
|
357
357
|
element=element.trim()
|
|
358
358
|
if (element.startsWith('<') && element.endsWith('>')){
|
|
359
359
|
return this.insertAdjacentHTML(position,element)
|
|
360
360
|
}
|
|
361
361
|
return this.insertAdjacentText(position,element)
|
|
362
362
|
}
|
|
363
363
|
return this.insertAdjacentElement(position,element)
|
|
364
364
|
}
|
|
365
365
|
set innerHTML(html){
|
|
366
366
|
this.childNodes=html.trim().startsWith('<') ? parseHTML(html).childNodes : [html]
|
|
367
367
|
this.children.forEach(child=>child.parent=this);
|
|
368
368
|
}
|
|
369
369
|
set outerHTML(html){
|
|
370
370
|
const parsed=parseHTML(html);
|
|
371
371
|
if (!this.parent) throw new Error('element has no parent node')
|
|
372
372
|
const index=this.childIndex
|
|
373
373
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
374
374
|
}
|
|
375
375
|
appendChild(newChild){
|
|
376
376
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
377
377
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
378
378
|
} else if (typeof newChild==='string') newChild=new TextNode(newChild)
|
|
379
379
|
else return newChild
|
|
380
380
|
this.childNodes.push(newChild);
|
|
381
381
|
newChild.parent=this;
|
|
382
382
|
return newChild;
|
|
383
383
|
}
|
|
384
384
|
get textContent(){
|
|
385
385
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
386
386
|
return this.childNodes.map(child=>{
|
|
387
387
|
if (child instanceof SingleNode) return ''
|
|
388
388
|
if (child instanceof TextNode) return child.nodeValue
|
|
389
389
|
if (child instanceof Node) return child.textContent;
|
|
390
390
|
else return child;
|
|
391
391
|
}).join('');
|
|
392
392
|
}
|
|
393
393
|
set textContent(value){
|
|
394
394
|
this.childNodes=[];
|
|
395
395
|
if (value!==null && value!==undefined){
|
|
396
396
|
this.childNodes.push(value.toString());
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
|
-
class SingleNode extends Node{
|
|
401
400
|
constructor(tagName,attributes={},parent=null){
|
|
402
401
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
403
402
|
super(tagName,attributes,parent);
|
|
404
403
|
this.isSingle=true
|
|
405
404
|
}
|
|
406
405
|
get outerHTML(){
|
|
407
406
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
408
407
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
409
408
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
410
409
|
}
|
|
411
410
|
get innerHTML(){ return ""; }
|
|
412
411
|
set innerHTML(_){ }
|
|
413
412
|
$(_){return null}
|
|
414
413
|
$$(_){return []}
|
|
415
414
|
querySelectorAll(_){ return []; }
|
|
416
415
|
querySelector(_){ return null; }
|
|
417
416
|
getElementsByClassName(_){ return []; }
|
|
418
417
|
getElementsByTagName(_){ return []; }
|
|
419
418
|
getElementById(_){ return null; }
|
|
420
419
|
get children(){ return []; }
|
|
421
420
|
insertAdjacentElement(position,newElement){
|
|
422
421
|
if(position==='afterbegin') position='beforebegin'
|
|
423
422
|
if(position==='beforeend') position='afterend'
|
|
424
423
|
super.insertAdjacentElement(position,newElement)
|
|
425
424
|
}
|
|
426
425
|
insertAdjacentHTML(position,html){
|
|
427
426
|
if(position==='afterbegin') position='beforebegin'
|
|
428
427
|
if(position==='beforeend') position='afterend'
|
|
429
428
|
super.insertAdjacentHTML(position,html)
|
|
430
429
|
}
|
|
431
430
|
insertAdjacentText(position,text){
|
|
432
431
|
if(position==='afterbegin') position='beforebegin'
|
|
433
432
|
if(position==='beforeend') position='afterend'
|
|
434
433
|
super.insertAdjacentText(position,text)
|
|
435
434
|
}
|
|
436
435
|
insert(position,element){
|
|
437
436
|
if(position===1) position=0
|
|
438
437
|
if(position===2) position=3
|
|
439
438
|
super.insert(position,element)
|
|
440
439
|
}
|
|
441
440
|
appendChild(_){ }
|
|
442
441
|
get textContent(){ return ""; }
|
|
443
442
|
set textContent(_){ }
|
|
443
|
+
class SingleNode extends Node{
|
|
444
444
|
constructor(tagName,attributes={},parent=null){
|
|
445
445
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
446
446
|
super(tagName,attributes,parent);
|
|
447
447
|
this.isSingle=true
|
|
448
448
|
}
|
|
449
449
|
get outerHTML(){
|
|
450
450
|
if (this._tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
451
451
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
452
452
|
return `<${this._tagName} ${attrs}${this._tagName==='?xml' ? '?' : ''}>`;
|
|
453
453
|
}
|
|
454
454
|
get innerHTML(){ return ""; }
|
|
455
455
|
set innerHTML(_){ }
|
|
456
456
|
$(_){return null}
|
|
457
457
|
$$(_){return []}
|
|
458
458
|
querySelectorAll(_){ return []; }
|
|
459
459
|
querySelector(_){ return null; }
|
|
460
460
|
getElementsByClassName(_){ return []; }
|
|
461
461
|
getElementsByTagName(_){ return []; }
|
|
462
462
|
getElementById(_){ return null; }
|
|
463
463
|
get children(){ return []; }
|
|
464
464
|
insertAdjacentElement(position,newElement){
|
|
465
465
|
if(position==='afterbegin') position='beforebegin'
|
|
466
466
|
if(position==='beforeend') position='afterend'
|
|
467
467
|
super.insertAdjacentElement(position,newElement)
|
|
468
468
|
}
|
|
469
469
|
insertAdjacentHTML(position,html){
|
|
470
470
|
if(position==='afterbegin') position='beforebegin'
|
|
471
471
|
if(position==='beforeend') position='afterend'
|
|
472
472
|
super.insertAdjacentHTML(position,html)
|
|
473
473
|
}
|
|
474
474
|
insertAdjacentText(position,text){
|
|
475
475
|
if(position==='afterbegin') position='beforebegin'
|
|
476
476
|
if(position==='beforeend') position='afterend'
|
|
477
477
|
super.insertAdjacentText(position,text)
|
|
478
478
|
}
|
|
479
479
|
insert(position,element){
|
|
480
480
|
if(position===1) position=0
|
|
481
481
|
if(position===2) position=3
|
|
482
482
|
super.insert(position,element)
|
|
483
483
|
}
|
|
484
484
|
appendChild(_){ }
|
|
485
485
|
get textContent(){ return ""; }
|
|
486
486
|
set textContent(_){ }
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
class Root extends Node{
|
|
490
490
|
constructor(){
|
|
491
491
|
super('ROOT',{},null);
|
|
492
492
|
this.isSingle=false
|
|
493
493
|
}
|
|
494
494
|
}
|
|
495
|
-
class Document extends Node{
|
|
496
495
|
constructor(){
|
|
497
496
|
super('DOCUMENT',{},null);
|
|
498
497
|
this.isSingle=false
|
|
499
498
|
this.innerHTML=/*html*/`<!DOCTYPE html><html lang="en"><head><title></title></head><body></body></html>`
|
|
500
499
|
}
|
|
501
500
|
get body(){return this.$('body')}
|
|
502
501
|
get head(){return this.$('head')}
|
|
503
502
|
get title(){return this.$('title')}
|
|
504
503
|
set title(title){return this.$('title').innerHTML=title}
|
|
504
|
+
class Document extends Node{
|
|
505
505
|
constructor(html,url){
|
|
506
506
|
super('ROOT',{},null);
|
|
507
507
|
this.isSingle=false
|
|
508
508
|
this.URL=url
|
|
509
509
|
if(html instanceof Document){
|
|
510
510
|
this.childNodes=[...buildFromCache(cacheDoc(html)).childNodes]
|
|
511
511
|
}
|
|
512
512
|
else if(typeof html==='string') this.innerHTML=html
|
|
513
513
|
else this.body
|
|
514
514
|
}
|
|
515
515
|
get documentElement(){return this.html}
|
|
516
516
|
get html(){
|
|
517
517
|
const html=this.$('html')
|
|
518
518
|
if(html===null) this.innerHTML=/*html*/`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title></title></head><body></body></html>`
|
|
519
519
|
return this.$('html')
|
|
520
520
|
}
|
|
521
521
|
get head(){
|
|
522
522
|
const head=this.$('head')
|
|
523
523
|
if(head===null) this.html.insert(1,'<head></head>')
|
|
524
524
|
return this.$('head')
|
|
525
525
|
}
|
|
526
526
|
get body(){
|
|
527
527
|
const body=this.$('body')
|
|
528
528
|
if(body===null) this.head.insert(3,'<body></body>')
|
|
529
529
|
return this.$('body')
|
|
530
530
|
}
|
|
531
531
|
get title(){
|
|
532
532
|
const title=this.$('title')
|
|
533
533
|
if(title===null) this.head.insert(1,'<title></title>')
|
|
534
534
|
return this.$('title').innerText
|
|
535
535
|
}
|
|
536
536
|
get charset(){return this.$('meta[charset]')}
|
|
537
537
|
set title(title){return this.$('title').innerText=title}
|
|
538
538
|
get clone(){return new Document(this)}
|
|
539
539
|
}
|
|
540
540
|
function parseAttributes(str){
|
|
541
541
|
const attrs={};
|
|
542
542
|
let key="";
|
|
543
543
|
let value="";
|
|
544
544
|
let isKey=true;
|
|
545
545
|
let quoteChar=null;
|
|
546
546
|
for (let i=0; i< str.length; i++){
|
|
547
547
|
const char=str[i];
|
|
548
548
|
if (isKey && (char==='=' || char===' ')){
|
|
549
549
|
if (char==='=') isKey=false;
|
|
550
550
|
else if (key.trim()){
|
|
551
551
|
attrs[key.trim()]=true;
|
|
552
552
|
key="";
|
|
553
553
|
}
|
|
554
554
|
continue;
|
|
555
555
|
}
|
|
556
556
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
557
557
|
quoteChar=char;
|
|
558
558
|
continue;
|
|
559
559
|
} else if (quoteChar && char===quoteChar){
|
|
560
560
|
quoteChar=null;
|
|
561
561
|
attrs[key.trim()]=value.trim();
|
|
562
562
|
key=""; value=""; isKey=true;
|
|
563
563
|
continue;
|
|
564
564
|
}
|
|
565
565
|
if (isKey) key+=char;
|
|
566
566
|
else value+=char;
|
|
567
567
|
}
|
|
568
568
|
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
569
569
|
return attrs;
|
|
570
570
|
}
|
|
571
571
|
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
572
572
|
function parseHTML(html){
|
|
573
573
|
const root=new Root();
|
|
574
574
|
const stack=[root];
|
|
575
575
|
let currentText="",i=0;
|
|
576
576
|
let max=0
|
|
577
577
|
function parseScript(){
|
|
578
578
|
if (!html.startsWith("<script",i)) return false;
|
|
579
579
|
const openTagEnd=html.indexOf(">",i);
|
|
580
580
|
if (openTagEnd===-1) return false;
|
|
581
581
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
582
582
|
const attributes=parseAttributes(attributesString);
|
|
583
583
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
584
584
|
if (closeTagStart===-1) return false;
|
|
585
585
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
586
586
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
587
587
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
588
588
|
i=closeTagStart+9;
|
|
589
589
|
return true;
|
|
590
590
|
}
|
|
591
591
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
592
592
|
if (!html.startsWith(startStr,i)) return false
|
|
593
593
|
const end=html.indexOf(endStr,i+n1);
|
|
594
594
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
595
595
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
596
596
|
i=end+n2;
|
|
597
597
|
return true
|
|
598
598
|
}
|
|
599
599
|
while (i< html.length){
|
|
600
600
|
if(i>=max) max=i;
|
|
601
601
|
else break;
|
|
602
602
|
if (parseScript()) continue
|
|
603
603
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
604
604
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
605
605
|
if (html.startsWith("<![CDATA[",i)){
|
|
606
606
|
const end=html.indexOf("]]>",i+9);
|
|
607
607
|
if (end===-1) break;
|
|
608
608
|
const content=html.substring(i+9,end);
|
|
609
609
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
610
610
|
cdataNode.nodeValue=content;
|
|
611
611
|
i=end+3;
|
|
612
612
|
continue;
|
|
613
613
|
}
|
|
614
614
|
if (html.startsWith("<",i)){
|
|
615
615
|
if (currentText && stack[stack.length-1]){
|
|
616
616
|
const textNode=new TextNode(currentText)
|
|
617
617
|
stack[stack.length-1].childNodes.push(textNode);
|
|
618
618
|
textNode.parent=stack[stack.length-1]
|
|
619
619
|
currentText="";
|
|
620
620
|
}
|
|
621
621
|
let tagEnd=i+1;
|
|
622
622
|
let insideQuotes=false;
|
|
623
623
|
let quoteChar=null;
|
|
624
624
|
while (tagEnd< html.length){
|
|
625
625
|
const char=html[tagEnd];
|
|
626
626
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
627
627
|
insideQuotes=true;
|
|
628
628
|
quoteChar=char;
|
|
629
629
|
} else if (insideQuotes && char===quoteChar){
|
|
630
630
|
insideQuotes=false;
|
|
631
631
|
quoteChar=null;
|
|
632
632
|
}
|
|
633
633
|
if (!insideQuotes && char==='>') break;
|
|
634
634
|
tagEnd++;
|
|
635
635
|
}
|
|
636
636
|
const tagContent=html.substring(i+1,tagEnd);
|
|
637
637
|
if (tagContent.startsWith("/")) stack.pop();
|
|
638
638
|
else{
|
|
639
639
|
let isSelfClosing=tagContent.endsWith('/');
|
|
640
640
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
641
641
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
642
642
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
643
643
|
const attributes=parseAttributes(attributesString);
|
|
644
644
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
645
645
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
646
646
|
}
|
|
647
647
|
i=tagEnd+1;
|
|
648
648
|
} else{
|
|
649
649
|
currentText+=html[i];
|
|
650
650
|
i++;
|
|
651
651
|
}
|
|
652
652
|
}
|
|
653
653
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
654
654
|
return root;
|
|
655
655
|
}
|
|
656
|
-
function buildFromCache(cached){
|
|
657
656
|
function buildNode(cache,parent=null){
|
|
658
657
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
659
658
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
660
659
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
661
660
|
if(isSingle) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
662
661
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
663
662
|
childNodes.forEach(childNode=>{
|
|
664
663
|
buildNode(childNode,newDoc)
|
|
665
664
|
});
|
|
666
665
|
return newDoc
|
|
667
666
|
}
|
|
668
667
|
return buildNode(cached)
|
|
668
|
+
function buildFromCache(cached){
|
|
669
669
|
function buildNode(cache,parent=null){
|
|
670
670
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
671
671
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
672
672
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
673
673
|
if(isSingle && parent) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
674
674
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
675
675
|
childNodes.forEach(childNode=>{
|
|
676
676
|
buildNode(childNode,newDoc)
|
|
677
677
|
});
|
|
678
678
|
return newDoc
|
|
679
679
|
}
|
|
680
680
|
return buildNode(cached)
|
|
681
681
|
}
|
|
682
682
|
|
|
683
683
|
function cacheDoc(doc){
|
|
684
684
|
const props=['isSingle','tagName','attributes']
|
|
685
685
|
function addToCache(element,cache={}){
|
|
686
686
|
if(typeof element==='string') return element
|
|
687
687
|
if(element.nodeName==='#text') return{textContent:element.textContent}
|
|
688
688
|
props.forEach(prop=>{
|
|
689
689
|
if(element[prop]){
|
|
690
690
|
cache[prop]=typeof element[prop]==='object' ?{...element[prop]} : element[prop]
|
|
691
691
|
}
|
|
692
692
|
});
|
|
693
693
|
if(!element.childNodes) return cache
|
|
694
694
|
cache.childNodes=[]
|
|
695
695
|
element.childNodes.forEach(childNode=>{
|
|
696
696
|
cache.childNodes.push(addToCache(childNode))
|
|
697
697
|
});
|
|
698
698
|
return cache
|
|
699
699
|
}
|
|
700
700
|
return addToCache(doc)
|
|
701
701
|
}
|