als-document 0.1.0
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 +584 -0
- package/package.json +14 -0
- package/readme.md +196 -0
package/document.js
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
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 scriptName = `{script${i}}`
|
|
77
|
+
let newScript = script.replace(scriptContent,scriptName)
|
|
78
|
+
this.events[scriptName] = scriptContent
|
|
79
|
+
this.htmlText = this.htmlText.replace(script,newScript)
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
addScripts() {
|
|
86
|
+
this.domList.forEach((element,i) => {
|
|
87
|
+
if(element.tagName == 'script' && !element.close) {
|
|
88
|
+
let text = this.domList[i+1].text
|
|
89
|
+
if(this.scripts[text] !== undefined) {
|
|
90
|
+
this.domList[i+1].text = this.scripts[text]
|
|
91
|
+
delete this.scripts[text]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
addEvents() {
|
|
98
|
+
this.domList.forEach((element,i) => {
|
|
99
|
+
if(element.attributes !== undefined) {
|
|
100
|
+
for(let attName in element.attributes) {
|
|
101
|
+
let attValue = element.attributes[attName]
|
|
102
|
+
if(this.events[attValue] !== undefined) {
|
|
103
|
+
this.domList[i].attributes[attName] = this.events[attValue]
|
|
104
|
+
delete this.events[attValue]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
parseTags() {
|
|
112
|
+
let outerHTML = this.htmlText
|
|
113
|
+
let outerHtmlForCut = outerHTML
|
|
114
|
+
let tags = outerHTML.match(/\<(\w*-?)*?(.*?)?([\S\s]*?)\>/gm)
|
|
115
|
+
let index = 0
|
|
116
|
+
if(tags !== null) tags.forEach((tag,i) => {
|
|
117
|
+
let node = outerHtmlForCut.split(tag)[0]
|
|
118
|
+
if(node.trim() !== '') {
|
|
119
|
+
this.domList.push({text:node,index})
|
|
120
|
+
index++
|
|
121
|
+
}
|
|
122
|
+
let obj = this.buildElementObj(tag)
|
|
123
|
+
let objName = obj.tagName+obj.order
|
|
124
|
+
obj.index = index
|
|
125
|
+
if(obj.order.length == 0) { // single tag
|
|
126
|
+
this.domList.push(obj)
|
|
127
|
+
} else this.domList.push({[objName]:obj,name:objName,index})
|
|
128
|
+
if(obj.close) {
|
|
129
|
+
let j = this.findOpen(objName)
|
|
130
|
+
if(j >=0) {
|
|
131
|
+
let closeTag = index
|
|
132
|
+
let openTagObj = this.domList[j][this.domList[j].name]
|
|
133
|
+
this.domList[j] = {...openTagObj,closeTag}
|
|
134
|
+
}
|
|
135
|
+
this.domList[obj.index] = obj
|
|
136
|
+
}
|
|
137
|
+
outerHtmlForCut = outerHtmlForCut.replace(node+tag,'')
|
|
138
|
+
index++
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// Build element
|
|
142
|
+
buildElementObj(string,obj={}) {
|
|
143
|
+
let singlTags = Document.singlTags
|
|
144
|
+
if(string.match(/\<\/(\w*-?)*/) !== null) obj.close = true
|
|
145
|
+
else obj.close = false
|
|
146
|
+
obj.tagName = string.match(/\<\/?(\w*-?)*/,'gm')[0].replace(/\<\/?/,'')
|
|
147
|
+
if(!singlTags.includes(obj.tagName)) {
|
|
148
|
+
obj.tagName = obj.tagName.replace('/','')
|
|
149
|
+
let tagName = obj.tagName
|
|
150
|
+
this.getType(tagName,obj)
|
|
151
|
+
obj.order = this.types[tagName].count
|
|
152
|
+
} else obj.order = ''
|
|
153
|
+
obj.attributes = this.getAttributes(string)
|
|
154
|
+
obj = this.buildClass(obj)
|
|
155
|
+
if(obj.attributes.id !== undefined) {
|
|
156
|
+
obj.id = obj.attributes.id
|
|
157
|
+
delete obj.attributes.id
|
|
158
|
+
}
|
|
159
|
+
return obj
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getType(tagName,obj) {
|
|
163
|
+
if(this.types[tagName] == undefined)
|
|
164
|
+
this.types[tagName] = {count:0,lastOperation:1}
|
|
165
|
+
else if(obj.close && this.types[tagName].lastOperation == 1)
|
|
166
|
+
this.types[tagName].lastOperation = 0
|
|
167
|
+
else if(obj.close && this.types[tagName].lastOperation == 0)
|
|
168
|
+
this.types[tagName].count--
|
|
169
|
+
else if(!obj.close && this.types[tagName].lastOperation == 0)
|
|
170
|
+
this.types[tagName].lastOperation = 1
|
|
171
|
+
else if(!obj.close && this.types[tagName].lastOperation == 1)
|
|
172
|
+
this.types[tagName].count++
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getAttributes(string,attributes = {}) {
|
|
176
|
+
let matches = string.match(/\s(\/?\w*\-?)*((\S)*?\s?=[\S\s]*?\"[\S\s]*?\")?/g)
|
|
177
|
+
if(matches !== null) matches.forEach(attribute => {
|
|
178
|
+
attribute = attribute.trim()
|
|
179
|
+
if(attribute !== '') {
|
|
180
|
+
let attName = attribute.split('=')[0]
|
|
181
|
+
let attValue = attribute.replace(attName,'').replace('=','').replace(/\"/g,'')
|
|
182
|
+
if(attName !== '/') attributes[attName] = attValue.trim()
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
return attributes
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
buildClass(obj) {
|
|
189
|
+
if(obj.attributes.class !== undefined) {
|
|
190
|
+
obj.classList = obj.attributes.class.split(' ')
|
|
191
|
+
} else obj.classList = []
|
|
192
|
+
obj.classList.add = newClass => {
|
|
193
|
+
obj.classList.push(newClass)
|
|
194
|
+
obj.attributes.class += ' '+newClass
|
|
195
|
+
return obj
|
|
196
|
+
}
|
|
197
|
+
obj.classList.remove = clas => {
|
|
198
|
+
let index = obj.classList.indexOf(clas)
|
|
199
|
+
if(index >=0) {
|
|
200
|
+
obj.attributes.class = obj.attributes.class.replace(obj.classList[index],'')
|
|
201
|
+
obj.classList.splice(index,1)
|
|
202
|
+
}
|
|
203
|
+
return obj
|
|
204
|
+
}
|
|
205
|
+
return obj
|
|
206
|
+
}
|
|
207
|
+
// Build dom tree
|
|
208
|
+
getPairs(start=0,end=this.domList.length,domTree=this.domTree) {
|
|
209
|
+
for(let index=start; index<end; index++) {
|
|
210
|
+
let node = this.domList[index]
|
|
211
|
+
if(node.closeTag !== undefined && !this.added.includes(index)) {
|
|
212
|
+
if(this.firstTag == undefined) this.firstTag = index
|
|
213
|
+
this.buildTag(index,domTree)
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
buildTag(openIndex,domTree,parent=this.domTree) {
|
|
220
|
+
this.added.push(openIndex)
|
|
221
|
+
let content = this.domList[openIndex]
|
|
222
|
+
content.children = []
|
|
223
|
+
content.innerText = ''
|
|
224
|
+
this.getChildNodes(openIndex,content.closeTag,content)
|
|
225
|
+
content.$ = (selector) => this.$(selector,content)
|
|
226
|
+
content.$$ = (selector) => this.$$(selector,content)
|
|
227
|
+
content.parent = parent
|
|
228
|
+
content = Document.buildMethods(content)
|
|
229
|
+
this.buildOperations(content)
|
|
230
|
+
if(content.innerText !== '') {
|
|
231
|
+
if(parent.innerText == undefined) parent.innerText = ''
|
|
232
|
+
parent.innerText += '|'+content.innerText
|
|
233
|
+
}
|
|
234
|
+
content.json = () => Document.removeMethods(content)
|
|
235
|
+
delete content.closeTag
|
|
236
|
+
delete content.close
|
|
237
|
+
delete content.index
|
|
238
|
+
delete content.order
|
|
239
|
+
domTree.push(content)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
static buildMethods(content) {
|
|
243
|
+
content.children.forEach((child,index) => { // prev and next
|
|
244
|
+
if(index == 0) child.prev = null
|
|
245
|
+
else child.prev = content.children[index-1]
|
|
246
|
+
if(index == content.children.length-1) child.next = null
|
|
247
|
+
else child.next = content.children[index+1]
|
|
248
|
+
});
|
|
249
|
+
return content
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
static changeByIndex(array,value,item,before = true) {
|
|
253
|
+
let index = array.indexOf(value);
|
|
254
|
+
if(index > -1) {
|
|
255
|
+
if(item == undefined) array.splice(index, 1); // remove value
|
|
256
|
+
else if(item !== undefined) {
|
|
257
|
+
if(before) array.splice(index, 0, item); // add item before value
|
|
258
|
+
else array.splice(index+1, 0, item); // add item after value
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static buildInnerText(element) {
|
|
264
|
+
element.innerText = ''
|
|
265
|
+
if(element.children !== undefined) element.children.forEach(child => {
|
|
266
|
+
if(child.text !== undefined && child.text !== '') element.innerText += child.text
|
|
267
|
+
if(child.innerText !== undefined && child.innerText !== '') element.innerText += '|'+child.innerText
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
buildOperations(content) {
|
|
272
|
+
content.remove = function() {
|
|
273
|
+
Document.changeByIndex(content.parent.children,content)
|
|
274
|
+
Document.buildInnerText(content.parent)
|
|
275
|
+
}
|
|
276
|
+
content.add = function(element,place) {
|
|
277
|
+
if(typeof element == 'string') element = Document.newElement(element)
|
|
278
|
+
else element.remove()
|
|
279
|
+
if(place == 1) content.children.unshift(element)
|
|
280
|
+
if(place == 2) content.children.push(element)
|
|
281
|
+
if(place == 1 || place == 2) build(content)
|
|
282
|
+
|
|
283
|
+
if(place == 0) Document.changeByIndex(content.parent.children,content,element,true)
|
|
284
|
+
if(place == 3) Document.changeByIndex(content.parent.children,content,element,false)
|
|
285
|
+
if(place == 0 || place == 3) build(content.parent)
|
|
286
|
+
|
|
287
|
+
function build(parent) {
|
|
288
|
+
element.parent = parent
|
|
289
|
+
element = Document.buildMethods(parent)
|
|
290
|
+
Document.buildInnerText(parent)
|
|
291
|
+
}
|
|
292
|
+
return element
|
|
293
|
+
}
|
|
294
|
+
content.add0 = element => content.add(element,0)
|
|
295
|
+
content.add1 = element => content.add(element,1)
|
|
296
|
+
content.add2 = element => content.add(element,2)
|
|
297
|
+
content.add3 = element => content.add(element,3)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static newElement(outerHtml) {
|
|
301
|
+
let document = new Document(outerHtml)
|
|
302
|
+
return document.domTree[0].json()
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
build(filePath,array=this.domTree,html = '',space='') {
|
|
306
|
+
let extraSpace = space+' '
|
|
307
|
+
let nl = `
|
|
308
|
+
`
|
|
309
|
+
array.forEach(element => {
|
|
310
|
+
if(element.text !== undefined) html += space+element.text+nl
|
|
311
|
+
else if(element.tagName == 'comment' ) {
|
|
312
|
+
html += space+element.comment + nl
|
|
313
|
+
} else {
|
|
314
|
+
let attributes = ''
|
|
315
|
+
for(let propName in element.attributes) {
|
|
316
|
+
let value = element.attributes[propName]
|
|
317
|
+
let attribute = ` ${propName}="${value}"`
|
|
318
|
+
if(attribute.length + attributes.length > 80) attribute = nl+space+attribute
|
|
319
|
+
attributes += attribute
|
|
320
|
+
}
|
|
321
|
+
let id = ''
|
|
322
|
+
if(element.id !== undefined) id = ` id="${element.id}"`
|
|
323
|
+
html += `${space}<${element.tagName}${id}${attributes}>${nl}`
|
|
324
|
+
if(element.children !== undefined)
|
|
325
|
+
if(element.children.length >0) html = this.build(filePath,element.children,html,extraSpace)
|
|
326
|
+
if(element.tagName !== undefined && !Document.singlTags.includes(element.tagName))
|
|
327
|
+
html += `${space}</${element.tagName}>${nl}`
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
if(array == this.domTree) {
|
|
331
|
+
html = this.doctype+nl+html
|
|
332
|
+
if(filePath !== undefined) Document.writeFile(filePath,html)
|
|
333
|
+
}
|
|
334
|
+
return html
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
getChildNodes(openIndex,closeIndex,content) {
|
|
338
|
+
for(let i = openIndex+1; i<closeIndex; i++) {
|
|
339
|
+
let node = this.domList[i]
|
|
340
|
+
if(!this.added.includes(i)) {
|
|
341
|
+
if(!node.close && node.text == undefined) this.buildTag(node.index,content.children,content)
|
|
342
|
+
else if(!node.close && node.text !== undefined) {
|
|
343
|
+
content.innerText += node.text
|
|
344
|
+
content.children.push(node);
|
|
345
|
+
delete node.index
|
|
346
|
+
}
|
|
347
|
+
this.added.push(i)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
findOpen(key,array=this.domList) {
|
|
353
|
+
return array.findIndex(function(obj, index) {
|
|
354
|
+
if(obj[key] !== undefined && obj[key].close == false && obj[key].closeTag == undefined)
|
|
355
|
+
return true;
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
// querySelector
|
|
359
|
+
$(selector,obj) {return this.$$(selector,obj,true)}
|
|
360
|
+
|
|
361
|
+
$$(selector='',obj=this.domTree[0],first=false) {
|
|
362
|
+
if(typeof selector !== 'string') return
|
|
363
|
+
this.selector = selector
|
|
364
|
+
this.selectors = []
|
|
365
|
+
this.getSelectors(selector)
|
|
366
|
+
let array = this.findElements(obj)
|
|
367
|
+
if(first) return array[0]
|
|
368
|
+
else return this.buildCollection(array)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
getSelectors() {
|
|
372
|
+
this.getPropSelctors() // remove all attributes from selector, build them, replace as [i] and put into selectors.attributes[]
|
|
373
|
+
let groups = this.selector.split(',')
|
|
374
|
+
groups.forEach(group => {
|
|
375
|
+
let result = {}
|
|
376
|
+
group = group.trim()
|
|
377
|
+
let family = group.split('>')
|
|
378
|
+
let prevBrother = group.split('+')
|
|
379
|
+
let nextBrother = group.split('~')
|
|
380
|
+
if(family.length > 1) {
|
|
381
|
+
result.parent = this.buildSingleSelector(family[0])
|
|
382
|
+
result.element = this.buildSingleSelector(family[1])
|
|
383
|
+
} else if(prevBrother.length > 1) {
|
|
384
|
+
result.prev = this.buildSingleSelector(prevBrother[0])
|
|
385
|
+
result.element = this.buildSingleSelector(prevBrother[1])
|
|
386
|
+
} else if(nextBrother.length > 1) {
|
|
387
|
+
result.next = this.buildSingleSelector(nextBrother[0])
|
|
388
|
+
result.element = this.buildSingleSelector(nextBrother[1])
|
|
389
|
+
} else result.element = this.buildSingleSelector(group)
|
|
390
|
+
this.selectors.push(result)
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
getPropSelctors() {
|
|
395
|
+
let signs = ['~','|','^','$','*']
|
|
396
|
+
let props = this.selector.match(/\[.*\]/g)
|
|
397
|
+
this.attributes = []
|
|
398
|
+
if(props !== null) props.forEach((prop,i) => {
|
|
399
|
+
this.selector = this.selector.replace(prop,`[${i}]`)
|
|
400
|
+
let attribute = prop.replace('[','').replace(']','')
|
|
401
|
+
let array = attribute.split('=')
|
|
402
|
+
let propName = array[0]
|
|
403
|
+
let propValue = (array[1] == undefined) ? '' : array[1].replace(/"/g,'')
|
|
404
|
+
let sign
|
|
405
|
+
if(signs.includes(propName.slice(-1))) {
|
|
406
|
+
sign = propName.slice(-1)
|
|
407
|
+
propName = propName.slice(0, -1)
|
|
408
|
+
}
|
|
409
|
+
this.attributes.push({propName,propValue,sign})
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
buildSingleSelector(selector,result={}) {
|
|
414
|
+
selector = selector.trim()
|
|
415
|
+
let props = selector.match(/\[.*\]/g)
|
|
416
|
+
if(props !== null) props.forEach(prop => {
|
|
417
|
+
selector = selector.replace(prop,'')
|
|
418
|
+
let index = prop.replace('[','').replace(']','')
|
|
419
|
+
result.attributes = this.attributes[index]
|
|
420
|
+
});
|
|
421
|
+
let id = selector.match(/#(\w*-?)*/)
|
|
422
|
+
if(id !== null) {
|
|
423
|
+
result.id = id[0].replace('#','')
|
|
424
|
+
selector = selector.replace(id[0],'')
|
|
425
|
+
}
|
|
426
|
+
let classes = selector.match(/\.(\w*-?)*/g)
|
|
427
|
+
if(classes !== null) {
|
|
428
|
+
result.classes = []
|
|
429
|
+
classes.forEach(clas => {
|
|
430
|
+
result.classes.push(clas.replace('.',''))
|
|
431
|
+
selector = selector.replace(clas,'')
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
let tag = selector.match(/(\w*-?)*/)
|
|
435
|
+
if(tag !== null) {
|
|
436
|
+
if(tag[0] !== '') {
|
|
437
|
+
result.tag = tag[0]
|
|
438
|
+
selector = selector.replace(tag[0],'')
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return result
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
findElements(obj,array = []) {
|
|
445
|
+
array = this.checkSelectors(obj,array)
|
|
446
|
+
if(obj.children !== undefined) {
|
|
447
|
+
obj.children.forEach(element => {
|
|
448
|
+
this.findElements(element,array)
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
return array
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
checkSelectors(element,array) {
|
|
455
|
+
this.selectors.forEach(group => {
|
|
456
|
+
let addIt = 0
|
|
457
|
+
if(group.parent !== undefined) addIt += this.checkElement(element.parent,group.parent)
|
|
458
|
+
if(group.prev !== undefined) addIt += this.checkElement(element.prev,group.prev)
|
|
459
|
+
if(group.next !== undefined) addIt += this.checkElement(element.next,group.next)
|
|
460
|
+
if(group.element !== undefined) addIt += this.checkElement(element,group.element)
|
|
461
|
+
if(addIt == 0) array.push(element)
|
|
462
|
+
});
|
|
463
|
+
return array
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
checkElement(element,selectors,addIt = 0) {
|
|
467
|
+
if(element == undefined) return -1
|
|
468
|
+
if(selectors.tag !== undefined)
|
|
469
|
+
if(element.tagName !== selectors.tag) addIt--
|
|
470
|
+
if(selectors.id !== undefined)
|
|
471
|
+
if(element.id !== selectors.id) addIt--
|
|
472
|
+
if(selectors.classes !== undefined ) {
|
|
473
|
+
if(element.classList !== undefined) selectors.classes.forEach(clas => {
|
|
474
|
+
if(!element.classList.includes(clas)) addIt--
|
|
475
|
+
});
|
|
476
|
+
else addIt--
|
|
477
|
+
}
|
|
478
|
+
if(selectors.attributes !== undefined) addIt += this.checkAttributes(
|
|
479
|
+
element.attributes,
|
|
480
|
+
selectors.attributes.propName,
|
|
481
|
+
selectors.attributes.propValue,
|
|
482
|
+
selectors.attributes.sign
|
|
483
|
+
)
|
|
484
|
+
return addIt
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
checkAttributes(props,propName,propValue,sign,addIt = 0) {
|
|
488
|
+
if(props == undefined) return -1
|
|
489
|
+
else {
|
|
490
|
+
if(propValue == '' && props[propName] == undefined) addIt--
|
|
491
|
+
if(propValue.length>0 && props[propName] !== undefined) {
|
|
492
|
+
if(sign == undefined) {
|
|
493
|
+
if(props[propName] !== propValue) addIt--
|
|
494
|
+
} else if(sign == '~') {
|
|
495
|
+
let r = new RegExp(`\\b${propValue}`)
|
|
496
|
+
if(props[propName].match(r) == null) addIt--
|
|
497
|
+
} else if(sign == '|') {
|
|
498
|
+
if(!props[propName].startsWith(propValue) || props[propName] !== propValue) addIt--
|
|
499
|
+
} else if(sign == '^') {
|
|
500
|
+
if(!props[propName].startsWith(propValue)) addIt--
|
|
501
|
+
} else if(sign == '$') {
|
|
502
|
+
if(!props[propName].endsWith(propValue)) addIt--
|
|
503
|
+
} else if(sign == '*') {
|
|
504
|
+
if(!props[propName].includes(propValue)) addIt--
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return addIt
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Collection array
|
|
512
|
+
buildCollection(collection) {
|
|
513
|
+
collection.each = function(fn) {return Document.each(this,fn)}
|
|
514
|
+
collection.parse = function(part,fn) {return Document.parse(this,part,fn)}
|
|
515
|
+
return collection
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
static each(collection,fn) {
|
|
519
|
+
for(let i = 0; i<collection.length; i++) {
|
|
520
|
+
fn(collection[i],i,collection)
|
|
521
|
+
}
|
|
522
|
+
return collection
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
static parse(collection,part,fn,array=[]) {
|
|
526
|
+
if(part !== undefined) {
|
|
527
|
+
collection.each((element,index,collection) => {
|
|
528
|
+
let content
|
|
529
|
+
if(element[part] !== undefined) content = element[part]
|
|
530
|
+
else if(element.attributes !== undefined)
|
|
531
|
+
if(element.attributes[part] !== undefined)
|
|
532
|
+
content = element.attributes[part]
|
|
533
|
+
|
|
534
|
+
if(content !== undefined) {
|
|
535
|
+
if(typeof content == 'string')
|
|
536
|
+
content = content.replace(/ /g,' ')
|
|
537
|
+
if(fn !== undefined) {
|
|
538
|
+
if(fn(content)) array.push(content)
|
|
539
|
+
} else array.push(content)
|
|
540
|
+
}
|
|
541
|
+
})
|
|
542
|
+
} else {
|
|
543
|
+
collection.forEach(element => {
|
|
544
|
+
array.push(Document.removeMethods(element))
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return array
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
static removeMethods(element) {
|
|
551
|
+
element = {...element}
|
|
552
|
+
let deleteThis = ['parent','next','prev','classList','json','$','$$','remove','add','add0','add1','add2','add3']
|
|
553
|
+
deleteThis.forEach(item => {
|
|
554
|
+
delete element[item]
|
|
555
|
+
});
|
|
556
|
+
if(element.children !== undefined) element.children.forEach((child,i) => {
|
|
557
|
+
element.children[i] = Document.removeMethods(child)
|
|
558
|
+
});
|
|
559
|
+
return element
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
static writeFile(path,obj,encoding = 'utf-8') {
|
|
563
|
+
let {writeFileSync} = require('fs')
|
|
564
|
+
path = Document.path(path)
|
|
565
|
+
if(typeof obj !== 'string') try {
|
|
566
|
+
obj = JSON.stringify(obj,null,4)
|
|
567
|
+
writeFileSync(path,obj,encoding)
|
|
568
|
+
} catch(e) {console.log(e)}
|
|
569
|
+
else writeFileSync(path,obj,encoding)
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
static readFile(path,encoding = 'utf-8') {
|
|
573
|
+
path = Document.path(path)
|
|
574
|
+
let {readFileSync} = require('fs')
|
|
575
|
+
return readFileSync(path,encoding)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
static path(path) {
|
|
579
|
+
let {join} = require('path')
|
|
580
|
+
if(Array.isArray(path)) return join(...path)
|
|
581
|
+
else return path
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
module.exports = Document
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "als-document",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "virtual dom",
|
|
5
|
+
"main": "document.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"virtual DOM"
|
|
11
|
+
],
|
|
12
|
+
"author": "Alex Sorkin",
|
|
13
|
+
"license": "ISC"
|
|
14
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Als-Document
|
|
2
|
+
|
|
3
|
+
*If something wrong or not working properly, please write me to: sh.mashkanta@gmail.com*
|
|
4
|
+
|
|
5
|
+
Document is a class which gets html as string and return new object with DOM tree.
|
|
6
|
+
You can add or remove elements from DOM tree and modify each element.
|
|
7
|
+
You can select elements and collections and read and modify them with given instruments.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Write and Read files
|
|
11
|
+
Document have 2 static methods to read and write files.
|
|
12
|
+
The syntax:
|
|
13
|
+
```javascript
|
|
14
|
+
Document.writeFile(filePath,obj,encoding = 'utf-8')
|
|
15
|
+
Document.readFile(filePath,encoding = 'utf-8')
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
* ``filepath`` - filepath can be string (absolute path to file) or array for joining.
|
|
19
|
+
* ``obj`` - obj can be string or object for stringify.
|
|
20
|
+
* ``encoding`` - encoding for read or write file
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
```javascript
|
|
24
|
+
let {Document} = require('als-document')
|
|
25
|
+
let html = Document.readFile([__dirname,'index.html'])
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Creating new object
|
|
30
|
+
|
|
31
|
+
Document constructor get single string parameter - the outerHTML for converting to virtual DOM tree.
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
let document = new Document(html) // html has to be string
|
|
35
|
+
document.domTree // includes virtual DOM tree as array of elements
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## QuerySelector for single element
|
|
40
|
+
Then document object has created, you can select elements or collections.
|
|
41
|
+
For selecting single element, use ``$(selector)`` and for selecting collections ``$$(selector)``.
|
|
42
|
+
|
|
43
|
+
**Selecting element**
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
document.$('div') // select first div in document
|
|
47
|
+
document.$('div.some') // select first div element with some class
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
At this time, selector supports this:
|
|
51
|
+
* Selects all elements - ``*``
|
|
52
|
+
* element - ``div``
|
|
53
|
+
* class - ``.some-class``
|
|
54
|
+
* id - ``#some-id``
|
|
55
|
+
* parent - ``div > p``
|
|
56
|
+
* next - ``div + p``
|
|
57
|
+
* previous - ``p ~ ul``
|
|
58
|
+
* attribute - ``[some-attribute="some value"]``
|
|
59
|
+
* ``[prop]``
|
|
60
|
+
* ``[prop~=value]``
|
|
61
|
+
* ``[prop|=value]``
|
|
62
|
+
* ``[prop^="value"]``
|
|
63
|
+
* ``[prop$="value"]``
|
|
64
|
+
* ``[prop*="value"]``
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
The folowing, **won't work**: ``div p``.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Each returned element, has the folowing:
|
|
71
|
+
```javascript
|
|
72
|
+
{
|
|
73
|
+
parent, // parent element
|
|
74
|
+
prev, // previous element (null if no exists)
|
|
75
|
+
next, // next element (null if no exists)
|
|
76
|
+
innerText, // innner text of element and it's childNodes separated by |
|
|
77
|
+
children, // array of childNodes(elements and text nodes) - includes text element too
|
|
78
|
+
tagName, // tag name of element
|
|
79
|
+
id, // id of element if exists (not included in attributes)
|
|
80
|
+
attributes, // object of attributes (id not included)
|
|
81
|
+
classList, // array of classes and add and remove methods
|
|
82
|
+
$(selector),
|
|
83
|
+
$$(selector),
|
|
84
|
+
json(), // remove all methods and circular objects from object
|
|
85
|
+
remove(), // remove this element
|
|
86
|
+
add(element/outerHtml,place),
|
|
87
|
+
add0(element/outerHtml),
|
|
88
|
+
add1(element/outerHtml),
|
|
89
|
+
add2(element/outerHtml),
|
|
90
|
+
add3(element/outerHtml),
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Text node has the folowing:
|
|
95
|
+
```javascript
|
|
96
|
+
{
|
|
97
|
+
text,
|
|
98
|
+
prev,
|
|
99
|
+
next
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Comment node:
|
|
104
|
+
```javascript
|
|
105
|
+
tagName:comment,
|
|
106
|
+
comment // comment it self
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
You can add or remove classes with classList methods.
|
|
110
|
+
Example:
|
|
111
|
+
```javascript
|
|
112
|
+
let element = document.$('div')
|
|
113
|
+
element.classList.remove('some')
|
|
114
|
+
element.classList.add('another')
|
|
115
|
+
element.classList.add('onemore')
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Also you can change element's id:
|
|
119
|
+
```javascript
|
|
120
|
+
let element = document.$('div')
|
|
121
|
+
element.id = 'new-id'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Element methods
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
json() // remove all methods and circular objects from object
|
|
128
|
+
remove() // remove this element
|
|
129
|
+
add(element/outerHtml,place) // adding AdjacentHTML or AdjacentElement to place(0-3)
|
|
130
|
+
add0(element/outerHtml) // adding AdjacentHTML or AdjacentElement beforebegin
|
|
131
|
+
add1(element/outerHtml) // adding AdjacentHTML or AdjacentElement afterbegin
|
|
132
|
+
add2(element/outerHtml) // adding AdjacentHTML or AdjacentElement beforeend
|
|
133
|
+
add3(element/outerHtml) // adding AdjacentHTML or AdjacentElement afterend
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
```javascript
|
|
138
|
+
let document = new Document(html)
|
|
139
|
+
let a = document.$('a')
|
|
140
|
+
let div = document.$('div')
|
|
141
|
+
div.add2('<div id="test">Hello world</div>')
|
|
142
|
+
div.add3(a)
|
|
143
|
+
a.remove()
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
Create new element with ``Document.newElement(outerHtml)``
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
Document.newElement('<div id="test">Hello world</div>')
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
## QuerySelector for Collection ``$$()``
|
|
155
|
+
To select few elements, use ``$$(selector)`` method.
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
document.$$('div') // return collection of all div elements
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The collection is array which has the elements and two methods: ``each`` and ``parse``.
|
|
162
|
+
|
|
163
|
+
``each`` method gets callback function with 3 parameters: element it self, index of the element in collection and collection itself.
|
|
164
|
+
|
|
165
|
+
Here example:
|
|
166
|
+
```javascript
|
|
167
|
+
let array = []
|
|
168
|
+
document.$$('div').each((element,index,collection) => {
|
|
169
|
+
if(element.innerText.includes('some text'))
|
|
170
|
+
array.push(element)
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
``parse`` method, gets two parameters: ``part`` and ``fn`` and return array with results.
|
|
175
|
+
* ``part`` is a part of element. It can be innerText, id, tagName or any property inside attributes.
|
|
176
|
+
* ``fn`` is a filter function which gets content of part. If return true, content will be included.
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
```javascript
|
|
180
|
+
new Document(htmlText).$$('div')
|
|
181
|
+
.parse('innerText',
|
|
182
|
+
content=> (content.length > 0) ? true : false)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Building html
|
|
186
|
+
|
|
187
|
+
For building html again, use ``build`` method.
|
|
188
|
+
Example:
|
|
189
|
+
```javascript
|
|
190
|
+
let element = document.$('div')
|
|
191
|
+
element.classList.add('another')
|
|
192
|
+
element.classList.remove('some')
|
|
193
|
+
element.id = 'new-id'
|
|
194
|
+
document.build() // return new html text
|
|
195
|
+
document.build([__dirname,'new-index.html']) // will create a file with new html text
|
|
196
|
+
```
|