als-document 0.11.0 → 1.0.0-alpha

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