summaryrefslogtreecommitdiff
path: root/frontend/delta/js/MochiKit/Selector.js
Unidiff
Diffstat (limited to 'frontend/delta/js/MochiKit/Selector.js') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/delta/js/MochiKit/Selector.js416
1 files changed, 416 insertions, 0 deletions
diff --git a/frontend/delta/js/MochiKit/Selector.js b/frontend/delta/js/MochiKit/Selector.js
new file mode 100644
index 0000000..2719b13
--- a/dev/null
+++ b/frontend/delta/js/MochiKit/Selector.js
@@ -0,0 +1,416 @@
1/*
2
3Copyright 2008-2013 Clipperz Srl
4
5This file is part of Clipperz, the online password manager.
6For further information about its features and functionalities please
7refer to http://www.clipperz.com.
8
9* Clipperz is free software: you can redistribute it and/or modify it
10 under the terms of the GNU Affero General Public License as published
11 by the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14* Clipperz is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU Affero General Public License for more details.
18
19* You should have received a copy of the GNU Affero General Public
20 License along with Clipperz. If not, see http://www.gnu.org/licenses/.
21
22*/
23
24/***
25
26MochiKit.Selector 1.5
27
28See <http://mochikit.com/> for documentation, downloads, license, etc.
29
30(c) 2005 Bob Ippolito and others. All rights Reserved.
31
32***/
33
34MochiKit.Base.module(MochiKit, 'Selector', '1.5', ['Base', 'DOM', 'Iter']);
35
36MochiKit.Selector.Selector = function (expression) {
37 this.params = {classNames: [], pseudoClassNames: []};
38 this.expression = expression.toString().replace(/(^\s+|\s+$)/g, '');
39 this.parseExpression();
40 this.compileMatcher();
41};
42
43MochiKit.Selector.Selector.prototype = {
44 /***
45
46 Selector class: convenient object to make CSS selections.
47
48 ***/
49 __class__: MochiKit.Selector.Selector,
50
51 /** @id MochiKit.Selector.Selector.prototype.parseExpression */
52 parseExpression: function () {
53 function abort(message) {
54 throw 'Parse error in selector: ' + message;
55 }
56
57 if (this.expression == '') {
58 abort('empty expression');
59 }
60
61 var repr = MochiKit.Base.repr;
62 var params = this.params;
63 var expr = this.expression;
64 var match, modifier, clause, rest;
65 while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!^$*]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
66 params.attributes = params.attributes || [];
67 params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
68 expr = match[1];
69 }
70
71 if (expr == '*') {
72 return this.params.wildcard = true;
73 }
74
75 while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+(?:\([^)]*\))?)(.*)/i)) {
76 modifier = match[1];
77 clause = match[2];
78 rest = match[3];
79 switch (modifier) {
80 case '#':
81 params.id = clause;
82 break;
83 case '.':
84 params.classNames.push(clause);
85 break;
86 case ':':
87 params.pseudoClassNames.push(clause);
88 break;
89 case '':
90 case undefined:
91 params.tagName = clause.toUpperCase();
92 break;
93 default:
94 abort(repr(expr));
95 }
96 expr = rest;
97 }
98
99 if (expr.length > 0) {
100 abort(repr(expr));
101 }
102 },
103
104 /** @id MochiKit.Selector.Selector.prototype.buildMatchExpression */
105 buildMatchExpression: function () {
106 var repr = MochiKit.Base.repr;
107 var params = this.params;
108 var conditions = [];
109 var clause, i;
110
111 function childElements(element) {
112 return "MochiKit.Base.filter(function (node) { return node.nodeType == 1; }, " + element + ".childNodes)";
113 }
114
115 if (params.wildcard) {
116 conditions.push('true');
117 }
118 if (clause = params.id) {
119 conditions.push('element.id == ' + repr(clause));
120 }
121 if (clause = params.tagName) {
122 conditions.push('element.tagName.toUpperCase() == ' + repr(clause));
123 }
124 if ((clause = params.classNames).length > 0) {
125 for (i = 0; i < clause.length; i++) {
126 conditions.push('MochiKit.DOM.hasElementClass(element, ' + repr(clause[i]) + ')');
127 }
128 }
129 if ((clause = params.pseudoClassNames).length > 0) {
130 for (i = 0; i < clause.length; i++) {
131 var match = clause[i].match(/^([^(]+)(?:\((.*)\))?$/);
132 var pseudoClass = match[1];
133 var pseudoClassArgument = match[2];
134 switch (pseudoClass) {
135 case 'root':
136 conditions.push('element.nodeType == 9 || element === element.ownerDocument.documentElement'); break;
137 case 'nth-child':
138 case 'nth-last-child':
139 case 'nth-of-type':
140 case 'nth-last-of-type':
141 match = pseudoClassArgument.match(/^((?:(\d+)n\+)?(\d+)|odd|even)$/);
142 if (!match) {
143 throw "Invalid argument to pseudo element nth-child: " + pseudoClassArgument;
144 }
145 var a, b;
146 if (match[0] == 'odd') {
147 a = 2;
148 b = 1;
149 } else if (match[0] == 'even') {
150 a = 2;
151 b = 0;
152 } else {
153 a = match[2] && parseInt(match, 10) || null;
154 b = parseInt(match[3], 10);
155 }
156 conditions.push('this.nthChild(element,' + a + ',' + b
157 + ',' + !!pseudoClass.match('^nth-last') // Reverse
158 + ',' + !!pseudoClass.match('of-type$') // Restrict to same tagName
159 + ')');
160 break;
161 case 'first-child':
162 conditions.push('this.nthChild(element, null, 1)');
163 break;
164 case 'last-child':
165 conditions.push('this.nthChild(element, null, 1, true)');
166 break;
167 case 'first-of-type':
168 conditions.push('this.nthChild(element, null, 1, false, true)');
169 break;
170 case 'last-of-type':
171 conditions.push('this.nthChild(element, null, 1, true, true)');
172 break;
173 case 'only-child':
174 conditions.push(childElements('element.parentNode') + '.length == 1');
175 break;
176 case 'only-of-type':
177 conditions.push('MochiKit.Base.filter(function (node) { return node.tagName == element.tagName; }, ' + childElements('element.parentNode') + ').length == 1');
178 break;
179 case 'empty':
180 conditions.push('element.childNodes.length == 0');
181 break;
182 case 'enabled':
183 conditions.push('(this.isUIElement(element) && element.disabled === false)');
184 break;
185 case 'disabled':
186 conditions.push('(this.isUIElement(element) && element.disabled === true)');
187 break;
188 case 'checked':
189 conditions.push('(this.isUIElement(element) && element.checked === true)');
190 break;
191 case 'not':
192 var subselector = new MochiKit.Selector.Selector(pseudoClassArgument);
193 conditions.push('!( ' + subselector.buildMatchExpression() + ')');
194 break;
195 }
196 }
197 }
198 if (clause = params.attributes) {
199 MochiKit.Base.map(function (attribute) {
200 var value = 'MochiKit.DOM.getNodeAttribute(element, ' + repr(attribute.name) + ')';
201 var splitValueBy = function (delimiter) {
202 return value + '.split(' + repr(delimiter) + ')';
203 };
204 conditions.push(value + ' != null');
205 switch (attribute.operator) {
206 case '=':
207 conditions.push(value + ' == ' + repr(attribute.value));
208 break;
209 case '~=':
210 conditions.push('MochiKit.Base.findValue(' + splitValueBy(' ') + ', ' + repr(attribute.value) + ') > -1');
211 break;
212 case '^=':
213 conditions.push(value + '.substring(0, ' + attribute.value.length + ') == ' + repr(attribute.value));
214 break;
215 case '$=':
216 conditions.push(value + '.substring(' + value + '.length - ' + attribute.value.length + ') == ' + repr(attribute.value));
217 break;
218 case '*=':
219 conditions.push(value + '.match(' + repr(attribute.value) + ')');
220 break;
221 case '|=':
222 conditions.push(splitValueBy('-') + '[0].toUpperCase() == ' + repr(attribute.value.toUpperCase()));
223 break;
224 case '!=':
225 conditions.push(value + ' != ' + repr(attribute.value));
226 break;
227 case '':
228 case undefined:
229 // Condition already added above
230 break;
231 default:
232 throw 'Unknown operator ' + attribute.operator + ' in selector';
233 }
234 }, clause);
235 }
236
237 return conditions.join(' && ');
238 },
239
240 /** @id MochiKit.Selector.Selector.prototype.compileMatcher */
241 compileMatcher: function () {
242 var code = 'return (!element.tagName) ? false : ' +
243 this.buildMatchExpression() + ';';
244 this.match = new Function('element', code);
245 },
246
247 /** @id MochiKit.Selector.Selector.prototype.nthChild */
248 nthChild: function (element, a, b, reverse, sametag){
249 var siblings = MochiKit.Base.filter(function (node) {
250 return node.nodeType == 1;
251 }, element.parentNode.childNodes);
252 if (sametag) {
253 siblings = MochiKit.Base.filter(function (node) {
254 return node.tagName == element.tagName;
255 }, siblings);
256 }
257 if (reverse) {
258 siblings = MochiKit.Iter.reversed(siblings);
259 }
260 if (a) {
261 var actualIndex = MochiKit.Base.findIdentical(siblings, element);
262 return ((actualIndex + 1 - b) / a) % 1 == 0;
263 } else {
264 return b == MochiKit.Base.findIdentical(siblings, element) + 1;
265 }
266 },
267
268 /** @id MochiKit.Selector.Selector.prototype.isUIElement */
269 isUIElement: function (element) {
270 return MochiKit.Base.findValue(['input', 'button', 'select', 'option', 'textarea', 'object'],
271 element.tagName.toLowerCase()) > -1;
272 },
273
274 /** @id MochiKit.Selector.Selector.prototype.findElements */
275 findElements: function (scope, axis) {
276 var element;
277
278 if (axis == undefined) {
279 axis = "";
280 }
281
282 function inScope(element, scope) {
283 if (axis == "") {
284 return MochiKit.DOM.isChildNode(element, scope);
285 } else if (axis == ">") {
286 return element.parentNode === scope;
287 } else if (axis == "+") {
288 return element === nextSiblingElement(scope);
289 } else if (axis == "~") {
290 var sibling = scope;
291 while (sibling = nextSiblingElement(sibling)) {
292 if (element === sibling) {
293 return true;
294 }
295 }
296 return false;
297 } else {
298 throw "Invalid axis: " + axis;
299 }
300 }
301
302 if (element = MochiKit.DOM.getElement(this.params.id)) {
303 if (this.match(element)) {
304 if (!scope || inScope(element, scope)) {
305 return [element];
306 }
307 }
308 }
309
310 function nextSiblingElement(node) {
311 node = node.nextSibling;
312 while (node && node.nodeType != 1) {
313 node = node.nextSibling;
314 }
315 return node;
316 }
317
318 if (axis == "") {
319 scope = (scope || MochiKit.DOM.currentDocument()).getElementsByTagName(this.params.tagName || '*');
320 } else if (axis == ">") {
321 if (!scope) {
322 throw "> combinator not allowed without preceeding expression";
323 }
324 scope = MochiKit.Base.filter(function (node) {
325 return node.nodeType == 1;
326 }, scope.childNodes);
327 } else if (axis == "+") {
328 if (!scope) {
329 throw "+ combinator not allowed without preceeding expression";
330 }
331 scope = nextSiblingElement(scope) && [nextSiblingElement(scope)];
332 } else if (axis == "~") {
333 if (!scope) {
334 throw "~ combinator not allowed without preceeding expression";
335 }
336 var newscope = [];
337 while (nextSiblingElement(scope)) {
338 scope = nextSiblingElement(scope);
339 newscope.push(scope);
340 }
341 scope = newscope;
342 }
343
344 if (!scope) {
345 return [];
346 }
347
348 var results = MochiKit.Base.filter(MochiKit.Base.bind(function (scopeElt) {
349 return this.match(scopeElt);
350 }, this), scope);
351
352 return results;
353 },
354
355 /** @id MochiKit.Selector.Selector.prototype.repr */
356 repr: function () {
357 return 'Selector(' + this.expression + ')';
358 },
359
360 toString: MochiKit.Base.forwardCall("repr")
361};
362
363MochiKit.Base.update(MochiKit.Selector, {
364
365 /** @id MochiKit.Selector.findChildElements */
366 findChildElements: function (element, expressions) {
367 element = MochiKit.DOM.getElement(element);
368 var uniq = function(arr) {
369 var res = [];
370 for (var i = 0; i < arr.length; i++) {
371 if (MochiKit.Base.findIdentical(res, arr[i]) < 0) {
372 res.push(arr[i]);
373 }
374 }
375 return res;
376 };
377 return MochiKit.Base.flattenArray(MochiKit.Base.map(function (expression) {
378 try {
379 var res = element.querySelectorAll(expression);
380 return Array.prototype.slice.call(res, 0);
381 } catch (ignore) {
382 // No querySelectorAll or extended expression syntax used
383 }
384 var nextScope = "";
385 var reducer = function (results, expr) {
386 var match = expr.match(/^[>+~]$/);
387 if (match) {
388 nextScope = match[0];
389 return results;
390 } else {
391 var selector = new MochiKit.Selector.Selector(expr);
392 var elements = MochiKit.Iter.reduce(function (elements, result) {
393 return MochiKit.Base.extend(elements, selector.findElements(result || element, nextScope));
394 }, results, []);
395 nextScope = "";
396 return elements;
397 }
398 };
399 var exprs = expression.replace(/(^\s+|\s+$)/g, '').split(/\s+/);
400 return uniq(MochiKit.Iter.reduce(reducer, exprs, [null]));
401 }, expressions));
402 },
403
404 findDocElements: function () {
405 return MochiKit.Selector.findChildElements(MochiKit.DOM.currentDocument(), arguments);
406 },
407
408 __new__: function () {
409 this.$$ = this.findDocElements;
410 MochiKit.Base.nameFunctions(this);
411 }
412});
413
414MochiKit.Selector.__new__();
415
416MochiKit.Base._exportSymbols(this, MochiKit.Selector);