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