als-document 0.12.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 CHANGED
@@ -1,590 +1,34 @@
1
- class Document {
2
- static singlTags = ['comment','area','base','br','col','command','embed','hr','img','input','keygen','link','meta','param','source','track','wbr']
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(/&nbsp;/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
- module.exports = Document
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
+ })()