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