als-document 1.0.2-alpha → 1.0.4-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 +4 -3
- package/index.js +4 -3
- package/index.mjs +4 -3
- package/package.json +1 -1
- package/src/build.js +2 -0
- package/src/node/node.js +29 -8
- package/src/parse/parser.js +27 -5
- package/src/query/check-element.js +1 -1
- package/tests/index.html +1 -1
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class Query{
|
|
2
2
|
static get(query){
|
|
3
3
|
let q=new Query(query)
|
|
4
4
|
return q.selectors
|
|
5
5
|
}
|
|
6
6
|
constructor(query){
|
|
7
7
|
this.query=query
|
|
8
8
|
this.selectors=[]
|
|
9
9
|
this.stringValues=[];
|
|
10
10
|
this.parseSelectors(query.split(','))
|
|
11
11
|
}
|
|
12
12
|
parseSelectors(selectors){
|
|
13
13
|
selectors.forEach(selector=>{
|
|
14
14
|
let originalSelector=selector.trim()
|
|
15
15
|
selector=this.removeSpaces(selector)
|
|
16
16
|
this.stringValues=[]
|
|
17
17
|
selector=selector.replace(/\[.*?\]/g,(value)=>{
|
|
18
18
|
this.stringValues.push(value)
|
|
19
19
|
return `[${this.stringValues.length-1}]`
|
|
20
20
|
})
|
|
21
21
|
let [element,ancestors]=this.splitAndCutLast(selector,' ')
|
|
22
22
|
element=this.getFamily(element)
|
|
23
23
|
if (ancestors.length>0)
|
|
24
24
|
element.ancestors=ancestors.map(ancestor=>this.getFamily(ancestor))
|
|
25
25
|
element.group=originalSelector
|
|
26
26
|
this.selectors.push(element)
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
splitAndCutLast(string,splitBy){
|
|
30
30
|
const array=string.split(splitBy);
|
|
31
31
|
const last=array.pop();
|
|
32
32
|
return [last,array];
|
|
33
33
|
}
|
|
34
34
|
getFamily(group,element,prev,prevAny,sign){
|
|
35
35
|
if (group.match(/\~|\+/)!==null){
|
|
36
36
|
let [last,prevBrothers]=this.splitAndCutLast(group,/\~|\+/)
|
|
37
37
|
let signs=group.replace(last,'')
|
|
38
38
|
prevBrothers.forEach(el=>signs=signs.replace(el,''))
|
|
39
39
|
signs=signs.match(/\~|\+/g)
|
|
40
40
|
if (signs.length==1){
|
|
41
41
|
sign=signs[0]
|
|
42
42
|
} else if (signs.length>1){
|
|
43
43
|
sign=signs.splice(signs.length-1,signs.length-1)[0]
|
|
44
44
|
prevBrothers[0]=prevBrothers.map((b,i)=>{
|
|
45
45
|
if (i< prevBrothers.length-1) b+=signs[i]
|
|
46
46
|
return b
|
|
47
47
|
}).join('')
|
|
48
48
|
prevBrothers[0]=this.getFamily(prevBrothers[0])
|
|
49
49
|
}
|
|
50
50
|
if (sign=='~') prevAny=prevBrothers[0]
|
|
51
51
|
else if (sign=='+') prev=prevBrothers[0]
|
|
52
52
|
element=last
|
|
53
53
|
} else element=group
|
|
54
54
|
let family
|
|
55
55
|
if (prev || prevAny){
|
|
56
56
|
family=this.getParents(element)
|
|
57
57
|
if (prev) family.prev=this.getParents(prev)
|
|
58
58
|
if (prevAny) family.prevAny=this.getParents(prevAny)
|
|
59
59
|
} else family=this.getParents(element)
|
|
60
60
|
if (family.query!==group) family.group=group
|
|
61
61
|
return family
|
|
62
62
|
}
|
|
63
63
|
getParents(selector){
|
|
64
64
|
if (typeof selector=='string'){
|
|
65
65
|
let [element,parents]=this.splitAndCutLast(selector,'>')
|
|
66
66
|
element=this.buildElement(element)
|
|
67
67
|
parents=parents.map(parent=>this.buildElement(parent))
|
|
68
68
|
if (parents.length>0) element.parents=parents
|
|
69
69
|
return element
|
|
70
70
|
} else return selector
|
|
71
71
|
}
|
|
72
72
|
buildElement(element,id=null,tag=null,classList=[]){
|
|
73
73
|
let query=element
|
|
74
74
|
element=element.replace(/\#(\w-?)*/,$id=>{
|
|
75
75
|
id=$id.replace(/^\#/,''); return ''
|
|
76
76
|
})
|
|
77
77
|
element=element.replace(/\.(\w-?)*/,$class=>{
|
|
78
78
|
classList.push($class.replace(/^\./,'')); return ''
|
|
79
79
|
})
|
|
80
80
|
element=element.replace(/(\w\:?-?)*/,$tag=>{
|
|
81
81
|
tag=$tag=='' ? null : $tag; return ''
|
|
82
82
|
})
|
|
83
83
|
let attribs=this.getAttributes(element)
|
|
84
84
|
element={ query }
|
|
85
85
|
if (id) element.id=id
|
|
86
86
|
if (tag) element.tag=tag
|
|
87
87
|
if (classList.length>0) element.classList=classList
|
|
88
88
|
if (attribs.length>0) element.attribs=attribs
|
|
89
89
|
return element
|
|
90
90
|
}
|
|
91
91
|
getAttributes(element){
|
|
92
92
|
let attribs=this.stringValues.filter((value,index)=>{
|
|
93
93
|
let searchValue=`[${index}]`
|
|
94
94
|
if (element.match(searchValue)) return true
|
|
95
95
|
else return false
|
|
96
96
|
})
|
|
97
97
|
attribs=attribs.map(attrib=>{
|
|
98
98
|
let query=attrib
|
|
99
99
|
attrib=attrib.replace('[','').replace(']','')
|
|
100
100
|
let [name,value]=attrib.split(/[\~\|\^\$\*]?\=/)
|
|
101
101
|
let sign=attrib.replace(name,'').replace(value,'')
|
|
102
102
|
attrib={ query }
|
|
103
103
|
if (name) attrib.name=name
|
|
104
104
|
if (value) attrib.value=value.trim().replace(/^\"/,'').replace(/\"$/,'')
|
|
105
105
|
if (sign){
|
|
106
106
|
attrib.sign=sign
|
|
107
107
|
attrib.check=this.getAttribFn(sign).bind(attrib)
|
|
108
108
|
}
|
|
109
109
|
return attrib
|
|
110
110
|
});
|
|
111
111
|
return attribs
|
|
112
112
|
}
|
|
113
113
|
getAttribFn(sign){
|
|
114
114
|
if (sign=='=') return function (value){ return value===this.value }
|
|
115
115
|
if (sign=='*=') return function (value){ return value.includes(this.value) }
|
|
116
116
|
if (sign=='^=') return function (value){ return value.startsWith(this.value) }
|
|
117
117
|
if (sign=='$=') return function (value){ return value.endsWith(this.value) }
|
|
118
118
|
if (sign=='|=') return function (value){
|
|
119
119
|
return value.trim().split(' ').length==1
|
|
120
120
|
&& (value.startsWith(this.value) || value.startsWith(this.value+'-'))
|
|
121
121
|
? true : false
|
|
122
122
|
}
|
|
123
123
|
if (sign=='~=') return function (value){
|
|
124
124
|
return this.value.trim().split(' ').length==1 && value.includes(this.value) ? true : false
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
removeSpaces(selector){
|
|
128
128
|
selector=selector.replace(/\s{2}/g,' ')
|
|
129
129
|
selector=selector.replace(/\s?\^?\$?\|?\~?\*?\=\s*/g,(m)=>m.trim())
|
|
130
130
|
selector=selector.replace(/\s?(\+|\~|\>)\s?/g,(m)=>m.trim())
|
|
131
131
|
return selector
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
-
function checkElement(el,selector){
|
|
135
134
|
if(selector==undefined) return true
|
|
136
135
|
if(el==null) return false
|
|
137
136
|
let{tag,classList,attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
138
137
|
if(typeof el==='string') return false
|
|
139
138
|
if(id!==undefined && el.id===null) return false
|
|
140
139
|
if(id && id!==el.id) return false
|
|
141
140
|
if(tag && el.tagName===undefined) return false
|
|
142
141
|
else if(tag && tag!==el.tagName) return false
|
|
143
142
|
const clas=el.attributes.class
|
|
144
143
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
145
144
|
else if(classList!==undefined){
|
|
146
145
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
147
146
|
}
|
|
148
147
|
if(checkattributes(attributes,el)===false) return false
|
|
149
148
|
if(checkElement(el.prev,prev)===false) return false
|
|
150
149
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
151
150
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
152
151
|
if(el.parent){
|
|
153
152
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
154
153
|
}
|
|
155
154
|
return true
|
|
155
|
+
function checkElement(el,selector){
|
|
156
156
|
if(selector==undefined) return true
|
|
157
157
|
if(el==null) return false
|
|
158
158
|
let{tag,classList,attribs:attributes,id,prev,ancestors,parents,prevAny}=selector
|
|
159
159
|
if(typeof el==='string') return false
|
|
160
160
|
if(id!==undefined && el.id===null) return false
|
|
161
161
|
if(id && id!==el.id) return false
|
|
162
162
|
if(tag && el.tagName===undefined) return false
|
|
163
163
|
else if(tag && tag!==el.tagName) return false
|
|
164
164
|
const clas=el.attributes.class
|
|
165
165
|
if(classList!==undefined && (clas===undefined || clas==='')) return false
|
|
166
166
|
else if(classList!==undefined){
|
|
167
167
|
if(classList.every(e=>el.classList.contains(e))===false) return false
|
|
168
168
|
}
|
|
169
169
|
if(checkattributes(attributes,el)===false) return false
|
|
170
170
|
if(checkElement(el.prev,prev)===false) return false
|
|
171
171
|
if(checkAncestors(el.ancestors,ancestors)===false) return false
|
|
172
172
|
if(checkParents(el.ancestors,parents)===false) return false
|
|
173
173
|
if(el.parent){
|
|
174
174
|
if(checkPrevAny(el.parent.children,el.childIndex,prevAny)==false) return false
|
|
175
175
|
}
|
|
176
176
|
return true
|
|
177
177
|
}
|
|
178
178
|
function checkattributes(attributes=[],el){
|
|
179
179
|
let elattributes=el.attributes
|
|
180
180
|
let names=Object.keys(elattributes)
|
|
181
181
|
let passedTests=0
|
|
182
182
|
if(attributes) for(let i=0; i<attributes.length; i++){
|
|
183
183
|
let{name,value,check}=attributes[i]
|
|
184
184
|
if(name=='inner' && value!==undefined && check && el.inner){
|
|
185
185
|
if(check(el.inner)) passedTests++
|
|
186
186
|
}
|
|
187
187
|
if(!names.includes(name)) continue
|
|
188
188
|
else if(value==undefined) passedTests++
|
|
189
189
|
else if(value && elattributes[name]){
|
|
190
190
|
if(check(elattributes[name])==false) continue
|
|
191
191
|
else passedTests++
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
if(passedTests==attributes.length) return true
|
|
195
195
|
else return false
|
|
196
196
|
}
|
|
197
197
|
function checkPrevAny(children=[],index,prevAny){
|
|
198
198
|
let size=children.length
|
|
199
199
|
if((size==0 || index==0) && prevAny) return false
|
|
200
200
|
for(let i=index; i>=0; i--){
|
|
201
201
|
if(checkElement(children[i],prevAny)) return true
|
|
202
202
|
}
|
|
203
203
|
return false
|
|
204
204
|
}
|
|
205
205
|
function checkAncestors(ancestors=[],selectorAncestors=[]){
|
|
206
206
|
let count=0
|
|
207
207
|
if(selectorAncestors.length==0) return true
|
|
208
208
|
let endIndex=ancestors.length-1
|
|
209
209
|
let selectorIndex=selectorAncestors.length-1
|
|
210
210
|
while(selectorIndex>=0){
|
|
211
211
|
for(let i=endIndex; i>=0; i--){
|
|
212
212
|
endIndex=i-1
|
|
213
213
|
if(checkElement(ancestors[i],selectorAncestors[selectorIndex])==true){
|
|
214
214
|
count++
|
|
215
215
|
break
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
selectorIndex--
|
|
219
219
|
}
|
|
220
220
|
if(count==selectorAncestors.length) return true
|
|
221
221
|
else return false
|
|
@@ -20,13 +20,14 @@ function buildStyle(attributes){
|
|
|
20
20
|
const styles=attributes.style || "";
|
|
21
21
|
con
|
|
22
22
|
|
|
23
23
|
class NodeClassList{
|
|
24
24
|
constructor(node){ this.node=node }
|
|
25
25
|
get classes(){ return (this.node.attributes.class || "").split(" ").filter(Boolean) }
|
|
26
26
|
set classes(val){ this.node.attributes.class=val.join(" ") }
|
|
27
27
|
contains(className){ return this.classes.includes(className) }
|
|
28
28
|
add(className){
|
|
29
29
|
const currentClasses=this.classes;
|
|
30
30
|
if (!currentClasses.includes(className)) this.classes=[...currentClasses,className];
|
|
31
31
|
}
|
|
32
32
|
remove(className){ this.classes=this.classes.filter(cls=>cls!==className); }
|
|
33
33
|
toggle(className){
|
|
34
34
|
if (this.classes.includes(className)) this.remove(className);
|
|
35
35
|
else this.add(className);
|
|
36
36
|
}
|
|
37
37
|
replace(oldClass,newClass){
|
|
38
38
|
if (this.classes.includes(oldClass)){
|
|
39
39
|
this.remove(oldClass);
|
|
40
40
|
this.add(newClass);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
class Node{
|
|
45
44
|
constructor(tagName,attributes={},parent=null){
|
|
46
45
|
this.isSingle=false;
|
|
47
46
|
this.tagName=tagName;
|
|
48
47
|
this.attributes=attributes;
|
|
49
48
|
this.childNodes=[];
|
|
50
49
|
if (parent!==null) parent.childNodes.push(this)
|
|
51
50
|
this.parent=parent;
|
|
52
51
|
this._classList=null;
|
|
53
52
|
this.__style=null;
|
|
54
53
|
this._dataset=null
|
|
55
54
|
}
|
|
56
55
|
get id(){ return this.attributes.id || null; }
|
|
57
56
|
get className(){return this.attributes.class || null}
|
|
58
57
|
get parentNode(){ return this.parent }
|
|
59
58
|
get ancestors(){
|
|
60
59
|
const ancestors=[]
|
|
61
60
|
let element=this.parent
|
|
62
61
|
while (element.tagName!=='ROOT'){
|
|
63
62
|
ancestors.push(element)
|
|
64
63
|
element=element.parent
|
|
65
64
|
}
|
|
66
65
|
return ancestors.reverse()
|
|
67
66
|
}
|
|
68
67
|
get childIndex(){ return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null }
|
|
69
68
|
get previousElementSibling(){ return this.prev }
|
|
70
69
|
get prev(){
|
|
71
70
|
if (!this.childIndex) return null
|
|
72
71
|
return this.parent.childNodes[this.childIndex-1]
|
|
73
72
|
}
|
|
74
73
|
get nextElementSibling(){ return this.next }
|
|
75
74
|
get next(){
|
|
76
75
|
if (!this.childIndex) return null
|
|
77
76
|
return this.parent.childNodes[this.childIndex+1] || null
|
|
78
77
|
}
|
|
79
78
|
get dataset(){
|
|
80
79
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
81
80
|
return this._dataset;
|
|
82
81
|
}
|
|
83
82
|
get classList(){
|
|
84
83
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
85
84
|
return this._classList;
|
|
86
85
|
}
|
|
87
86
|
get style(){
|
|
88
87
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
89
88
|
return this.__style
|
|
90
89
|
}
|
|
91
90
|
get outerHTML(){
|
|
92
91
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
93
92
|
return `<${this.tagName} ${attrs}>${this.innerHTML}</${this.tagName}>`;
|
|
94
93
|
}
|
|
95
94
|
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
96
95
|
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
97
96
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
98
97
|
remove(){
|
|
99
98
|
if (!this.parent) return
|
|
100
99
|
const index=this.childIndex;
|
|
101
100
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
102
101
|
}
|
|
103
102
|
get innerHTML(){
|
|
104
103
|
return this.childNodes.map(child=>{
|
|
105
104
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
106
105
|
else if (child instanceof TextNode) return child.textContent;
|
|
107
106
|
else return child
|
|
108
107
|
}).join("");
|
|
109
108
|
}
|
|
110
109
|
$$(query){return this.querySelectorAll(query)}
|
|
111
110
|
querySelectorAll(query){
|
|
112
111
|
const selectors=Query.get(query)
|
|
113
112
|
return find(selectors,this,new Set())
|
|
114
113
|
}
|
|
115
114
|
$(query){return this.querySelector(query)}
|
|
116
115
|
querySelector(query){
|
|
117
116
|
const selectors=Query.get(query)
|
|
118
117
|
return find(selectors,this,new Set(),true)[0] || null
|
|
119
118
|
}
|
|
120
119
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
121
120
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
122
121
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
123
122
|
get children(){
|
|
124
123
|
return this.childNodes.filter(child=>{
|
|
125
124
|
if (!(child instanceof Node)) return false
|
|
126
125
|
if (child.tagName==='#comment') return false
|
|
127
126
|
return true
|
|
128
127
|
});
|
|
129
128
|
}
|
|
130
129
|
insertAdjacentElement(position,newElement){
|
|
131
130
|
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
132
131
|
const pos=position.toLowerCase();
|
|
133
132
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
134
133
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
135
134
|
if (!this.parent) return newElement
|
|
136
135
|
if (pos==="beforebegin") this.parent.childNodes.unshift(newElement);
|
|
137
136
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childIndex+1,0,newElement);
|
|
138
137
|
return newElement
|
|
139
138
|
}
|
|
140
139
|
insertAdjacentHTML(position,html){
|
|
141
140
|
const newNode=parseHTML(html);
|
|
142
141
|
return this.insertAdjacentElement(position,newNode);
|
|
143
142
|
}
|
|
144
143
|
insertAdjacentText(position,text){
|
|
145
144
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
146
145
|
}
|
|
147
146
|
set innerHTML(html){
|
|
148
147
|
const parsed=parseHTML(html);
|
|
149
148
|
this.childNodes=parsed.childNodes;
|
|
150
149
|
}
|
|
151
150
|
set outerHTML(html){
|
|
152
151
|
const parsed=parseHTML(html);
|
|
153
152
|
if (!this.parent) return console.log('element has no parent node')
|
|
154
153
|
const index=this.childIndex
|
|
155
154
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
156
155
|
}
|
|
157
156
|
appendChild(newChild){
|
|
158
157
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
159
158
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
160
159
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
161
160
|
else return newChild
|
|
162
161
|
this.childNodes.push(newChild);
|
|
163
162
|
newChild.parent=this;
|
|
164
163
|
return newChild;
|
|
165
164
|
}
|
|
166
165
|
get textContent(){
|
|
167
166
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
168
167
|
return this.childNodes.map(child=>{
|
|
169
168
|
if(child instanceof SingleNode) return ''
|
|
170
169
|
if(child instanceof TextNode) return child.nodeValue
|
|
171
170
|
if(child instanceof Node) return child.textContent;
|
|
172
171
|
else return child;
|
|
173
172
|
}).join(" ");
|
|
174
173
|
}
|
|
175
174
|
set textContent(value){
|
|
176
175
|
this.childNodes=[];
|
|
177
176
|
if (value!==null && value!==undefined){
|
|
178
177
|
this.childNodes.push(value.toString());
|
|
179
178
|
}
|
|
180
179
|
}
|
|
180
|
+
function insertBefore(arr,index,newItem){
|
|
181
181
|
const existingIndex=arr.indexOf(newItem);
|
|
182
182
|
if (existingIndex!==-1) arr.splice(existingIndex,1);
|
|
183
183
|
arr.splice(index,0,newItem);
|
|
184
|
+
}
|
|
184
185
|
class Node{
|
|
185
186
|
constructor(tagName,attributes={},parent=null){
|
|
186
187
|
this.isSingle=false;
|
|
187
188
|
this.tagName=tagName;
|
|
188
189
|
this.attributes=attributes;
|
|
189
190
|
this.childNodes=[];
|
|
190
191
|
if (parent!==null) parent.childNodes.push(this)
|
|
191
192
|
this.parent=parent;
|
|
192
193
|
this._classList=null;
|
|
193
194
|
this.__style=null;
|
|
194
195
|
this._dataset=null
|
|
195
196
|
}
|
|
196
197
|
get id(){ return this.attributes.id ? this.attributes.id : null; }
|
|
197
198
|
set id(newValue){ this.attributes.id=newValue; }
|
|
198
199
|
get className(){return this.attributes.class || null}
|
|
199
200
|
get parentNode(){ return this.parent }
|
|
200
201
|
get ancestors(){
|
|
201
202
|
if(!this.parent) return []
|
|
202
203
|
const ancestors=[]
|
|
203
204
|
let element=this.parent
|
|
204
205
|
while (element.tagName!=='ROOT'){
|
|
205
206
|
ancestors.push(element)
|
|
206
207
|
element=element.parent
|
|
207
208
|
}
|
|
208
209
|
return ancestors.reverse()
|
|
209
210
|
}
|
|
210
211
|
get childNodeIndex(){
|
|
211
212
|
if(!this.parent) return null
|
|
212
213
|
return this.parent.childNodes ? this.parent.childNodes.indexOf(this) : null
|
|
213
214
|
}
|
|
214
215
|
get childIndex(){
|
|
215
216
|
if(!this.parent) return null
|
|
216
217
|
return this.parent.children ? this.parent.children.indexOf(this) : null
|
|
217
218
|
}
|
|
218
219
|
get previousElementSibling(){ return this.prev }
|
|
219
220
|
get prev(){
|
|
220
221
|
if (!this.childIndex) return null
|
|
221
222
|
return this.parent.children[this.childIndex-1]
|
|
222
223
|
}
|
|
223
224
|
get nextElementSibling(){ return this.next }
|
|
224
225
|
get next(){
|
|
225
226
|
if (!this.childIndex) return null
|
|
226
227
|
return this.parent.children[this.childIndex+1] || null
|
|
227
228
|
}
|
|
228
229
|
get dataset(){
|
|
229
230
|
if (!this._dataset) this._dataset=getDataset(this);
|
|
230
231
|
return this._dataset;
|
|
231
232
|
}
|
|
232
233
|
get classList(){
|
|
233
234
|
if (!this._classList) this._classList=new NodeClassList(this);
|
|
234
235
|
return this._classList;
|
|
235
236
|
}
|
|
236
237
|
get style(){
|
|
237
238
|
if (!this.__style) this.__style=buildStyle(this.attributes)
|
|
238
239
|
return this.__style
|
|
239
240
|
}
|
|
240
241
|
get outerHTML(){
|
|
241
242
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
242
243
|
return `<${this.tagName}${attrs ? ' '+attrs : ''}>${this.innerHTML}</${this.tagName}>`;
|
|
243
244
|
}
|
|
244
245
|
getAttribute(attrName){ return this.attributes[attrName] || null }
|
|
245
246
|
setAttribute(attrName,value){ this.attributes[attrName]=value }
|
|
246
247
|
removeAttribute(attrName){ delete this.attributes[attrName] }
|
|
247
248
|
remove(){
|
|
248
249
|
if (!this.parent) return
|
|
249
250
|
const index=this.childIndex;
|
|
250
251
|
if (index!==null) this.parent.childNodes.splice(index,1);
|
|
251
252
|
}
|
|
252
253
|
get innerHTML(){
|
|
253
254
|
return this.childNodes.map(child=>{
|
|
254
255
|
if (child instanceof Node || child instanceof SingleNode) return child.outerHTML;
|
|
255
256
|
else if (child instanceof TextNode) return child.textContent;
|
|
256
257
|
else return child
|
|
257
258
|
}).join("");
|
|
258
259
|
}
|
|
259
260
|
$$(query){return this.querySelectorAll(query)}
|
|
260
261
|
querySelectorAll(query){
|
|
261
262
|
const selectors=Query.get(query)
|
|
262
263
|
return find(selectors,this,new Set())
|
|
263
264
|
}
|
|
264
265
|
$(query){return this.querySelector(query)}
|
|
265
266
|
querySelector(query){
|
|
266
267
|
const selectors=Query.get(query)
|
|
267
268
|
return find(selectors,this,new Set(),true)[0] || null
|
|
268
269
|
}
|
|
269
270
|
getElementsByClassName(query){ return this.querySelectorAll('.'+query) }
|
|
270
271
|
getElementsByTagName(query){ return this.querySelectorAll(query) }
|
|
271
272
|
getElementById(query){ return this.querySelector('#'+query) }
|
|
272
273
|
get children(){
|
|
273
274
|
return this.childNodes.filter(child=>{
|
|
274
275
|
if (!(child instanceof Node)) return false
|
|
275
276
|
if (child.tagName==='#comment') return false
|
|
276
277
|
return true
|
|
277
278
|
});
|
|
278
279
|
}
|
|
279
280
|
insertAdjacentElement(position,newElement){
|
|
280
281
|
if(newElement.tagName==='ROOT' && newElement.childNodes.length>0) newElement=newElement.childNodes[0]
|
|
281
282
|
const pos=position.toLowerCase();
|
|
282
283
|
if (pos==="afterbegin") this.childNodes.unshift(newElement);
|
|
283
284
|
else if (pos==="beforeend") this.childNodes.push(newElement);
|
|
284
285
|
newElement.parent=this
|
|
285
286
|
if (!this.parent) return newElement
|
|
286
287
|
if (pos==="beforebegin") insertBefore(this.parent.childNodes,this.childNodeIndex,newElement)
|
|
287
288
|
else if (pos==="afterend") this.parent.childNodes.splice(this.childNodeIndex+1,0,newElement);
|
|
288
289
|
newElement.parent=this.parent
|
|
289
290
|
return newElement
|
|
290
291
|
}
|
|
291
292
|
insertAdjacentHTML(position,html){
|
|
292
293
|
const newNode=parseHTML(html);
|
|
293
294
|
newNode.childNodes.reverse().forEach(node=>{
|
|
294
295
|
this.insertAdjacentElement(position,node);
|
|
295
296
|
});
|
|
296
297
|
return newNode
|
|
297
298
|
}
|
|
298
299
|
insertAdjacentText(position,text){
|
|
299
300
|
return this.insertAdjacentElement(position,new TextNode(text));
|
|
300
301
|
}
|
|
301
302
|
set innerHTML(html){
|
|
302
303
|
const parsed=parseHTML(html);
|
|
303
304
|
this.childNodes=parsed.childNodes;
|
|
304
305
|
}
|
|
305
306
|
set outerHTML(html){
|
|
306
307
|
const parsed=parseHTML(html);
|
|
307
308
|
if (!this.parent) return console.log('element has no parent node')
|
|
308
309
|
const index=this.childIndex
|
|
309
310
|
if (index!==null) this.parent.childNodes.splice(index,1,...parsed.childNodes);
|
|
310
311
|
}
|
|
311
312
|
appendChild(newChild){
|
|
312
313
|
if (newChild instanceof Node || newChild instanceof TextNode || newChild instanceof SingleNode){
|
|
313
314
|
if (newChild.parent) newChild.parent.childNodes=newChild.parent.childNodes.filter(child=>child!==newChild);
|
|
314
315
|
} else if(typeof newChild==='string') newChild=new TextNode(newChild)
|
|
315
316
|
else return newChild
|
|
316
317
|
this.childNodes.push(newChild);
|
|
317
318
|
newChild.parent=this;
|
|
318
319
|
return newChild;
|
|
319
320
|
}
|
|
320
321
|
get textContent(){
|
|
321
322
|
if (this.childNodes.length===0) return this.nodeName==='#text' ? this.nodeValue : '';
|
|
322
323
|
return this.childNodes.map(child=>{
|
|
323
324
|
if(child instanceof SingleNode) return ''
|
|
324
325
|
if(child instanceof TextNode) return child.nodeValue
|
|
325
326
|
if(child instanceof Node) return child.textContent;
|
|
326
327
|
else return child;
|
|
327
328
|
}).join(" ");
|
|
328
329
|
}
|
|
329
330
|
set textContent(value){
|
|
330
331
|
this.childNodes=[];
|
|
331
332
|
if (value!==null && value!==undefined){
|
|
332
333
|
this.childNodes.push(value.toString());
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
}
|
|
336
337
|
class SingleNode extends Node{
|
|
337
338
|
constructor(tagName,attributes={},parent=null){
|
|
338
339
|
if(attributes['?'] && tagName==='?xml') delete attributes['?']
|
|
339
340
|
super(tagName,attributes,parent);
|
|
340
341
|
this.isSingle=true
|
|
341
342
|
}
|
|
342
343
|
get outerHTML(){
|
|
343
344
|
if (this.tagName==="#cdata-section") return `<![CDATA[${this.nodeValue}]]>`;
|
|
344
345
|
const attrs=Object.entries(this.attributes).map(([key,val])=>`${key}="${val}"`).join(" ");
|
|
345
346
|
return `<${this.tagName} ${attrs}${this.tagName==='?xml' ? '?' : ''}>`;
|
|
346
347
|
}
|
|
347
348
|
get innerHTML(){ return ""; }
|
|
348
349
|
set innerHTML(_){ }
|
|
349
350
|
$(_){return null}
|
|
350
351
|
$$(_){return []}
|
|
351
352
|
querySelectorAll(_){ return []; }
|
|
352
353
|
querySelector(_){ return null; }
|
|
353
354
|
getElementsByClassName(_){ return []; }
|
|
354
355
|
getElementsByTagName(_){ return []; }
|
|
355
356
|
getElementById(_){ return null; }
|
|
356
357
|
get children(){ return []; }
|
|
357
358
|
insertAdjacentElement(_,__){ }
|
|
358
359
|
insertAdjacentHTML(_,__){ }
|
|
359
360
|
insertAdjacentText(_,__){ }
|
|
360
361
|
appendChild(_){ }
|
|
361
362
|
get textContent(){ return ""; }
|
|
362
363
|
set textContent(_){ }
|
|
363
364
|
}
|
|
364
365
|
function parseAttributes(str){
|
|
365
366
|
const attrs={};
|
|
366
367
|
let key="";
|
|
367
368
|
let value="";
|
|
368
369
|
let isKey=true;
|
|
369
370
|
let quoteChar=null;
|
|
370
371
|
for (let i=0; i< str.length; i++){
|
|
371
372
|
const char=str[i];
|
|
372
373
|
if (isKey && (char==='=' || char===' ')){
|
|
373
374
|
if (char==='=') isKey=false;
|
|
374
375
|
else if (key.trim()){
|
|
375
376
|
attrs[key.trim()]=true;
|
|
376
377
|
key="";
|
|
377
378
|
}
|
|
378
379
|
continue;
|
|
379
380
|
}
|
|
380
381
|
if (!quoteChar && (char==='"' || char==="'")){
|
|
381
382
|
quoteChar=char;
|
|
382
383
|
continue;
|
|
383
384
|
} else if (quoteChar && char===quoteChar){
|
|
384
385
|
quoteChar=null;
|
|
385
386
|
attrs[key.trim()]=value.trim();
|
|
386
387
|
key=""; value=""; isKey=true;
|
|
387
388
|
continue;
|
|
388
389
|
}
|
|
389
390
|
if (isKey) key+=char;
|
|
390
391
|
else value+=char;
|
|
391
392
|
}
|
|
392
393
|
if (key.trim() &&!value) attrs[key.trim()]=true;
|
|
393
394
|
return attrs;
|
|
394
395
|
}
|
|
395
396
|
const VOID_TAGS=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr","!doctype",'?xml']);
|
|
396
|
-
function parseHTML(html){
|
|
397
397
|
const root=new Node("ROOT");
|
|
398
398
|
const stack=[root];
|
|
399
399
|
let currentText="",i=0;
|
|
400
400
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
401
401
|
if (!html.startsWith(startStr,i)) return false
|
|
402
402
|
const end=html.indexOf(endStr,i+n1);
|
|
403
403
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
404
404
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
405
405
|
i=end+n2;
|
|
406
406
|
return true
|
|
407
407
|
}
|
|
408
408
|
while (i< html.length){
|
|
409
409
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
410
410
|
if (parseSpecial("<script","</script>",8,9,'script')) continue
|
|
411
411
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
412
412
|
if (html.startsWith("<![CDATA[",i)){
|
|
413
413
|
const end=html.indexOf("]]>",i+9);
|
|
414
414
|
if (end===-1) break;
|
|
415
415
|
const content=html.substring(i+9,end);
|
|
416
416
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
417
417
|
cdataNode.nodeValue=content;
|
|
418
418
|
i=end+3;
|
|
419
419
|
continue;
|
|
420
420
|
}
|
|
421
421
|
if (html.startsWith("<",i)){
|
|
422
422
|
if (currentText.trim()){
|
|
423
423
|
stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
424
424
|
currentText="";
|
|
425
425
|
}
|
|
426
426
|
let tagEnd=i+1;
|
|
427
427
|
let insideQuotes=false;
|
|
428
428
|
let quoteChar=null;
|
|
429
429
|
while (tagEnd< html.length){
|
|
430
430
|
const char=html[tagEnd];
|
|
431
431
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
432
432
|
insideQuotes=true;
|
|
433
433
|
quoteChar=char;
|
|
434
434
|
} else if (insideQuotes && char===quoteChar){
|
|
435
435
|
insideQuotes=false;
|
|
436
436
|
quoteChar=null;
|
|
437
437
|
}
|
|
438
438
|
if (!insideQuotes && char==='>') break;
|
|
439
439
|
tagEnd++;
|
|
440
440
|
}
|
|
441
441
|
const tagContent=html.substring(i+1,tagEnd);
|
|
442
442
|
if (tagContent.startsWith("/")) stack.pop();
|
|
443
443
|
else{
|
|
444
444
|
let isSelfClosing=tagContent.endsWith('/');
|
|
445
445
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
446
446
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
447
447
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
448
448
|
const attributes=parseAttributes(attributesString);
|
|
449
449
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
450
450
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
451
451
|
}
|
|
452
452
|
i=tagEnd+1;
|
|
453
453
|
} else{
|
|
454
454
|
currentText+=html[i];
|
|
455
455
|
i++;
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText.trim()));
|
|
459
459
|
return root;
|
|
460
|
+
function parseHTML(html){
|
|
460
461
|
const root=new Node("ROOT");
|
|
461
462
|
const stack=[root];
|
|
462
463
|
let currentText="",i=0;
|
|
463
464
|
let max=0
|
|
464
465
|
function parseScript(){
|
|
465
466
|
if (!html.startsWith("<script",i)) return false;
|
|
466
467
|
const openTagEnd=html.indexOf(">",i);
|
|
467
468
|
if (openTagEnd===-1) return false;
|
|
468
469
|
const attributesString=html.substring(i+7,openTagEnd).trim();
|
|
469
470
|
const attributes=parseAttributes(attributesString);
|
|
470
471
|
let closeTagStart=html.indexOf("</script>",openTagEnd);
|
|
471
472
|
if (closeTagStart===-1) return false;
|
|
472
473
|
const content=html.substring(openTagEnd+1,closeTagStart);
|
|
473
474
|
const scriptNode=new Node('script',attributes,stack[stack.length-1]);
|
|
474
475
|
if(content.length>0) scriptNode.childNodes.push(content);
|
|
475
476
|
i=closeTagStart+9;
|
|
476
477
|
return true;
|
|
477
478
|
}
|
|
478
479
|
function parseSpecial(startStr,endStr,n1,n2,tag){
|
|
479
480
|
if (!html.startsWith(startStr,i)) return false
|
|
480
481
|
const end=html.indexOf(endStr,i+n1);
|
|
481
482
|
const strNode=new Node(tag,{},stack[stack.length-1]);
|
|
482
483
|
strNode.childNodes.push(html.substring(i+n1,end));
|
|
483
484
|
i=end+n2;
|
|
484
485
|
return true
|
|
485
486
|
}
|
|
486
487
|
while (i< html.length){
|
|
487
488
|
if(i>=max) max=i;
|
|
488
489
|
else break;
|
|
489
490
|
if (parseScript()) continue
|
|
490
491
|
if (parseSpecial("<!--","-->",4,3,'#comment')) continue
|
|
491
492
|
if (parseSpecial("<style","</style>",7,8,'style')) continue
|
|
492
493
|
if (html.startsWith("<![CDATA[",i)){
|
|
493
494
|
const end=html.indexOf("]]>",i+9);
|
|
494
495
|
if (end===-1) break;
|
|
495
496
|
const content=html.substring(i+9,end);
|
|
496
497
|
const cdataNode=new SingleNode("#cdata-section",{},stack[stack.length-1]);
|
|
497
498
|
cdataNode.nodeValue=content;
|
|
498
499
|
i=end+3;
|
|
499
500
|
continue;
|
|
500
501
|
}
|
|
501
502
|
if (html.startsWith("<",i)){
|
|
502
503
|
if (currentText){
|
|
503
504
|
stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
504
505
|
currentText="";
|
|
505
506
|
}
|
|
506
507
|
let tagEnd=i+1;
|
|
507
508
|
let insideQuotes=false;
|
|
508
509
|
let quoteChar=null;
|
|
509
510
|
while (tagEnd< html.length){
|
|
510
511
|
const char=html[tagEnd];
|
|
511
512
|
if (!insideQuotes && (char==='"' || char==="'")){
|
|
512
513
|
insideQuotes=true;
|
|
513
514
|
quoteChar=char;
|
|
514
515
|
} else if (insideQuotes && char===quoteChar){
|
|
515
516
|
insideQuotes=false;
|
|
516
517
|
quoteChar=null;
|
|
517
518
|
}
|
|
518
519
|
if (!insideQuotes && char==='>') break;
|
|
519
520
|
tagEnd++;
|
|
520
521
|
}
|
|
521
522
|
const tagContent=html.substring(i+1,tagEnd);
|
|
522
523
|
if (tagContent.startsWith("/")) stack.pop();
|
|
523
524
|
else{
|
|
524
525
|
let isSelfClosing=tagContent.endsWith('/');
|
|
525
526
|
const tagNameEnd=tagContent.search(/\s|>|\//);
|
|
526
527
|
const tagName=tagContent.substring(0,tagNameEnd>0 ? tagNameEnd : tagEnd-i-1);
|
|
527
528
|
const attributesString=tagContent.substring(tagName.length,isSelfClosing ? tagContent.length-1 : tagContent.length).trim();
|
|
528
529
|
const attributes=parseAttributes(attributesString);
|
|
529
530
|
if (VOID_TAGS.has(tagName.toLowerCase()) || isSelfClosing) new SingleNode(tagName,attributes,stack[stack.length-1])
|
|
530
531
|
else stack.push(new Node(tagName,attributes,stack[stack.length-1]));
|
|
531
532
|
}
|
|
532
533
|
i=tagEnd+1;
|
|
533
534
|
} else{
|
|
534
535
|
currentText+=html[i];
|
|
535
536
|
i++;
|
|
536
537
|
}
|
|
537
538
|
}
|
|
538
539
|
if (currentText.trim()) stack[stack.length-1].childNodes.push(new TextNode(currentText));
|
|
539
540
|
return root;
|
|
540
541
|
}
|
|
541
542
|
module.exports = { parseHTML, Node, Query, TextNode, SingleNode }
|