Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81144 views
1
/*
2
* Copyright (C) 2007-2015 Diego Perini
3
* All rights reserved.
4
*
5
* CSS3 pseudo-classes extension for NWMatcher
6
*
7
* Added capabilities:
8
*
9
* - structural pseudo-classes
10
*
11
* :root, :empty,
12
* :nth-child(), nth-of-type(),
13
* :nth-last-child(), nth-last-of-type(),
14
* :first-child, :last-child, :only-child
15
* :first-of-type, :last-of-type, :only-of-type
16
*
17
* - negation, language, target and UI element pseudo-classes
18
*
19
* :not(), :target, :lang(), :target
20
* :link, :visited, :active, :focus, :hover,
21
* :checked, :disabled, :enabled, :selected
22
*/
23
24
(function(global) {
25
26
var LINK_NODES = global.Object({
27
'a': 1, 'A': 1,
28
'area': 1, 'AREA': 1,
29
'link': 1, 'LINK': 1
30
}),
31
32
root = document.documentElement,
33
34
contains = 'compareDocumentPosition' in root ?
35
function(container, element) {
36
return (container.compareDocumentPosition(element) & 16) == 16;
37
} : 'contains' in root ?
38
function(container, element) {
39
return element.nodeType == 1 && container.contains(element);
40
} :
41
function(container, element) {
42
while ((element = element.parentNode) && element.nodeType == 1) {
43
if (element === container) return true;
44
}
45
return false;
46
},
47
48
isLink =
49
function(element) {
50
return element.getAttribute('href') && LINK_NODES[element.nodeName];
51
},
52
53
isEmpty =
54
function(node) {
55
node = node.firstChild;
56
while (node) {
57
if (node.nodeType == 3 || node.nodeName > '@') return false;
58
node = node.nextSibling;
59
}
60
return true;
61
},
62
63
nthElement =
64
function(element, last) {
65
var count = 1, succ = last ? 'nextSibling' : 'previousSibling';
66
while ((element = element[succ])) {
67
if (element.nodeName > '@') ++count;
68
}
69
return count;
70
},
71
72
nthOfType =
73
function(element, last) {
74
var count = 1, succ = last ? 'nextSibling' : 'previousSibling', type = element.nodeName;
75
while ((element = element[succ])) {
76
if (element.nodeName == type) ++count;
77
}
78
return count;
79
};
80
81
NW.Dom.Snapshot['contains'] = contains;
82
83
NW.Dom.Snapshot['isLink'] = isLink;
84
NW.Dom.Snapshot['isEmpty'] = isEmpty;
85
NW.Dom.Snapshot['nthOfType'] = nthOfType;
86
NW.Dom.Snapshot['nthElement'] = nthElement;
87
88
})(this);
89
90
NW.Dom.registerSelector(
91
'nwmatcher:spseudos',
92
/^\:(root|empty|(?:first|last|only)(?:-child|-of-type)|nth(?:-last)?(?:-child|-of-type)\(\s*(even|odd|(?:[-+]{0,1}\d*n\s*)?[-+]{0,1}\s*\d*)\s*\))?(.*)/i,
93
(function(global) {
94
95
return function(match, source) {
96
97
var a, n, b, status = true, test, type;
98
99
switch (match[1]) {
100
101
case 'root':
102
if (match[3])
103
source = 'if(e===h||s.contains(h,e)){' + source + '}';
104
else
105
source = 'if(e===h){' + source + '}';
106
break;
107
108
case 'empty':
109
source = 'if(s.isEmpty(e)){' + source + '}';
110
break;
111
112
default:
113
if (match[1] && match[2]) {
114
115
if (match[2] == 'n') {
116
source = 'if(e!==h){' + source + '}';
117
break;
118
} else if (match[2] == 'even') {
119
a = 2;
120
b = 0;
121
} else if (match[2] == 'odd') {
122
a = 2;
123
b = 1;
124
} else {
125
b = ((n = match[2].match(/(-?\d+)$/)) ? global.parseInt(n[1], 10) : 0);
126
a = ((n = match[2].match(/(-?\d*)n/i)) ? global.parseInt(n[1], 10) : 0);
127
if (n && n[1] == '-') a = -1;
128
}
129
test = a > 1 ?
130
(/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' :
131
'n>=' + b + '&&(n-(' + b + '))%' + a + '==0' : a < -1 ?
132
(/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' :
133
'n<=' + b + '&&(n-(' + b + '))%' + a + '==0' : a === 0 ?
134
'n==' + b : a == -1 ? 'n<=' + b : 'n>=' + b;
135
source =
136
'if(e!==h){' +
137
'n=s[' + (/-of-type/i.test(match[1]) ? '"nthOfType"' : '"nthElement"') + ']' +
138
'(e,' + (/last/i.test(match[1]) ? 'true' : 'false') + ');' +
139
'if(' + test + '){' + source + '}' +
140
'}';
141
142
} else if (match[1]) {
143
144
a = /first/i.test(match[1]) ? 'previous' : 'next';
145
n = /only/i.test(match[1]) ? 'previous' : 'next';
146
b = /first|last/i.test(match[1]);
147
type = /-of-type/i.test(match[1]) ? '&&n.nodeName!==e.nodeName' : '&&n.nodeName<"@"';
148
source = 'if(e!==h){' +
149
( 'n=e;while((n=n.' + a + 'Sibling)' + type + ');if(!n){' + (b ? source :
150
'n=e;while((n=n.' + n + 'Sibling)' + type + ');if(!n){' + source + '}') + '}' ) + '}';
151
152
} else {
153
154
status = false;
155
156
}
157
break;
158
}
159
160
return global.Object({
161
'source': source,
162
'status': status
163
});
164
165
};
166
167
})(this));
168
169
NW.Dom.registerSelector(
170
'nwmatcher:dpseudos',
171
/^\:(link|visited|target|active|focus|hover|checked|disabled|enabled|selected|lang\(([-\w]{2,})\)|not\(([^()]*|.*)\))?(.*)/i,
172
(function(global) {
173
174
var doc = global.document,
175
Config = NW.Dom.Config,
176
Tokens = NW.Dom.Tokens,
177
178
reTrimSpace = global.RegExp('^\\s+|\\s+$', 'g'),
179
180
reSimpleNot = global.RegExp('^((?!:not)' +
181
'(' + Tokens.prefixes + '|' + Tokens.identifier +
182
'|\\([^()]*\\))+|\\[' + Tokens.attributes + '\\])$');
183
184
return function(match, source) {
185
186
var expr, status = true, test;
187
188
switch (match[1].match(/^\w+/)[0]) {
189
190
case 'not':
191
expr = match[3].replace(reTrimSpace, '');
192
if (Config.SIMPLENOT && !reSimpleNot.test(expr)) {
193
NW.Dom.emit('Negation pseudo-class only accepts simple selectors "' + match.join('') + '"');
194
} else {
195
if ('compatMode' in doc) {
196
source = 'if(!' + NW.Dom.compile(expr, '', false) + '(e,s,r,d,h,g)){' + source + '}';
197
} else {
198
source = 'if(!s.match(e, "' + expr.replace(/\x22/g, '\\"') + '",g)){' + source +'}';
199
}
200
}
201
break;
202
203
case 'checked':
204
source = 'if((typeof e.form!=="undefined"&&(/^(?:radio|checkbox)$/i).test(e.type)&&e.checked)' +
205
(Config.USE_HTML5 ? '||(/^option$/i.test(e.nodeName)&&(e.selected||e.checked))' : '') +
206
'){' + source + '}';
207
break;
208
209
case 'disabled':
210
source = 'if(((typeof e.form!=="undefined"' +
211
(Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') +
212
')||s.isLink(e))&&e.disabled===true){' + source + '}';
213
break;
214
215
case 'enabled':
216
source = 'if(((typeof e.form!=="undefined"' +
217
(Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') +
218
')||s.isLink(e))&&e.disabled===false){' + source + '}';
219
break;
220
221
case 'lang':
222
test = '';
223
if (match[2]) test = match[2].substr(0, 2) + '-';
224
source = 'do{(n=e.lang||"").toLowerCase();' +
225
'if((n==""&&h.lang=="' + match[2].toLowerCase() + '")||' +
226
'(n&&(n=="' + match[2].toLowerCase() +
227
'"||n.substr(0,3)=="' + test.toLowerCase() + '")))' +
228
'{' + source + 'break;}}while((e=e.parentNode)&&e!==g);';
229
break;
230
231
case 'target':
232
source = 'if(e.id==d.location.hash.slice(1)){' + source + '}';
233
break;
234
235
case 'link':
236
source = 'if(s.isLink(e)&&!e.visited){' + source + '}';
237
break;
238
239
case 'visited':
240
source = 'if(s.isLink(e)&&e.visited){' + source + '}';
241
break;
242
243
case 'active':
244
source = 'if(e===d.activeElement){' + source + '}';
245
break;
246
247
case 'hover':
248
source = 'if(e===d.hoverElement){' + source + '}';
249
break;
250
251
case 'focus':
252
source = 'hasFocus' in doc ?
253
'if(e===d.activeElement&&d.hasFocus()&&(e.type||e.href||typeof e.tabIndex=="number")){' + source + '}' :
254
'if(e===d.activeElement&&(e.type||e.href)){' + source + '}';
255
break;
256
257
case 'selected':
258
source = 'if(/^option$/i.test(e.nodeName)&&(e.selected||e.checked)){' + source + '}';
259
break;
260
261
default:
262
status = false;
263
break;
264
}
265
266
return global.Object({
267
'source': source,
268
'status': status
269
});
270
271
};
272
273
})(this));
274
275