blob: 9d7159145ecda1510223a90502ecb59d046e78ab [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001var falseFunc = require("boolbase").falseFunc;
2
3//https://github.com/slevithan/XRegExp/blob/master/src/xregexp.js#L469
4var reChars = /[-[\]{}()*+?.,\\^$|#\s]/g;
5
6/*
7 attribute selectors
8*/
9var attributeRules = {
10 __proto__: null,
11 equals: function(next, data, options) {
12 var name = data.name;
13 var value = data.value;
14 var adapter = options.adapter;
15
16 if (data.ignoreCase) {
17 value = value.toLowerCase();
18
19 return function equalsIC(elem) {
20 var attr = adapter.getAttributeValue(elem, name);
21 return attr != null && attr.toLowerCase() === value && next(elem);
22 };
23 }
24
25 return function equals(elem) {
26 return adapter.getAttributeValue(elem, name) === value && next(elem);
27 };
28 },
29 hyphen: function(next, data, options) {
30 var name = data.name;
31 var value = data.value;
32 var len = value.length;
33 var adapter = options.adapter;
34
35 if (data.ignoreCase) {
36 value = value.toLowerCase();
37
38 return function hyphenIC(elem) {
39 var attr = adapter.getAttributeValue(elem, name);
40 return (
41 attr != null &&
42 (attr.length === len || attr.charAt(len) === "-") &&
43 attr.substr(0, len).toLowerCase() === value &&
44 next(elem)
45 );
46 };
47 }
48
49 return function hyphen(elem) {
50 var attr = adapter.getAttributeValue(elem, name);
51 return (
52 attr != null &&
53 attr.substr(0, len) === value &&
54 (attr.length === len || attr.charAt(len) === "-") &&
55 next(elem)
56 );
57 };
58 },
59 element: function(next, data, options) {
60 var name = data.name;
61 var value = data.value;
62 var adapter = options.adapter;
63
64 if (/\s/.test(value)) {
65 return falseFunc;
66 }
67
68 value = value.replace(reChars, "\\$&");
69
70 var pattern = "(?:^|\\s)" + value + "(?:$|\\s)",
71 flags = data.ignoreCase ? "i" : "",
72 regex = new RegExp(pattern, flags);
73
74 return function element(elem) {
75 var attr = adapter.getAttributeValue(elem, name);
76 return attr != null && regex.test(attr) && next(elem);
77 };
78 },
79 exists: function(next, data, options) {
80 var name = data.name;
81 var adapter = options.adapter;
82
83 return function exists(elem) {
84 return adapter.hasAttrib(elem, name) && next(elem);
85 };
86 },
87 start: function(next, data, options) {
88 var name = data.name;
89 var value = data.value;
90 var len = value.length;
91 var adapter = options.adapter;
92
93 if (len === 0) {
94 return falseFunc;
95 }
96
97 if (data.ignoreCase) {
98 value = value.toLowerCase();
99
100 return function startIC(elem) {
101 var attr = adapter.getAttributeValue(elem, name);
102 return attr != null && attr.substr(0, len).toLowerCase() === value && next(elem);
103 };
104 }
105
106 return function start(elem) {
107 var attr = adapter.getAttributeValue(elem, name);
108 return attr != null && attr.substr(0, len) === value && next(elem);
109 };
110 },
111 end: function(next, data, options) {
112 var name = data.name;
113 var value = data.value;
114 var len = -value.length;
115 var adapter = options.adapter;
116
117 if (len === 0) {
118 return falseFunc;
119 }
120
121 if (data.ignoreCase) {
122 value = value.toLowerCase();
123
124 return function endIC(elem) {
125 var attr = adapter.getAttributeValue(elem, name);
126 return attr != null && attr.substr(len).toLowerCase() === value && next(elem);
127 };
128 }
129
130 return function end(elem) {
131 var attr = adapter.getAttributeValue(elem, name);
132 return attr != null && attr.substr(len) === value && next(elem);
133 };
134 },
135 any: function(next, data, options) {
136 var name = data.name;
137 var value = data.value;
138 var adapter = options.adapter;
139
140 if (value === "") {
141 return falseFunc;
142 }
143
144 if (data.ignoreCase) {
145 var regex = new RegExp(value.replace(reChars, "\\$&"), "i");
146
147 return function anyIC(elem) {
148 var attr = adapter.getAttributeValue(elem, name);
149 return attr != null && regex.test(attr) && next(elem);
150 };
151 }
152
153 return function any(elem) {
154 var attr = adapter.getAttributeValue(elem, name);
155 return attr != null && attr.indexOf(value) >= 0 && next(elem);
156 };
157 },
158 not: function(next, data, options) {
159 var name = data.name;
160 var value = data.value;
161 var adapter = options.adapter;
162
163 if (value === "") {
164 return function notEmpty(elem) {
165 return !!adapter.getAttributeValue(elem, name) && next(elem);
166 };
167 } else if (data.ignoreCase) {
168 value = value.toLowerCase();
169
170 return function notIC(elem) {
171 var attr = adapter.getAttributeValue(elem, name);
172 return attr != null && attr.toLowerCase() !== value && next(elem);
173 };
174 }
175
176 return function not(elem) {
177 return adapter.getAttributeValue(elem, name) !== value && next(elem);
178 };
179 }
180};
181
182module.exports = {
183 compile: function(next, data, options) {
184 if (options && options.strict && (data.ignoreCase || data.action === "not")) {
185 throw new Error("Unsupported attribute selector");
186 }
187 return attributeRules[data.action](next, data, options);
188 },
189 rules: attributeRules
190};