als-document 0.12.0 → 1.0.1-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 -589
- package/index.js +32 -0
- package/index.mjs +32 -0
- package/package.json +13 -10
- package/readme.md +196 -156
- 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,590 +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.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,attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
723
139
|
if(typeof el==='string') return false
|
|
724
140
|
if(el.isSpecial) return false
|
|
725
141
|
if(id!==undefined && el.id===null) return false
|
|
726
142
|
if(id && id!==el.id) return false
|
|
727
143
|
if(tag && el.tagName===undefined) return false
|
|
728
144
|
else if(tag && tag!==el.tagName) return false
|
|
729
145
|
const clas=el.attributes.class
|
|
730
146
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
731
147
|
else if(classList!==undefined){
|
|
732
148
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
733
149
|
}
|
|
734
150
|
if(checkattributes(attributes,el)===false) return false
|
|
735
151
|
if(checkElement(el.prev,prev)===false) return false
|
|
736
152
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
737
153
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
738
154
|
if(el.parent){
|
|
739
155
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
740
156
|
}
|
|
741
157
|
return true
|
|
158
|
+
}
|
|
742
159
|
function checkattributes(attributes=[],el){
|
|
743
160
|
let elattributes=el.attributes
|
|
744
161
|
let names=Object.keys(elattributes)
|
|
745
162
|
let passedTests=0
|
|
746
163
|
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
747
164
|
let{name,value,check}=attributes[i]
|
|
748
165
|
if(name=='inner' && value!==undefined && check && el.inner){
|
|
749
166
|
if(check(el.inner)) passedTests++
|
|
750
167
|
}
|
|
751
168
|
if(!names.includes(name)) continue
|
|
752
169
|
else if(value==undefined) passedTests++
|
|
753
170
|
else if(value && elattributes[name]){
|
|
754
171
|
if(check(elattributes[name])==false) continue
|
|
755
172
|
else passedTests++
|
|
756
173
|
}
|
|
757
174
|
}
|
|
758
175
|
if(passedTests==attributes.length) return true
|
|
759
176
|
else return false
|
|
177
|
+
}
|
|
760
178
|
function checkPrevAny(children=[],index,prevAny){
|
|
761
179
|
let size=children.length
|
|
762
180
|
if((size==0 || index==0) && prevAny) return false
|
|
763
181
|
for(let i=index; i>=0; i--){
|
|
764
182
|
if(checkElement(children[i],prevAny)) return true
|
|
765
183
|
}
|
|
766
184
|
return false
|
|
185
|
+
}
|
|
767
186
|
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
768
187
|
let count=0
|
|
769
188
|
if(selectorAncestors.length==0) return true
|
|
770
189
|
let endIndex=ancestors.length-1
|
|
771
190
|
let selectorIndex=selectorAncestors.length-1
|
|
772
191
|
while(selectorIndex>=0){
|
|
773
192
|
for(let i=endIndex; i>=0; i--){
|
|
774
193
|
endIndex=i-1
|
|
775
194
|
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
776
195
|
count++
|
|
777
196
|
break
|
|
778
197
|
}
|
|
779
198
|
}
|
|
780
199
|
selectorIndex--
|
|
781
200
|
}
|
|
782
201
|
if(count==selectorAncestors.length) return true
|
|
783
202
|
else return false
|
|
203
|
+
}
|
|
784
204
|
function checkParents(ancestors=[],selectorParents=[]){
|
|
785
205
|
if(selectorParents.length===0) return true
|
|
786
206
|
if(ancestors.length< selectorParents.length) return false
|
|
787
207
|
let index=ancestors.length-1
|
|
788
208
|
for(let i=selectorParents.length-1; i>=0; i--){
|
|
789
209
|
if(checkElement(ancestors[index],selectorParents[i])===false) return false
|
|
790
210
|
index--
|
|
791
211
|
}
|
|
792
212
|
return true
|
|
213
|
+
}
|
|
214
|
+
const getDataName=prop=>'data-'+prop.toLowerCase()
|
|
215
|
+
function getDataset(element){
|
|
793
216
|
return new Proxy(element.attributes,{
|
|
794
217
|
get: (target,prop)=>{return target[getDataName(prop)]},set: (target,prop,value)=>{target[getDataName(prop)]=value; return true},deleteProperty: (target,prop)=>{
|
|
795
218
|
const dataAttr=getDataName(prop)
|
|
796
219
|
if (dataAttr in target){
|
|
797
220
|
delete target[dataAttr];
|
|
798
221
|
return true;
|
|
799
222
|
}
|
|
800
223
|
return false;
|
|
801
224
|
}
|
|
802
225
|
});
|
|
803
226
|
}
|
|
804
|
-
|
|
227
|
+
|
|
228
|
+
function find(selectors,element,collection,first=false,firstTime=true){
|
|
805
229
|
for(let selector of selectors){
|
|
806
230
|
if(checkElement(element,selector)) collection.add(element)
|
|
807
231
|
}
|
|
808
232
|
if(element.children)
|
|
809
233
|
element.children.forEach(child=>{
|
|
810
234
|
if(first && collection.size>0) return
|
|
811
235
|
find(selectors,child,collection,first,false)
|
|
812
236
|
})
|
|
813
237
|
return firstTime ? [...collection] : collection
|
|
238
|
+
}
|
|
239
|
+
class TextNode{
|
|
814
240
|
constructor(data){
|
|
815
241
|
this.nodeName='#text';
|
|
816
242
|
this.parent=null;
|
|
817
243
|
this.textContent=data;
|
|
818
244
|
}
|
|
819
245
|
get nodeValue(){return this.textContent}
|
|
820
246
|
get parentNode(){return this.parent}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function buildStyle(attributes){
|
|
821
250
|
const styles=attributes.style || "";
|
|
822
251
|
const camelToKebab=str=>str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,'$1-$2').toLowerCase();
|
|
823
252
|
const kebabToCamel=str=>str.replace(/-([a-z])/g,g=>g[1].toUpperCase());
|
|
824
253
|
const baseStyleObj=styles.split(";").reduce((acc,style)=>{
|
|
825
254
|
const [key,value]=style.split(":").map(s=>s.trim());
|
|
826
255
|
if (key && value) acc[kebabToCamel(key)]=value;
|
|
827
256
|
return acc;
|
|
828
257
|
},{});
|
|
829
258
|
return new Proxy(baseStyleObj,{
|
|
830
259
|
get: (obj,prop)=>obj[camelToKebab(prop)] || obj[prop],set: (obj,prop,value)=>{
|
|
831
260
|
obj[camelToKebab(prop)]=value;
|
|
832
261
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
833
262
|
return true;
|
|
834
263
|
},deleteProperty: (obj,prop)=>{
|
|
835
264
|
delete obj[camelToKebab(prop)];
|
|
836
265
|
attributes.style=Object.entries(obj).map(([k,v])=>`${camelToKebab(k)}: ${v}`).join("; ");
|
|
837
266
|
return true;
|
|
838
267
|
}
|
|
839
268
|
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
class NodeClassList{
|
|
840
272
|
constructor(node){ this.node=node }
|
|
841
273
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
842
274
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
843
275
|
contains(className){ return this.classes.includes(className) }
|
|
844
276
|
add(className){
|
|
845
277
|
const currentClasses=this.classes;
|
|
846
278
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
847
279
|
}
|
|
848
280
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
849
281
|
toggle(className){
|
|
850
282
|
if (this.classes.includes(className)) this.remove(className);
|
|
851
283
|
else this.add(className);
|
|
852
284
|
}
|
|
853
285
|
replace(oldClass,newClass){
|
|
854
286
|
if (this.classes.includes(oldClass)){
|
|
855
287
|
this.remove(oldClass);
|
|
856
288
|
this.add(newClass);
|
|
857
289
|
}
|
|
858
290
|
}
|
|
291
|
+
}
|
|
292
|
+
class Node{
|
|
859
293
|
constructor(tagName,attributes={},parent=null){
|
|
860
294
|
this.isSingle=false;
|
|
861
295
|
this.tagName=tagName;
|
|
862
296
|
this.attributes=attributes;
|
|
863
297
|
this.childNodes=[];
|
|
864
298
|
if (parent!==null) parent.childNodes.push(this)
|
|
865
299
|
this.parent=parent;
|
|
866
300
|
this._classList=null;
|
|
867
301
|
this.__style=null;
|
|
868
302
|
this._dataset=null
|
|
869
303
|
}
|
|
870
304
|
get id(){ return this.attributes.id || null; }
|
|
871
305
|
get className(){return this.attributes.class || null}
|
|
872
306
|
get parentNode(){ return this.parent }
|
|
873
307
|
get ancestors(){
|
|
874
308
|
const ancestors=[]
|
|
875
309
|
let element=this.parent
|
|
876
310
|
while (element.tagName!=='ROOT'){
|
|
877
311
|
ancestors.push(element)
|
|
878
312
|
element=element.parent
|
|
879
313
|
}
|
|
880
314
|
return ancestors.reverse()
|
|
881
315
|
}
|
|
882
316
|
get childIndex(){ return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null }
|
|
883
317
|
get previousElementSibling(){ return this.prev }
|
|
884
318
|
get prev(){
|
|
885
319
|
if (!this.childIndex) return null
|
|
886
320
|
return this.parent.childNodes[this.childIndex-1]
|
|
887
321
|
}
|
|
888
322
|
get nextElementSibling(){ return this.next }
|
|
889
323
|
get next(){
|
|
890
324
|
if (!this.childIndex) return null
|
|
891
325
|
return this.parent.childNodes[this.childIndex+1] || null
|
|
892
326
|
}
|
|
893
327
|
get dataset(){
|
|
894
328
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
895
329
|
return this._dataset;
|
|
896
330
|
}
|
|
897
331
|
get classList(){
|
|
898
332
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
899
333
|
return this._classList;
|
|
900
334
|
}
|
|
901
335
|
get style(){
|
|
902
336
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
903
337
|
return this.__style
|
|
904
338
|
}
|
|
905
339
|
get outerHTML(){
|
|
906
340
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
907
341
|
return `<${this.tagName} ${attrs}>${this.innerHTML}</${this.tagName}>`;
|
|
908
342
|
}
|
|
909
343
|
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
910
344
|
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
911
345
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
912
346
|
remove(){
|
|
913
347
|
if (!this.parent) return
|
|
914
348
|
const index=this.childIndex;
|
|
915
349
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
916
350
|
}
|
|
917
351
|
get innerHTML(){
|
|
918
352
|
return this.childNodes.map(child=>{
|
|
919
353
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
920
354
|
else if (child instanceof TextNode) return child.textContent;
|
|
921
355
|
else return child
|
|
922
356
|
}).join("");
|
|
923
357
|
}
|
|
924
358
|
$$(query){return this.querySelectorAll(query)}
|
|
925
359
|
querySelectorAll(query){
|
|
926
360
|
const selectors=Query.get(query)
|
|
927
361
|
return find(selectors,this,new Set())
|
|
928
362
|
}
|
|
929
363
|
$(query){return this.querySelector(query)}
|
|
930
364
|
querySelector(query){
|
|
931
365
|
const selectors=Query.get(query)
|
|
932
366
|
return find(selectors,this,new Set(),true)[0]
|
|
933
367
|
}
|
|
934
368
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
935
369
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
936
370
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
937
371
|
get children(){
|
|
938
372
|
return this.childNodes.filter(child=>{
|
|
939
373
|
if (!(child instanceof Node)) return false
|
|
940
374
|
if (child.tagName==='#comment') return false
|
|
941
375
|
return true
|
|
942
376
|
});
|
|
943
377
|
}
|
|
944
378
|
insertAdjacentElement(position,newElement){
|
|
945
379
|
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
946
380
|
const pos=position.toLowerCase();
|
|
947
381
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
948
382
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
949
383
|
if (!this.parent) return newElement
|
|
950
384
|
if (pos==="beforebegin") this.parent.childNodes.unshift(newElement);
|
|
951
385
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childIndex+1,0,newElement);
|
|
952
386
|
return newElement
|
|
953
387
|
}
|
|
954
388
|
insertAdjacentHTML(position,html){
|
|
955
389
|
const newNode=parseHTML(html);
|
|
956
390
|
return this.insertAdjacentElement(position,newNode);
|
|
957
391
|
}
|
|
958
392
|
insertAdjacentText(position,text){
|
|
959
393
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
960
394
|
}
|
|
961
395
|
set innerHTML(html){
|
|
962
396
|
const parsed=parseHTML(html);
|
|
963
397
|
this.childNodes=parsed.childNodes;
|
|
964
398
|
}
|
|
965
399
|
set outerHTML(html){
|
|
966
400
|
const parsed=parseHTML(html);
|
|
967
401
|
if (!this.parent) return console.log('element has no parent node')
|
|
968
402
|
const index=this.childIndex
|
|
969
403
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
970
404
|
}
|
|
971
405
|
appendChild(newChild){
|
|
972
406
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
973
407
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
974
408
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
975
409
|
else return newChild
|
|
976
410
|
this.childNodes.push(newChild);
|
|
977
411
|
newChild.parent=this;
|
|
978
412
|
return newChild;
|
|
979
413
|
}
|
|
980
414
|
get textContent(){
|
|
981
415
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
982
416
|
return this.childNodes.map(child=>{
|
|
983
417
|
if(child instanceof SingleNode) return ''
|
|
984
418
|
if(child instanceof TextNode) return child.nodeValue
|
|
985
419
|
if(child instanceof Node) return child.textContent;
|
|
986
420
|
else return child;
|
|
987
421
|
}).join(" ");
|
|
988
422
|
}
|
|
989
423
|
set textContent(value){
|
|
990
424
|
this.childNodes=[];
|
|
991
425
|
if (value!==null && value!==undefined){
|
|
992
426
|
this.childNodes.push(value.toString());
|
|
993
427
|
}
|
|
994
428
|
}
|
|
429
|
+
}
|
|
430
|
+
class SingleNode extends Node{
|
|
995
431
|
constructor(tagName,attributes={},parent=null){
|
|
996
432
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
997
433
|
super(tagName,attributes,parent);
|
|
998
434
|
this.isSingle=true
|
|
999
435
|
}
|
|
1000
436
|
get outerHTML(){
|
|
1001
437
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.textContent}]]>`;
|
|
1002
438
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
1003
439
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
1004
440
|
}
|
|
1005
441
|
get innerHTML(){ return ""; }
|
|
1006
442
|
set innerHTML(_){ }
|
|
1007
443
|
$(_){return null}
|
|
1008
444
|
$$(_){return []}
|
|
1009
445
|
querySelectorAll(_){ return []; }
|
|
1010
446
|
querySelector(_){ return null; }
|
|
1011
447
|
getElementsByClassName(_){ return []; }
|
|
1012
448
|
getElementsByTagName(_){ return []; }
|
|
1013
449
|
getElementById(_){ return null; }
|
|
1014
450
|
get children(){ return []; }
|
|
1015
451
|
insertAdjacentElement(_,__){ }
|
|
1016
452
|
insertAdjacentHTML(_,__){ }
|
|
1017
453
|
insertAdjacentText(_,__){ }
|
|
1018
454
|
appendChild(_){ }
|
|
1019
455
|
get textContent(){ return ""; }
|
|
1020
456
|
set textContent(_){ }
|
|
457
|
+
}
|
|
458
|
+
function parseAttributes(str){
|
|
1021
459
|
const attrs={};
|
|
1022
460
|
let key="";
|
|
1023
461
|
let value="";
|
|
1024
462
|
let isKey=true;
|
|
1025
463
|
let quoteChar=null;
|
|
1026
464
|
for (let i=0; i< str.length; i++){
|
|
1027
465
|
const char=str[i];
|
|
1028
466
|
if (isKey && (char==='=' || char===' ')){
|
|
1029
467
|
if (char==='=') isKey=false;
|
|
1030
468
|
else if (key.trim()){
|
|
1031
469
|
attrs[key.trim()]=true;
|
|
1032
470
|
key="";
|
|
1033
471
|
}
|
|
1034
472
|
continue;
|
|
1035
473
|
}
|
|
1036
474
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
1037
475
|
quoteChar=char;
|
|
1038
476
|
continue;
|
|
1039
477
|
} else if (quoteChar && char===quoteChar){
|
|
1040
478
|
quoteChar=null;
|
|
1041
479
|
attrs[key.trim()]=value.trim();
|
|
1042
480
|
key=""; value=""; isKey=true;
|
|
1043
481
|
continue;
|
|
1044
482
|
}
|
|
1045
483
|
if (isKey) key+=char;
|
|
1046
484
|
else value+=char;
|
|
1047
485
|
}
|
|
1048
486
|
if (key.trim() &&!value) attrs[key.trim()]=true;
|
|
1049
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){
|
|
1050
491
|
const root=new Node("ROOT");
|
|
1051
492
|
const stack=[root];
|
|
1052
493
|
let currentText="",i=0;
|
|
1053
494
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
1054
495
|
if (!html.startsWith(startStr,i)) return false
|
|
1055
496
|
const end=html.indexOf(endStr,i+n1);
|
|
1056
497
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
1057
498
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
1058
499
|
i=end+n2;
|
|
1059
500
|
return true
|
|
1060
501
|
}
|
|
1061
502
|
while (i< html.length){
|
|
1062
503
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
1063
504
|
if (parseSpecial("<script","</script>",8,9,'script')) continue
|
|
1064
505
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
1065
506
|
if (html.startsWith("<![CDATA[",i)){
|
|
1066
507
|
const end=html.indexOf("]]>",i+9);
|
|
1067
508
|
if (end===-1) break;
|
|
1068
509
|
const content=html.substring(i+9,end);
|
|
1069
510
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
1070
511
|
cdataNode.nodeValue=content;
|
|
1071
512
|
i=end+3;
|
|
1072
513
|
continue;
|
|
1073
514
|
}
|
|
1074
515
|
if (html.startsWith("<",i)){
|
|
1075
516
|
if (currentText.trim()){
|
|
1076
517
|
stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
1077
518
|
currentText="";
|
|
1078
519
|
}
|
|
1079
520
|
let tagEnd=i+1;
|
|
1080
521
|
let insideQuotes=false;
|
|
1081
522
|
let quoteChar=null;
|
|
1082
523
|
while (tagEnd< html.length){
|
|
1083
524
|
const char=html[tagEnd];
|
|
1084
525
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
1085
526
|
insideQuotes=true;
|
|
1086
527
|
quoteChar=char;
|
|
1087
528
|
} else if (insideQuotes && char===quoteChar){
|
|
1088
529
|
insideQuotes=false;
|
|
1089
530
|
quoteChar=null;
|
|
1090
531
|
}
|
|
1091
532
|
if (!insideQuotes && char==='>') break;
|
|
1092
533
|
tagEnd++;
|
|
1093
534
|
}
|
|
1094
535
|
const tagContent=html.substring(i+1,tagEnd);
|
|
1095
536
|
if (tagContent.startsWith("/")) stack.pop();
|
|
1096
537
|
else{
|
|
1097
538
|
let isSelfClosing=tagContent.endsWith('/');
|
|
1098
539
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
1099
540
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
1100
541
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
1101
542
|
const attributes=parseAttributes(attributesString);
|
|
1102
543
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
1103
544
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
1104
545
|
}
|
|
1105
546
|
i=tagEnd+1;
|
|
1106
547
|
} else{
|
|
1107
548
|
currentText+=html[i];
|
|
1108
549
|
i++;
|
|
1109
550
|
}
|
|
1110
551
|
}
|
|
1111
552
|
if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
1112
553
|
return root;
|
|
554
|
+
}
|
|
555
|
+
return { parseHTML, Node, Query, TextNode, SingleNode }
|
|
556
|
+
})()
|