als-document 0.12.0 → 1.0.0-beta
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/document.js +41 -589
- package/index.js +40 -0
- package/index.mjs +40 -0
- package/package.json +9 -9
- package/readme.md +219 -150
- package/src/build.js +66 -0
- package/src/node/class-list.js +25 -0
- package/src/node/dataset.js +15 -0
- package/src/node/find.js +12 -0
- package/src/node/node.js +193 -0
- package/src/node/root.js +11 -0
- package/src/node/single-node.js +31 -0
- package/src/node/style.js +26 -0
- package/src/node/text-node.js +9 -0
- package/src/parse/cache.js +33 -0
- package/src/parse/parse-atts.js +36 -0
- package/src/parse/parser.js +98 -0
- package/src/parse/void-tags.js +5 -0
- package/src/query/check-element.js +83 -0
- package/src/query/query.js +142 -0
- package/tests/cache.js +19 -0
- package/tests/data/html1.js +2579 -0
- package/tests/data/html2.js +1124 -0
- package/tests/data/svg.js +66 -0
- package/tests/index.html +31 -0
- package/tests/node.js +196 -0
- package/tests/parse-real.js +53 -0
- package/tests/parser.js +351 -0
- package/tests/query.js +66 -0
- package/tests/test.js +169 -0
- package/tests/utils.js +37 -0
package/document.js
CHANGED
|
@@ -1,590 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
constructor(htmlText) {
|
|
4
|
-
if(typeof htmlText !== 'string') return
|
|
5
|
-
this.types = {}
|
|
6
|
-
this.domTree = []
|
|
7
|
-
this.added = []
|
|
8
|
-
this.domList = []
|
|
9
|
-
this.comments = []
|
|
10
|
-
this.scripts = {}
|
|
11
|
-
this.events = {}
|
|
12
|
-
this.htmlText = htmlText
|
|
13
|
-
this.doctype = ''
|
|
14
|
-
this.getDoctype()
|
|
15
|
-
this.removeComments()
|
|
16
|
-
this.removeScripts()
|
|
17
|
-
this.removeEvents()
|
|
18
|
-
this.parseTags()
|
|
19
|
-
this.addScripts()
|
|
20
|
-
this.addEvents()
|
|
21
|
-
this.addComments()
|
|
22
|
-
this.getPairs()
|
|
23
|
-
}
|
|
24
|
-
// Get domList
|
|
25
|
-
getDoctype() {
|
|
26
|
-
let doctype = this.htmlText.match(/\<\!DOCTYPE([\S\s]*?)\>/gm,'')
|
|
27
|
-
if(doctype !== null) {
|
|
28
|
-
this.doctype = doctype[0]
|
|
29
|
-
this.htmlText = this.htmlText.replace(/\<\!DOCTYPE([\S\s]*?)\>/gm,'')
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
removeComments() {
|
|
34
|
-
let comments = this.htmlText.match(/\<\!\-\-([\S\s]*?)\-\-\>/gm,'')
|
|
35
|
-
if(comments !== null) comments.forEach((comment,i) => {
|
|
36
|
-
let commentTag = `<comment comment-id="${i}">`
|
|
37
|
-
this.comments.push(comment)
|
|
38
|
-
this.htmlText = this.htmlText.replace(comment,commentTag)
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
addComments() {
|
|
43
|
-
this.domList.forEach((element,i) => {
|
|
44
|
-
if(element.tagName == 'comment') {
|
|
45
|
-
let id = this.domList[i].attributes['comment-id']
|
|
46
|
-
let comment = this.comments[id]
|
|
47
|
-
if(comment !== undefined) {
|
|
48
|
-
this.domList[i].comment = comment
|
|
49
|
-
delete this.comments[i]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
removeScripts() {
|
|
56
|
-
let scripts = this.htmlText.match(/\<script(.*?)\>[\S\s]*?\<\/script\>/gm)
|
|
57
|
-
if(scripts !== null) scripts.forEach((script,i) => {
|
|
58
|
-
let scriptContent = script.replace(/\<script(.*?)\>/,'').replace('</script>','')
|
|
59
|
-
if(scriptContent.trim() !== '') {
|
|
60
|
-
let scriptName = `{script${i}}`
|
|
61
|
-
let newScript = script.replace(scriptContent,scriptName)
|
|
62
|
-
this.scripts[scriptName] = scriptContent
|
|
63
|
-
this.htmlText = this.htmlText.replace(script,newScript)
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
removeEvents() {
|
|
69
|
-
let events = ['abort','afterprint','animationend','animationiteration','animationstart','beforeprint','beforeunload','blur','canplay','canplaythrough','change','click','contextmenu','copy','cut','dblclick','drag','dragend','dragenter','dragleave','dragover','dragstart','drop','durationchange','ended','error','focus','focusin','focusout','fullscreenchange','fullscreenerror','hashchange','input','invalid','keydown','keypress','keyup','load','loadeddata','loadedmetadata','loadstart','message','mousedown','mouseenter','mouseleave','mousemove','mouseover','mouseout','mouseup','mousewheel','offline','online','open','pagehide','pageshow','paste','pause','play','playing','popstate','progress','ratechange','resize','reset','scroll','search','seeked','seeking','select','show','stalled','storage','submit','suspend','timeupdate','toggle','touchcancel','touchend','touchmove','touchstart','transitionend','unload','volumechange','waiting','wheel']
|
|
70
|
-
events.forEach(event => {
|
|
71
|
-
let r = new RegExp(`on${event}\\s?\\=\\s?"[\\S\\s]*?\\"`,'g')
|
|
72
|
-
let scripts = this.htmlText.match(r)
|
|
73
|
-
if(scripts !== null) scripts.forEach((script,i) => {
|
|
74
|
-
let scriptContent = script.replace(`on${event}`,'').replace('=','').replace(/\"/g,'')
|
|
75
|
-
if(scriptContent.trim() !== '') {
|
|
76
|
-
let index = Object.keys(this.events).length
|
|
77
|
-
let scriptName = `{event${index}}`
|
|
78
|
-
let newScript = script.replace(scriptContent,scriptName)
|
|
79
|
-
this.events[scriptName] = scriptContent
|
|
80
|
-
this.htmlText = this.htmlText.replace(script,newScript)
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
addScripts() {
|
|
87
|
-
this.domList.forEach((element,i) => {
|
|
88
|
-
if(element.tagName == 'script' && !element.close) {
|
|
89
|
-
let text = this.domList[i+1].text
|
|
90
|
-
if(this.scripts[text] !== undefined) {
|
|
91
|
-
this.domList[i+1].text = this.scripts[text]
|
|
92
|
-
delete this.scripts[text]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
addEvents() {
|
|
99
|
-
this.domList.forEach((element,i) => {
|
|
100
|
-
if(element.attributes !== undefined) {
|
|
101
|
-
for(let attName in element.attributes) {
|
|
102
|
-
let attValue = element.attributes[attName]
|
|
103
|
-
if(this.events[attValue] !== undefined) {
|
|
104
|
-
if(this.domList[i].attributes[attName] == attValue) {
|
|
105
|
-
this.domList[i].attributes[attName] = this.events[attValue]
|
|
106
|
-
delete this.events[attValue]
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
parseTags() {
|
|
115
|
-
let outerHTML = this.htmlText
|
|
116
|
-
let outerHtmlForCut = outerHTML
|
|
117
|
-
let tags = outerHTML.match(/\<(\w*-?)*?(.*?)?([\S\s]*?)\>/gm)
|
|
118
|
-
let index = 0
|
|
119
|
-
if(tags !== null) tags.forEach((tag,i) => {
|
|
120
|
-
let node = outerHtmlForCut.split(tag)[0]
|
|
121
|
-
if(node.trim() !== '') {
|
|
122
|
-
this.domList.push({text:node,index})
|
|
123
|
-
index++
|
|
124
|
-
}
|
|
125
|
-
let obj = this.buildElementObj(tag)
|
|
126
|
-
let objName = obj.tagName+obj.order
|
|
127
|
-
obj.index = index
|
|
128
|
-
if(obj.order.length == 0) { // single tag
|
|
129
|
-
this.domList.push(obj)
|
|
130
|
-
} else this.domList.push({[objName]:obj,name:objName,index})
|
|
131
|
-
if(obj.close) {
|
|
132
|
-
let j = this.findOpen(objName)
|
|
133
|
-
if(j >=0) {
|
|
134
|
-
let closeTag = index
|
|
135
|
-
let openTagObj = this.domList[j][this.domList[j].name]
|
|
136
|
-
this.domList[j] = {...openTagObj,closeTag}
|
|
137
|
-
}
|
|
138
|
-
this.domList[obj.index] = obj
|
|
139
|
-
}
|
|
140
|
-
outerHtmlForCut = outerHtmlForCut.replace(node+tag,'')
|
|
141
|
-
index++
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
// Build element
|
|
145
|
-
buildElementObj(string,obj={}) {
|
|
146
|
-
let singlTags = Document.singlTags
|
|
147
|
-
if(string.match(/\<\/(\w*-?)*/) !== null) obj.close = true
|
|
148
|
-
else obj.close = false
|
|
149
|
-
obj.tagName = string.match(/\<\/?(\w*-?)*/,'gm')[0].replace(/\<\/?/,'')
|
|
150
|
-
if(!singlTags.includes(obj.tagName)) {
|
|
151
|
-
obj.tagName = obj.tagName.replace('/','')
|
|
152
|
-
let tagName = obj.tagName
|
|
153
|
-
this.getType(tagName,obj)
|
|
154
|
-
obj.order = this.types[tagName].count
|
|
155
|
-
} else obj.order = ''
|
|
156
|
-
obj.attributes = this.getAttributes(string)
|
|
157
|
-
obj.getAttribute = (attName) => obj.attributes[attName]
|
|
158
|
-
obj.setAttribute = (attName,attValue) => {obj.attributes[attName] = attValue}
|
|
159
|
-
obj = this.buildClass(obj)
|
|
160
|
-
if(obj.attributes.id !== undefined) {
|
|
161
|
-
obj.id = obj.attributes.id
|
|
162
|
-
delete obj.attributes.id
|
|
163
|
-
}
|
|
164
|
-
return obj
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
getType(tagName,obj) {
|
|
168
|
-
if(this.types[tagName] == undefined)
|
|
169
|
-
this.types[tagName] = {count:0,lastOperation:1}
|
|
170
|
-
else if(obj.close && this.types[tagName].lastOperation == 1)
|
|
171
|
-
this.types[tagName].lastOperation = 0
|
|
172
|
-
else if(obj.close && this.types[tagName].lastOperation == 0)
|
|
173
|
-
this.types[tagName].count--
|
|
174
|
-
else if(!obj.close && this.types[tagName].lastOperation == 0)
|
|
175
|
-
this.types[tagName].lastOperation = 1
|
|
176
|
-
else if(!obj.close && this.types[tagName].lastOperation == 1)
|
|
177
|
-
this.types[tagName].count++
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
getAttributes(string,attributes = {}) {
|
|
181
|
-
let matches = string.match(/\s(\/?\w*\-?)*((\S)*?\s?=[\S\s]*?\"[\S\s]*?\")?/g)
|
|
182
|
-
if(matches !== null) matches.forEach(attribute => {
|
|
183
|
-
attribute = attribute.trim()
|
|
184
|
-
if(attribute !== '') {
|
|
185
|
-
let attName = attribute.split('=')[0]
|
|
186
|
-
let attValue = attribute.replace(attName,'').replace('=','').replace(/\"/g,'')
|
|
187
|
-
if(attName !== '/') attributes[attName] = attValue.trim()
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
return attributes
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
buildClass(obj) {
|
|
194
|
-
if(obj.attributes.class !== undefined) {
|
|
195
|
-
obj.classList = obj.attributes.class.split(' ')
|
|
196
|
-
} else obj.classList = []
|
|
197
|
-
obj.classList.add = newClass => {
|
|
198
|
-
obj.classList.push(newClass)
|
|
199
|
-
obj.attributes.class += ' '+newClass
|
|
200
|
-
return obj
|
|
201
|
-
}
|
|
202
|
-
obj.classList.remove = clas => {
|
|
203
|
-
let index = obj.classList.indexOf(clas)
|
|
204
|
-
if(index >=0) {
|
|
205
|
-
obj.attributes.class = obj.attributes.class.replace(obj.classList[index],'')
|
|
206
|
-
obj.classList.splice(index,1)
|
|
207
|
-
}
|
|
208
|
-
return obj
|
|
209
|
-
}
|
|
210
|
-
return obj
|
|
211
|
-
}
|
|
212
|
-
// Build dom tree
|
|
213
|
-
getPairs(start=0,end=this.domList.length,domTree=this.domTree) {
|
|
214
|
-
for(let index=start; index<end; index++) {
|
|
215
|
-
let node = this.domList[index]
|
|
216
|
-
if(node.closeTag !== undefined && !this.added.includes(index)) {
|
|
217
|
-
if(this.firstTag == undefined) this.firstTag = index
|
|
218
|
-
this.buildTag(index,domTree)
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
buildTag(openIndex,domTree,parent=this.domTree) {
|
|
225
|
-
this.added.push(openIndex)
|
|
226
|
-
let content = this.domList[openIndex]
|
|
227
|
-
content.children = []
|
|
228
|
-
content.innerText = ''
|
|
229
|
-
this.getChildNodes(openIndex,content.closeTag,content)
|
|
230
|
-
content.$ = (selector) => this.$(selector,content)
|
|
231
|
-
content.$$ = (selector) => this.$$(selector,content)
|
|
232
|
-
content.parent = parent
|
|
233
|
-
content = Document.buildMethods(content)
|
|
234
|
-
this.buildOperations(content)
|
|
235
|
-
if(content.innerText !== '') {
|
|
236
|
-
if(parent.innerText == undefined) parent.innerText = ''
|
|
237
|
-
parent.innerText += '|'+content.innerText
|
|
238
|
-
}
|
|
239
|
-
content.json = () => Document.removeMethods(content)
|
|
240
|
-
delete content.closeTag
|
|
241
|
-
delete content.close
|
|
242
|
-
delete content.index
|
|
243
|
-
delete content.order
|
|
244
|
-
domTree.push(content)
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
static buildMethods(content) {
|
|
248
|
-
content.children.forEach((child,index) => { // prev and next
|
|
249
|
-
if(index == 0) child.prev = null
|
|
250
|
-
else child.prev = content.children[index-1]
|
|
251
|
-
if(index == content.children.length-1) child.next = null
|
|
252
|
-
else child.next = content.children[index+1]
|
|
253
|
-
});
|
|
254
|
-
return content
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
static changeByIndex(array,value,item,before = true) {
|
|
258
|
-
let index = array.indexOf(value);
|
|
259
|
-
if(index > -1) {
|
|
260
|
-
if(item == undefined) array.splice(index, 1); // remove value
|
|
261
|
-
else if(item !== undefined) {
|
|
262
|
-
if(before) array.splice(index, 0, item); // add item before value
|
|
263
|
-
else array.splice(index+1, 0, item); // add item after value
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
static buildInnerText(element) {
|
|
269
|
-
element.innerText = ''
|
|
270
|
-
if(element.children !== undefined) element.children.forEach(child => {
|
|
271
|
-
if(child.text !== undefined && child.text !== '') element.innerText += child.text
|
|
272
|
-
if(child.innerText !== undefined && child.innerText !== '') element.innerText += '|'+child.innerText
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
buildOperations(content) {
|
|
277
|
-
content.remove = function() {
|
|
278
|
-
Document.changeByIndex(content.parent.children,content)
|
|
279
|
-
Document.buildInnerText(content.parent)
|
|
280
|
-
}
|
|
281
|
-
content.add = function(element,place) {
|
|
282
|
-
if(typeof element == 'string') element = Document.newElement(element)
|
|
283
|
-
else element.remove()
|
|
284
|
-
if(place == 1) content.children.unshift(element)
|
|
285
|
-
if(place == 2) content.children.push(element)
|
|
286
|
-
if(place == 1 || place == 2) build(content)
|
|
287
|
-
|
|
288
|
-
if(place == 0) Document.changeByIndex(content.parent.children,content,element,true)
|
|
289
|
-
if(place == 3) Document.changeByIndex(content.parent.children,content,element,false)
|
|
290
|
-
if(place == 0 || place == 3) build(content.parent)
|
|
291
|
-
|
|
292
|
-
function build(parent) {
|
|
293
|
-
element.parent = parent
|
|
294
|
-
element = Document.buildMethods(parent)
|
|
295
|
-
Document.buildInnerText(parent)
|
|
296
|
-
}
|
|
297
|
-
return element
|
|
298
|
-
}
|
|
299
|
-
content.add0 = element => content.add(element,0)
|
|
300
|
-
content.add1 = element => content.add(element,1)
|
|
301
|
-
content.add2 = element => content.add(element,2)
|
|
302
|
-
content.add3 = element => content.add(element,3)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
static newElement(outerHtml) {
|
|
306
|
-
let document = new Document(outerHtml)
|
|
307
|
-
return document.domTree[0].json()
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
build(filePath,array=this.domTree,html = '',space='') {
|
|
311
|
-
let extraSpace = space+' '
|
|
312
|
-
let nl = `
|
|
313
|
-
`
|
|
314
|
-
array.forEach(element => {
|
|
315
|
-
if(element.text !== undefined) html += space+element.text+nl
|
|
316
|
-
else if(element.tagName == 'comment' ) {
|
|
317
|
-
html += space+element.comment + nl
|
|
318
|
-
} else {
|
|
319
|
-
let attributes = ''
|
|
320
|
-
for(let propName in element.attributes) {
|
|
321
|
-
let value = element.attributes[propName]
|
|
322
|
-
let attribute = ` ${propName}="${value}"`
|
|
323
|
-
if(attribute.length + attributes.length > 80) attribute = nl+space+attribute
|
|
324
|
-
attributes += attribute
|
|
325
|
-
}
|
|
326
|
-
let id = ''
|
|
327
|
-
if(element.id !== undefined) id = ` id="${element.id}"`
|
|
328
|
-
html += `${space}<${element.tagName}${id}${attributes}>${nl}`
|
|
329
|
-
if(element.children !== undefined)
|
|
330
|
-
if(element.children.length >0) html = this.build(filePath,element.children,html,extraSpace)
|
|
331
|
-
if(element.tagName !== undefined && !Document.singlTags.includes(element.tagName))
|
|
332
|
-
html += `${space}</${element.tagName}>${nl}`
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
if(array == this.domTree) {
|
|
336
|
-
html = this.doctype+nl+html
|
|
337
|
-
if(filePath !== undefined) Document.writeFile(filePath,html)
|
|
338
|
-
}
|
|
339
|
-
return html
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
getChildNodes(openIndex,closeIndex,content) {
|
|
343
|
-
for(let i = openIndex+1; i<closeIndex; i++) {
|
|
344
|
-
let node = this.domList[i]
|
|
345
|
-
if(!this.added.includes(i)) {
|
|
346
|
-
if(!node.close && node.text == undefined) this.buildTag(node.index,content.children,content)
|
|
347
|
-
else if(!node.close && node.text !== undefined) {
|
|
348
|
-
content.innerText += node.text
|
|
349
|
-
content.children.push(node);
|
|
350
|
-
delete node.index
|
|
351
|
-
}
|
|
352
|
-
this.added.push(i)
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
findOpen(key,array=this.domList) {
|
|
358
|
-
return array.findIndex(function(obj, index) {
|
|
359
|
-
if(obj[key] !== undefined && obj[key].close == false && obj[key].closeTag == undefined)
|
|
360
|
-
return true;
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
// querySelector
|
|
364
|
-
$(selector,obj) {return this.$$(selector,obj,true)}
|
|
365
|
-
|
|
366
|
-
$$(selector='',obj=this.domTree[0],first=false) {
|
|
367
|
-
if(typeof selector !== 'string') return
|
|
368
|
-
this.selector = selector
|
|
369
|
-
this.selectors = []
|
|
370
|
-
this.getSelectors(selector)
|
|
371
|
-
let array = this.findElements(obj)
|
|
372
|
-
if(first) return array[0]
|
|
373
|
-
else return this.buildCollection(array)
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
getSelectors() {
|
|
377
|
-
this.getPropSelctors() // remove all attributes from selector, build them, replace as [i] and put into selectors.attributes[]
|
|
378
|
-
let groups = this.selector.split(',')
|
|
379
|
-
groups.forEach(group => {
|
|
380
|
-
let result = {}
|
|
381
|
-
group = group.trim()
|
|
382
|
-
let family = group.split('>')
|
|
383
|
-
let prevBrother = group.split('+')
|
|
384
|
-
let nextBrother = group.split('~')
|
|
385
|
-
if(family.length > 1) {
|
|
386
|
-
result.parent = this.buildSingleSelector(family[0])
|
|
387
|
-
result.element = this.buildSingleSelector(family[1])
|
|
388
|
-
} else if(prevBrother.length > 1) {
|
|
389
|
-
result.prev = this.buildSingleSelector(prevBrother[0])
|
|
390
|
-
result.element = this.buildSingleSelector(prevBrother[1])
|
|
391
|
-
} else if(nextBrother.length > 1) {
|
|
392
|
-
result.next = this.buildSingleSelector(nextBrother[0])
|
|
393
|
-
result.element = this.buildSingleSelector(nextBrother[1])
|
|
394
|
-
} else result.element = this.buildSingleSelector(group)
|
|
395
|
-
this.selectors.push(result)
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
getPropSelctors() {
|
|
400
|
-
let signs = ['~','|','^','$','*']
|
|
401
|
-
let props = this.selector.match(/\[.*\]/g)
|
|
402
|
-
this.attributes = []
|
|
403
|
-
if(props !== null) props.forEach((prop,i) => {
|
|
404
|
-
this.selector = this.selector.replace(prop,`[${i}]`)
|
|
405
|
-
let attribute = prop.replace('[','').replace(']','')
|
|
406
|
-
let array = attribute.split('=')
|
|
407
|
-
let propName = array[0]
|
|
408
|
-
let propValue = (array[1] == undefined) ? '' : array[1].replace(/"/g,'')
|
|
409
|
-
let sign
|
|
410
|
-
if(signs.includes(propName.slice(-1))) {
|
|
411
|
-
sign = propName.slice(-1)
|
|
412
|
-
propName = propName.slice(0, -1)
|
|
413
|
-
}
|
|
414
|
-
this.attributes.push({propName,propValue,sign})
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
buildSingleSelector(selector,result={}) {
|
|
419
|
-
selector = selector.trim()
|
|
420
|
-
let props = selector.match(/\[.*\]/g)
|
|
421
|
-
if(props !== null) props.forEach(prop => {
|
|
422
|
-
selector = selector.replace(prop,'')
|
|
423
|
-
let index = prop.replace('[','').replace(']','')
|
|
424
|
-
result.attributes = this.attributes[index]
|
|
425
|
-
});
|
|
426
|
-
let id = selector.match(/#(\w*-?)*/)
|
|
427
|
-
if(id !== null) {
|
|
428
|
-
result.id = id[0].replace('#','')
|
|
429
|
-
selector = selector.replace(id[0],'')
|
|
430
|
-
}
|
|
431
|
-
let classes = selector.match(/\.(\w*-?)*/g)
|
|
432
|
-
if(classes !== null) {
|
|
433
|
-
result.classes = []
|
|
434
|
-
classes.forEach(clas => {
|
|
435
|
-
result.classes.push(clas.replace('.',''))
|
|
436
|
-
selector = selector.replace(clas,'')
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
let tag = selector.match(/(\w*-?)*/)
|
|
440
|
-
if(tag !== null) {
|
|
441
|
-
if(tag[0] !== '') {
|
|
442
|
-
result.tag = tag[0]
|
|
443
|
-
selector = selector.replace(tag[0],'')
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
return result
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
findElements(obj,array = []) {
|
|
450
|
-
array = this.checkSelectors(obj,array)
|
|
451
|
-
if(obj.children !== undefined) {
|
|
452
|
-
obj.children.forEach(element => {
|
|
453
|
-
this.findElements(element,array)
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
return array
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
checkSelectors(element,array) {
|
|
460
|
-
this.selectors.forEach(group => {
|
|
461
|
-
let addIt = 0
|
|
462
|
-
if(group.parent !== undefined) addIt += this.checkElement(element.parent,group.parent)
|
|
463
|
-
if(group.prev !== undefined) addIt += this.checkElement(element.prev,group.prev)
|
|
464
|
-
if(group.next !== undefined) addIt += this.checkElement(element.next,group.next)
|
|
465
|
-
if(group.element !== undefined) addIt += this.checkElement(element,group.element)
|
|
466
|
-
if(addIt == 0) array.push(element)
|
|
467
|
-
});
|
|
468
|
-
return array
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
checkElement(element,selectors,addIt = 0) {
|
|
472
|
-
if(element == undefined) return -1
|
|
473
|
-
if(selectors.tag !== undefined)
|
|
474
|
-
if(element.tagName !== selectors.tag) addIt--
|
|
475
|
-
if(selectors.id !== undefined)
|
|
476
|
-
if(element.id !== selectors.id) addIt--
|
|
477
|
-
if(selectors.classes !== undefined ) {
|
|
478
|
-
if(element.classList !== undefined) selectors.classes.forEach(clas => {
|
|
479
|
-
if(!element.classList.includes(clas)) addIt--
|
|
480
|
-
});
|
|
481
|
-
else addIt--
|
|
482
|
-
}
|
|
483
|
-
if(selectors.attributes !== undefined) addIt += this.checkAttributes(
|
|
484
|
-
element.attributes,
|
|
485
|
-
selectors.attributes.propName,
|
|
486
|
-
selectors.attributes.propValue,
|
|
487
|
-
selectors.attributes.sign
|
|
488
|
-
)
|
|
489
|
-
return addIt
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
checkAttributes(props,propName,propValue,sign,addIt = 0) {
|
|
493
|
-
if(props == undefined) return -1
|
|
494
|
-
else {
|
|
495
|
-
if(props[propName] == undefined) addIt--
|
|
496
|
-
if(propValue == '' && props[propName] == undefined) addIt--
|
|
497
|
-
if(propValue.length>0 && props[propName] !== undefined) {
|
|
498
|
-
if(sign == undefined) {
|
|
499
|
-
if(props[propName] !== propValue) addIt--
|
|
500
|
-
} else if(sign == '~') {
|
|
501
|
-
let r = new RegExp(`\\b${propValue}`)
|
|
502
|
-
if(props[propName].match(r) == null) addIt--
|
|
503
|
-
} else if(sign == '|') {
|
|
504
|
-
if(!props[propName].startsWith(propValue) || props[propName] !== propValue) addIt--
|
|
505
|
-
} else if(sign == '^') {
|
|
506
|
-
if(!props[propName].startsWith(propValue)) addIt--
|
|
507
|
-
} else if(sign == '$') {
|
|
508
|
-
if(!props[propName].endsWith(propValue)) addIt--
|
|
509
|
-
} else if(sign == '*') {
|
|
510
|
-
if(!props[propName].includes(propValue)) addIt--
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return addIt
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Collection array
|
|
518
|
-
buildCollection(collection) {
|
|
519
|
-
collection.each = function(fn) {return Document.each(this,fn)}
|
|
520
|
-
collection.parse = function(part,fn) {return Document.parse(this,part,fn)}
|
|
521
|
-
return collection
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
static each(collection,fn) {
|
|
525
|
-
for(let i = 0; i<collection.length; i++) {
|
|
526
|
-
fn(collection[i],i,collection)
|
|
527
|
-
}
|
|
528
|
-
return collection
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
static parse(collection,part,fn,array=[]) {
|
|
532
|
-
if(part !== undefined) {
|
|
533
|
-
collection.each((element,index,collection) => {
|
|
534
|
-
let content
|
|
535
|
-
if(element[part] !== undefined) content = element[part]
|
|
536
|
-
else if(element.attributes !== undefined)
|
|
537
|
-
if(element.attributes[part] !== undefined)
|
|
538
|
-
content = element.attributes[part]
|
|
539
|
-
|
|
540
|
-
if(content !== undefined) {
|
|
541
|
-
if(typeof content == 'string')
|
|
542
|
-
content = content.replace(/ /g,' ')
|
|
543
|
-
if(fn !== undefined) {
|
|
544
|
-
if(fn(content)) array.push(content)
|
|
545
|
-
} else array.push(content)
|
|
546
|
-
}
|
|
547
|
-
})
|
|
548
|
-
} else {
|
|
549
|
-
collection.forEach(element => {
|
|
550
|
-
array.push(Document.removeMethods(element))
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
return array
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
static removeMethods(element) {
|
|
557
|
-
element = {...element}
|
|
558
|
-
let deleteThis = ['parent','next','prev','classList','json','$','$$','remove','add','add0','add1','add2','add3','getAttribute','setAttribute']
|
|
559
|
-
deleteThis.forEach(item => {
|
|
560
|
-
delete element[item]
|
|
561
|
-
});
|
|
562
|
-
if(element.children !== undefined) element.children.forEach((child,i) => {
|
|
563
|
-
element.children[i] = Document.removeMethods(child)
|
|
564
|
-
});
|
|
565
|
-
return element
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
static writeFile(path,obj,encoding = 'utf-8') {
|
|
569
|
-
let {writeFileSync} = require('fs')
|
|
570
|
-
path = Document.path(path)
|
|
571
|
-
if(typeof obj !== 'string') try {
|
|
572
|
-
obj = JSON.stringify(obj,null,4)
|
|
573
|
-
writeFileSync(path,obj,encoding)
|
|
574
|
-
} catch(e) {console.log(e)}
|
|
575
|
-
else writeFileSync(path,obj,encoding)
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
static readFile(path,encoding = 'utf-8') {
|
|
579
|
-
path = Document.path(path)
|
|
580
|
-
let {readFileSync} = require('fs')
|
|
581
|
-
return readFileSync(path,encoding)
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
static path(path) {
|
|
585
|
-
let {join} = require('path')
|
|
586
|
-
if(Array.isArray(path)) return join(...path)
|
|
587
|
-
else return path
|
|
588
|
-
}
|
|
1
|
+
const alsDocument = (function(){
|
|
2
|
+
class Query{
|
|
589
3
|
static get(query){
|
|
590
4
|
let q=new Query(query)
|
|
591
5
|
return q.selectors
|
|
592
6
|
}
|
|
593
7
|
constructor(query){
|
|
594
8
|
this.query=query
|
|
595
9
|
this.selectors=[]
|
|
596
10
|
this.stringValues=[];
|
|
597
11
|
this.parseSelectors(query.split(','))
|
|
598
12
|
}
|
|
599
13
|
parseSelectors(selectors){
|
|
600
14
|
selectors.forEach(selector=>{
|
|
601
15
|
let originalSelector=selector.trim()
|
|
602
16
|
selector=this.removeSpaces(selector)
|
|
603
17
|
this.stringValues=[]
|
|
604
18
|
selector=selector.replace(/\[.*?\]/g,(value)=>{
|
|
605
19
|
this.stringValues.push(value)
|
|
606
20
|
return `[${this.stringValues.length-1}]`
|
|
607
21
|
})
|
|
608
22
|
let [element,ancestors]=this.splitAndCutLast(selector,' ')
|
|
609
23
|
element=this.getFamily(element)
|
|
610
24
|
if (ancestors.length>0)
|
|
611
25
|
element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
|
|
612
26
|
element.group=originalSelector
|
|
613
27
|
this.selectors.push(element)
|
|
614
28
|
});
|
|
615
29
|
}
|
|
616
30
|
splitAndCutLast(string,splitBy){
|
|
617
31
|
const array=string.split(splitBy);
|
|
618
32
|
const last=array.pop();
|
|
619
33
|
return [last,array];
|
|
620
34
|
}
|
|
621
35
|
getFamily(group,element,prev,prevAny,sign){
|
|
622
36
|
if (group.match(/\~|\+/)!==null){
|
|
623
37
|
let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
|
|
624
38
|
let signs=group.replace(last,'')
|
|
625
39
|
prevBrothers.forEach(el=>signs=signs.replace(el,''))
|
|
626
40
|
signs=signs.match(/\~|\+/g)
|
|
627
41
|
if (signs.length==1){
|
|
628
42
|
sign=signs[0]
|
|
629
43
|
} else if (signs.length>1){
|
|
630
44
|
sign=signs.splice(signs.length-1,signs.length-1)[0]
|
|
631
45
|
prevBrothers[0]=prevBrothers.map((b,i)=>{
|
|
632
46
|
if (i< prevBrothers.length-1) b+=signs[i]
|
|
633
47
|
return b
|
|
634
48
|
}).join('')
|
|
635
49
|
prevBrothers[0]=this.getFamily(prevBrothers[0])
|
|
636
50
|
}
|
|
637
51
|
if (sign=='~') prevAny=prevBrothers[0]
|
|
638
52
|
else if (sign=='+') prev=prevBrothers[0]
|
|
639
53
|
element=last
|
|
640
54
|
} else element=group
|
|
641
55
|
let family
|
|
642
56
|
if (prev || prevAny){
|
|
643
57
|
family=this.getParents(element)
|
|
644
58
|
if (prev) family.prev=this.getParents(prev)
|
|
645
59
|
if (prevAny) family.prevAny=this.getParents(prevAny)
|
|
646
60
|
} else family=this.getParents(element)
|
|
647
61
|
if (family.query!==group) family.group=group
|
|
648
62
|
return family
|
|
649
63
|
}
|
|
650
64
|
getParents(selector){
|
|
651
65
|
if (typeof selector=='string'){
|
|
652
66
|
let [element,parents]=this.splitAndCutLast(selector,'>')
|
|
653
67
|
element=this.buildElement(element)
|
|
654
68
|
parents=parents.map(parent=>this.buildElement(parent))
|
|
655
69
|
if (parents.length>0) element.parents=parents
|
|
656
70
|
return element
|
|
657
71
|
} else return selector
|
|
658
72
|
}
|
|
659
73
|
buildElement(element,id=null,tag=null,classList=[]){
|
|
660
74
|
let query=element
|
|
661
75
|
element=element.replace(/\#(\w-?)*/,$id=>{
|
|
662
76
|
id=$id.replace(/^\#/,''); return ''
|
|
663
77
|
})
|
|
664
78
|
element=element.replace(/\.(\w-?)*/,$class=>{
|
|
665
79
|
classList.push($class.replace(/^\./,'')); return ''
|
|
666
80
|
})
|
|
667
81
|
element=element.replace(/(\w\:?-?)*/,$tag=>{
|
|
668
82
|
tag=$tag=='' ? null : $tag; return ''
|
|
669
83
|
})
|
|
670
84
|
let attribs=this.getAttributes(element)
|
|
671
85
|
element={ query }
|
|
672
86
|
if (id) element.id=id
|
|
673
87
|
if (tag) element.tag=tag
|
|
674
88
|
if (classList.length>0) element.classList=classList
|
|
675
89
|
if (attribs.length>0) element.attribs=attribs
|
|
676
90
|
return element
|
|
677
91
|
}
|
|
678
92
|
getAttributes(element){
|
|
679
93
|
let attribs=this.stringValues.filter((value,index)=>{
|
|
680
94
|
let searchValue=`[${index}]`
|
|
681
95
|
if (element.match(searchValue)) return true
|
|
682
96
|
else return false
|
|
683
97
|
})
|
|
684
98
|
attribs=attribs.map(attrib=>{
|
|
685
99
|
let query=attrib
|
|
686
100
|
attrib=attrib.replace('[','').replace(']','')
|
|
687
101
|
let [name,value]=attrib.split(/[\~\|\^\$\*]?\=/)
|
|
688
102
|
let sign=attrib.replace(name,'').replace(value,'')
|
|
689
103
|
attrib={ query }
|
|
690
104
|
if (name) attrib.name=name
|
|
691
105
|
if (value) attrib.value=value.trim().replace(/^\"/,'').replace(/\"$/,'')
|
|
692
106
|
if (sign){
|
|
693
107
|
attrib.sign=sign
|
|
694
108
|
attrib.check=this.getAttribFn(sign).bind(attrib)
|
|
695
109
|
}
|
|
696
110
|
return attrib
|
|
697
111
|
});
|
|
698
112
|
return attribs
|
|
699
113
|
}
|
|
700
114
|
getAttribFn(sign){
|
|
701
115
|
if (sign=='=') return function (value){ return value===this.value }
|
|
702
116
|
if (sign=='*=') return function (value){ return value.includes(this.value) }
|
|
703
117
|
if (sign=='^=') return function (value){ return value.startsWith(this.value) }
|
|
704
118
|
if (sign=='$=') return function (value){ return value.endsWith(this.value) }
|
|
705
119
|
if (sign=='|=') return function (value){
|
|
706
120
|
return value.trim().split(' ').length==1
|
|
707
121
|
&& (value.startsWith(this.value) || value.startsWith(this.value+'-'))
|
|
708
122
|
? true : false
|
|
709
123
|
}
|
|
710
124
|
if (sign=='~=') return function (value){
|
|
711
125
|
return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
|
|
712
126
|
}
|
|
713
127
|
}
|
|
714
128
|
removeSpaces(selector){
|
|
715
129
|
selector=selector.replace(/\s{2}/g,' ')
|
|
716
130
|
selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
|
|
717
131
|
selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
|
|
718
132
|
return selector
|
|
719
133
|
}
|
|
134
|
+
}
|
|
135
|
+
function checkElement(el,selector){
|
|
720
136
|
if(selector==undefined) return true
|
|
721
137
|
if(el==null) return false
|
|
722
138
|
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
723
139
|
if(typeof el==='string') return false
|
|
724
140
|
if(id!==undefined && el.id===null) return false
|
|
725
141
|
if(id && id!==el.id) return false
|
|
726
142
|
if(tag && el.tagName===undefined) return false
|
|
727
143
|
else if(tag && tag!==el.tagName) return false
|
|
728
144
|
const clas=el.attributes.class
|
|
729
145
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
730
146
|
else if(classList!==undefined){
|
|
731
147
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
732
148
|
}
|
|
733
149
|
if(checkattributes(attributes,el)===false) return false
|
|
734
150
|
if(checkElement(el.prev,prev)===false) return false
|
|
735
151
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
736
152
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
737
153
|
if(el.parent){
|
|
738
154
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
739
155
|
}
|
|
740
156
|
return true
|
|
157
|
+
}
|
|
741
158
|
function checkattributes(attributes=[],el){
|
|
742
159
|
let elattributes=el.attributes
|
|
743
160
|
let names=Object.keys(elattributes)
|
|
744
161
|
let passedTests=0
|
|
745
162
|
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
746
163
|
let{name,value,check}=attributes[i]
|
|
747
164
|
if(name=='inner' && value!==undefined && check && el.inner){
|
|
748
165
|
if(check(el.inner)) passedTests++
|
|
749
166
|
}
|
|
750
167
|
if(!names.includes(name)) continue
|
|
751
168
|
else if(value==undefined) passedTests++
|
|
752
169
|
else if(value && elattributes[name]){
|
|
753
170
|
if(check(elattributes[name])==false) continue
|
|
754
171
|
else passedTests++
|
|
755
172
|
}
|
|
756
173
|
}
|
|
757
174
|
if(passedTests==attributes.length) return true
|
|
758
175
|
else return false
|
|
176
|
+
}
|
|
759
177
|
function checkPrevAny(children=[],index,prevAny){
|
|
760
178
|
let size=children.length
|
|
761
179
|
if((size==0 || index==0) && prevAny) return false
|
|
762
180
|
for(let i=index; i>=0; i--){
|
|
763
181
|
if(checkElement(children[i],prevAny)) return true
|
|
764
182
|
}
|
|
765
183
|
return false
|
|
184
|
+
}
|
|
766
185
|
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
767
186
|
let count=0
|
|
768
187
|
if(selectorAncestors.length==0) return true
|
|
769
188
|
let endIndex=ancestors.length-1
|
|
770
189
|
let selectorIndex=selectorAncestors.length-1
|
|
771
190
|
while(selectorIndex>=0){
|
|
772
191
|
for(let i=endIndex; i>=0; i--){
|
|
773
192
|
endIndex=i-1
|
|
774
193
|
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
775
194
|
count++
|
|
776
195
|
break
|
|
777
196
|
}
|
|
778
197
|
}
|
|
779
198
|
selectorIndex--
|
|
780
199
|
}
|
|
781
200
|
if(count==selectorAncestors.length) return true
|
|
782
201
|
else return false
|
|
202
|
+
}
|
|
783
203
|
function checkParents(ancestors=[],selectorParents=[]){
|
|
784
204
|
if(selectorParents.length===0) return true
|
|
785
205
|
if(ancestors.length< selectorParents.length) return false
|
|
786
206
|
let index=ancestors.length-1
|
|
787
207
|
for(let i=selectorParents.length-1; i>=0; i--){
|
|
788
208
|
if(checkElement(ancestors[index],selectorParents[i])===false) return false
|
|
789
209
|
index--
|
|
790
210
|
}
|
|
791
211
|
return true
|
|
212
|
+
}
|
|
213
|
+
const getDataName=prop=>'data-'+prop.toLowerCase()
|
|
214
|
+
function getDataset(element){
|
|
792
215
|
return new Proxy(element.attributes,{
|
|
793
216
|
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
794
217
|
const dataAttr=getDataName(prop)
|
|
795
218
|
if (dataAttr in target){
|
|
796
219
|
delete target[dataAttr];
|
|
797
220
|
return true;
|
|
798
221
|
}
|
|
799
222
|
return false;
|
|
800
223
|
}
|
|
801
224
|
});
|
|
802
225
|
}
|
|
803
|
-
|
|
226
|
+
|
|
227
|
+
function find(selectors,element,collection,first=false,firstTime=true){
|
|
804
228
|
for(let selector of selectors){
|
|
805
229
|
if(checkElement(element,selector)) collection.add(element)
|
|
806
230
|
}
|
|
807
231
|
if(element.children)
|
|
808
232
|
element.children.forEach(child=>{
|
|
809
233
|
if(first && collection.size>0) return
|
|
810
234
|
find(selectors,child,collection,first,false)
|
|
811
235
|
})
|
|
812
236
|
return firstTime ? [...collection] : collection
|
|
237
|
+
}
|
|
238
|
+
class TextNode{
|
|
813
239
|
constructor(data){
|
|
814
240
|
this.nodeName='#text';
|
|
815
241
|
this.parent=null;
|
|
816
242
|
this.textContent=data;
|
|
817
243
|
}
|
|
818
244
|
get nodeValue(){return this.textContent}
|
|
819
245
|
get parentNode(){return this.parent}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function buildStyle(attributes){
|
|
820
249
|
const styles=attributes.style || "";
|
|
821
250
|
const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
|
|
822
251
|
const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
|
|
823
252
|
const baseStyleObj=styles.split(";").reduce((acc,style)=>{
|
|
824
253
|
const [key,value]=style.split(":").map(s=>s.trim());
|
|
825
254
|
if (key && value) acc[kebabToCamel(key)]=value;
|
|
826
255
|
return acc;
|
|
827
256
|
},{});
|
|
828
257
|
return new Proxy(baseStyleObj,{
|
|
829
258
|
get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
|
|
830
259
|
obj[camelToKebab(prop)]=value;
|
|
831
260
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
832
261
|
return true;
|
|
833
262
|
},deleteProperty: (obj,prop)=>{
|
|
834
263
|
delete obj[camelToKebab(prop)];
|
|
835
264
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
836
265
|
return true;
|
|
837
266
|
}
|
|
838
267
|
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
class NodeClassList{
|
|
839
271
|
constructor(node){ this.node=node }
|
|
840
272
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
841
273
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
842
274
|
contains(className){ return this.classes.includes(className) }
|
|
843
275
|
add(className){
|
|
844
276
|
const currentClasses=this.classes;
|
|
845
277
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
846
278
|
}
|
|
847
279
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
848
280
|
toggle(className){
|
|
849
281
|
if (this.classes.includes(className)) this.remove(className);
|
|
850
282
|
else this.add(className);
|
|
851
283
|
}
|
|
852
284
|
replace(oldClass,newClass){
|
|
853
285
|
if (this.classes.includes(oldClass)){
|
|
854
286
|
this.remove(oldClass);
|
|
855
287
|
this.add(newClass);
|
|
856
288
|
}
|
|
857
289
|
}
|
|
290
|
+
}
|
|
291
|
+
function insertBefore(arr,index,newItem){
|
|
858
292
|
const existingIndex=arr.indexOf(newItem);
|
|
859
293
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
860
294
|
arr.splice(index,0,newItem);
|
|
295
|
+
}
|
|
861
296
|
class Node{
|
|
862
297
|
constructor(tagName,attributes={},parent=null){
|
|
863
298
|
this.isSingle=false;
|
|
864
299
|
this.tagName=tagName;
|
|
865
300
|
this.attributes=attributes;
|
|
866
301
|
this.childNodes=[];
|
|
867
302
|
if (parent!==null) parent.childNodes.push(this)
|
|
868
303
|
this.parent=parent;
|
|
869
304
|
this._classList=null;
|
|
870
305
|
this.__style=null;
|
|
871
306
|
this._dataset=null
|
|
872
307
|
}
|
|
873
308
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
874
309
|
set id(newValue){ this.attributes.id=newValue; }
|
|
875
310
|
get className(){return this.attributes.class || null}
|
|
876
311
|
get parentNode(){ return this.parent }
|
|
877
312
|
get ancestors(){
|
|
878
313
|
if(!this.parent) return []
|
|
879
314
|
const ancestors=[]
|
|
880
315
|
let element=this.parent
|
|
881
316
|
while (element.tagName!=='ROOT'){
|
|
882
317
|
ancestors.push(element)
|
|
883
318
|
element=element.parent
|
|
884
319
|
}
|
|
885
320
|
return ancestors.reverse()
|
|
886
321
|
}
|
|
887
322
|
get childNodeIndex(){
|
|
888
323
|
if(!this.parent) return null
|
|
889
324
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
890
325
|
}
|
|
891
326
|
get childIndex(){
|
|
892
327
|
if(!this.parent) return null
|
|
893
328
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
894
329
|
}
|
|
895
330
|
get previousElementSibling(){ return this.prev }
|
|
896
331
|
get prev(){
|
|
897
332
|
if (!this.childIndex) return null
|
|
898
333
|
return this.parent.children[this.childIndex-1]
|
|
899
334
|
}
|
|
900
335
|
get nextElementSibling(){ return this.next }
|
|
901
336
|
get next(){
|
|
902
337
|
if (!this.childIndex) return null
|
|
903
338
|
return this.parent.children[this.childIndex+1] || null
|
|
904
339
|
}
|
|
905
340
|
get dataset(){
|
|
906
341
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
907
342
|
return this._dataset;
|
|
908
343
|
}
|
|
909
344
|
get classList(){
|
|
910
345
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
911
346
|
return this._classList;
|
|
912
347
|
}
|
|
913
348
|
get style(){
|
|
914
349
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
915
350
|
return this.__style
|
|
916
351
|
}
|
|
917
352
|
get outerHTML(){
|
|
918
353
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
919
354
|
return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
|
|
920
355
|
}
|
|
921
356
|
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
922
357
|
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
923
358
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
924
359
|
remove(){
|
|
925
360
|
if (!this.parent) return
|
|
926
361
|
const index=this.childNodeIndex;
|
|
927
362
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
928
363
|
}
|
|
929
364
|
get innerHTML(){
|
|
930
365
|
return this.childNodes.map(child=>{
|
|
931
366
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
932
367
|
else if (child instanceof TextNode) return child.textContent;
|
|
933
368
|
else return child
|
|
934
369
|
}).join("");
|
|
935
370
|
}
|
|
936
371
|
$$(query){return this.querySelectorAll(query)}
|
|
937
372
|
querySelectorAll(query){
|
|
938
373
|
const selectors=Query.get(query)
|
|
939
374
|
return find(selectors,this,new Set())
|
|
940
375
|
}
|
|
941
376
|
$(query){return this.querySelector(query)}
|
|
942
377
|
querySelector(query){
|
|
943
378
|
const selectors=Query.get(query)
|
|
944
379
|
return find(selectors,this,new Set(),true)[0] || null
|
|
945
380
|
}
|
|
946
381
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
947
382
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
948
383
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
949
384
|
get children(){
|
|
950
385
|
return this.childNodes.filter(child=>{
|
|
951
386
|
if (!(child instanceof Node)) return false
|
|
952
387
|
if (child.tagName==='#comment') return false
|
|
953
388
|
return true
|
|
954
389
|
});
|
|
955
390
|
}
|
|
956
391
|
insertAdjacentElement(position,newElement){
|
|
957
392
|
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
958
393
|
const pos=position.toLowerCase();
|
|
959
394
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
960
395
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
961
396
|
newElement.parent=this
|
|
962
397
|
if (!this.parent) return newElement
|
|
963
398
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
964
399
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
965
400
|
newElement.parent=this.parent
|
|
966
401
|
return newElement
|
|
967
402
|
}
|
|
968
403
|
insertAdjacentHTML(position,html){
|
|
969
404
|
const newNode=parseHTML(html);
|
|
970
405
|
newNode.childNodes.reverse().forEach(node=>{
|
|
971
406
|
this.insertAdjacentElement(position,node);
|
|
972
407
|
});
|
|
973
408
|
return newNode
|
|
974
409
|
}
|
|
975
410
|
insertAdjacentText(position,text){
|
|
976
411
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
977
412
|
}
|
|
978
413
|
insert(position,element){
|
|
979
414
|
const positions=['beforebegin','afterbegin','beforeend','afterend']
|
|
980
415
|
if(positions[position]) position=positions[position]
|
|
981
416
|
if(typeof element==='string'){
|
|
982
417
|
element=element.trim()
|
|
983
418
|
if(element.startsWith('<') && element.endsWith('>')){
|
|
984
419
|
return this.insertAdjacentHTML(position,element)
|
|
985
420
|
}
|
|
986
421
|
return this.insertAdjacentText(position,element)
|
|
987
422
|
}
|
|
988
423
|
return this.insertAdjacentElement(position,element)
|
|
989
424
|
}
|
|
990
425
|
set innerHTML(html){
|
|
991
426
|
const parsed=parseHTML(html);
|
|
992
427
|
this.childNodes=parsed.childNodes;
|
|
993
428
|
}
|
|
994
429
|
set outerHTML(html){
|
|
995
430
|
const parsed=parseHTML(html);
|
|
996
431
|
if (!this.parent) return console.log('element has no parent node')
|
|
997
432
|
const index=this.childIndex
|
|
998
433
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
999
434
|
}
|
|
1000
435
|
appendChild(newChild){
|
|
1001
436
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
1002
437
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
1003
438
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
1004
439
|
else return newChild
|
|
1005
440
|
this.childNodes.push(newChild);
|
|
1006
441
|
newChild.parent=this;
|
|
1007
442
|
return newChild;
|
|
1008
443
|
}
|
|
1009
444
|
get textContent(){
|
|
1010
445
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
1011
446
|
return this.childNodes.map(child=>{
|
|
1012
447
|
if(child instanceof SingleNode) return ''
|
|
1013
448
|
if(child instanceof TextNode) return child.nodeValue
|
|
1014
449
|
if(child instanceof Node) return child.textContent;
|
|
1015
450
|
else return child;
|
|
1016
451
|
}).join(" ");
|
|
1017
452
|
}
|
|
1018
453
|
set textContent(value){
|
|
1019
454
|
this.childNodes=[];
|
|
1020
455
|
if (value!==null && value!==undefined){
|
|
1021
456
|
this.childNodes.push(value.toString());
|
|
1022
457
|
}
|
|
1023
458
|
}
|
|
459
|
+
}
|
|
460
|
+
class SingleNode extends Node{
|
|
1024
461
|
constructor(tagName,attributes={},parent=null){
|
|
1025
462
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
1026
463
|
super(tagName,attributes,parent);
|
|
1027
464
|
this.isSingle=true
|
|
1028
465
|
}
|
|
1029
466
|
get outerHTML(){
|
|
1030
467
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
1031
468
|
const attrs=Object.entries(this.attributes).map(([key,val])=>val.length ? `${key}="${val}"` : key).join(" ");
|
|
1032
469
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
1033
470
|
}
|
|
1034
471
|
get innerHTML(){ return ""; }
|
|
1035
472
|
set innerHTML(_){ }
|
|
1036
473
|
$(_){return null}
|
|
1037
474
|
$$(_){return []}
|
|
1038
475
|
querySelectorAll(_){ return []; }
|
|
1039
476
|
querySelector(_){ return null; }
|
|
1040
477
|
getElementsByClassName(_){ return []; }
|
|
1041
478
|
getElementsByTagName(_){ return []; }
|
|
1042
479
|
getElementById(_){ return null; }
|
|
1043
480
|
get children(){ return []; }
|
|
1044
481
|
insertAdjacentElement(_,__){ }
|
|
1045
482
|
insertAdjacentHTML(_,__){ }
|
|
1046
483
|
insertAdjacentText(_,__){ }
|
|
1047
484
|
appendChild(_){ }
|
|
1048
485
|
insert(_,__){ }
|
|
1049
486
|
get textContent(){ return ""; }
|
|
1050
487
|
set textContent(_){ }
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
class Root extends Node{
|
|
1051
491
|
constructor(){
|
|
1052
492
|
super('ROOT',{},null);
|
|
1053
493
|
this.isSingle=false
|
|
1054
494
|
}
|
|
1055
495
|
get body(){return this.$('body')}
|
|
1056
496
|
get head(){return this.$('head')}
|
|
1057
497
|
get title(){return this.$('title')}
|
|
1058
498
|
set title(title){return this.$('title').innerHTML=title}
|
|
499
|
+
}
|
|
500
|
+
function parseAttributes(str){
|
|
1059
501
|
const attrs={};
|
|
1060
502
|
let key="";
|
|
1061
503
|
let value="";
|
|
1062
504
|
let isKey=true;
|
|
1063
505
|
let quoteChar=null;
|
|
1064
506
|
for (let i=0; i< str.length; i++){
|
|
1065
507
|
const char=str[i];
|
|
1066
508
|
if (isKey && (char==='=' || char===' ')){
|
|
1067
509
|
if (char==='=') isKey=false;
|
|
1068
510
|
else if (key.trim()){
|
|
1069
511
|
attrs[key.trim()]=true;
|
|
1070
512
|
key="";
|
|
1071
513
|
}
|
|
1072
514
|
continue;
|
|
1073
515
|
}
|
|
1074
516
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
1075
517
|
quoteChar=char;
|
|
1076
518
|
continue;
|
|
1077
519
|
} else if (quoteChar && char===quoteChar){
|
|
1078
520
|
quoteChar=null;
|
|
1079
521
|
attrs[key.trim()]=value.trim();
|
|
1080
522
|
key=""; value=""; isKey=true;
|
|
1081
523
|
continue;
|
|
1082
524
|
}
|
|
1083
525
|
if (isKey) key+=char;
|
|
1084
526
|
else value+=char;
|
|
1085
527
|
}
|
|
1086
528
|
if (key.trim() &&!value) attrs[key.trim()]='';
|
|
1087
529
|
return attrs;
|
|
530
|
+
}
|
|
531
|
+
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
532
|
+
function parseHTML(html){
|
|
1088
533
|
const root=new Root();
|
|
1089
534
|
const stack=[root];
|
|
1090
535
|
let currentText="",i=0;
|
|
1091
536
|
let max=0
|
|
1092
537
|
function parseScript(){
|
|
1093
538
|
if (!html.startsWith("<script",i)) return false;
|
|
1094
539
|
const openTagEnd=html.indexOf(">",i);
|
|
1095
540
|
if (openTagEnd===-1) return false;
|
|
1096
541
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
1097
542
|
const attributes=parseAttributes(attributesString);
|
|
1098
543
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
1099
544
|
if (closeTagStart===-1) return false;
|
|
1100
545
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
1101
546
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
1102
547
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
1103
548
|
i=closeTagStart+9;
|
|
1104
549
|
return true;
|
|
1105
550
|
}
|
|
1106
551
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
1107
552
|
if (!html.startsWith(startStr,i)) return false
|
|
1108
553
|
const end=html.indexOf(endStr,i+n1);
|
|
1109
554
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
1110
555
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
1111
556
|
i=end+n2;
|
|
1112
557
|
return true
|
|
1113
558
|
}
|
|
1114
559
|
while (i< html.length){
|
|
1115
560
|
if(i>=max) max=i;
|
|
1116
561
|
else break;
|
|
1117
562
|
if (parseScript()) continue
|
|
1118
563
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
1119
564
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
1120
565
|
if (html.startsWith("<![CDATA[",i)){
|
|
1121
566
|
const end=html.indexOf("]]>",i+9);
|
|
1122
567
|
if (end===-1) break;
|
|
1123
568
|
const content=html.substring(i+9,end);
|
|
1124
569
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
1125
570
|
cdataNode.nodeValue=content;
|
|
1126
571
|
i=end+3;
|
|
1127
572
|
continue;
|
|
1128
573
|
}
|
|
1129
574
|
if (html.startsWith("<",i)){
|
|
1130
575
|
if (currentText && stack[stack.length-1]){
|
|
1131
576
|
const textNode=new TextNode(currentText)
|
|
1132
577
|
stack[stack.length-1].childNodes.push(textNode);
|
|
1133
578
|
textNode.parent=stack[stack.length-1]
|
|
1134
579
|
currentText="";
|
|
1135
580
|
}
|
|
1136
581
|
let tagEnd=i+1;
|
|
1137
582
|
let insideQuotes=false;
|
|
1138
583
|
let quoteChar=null;
|
|
1139
584
|
while (tagEnd< html.length){
|
|
1140
585
|
const char=html[tagEnd];
|
|
1141
586
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
1142
587
|
insideQuotes=true;
|
|
1143
588
|
quoteChar=char;
|
|
1144
589
|
} else if (insideQuotes && char===quoteChar){
|
|
1145
590
|
insideQuotes=false;
|
|
1146
591
|
quoteChar=null;
|
|
1147
592
|
}
|
|
1148
593
|
if (!insideQuotes && char==='>') break;
|
|
1149
594
|
tagEnd++;
|
|
1150
595
|
}
|
|
1151
596
|
const tagContent=html.substring(i+1,tagEnd);
|
|
1152
597
|
if (tagContent.startsWith("/")) stack.pop();
|
|
1153
598
|
else{
|
|
1154
599
|
let isSelfClosing=tagContent.endsWith('/');
|
|
1155
600
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
1156
601
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
1157
602
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
1158
603
|
const attributes=parseAttributes(attributesString);
|
|
1159
604
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
1160
605
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
1161
606
|
}
|
|
1162
607
|
i=tagEnd+1;
|
|
1163
608
|
} else{
|
|
1164
609
|
currentText+=html[i];
|
|
1165
610
|
i++;
|
|
1166
611
|
}
|
|
1167
612
|
}
|
|
1168
613
|
if (currentText.trim() && stack[stack.length-1]) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
1169
614
|
return root;
|
|
615
|
+
}
|
|
616
|
+
function buildFromCache(cached){
|
|
1170
617
|
function buildNode(cache,parent=null){
|
|
1171
618
|
if(typeof cache==='string') return parent.childNodes.push(cache)
|
|
1172
619
|
const{isSingle,tagName,attributes,childNodes,textContent}=cache
|
|
1173
620
|
if(textContent) return parent.childNodes.push(new TextNode(textContent))
|
|
1174
621
|
if(isSingle) return parent.childNodes.push(new SingleNode(tagName,attributes))
|
|
1175
622
|
const newDoc=tagName==='ROOT' ? new Root() : new Node(tagName,attributes,parent)
|
|
1176
623
|
childNodes.forEach(childNode=>{
|
|
1177
624
|
buildNode(childNode,newDoc)
|
|
1178
625
|
});
|
|
1179
626
|
return newDoc
|
|
1180
627
|
}
|
|
1181
628
|
return buildNode(cached)
|
|
629
|
+
}
|
|
1182
630
|
|
|
631
|
+
function cacheDoc(doc){
|
|
1183
632
|
const props=['isSingle','tagName','attributes']
|
|
1184
633
|
function addToCache(element,cache={}){
|
|
1185
634
|
if(typeof element==='string') return element
|
|
1186
635
|
if(element.nodeName==='#text') return{textContent:element.textContent}
|
|
1187
636
|
props.forEach(prop=>{
|
|
1188
637
|
if(element[prop]) cache[prop]=element[prop]
|
|
1189
638
|
});
|
|
1190
639
|
if(!element.childNodes) return cache
|
|
1191
640
|
cache.childNodes=[]
|
|
1192
641
|
element.childNodes.forEach(childNode=>{
|
|
1193
642
|
cache.childNodes.push(addToCache(childNode))
|
|
1194
643
|
});
|
|
1195
644
|
return cache
|
|
1196
645
|
}
|
|
1197
646
|
return addToCache(doc)
|
|
647
|
+
}
|
|
648
|
+
return { parseHTML, Node, Query, TextNode, SingleNode, buildFromCache, cacheDoc, Root }
|
|
649
|
+
})()
|