Skip to content

Commit 7156ecf

Browse files
committed
reorganized and updated pseudo-classes grouping and names as for latest changes in Selectors Level 4, more white-space fixes in different contexts
1 parent 2c5fce4 commit 7156ecf

File tree

1 file changed

+88
-49
lines changed

1 file changed

+88
-49
lines changed

src/nwsapi.js

Lines changed: 88 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Author: Diego Perini <diego.perini at gmail com>
88
* Version: 2.0.8
99
* Created: 20070722
10-
* Release: 20180727
10+
* Release: 20180901
1111
*
1212
* License:
1313
* http://javascript.nwbox.com/nwsapi/MIT-LICENSE
@@ -38,40 +38,58 @@
3838
WSP = '[\\x20\\t\\r\\n\\f]',
3939

4040
CFG = {
41+
// extensions
4142
operators: '[~*^$|]=|=',
42-
combinators: '[\\x20\\t\\r\\n\\f>+~](?=[^>+~])'
43+
combinators: '[\\x20\\t>+~](?=[^>+~])'
44+
},
45+
46+
NOT = {
47+
// not enclosed in double/single/parens/square
48+
double_enc: '(?=(?:[^"]*["][^"]*["])*[^"]*$)',
49+
single_enc: "(?=(?:[^']*['][^']*['])*[^']*$)",
50+
parens_enc: '(?![^\\x28]*\\x29)',
51+
square_enc: '(?![^\\x5b]*\\x5d)'
4352
},
4453

4554
REX = {
55+
// regular expressions
4656
HasEscapes: RegExp('\\\\'),
4757
HexNumbers: RegExp('^[0-9a-fA-F]'),
4858
EscOrQuote: RegExp('^\\\\|[\\x22\\x27]'),
49-
RegExpChar: RegExp('(?:(?!\\\\)[\\\\^$.*+?()[\\]{}|\\/])' ,'g'),
50-
TrimSpaces: RegExp('[\\r\\n\\f]|^' + WSP + '+|' + WSP + '+$', 'g'),
59+
RegExpChar: RegExp('(?:(?!\\\\)[\\\\^$.*+?()[\\]{}|\\/])', 'g'),
60+
TrimSpaces: RegExp('[\\n\\r\\f]+|^' + WSP + '+|' + WSP + '+$', 'g'),
5161
FixEscapes: RegExp('\\\\([0-9a-fA-F]{1,6}' + WSP + '?|.)|([\\x22\\x27])', 'g'),
52-
SplitGroup: RegExp(WSP + '*,' + WSP + '*(?![^\\[]*\\]|[^\\(]*\\)|[^\\{]*\\})', 'g')
62+
CombineWSP: RegExp('[\\n\\r\\f\\x20]+' + NOT.single_enc + NOT.double_enc, 'g'),
63+
TabCharWSP: RegExp('(\\x20?\\t+\\x20?)' + NOT.single_enc + NOT.double_enc, 'g'),
64+
CommaGroup: RegExp(WSP + '+,' + WSP + '+' + NOT.single_enc + NOT.double_enc, 'g'),
65+
SplitGroup: RegExp(WSP + '?,' + WSP + '?' + NOT.square_enc + NOT.parens_enc, 'g')
5366
},
5467

55-
struct_1 = '(root|empty|(?:(?:first|last|only)(?:-child|-of-type)))\\b',
56-
struct_2 = '(nth(?:-last)?(?:-child|-of-type))(?:\\(\\s?(even|odd|(?:[-+]?\\d*)(?:n\\s?[-+]?\\s?\\d*)?)\\s?(?:\\)|$))',
57-
58-
pseudo_1 = '(dir|lang)\\x28\\s?([-\\w]{2,})\\s?(?:\\x29|$)',
59-
pseudo_2 = ':?(after|before|first-letter|first-line|selection|backdrop|placeholder)\\b',
60-
61-
noparm_1 = '(link|visited|target|scope|hover|active|focus|enabled|disabled|read-only|read-write|placeholder-shown)\\b',
62-
noparm_2 = '(default|checked|indeterminate|required|optional|valid|invalid|in-range|out-of-range)\\b',
63-
64-
logicals = '(matches|not)\\x28\\s?([^()]*|[^\\x28]*\\x28[^\\x29]*\\x29)\\s?(?:\\x29|$)',
68+
GROUPS = {
69+
// pseudo-classes requiring parameters
70+
linguistic: '(dir|lang)\\x28\\s?([-\\w]{2,})\\s?(?:\\x29|$)',
71+
logicalsel: '(matches|not)\\x28\\s?([^()]*|[^\\x28]*\\x28[^\\x29]*\\x29)\\s?(?:\\x29|$)',
72+
treestruct: '(nth(?:-last)?(?:-child|-of-type))(?:\\x28\\s?(even|odd|(?:[-+]?\\d*)(?:n\\s?[-+]?\\s?\\d*)?)\\s?(?:\\x29|$))',
73+
// pseudo-classes not requiring parameters
74+
locationpc: '(link|visited|target|scope)\\b',
75+
useraction: '(hover|active|focus|focus-within)\\b',
76+
structural: '(root|empty|(?:(?:first|last|only)(?:-child|-of-type)))\\b',
77+
pseudoelem: ':?(after|before|first-letter|first-line|selection|placeholder)\\b',
78+
inputstate: '(enabled|disabled|read-only|read-write|placeholder-shown|default)\\b',
79+
inputvalue: '(checked|indeterminate|required|optional|valid|invalid|in-range|out-of-range)\\b'
80+
},
6581

6682
Patterns = {
6783
// pseudo-classes
68-
struct_n: RegExp('^:(?:' + struct_1 + ')(.*)', 'i'),
69-
struct_p: RegExp('^:(?:' + struct_2 + ')(.*)', 'i'),
70-
hpseudos: RegExp('^:(?:' + pseudo_1 + ')(.*)', 'i'),
71-
epseudos: RegExp('^:(?:' + pseudo_2 + ')(.*)', 'i'),
72-
lpseudos: RegExp('^:(?:' + logicals + ')(.*)', 'i'),
73-
fpseudos: RegExp('^:(?:' + noparm_1 + ')(.*)', 'i'),
74-
ipseudos: RegExp('^:(?:' + noparm_2 + ')(.*)', 'i'),
84+
treestruct: RegExp('^:(?:' + GROUPS.treestruct + ')(.*)', 'i'),
85+
structural: RegExp('^:(?:' + GROUPS.structural + ')(.*)', 'i'),
86+
linguistic: RegExp('^:(?:' + GROUPS.linguistic + ')(.*)', 'i'),
87+
pseudoelem: RegExp('^:(?:' + GROUPS.pseudoelem + ')(.*)', 'i'),
88+
useraction: RegExp('^:(?:' + GROUPS.useraction + ')(.*)', 'i'),
89+
inputstate: RegExp('^:(?:' + GROUPS.inputstate + ')(.*)', 'i'),
90+
inputvalue: RegExp('^:(?:' + GROUPS.inputvalue + ')(.*)', 'i'),
91+
locationpc: RegExp('^:(?:' + GROUPS.locationpc + ')(.*)', 'i'),
92+
logicalsel: RegExp('^:(?:' + GROUPS.logicalsel + ')(.*)', 'i'),
7593
// combinators symbols
7694
children: RegExp('^' + WSP + '?\\>' + WSP + '?(.*)'),
7795
adjacent: RegExp('^' + WSP + '?\\+' + WSP + '?(.*)'),
@@ -890,7 +908,7 @@
890908
// :first-child, :last-child, :only-child,
891909
// :first-of-type, :last-of-type, :only-of-type,
892910
case ':':
893-
if ((match = selector.match(Patterns.struct_n))) {
911+
if ((match = selector.match(Patterns.structural))) {
894912
match[1] = match[1].toLowerCase();
895913
switch (match[1]) {
896914
case 'root':
@@ -936,7 +954,7 @@
936954
// *** child-indexed & typed child-indexed pseudo-classes
937955
// :nth-child, :nth-of-type, :nth-last-child, :nth-last-of-type
938956
// 4 cases: 1 (nth) x 4 (child, of-type, last-child, last-of-type)
939-
else if ((match = selector.match(Patterns.struct_p))) {
957+
else if ((match = selector.match(Patterns.treestruct))) {
940958
match[1] = match[1].toLowerCase();
941959
switch (match[1]) {
942960
case 'nth-child':
@@ -987,7 +1005,7 @@
9871005

9881006
// *** logical combination pseudo-classes
9891007
// :matches( s1, [ s2, ... ]), :not( s1, [ s2, ... ])
990-
else if ((match = selector.match(Patterns.lpseudos))) {
1008+
else if ((match = selector.match(Patterns.logicalsel))) {
9911009
match[1] = match[1].toLowerCase();
9921010
switch (match[1]) {
9931011
case 'matches':
@@ -1010,7 +1028,7 @@
10101028

10111029
// *** linguistic pseudo-classes
10121030
// :dir( ltr / rtl ), :lang( en )
1013-
else if ((match = selector.match(Patterns.hpseudos))) {
1031+
else if ((match = selector.match(Patterns.linguistic))) {
10141032
match[1] = match[1].toLowerCase();
10151033
switch (match[1]) {
10161034
case 'dir':
@@ -1033,12 +1051,11 @@
10331051
}
10341052
}
10351053

1036-
// *** location, user actiond and input pseudo-classes
1037-
else if ((match = selector.match(Patterns.fpseudos))) {
1054+
// *** location pseudo-classes
1055+
// :link, :visited, :target, :scope
1056+
else if ((match = selector.match(Patterns.locationpc))) {
10381057
match[1] = match[1].toLowerCase();
10391058
switch (match[1]) {
1040-
// *** location pseudo-classes
1041-
// :link, :visited, :target, :scope
10421059
case 'link':
10431060
source = 'if(' + N + '(/^a|area|link$/i.test(e.nodeName)&&e.hasAttribute("href"))){' + source + '}';
10441061
break;
@@ -1051,9 +1068,17 @@
10511068
case 'scope':
10521069
source = 'if((s.from.compareDocumentPosition(e)&20)==20){' + source + '}';
10531070
break;
1071+
default:
1072+
emit('\'' + selector_string + '\'' + qsInvalid);
1073+
break;
1074+
}
1075+
}
10541076

1055-
// *** user actions pseudo-classes
1056-
// :hover, :active, :focus
1077+
// *** user actions pseudo-classes
1078+
// :hover, :active, :focus
1079+
else if ((match = selector.match(Patterns.useraction))) {
1080+
match[1] = match[1].toLowerCase();
1081+
switch (match[1]) {
10571082
case 'hover':
10581083
source = 'hasFocus' in doc && doc.hasFocus() ?
10591084
'if(' + N + '(e===s.doc.hoverElement)){' + source + '}' :
@@ -1069,9 +1094,17 @@
10691094
'if(' + N + '(e===s.doc.activeElement&&s.doc.hasFocus()&&(e.type||e.href||typeof e.tabIndex=="number"))){' + source + '}' :
10701095
'if(' + N + '(e===s.doc.activeElement&&(e.type||e.href))){' + source + '}';
10711096
break;
1097+
default:
1098+
emit('\'' + selector_string + '\'' + qsInvalid);
1099+
break;
1100+
}
1101+
}
10721102

1073-
// *** user interface and form pseudo-classes
1074-
// :enabled, :disabled, :read-only, :read-write, :placeholder-shown
1103+
// *** user interface and form pseudo-classes
1104+
// :enabled, :disabled, :read-only, :read-write, :placeholder-shown, :default
1105+
else if ((match = selector.match(Patterns.inputstate))) {
1106+
match[1] = match[1].toLowerCase();
1107+
switch (match[1]) {
10751108
case 'enabled':
10761109
source = 'if(' + N + '(("form" in e||/^optgroup$/i.test(e.nodeName))&&"disabled" in e &&e.disabled===false' +
10771110
')){' + source + '}';
@@ -1105,18 +1138,6 @@
11051138
'(!s.match(":focus",e))' +
11061139
')){' + source + '}';
11071140
break;
1108-
default:
1109-
emit('\'' + selector_string + '\'' + qsInvalid);
1110-
break;
1111-
}
1112-
}
1113-
1114-
// *** input pseudo-classes for form validation (was web-forms)
1115-
// :default, :checked, :indeterminate, :valid, :invalid
1116-
// :in-range, :out-of-range, :required, :optional
1117-
else if ((match = selector.match(Patterns.ipseudos))) {
1118-
match[1] = match[1].toLowerCase();
1119-
switch (match[1]) {
11201141
case 'default':
11211142
source =
11221143
'if(' + N + '("form" in e && e.form)){' +
@@ -1134,6 +1155,18 @@
11341155
'(("|radio|checkbox|".includes("|"+e.type+"|"))&&e.defaultChecked)' +
11351156
')){' + source + '}';
11361157
break;
1158+
default:
1159+
emit('\'' + selector_string + '\'' + qsInvalid);
1160+
break;
1161+
}
1162+
}
1163+
1164+
// *** input pseudo-classes (for form validation)
1165+
// :checked, :indeterminate, :valid, :invalid
1166+
// :in-range, :out-of-range, :required, :optional
1167+
else if ((match = selector.match(Patterns.inputvalue))) {
1168+
match[1] = match[1].toLowerCase();
1169+
switch (match[1]) {
11371170
case 'checked':
11381171
source = 'if(' + N + '(/^input$/i.test(e.nodeName)&&' +
11391172
'("|radio|checkbox|".includes("|"+e.type+"|")&&e.checked)||' +
@@ -1203,7 +1236,7 @@
12031236
}
12041237

12051238
// allow pseudo-elements as :after/:before (single or double colon)
1206-
else if ((match = selector.match(Patterns.epseudos))) {
1239+
else if ((match = selector.match(Patterns.pseudoelem))) {
12071240
source = 'if(' + D + '(/1|11/).test(e.nodeType)){' + source + '}';
12081241
}
12091242

@@ -1318,9 +1351,12 @@
13181351
selector = '' + selector;
13191352
}
13201353

1321-
// normalize selector
1354+
// normalize input selector string
13221355
selector = selector.
13231356
replace(/\x00|\\$/g, '\ufffd').
1357+
replace(REX.CombineWSP, '\x20').
1358+
replace(REX.TabCharWSP, '\t').
1359+
replace(REX.CommaGroup, ',').
13241360
replace(REX.TrimSpaces, '');
13251361

13261362
// parse, validate and split possible selector groups
@@ -1396,9 +1432,12 @@
13961432
selector = '' + selector;
13971433
}
13981434

1399-
// normalize selector
1435+
// normalize input selector string
14001436
selector = selector.
14011437
replace(/\x00|\\$/g, '\ufffd').
1438+
replace(REX.CombineWSP, '\x20').
1439+
replace(REX.TabCharWSP, '\t').
1440+
replace(REX.CommaGroup, ',').
14021441
replace(REX.TrimSpaces, '');
14031442

14041443
// parse, validate and split possible selector groups

0 commit comments

Comments
 (0)