]> ToastFreeware Gitweb - philipp/winterrodeln/wradmin.git/blob - wradmin/public/yui/selector/selector-debug.js
Additional cleanup.
[philipp/winterrodeln/wradmin.git] / wradmin / public / yui / selector / selector-debug.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.7.0
6 */
7 /**
8  * The selector module provides helper methods allowing CSS3 Selectors to be used with DOM elements.
9  * @module selector
10  * @title Selector Utility
11  * @namespace YAHOO.util
12  * @requires yahoo, dom
13  */
14
15 (function() {
16 var Y = YAHOO.util;
17
18 /**
19  * Provides helper methods for collecting and filtering DOM elements.
20  * @namespace YAHOO.util
21  * @class Selector
22  * @static
23  */
24
25 Y.Selector = {
26     _foundCache: [],
27     _regexCache: {},
28
29     _re: {
30         nth: /^(?:([-]?\d*)(n){1}|(odd|even)$)*([-+]?\d*)$/,
31         attr: /(\[.*\])/g,
32         urls: /^(?:href|src)/
33     },
34
35     /**
36      * Default document for use queries 
37      * @property document
38      * @type object
39      * @default window.document
40      */
41     document: window.document,
42     /**
43      * Mapping of attributes to aliases, normally to work around HTMLAttributes
44      * that conflict with JS reserved words.
45      * @property attrAliases
46      * @type object
47      */
48     attrAliases: {
49     },
50
51     /**
52      * Mapping of shorthand tokens to corresponding attribute selector 
53      * @property shorthand
54      * @type object
55      */
56     shorthand: {
57         //'(?:(?:[^\\)\\]\\s*>+~,]+)(?:-?[_a-z]+[-\\w]))+#(-?[_a-z]+[-\\w]*)': '[id=$1]',
58         '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
59         '\\.(-?[_a-z]+[-\\w]*)': '[class~=$1]'
60     },
61
62     /**
63      * List of operators and corresponding boolean functions. 
64      * These functions are passed the attribute and the current node's value of the attribute.
65      * @property operators
66      * @type object
67      */
68     operators: {
69         '=': function(attr, val) { return attr === val; }, // Equality
70         '!=': function(attr, val) { return attr !== val; }, // Inequality
71         '~=': function(attr, val) { // Match one of space seperated words 
72             var s = ' ';
73             return (s + attr + s).indexOf((s + val + s)) > -1;
74         },
75         '|=': function(attr, val) { return attr === val || attr.slice(0, val.length + 1) === val + '-'; }, // Matches value followed by optional hyphen
76         '^=': function(attr, val) { return attr.indexOf(val) === 0; }, // Match starts with value
77         '$=': function(attr, val) { return attr.slice(-val.length) === val; }, // Match ends with value
78         '*=': function(attr, val) { return attr.indexOf(val) > -1; }, // Match contains value as substring 
79         '': function(attr, val) { return attr; } // Just test for existence of attribute
80     },
81
82     /**
83      * List of pseudo-classes and corresponding boolean functions. 
84      * These functions are called with the current node, and any value that was parsed with the pseudo regex.
85      * @property pseudos
86      * @type object
87      */
88     pseudos: {
89         'root': function(node) {
90             return node === node.ownerDocument.documentElement;
91         },
92
93         'nth-child': function(node, val) {
94             return Y.Selector._getNth(node, val);
95         },
96
97         'nth-last-child': function(node, val) {
98             return Y.Selector._getNth(node, val, null, true);
99         },
100
101         'nth-of-type': function(node, val) {
102             return Y.Selector._getNth(node, val, node.tagName);
103         },
104          
105         'nth-last-of-type': function(node, val) {
106             return Y.Selector._getNth(node, val, node.tagName, true);
107         },
108          
109         'first-child': function(node) {
110             return Y.Selector._getChildren(node.parentNode)[0] === node;
111         },
112
113         'last-child': function(node) {
114             var children = Y.Selector._getChildren(node.parentNode);
115             return children[children.length - 1] === node;
116         },
117
118         'first-of-type': function(node, val) {
119             return Y.Selector._getChildren(node.parentNode, node.tagName)[0];
120         },
121          
122         'last-of-type': function(node, val) {
123             var children = Y.Selector._getChildren(node.parentNode, node.tagName);
124             return children[children.length - 1];
125         },
126          
127         'only-child': function(node) {
128             var children = Y.Selector._getChildren(node.parentNode);
129             return children.length === 1 && children[0] === node;
130         },
131
132         'only-of-type': function(node) {
133             return Y.Selector._getChildren(node.parentNode, node.tagName).length === 1;
134         },
135
136         'empty': function(node) {
137             return node.childNodes.length === 0;
138         },
139
140         'not': function(node, simple) {
141             return !Y.Selector.test(node, simple);
142         },
143
144         'contains': function(node, str) {
145             var text = node.innerText || node.textContent || '';
146             return text.indexOf(str) > -1;
147         },
148         'checked': function(node) {
149             return node.checked === true;
150         }
151     },
152
153     /**
154      * Test if the supplied node matches the supplied selector.
155      * @method test
156      *
157      * @param {HTMLElement | String} node An id or node reference to the HTMLElement being tested.
158      * @param {string} selector The CSS Selector to test the node against.
159      * @return{boolean} Whether or not the node matches the selector.
160      * @static
161     
162      */
163     test: function(node, selector) {
164         node = Y.Selector.document.getElementById(node) || node;
165
166         if (!node) {
167             return false;
168         }
169
170         var groups = selector ? selector.split(',') : [];
171         if (groups.length) {
172             for (var i = 0, len = groups.length; i < len; ++i) {
173                 if ( Y.Selector._test(node, groups[i]) ) { // passes if ANY group matches
174                     return true;
175                 }
176             }
177             return false;
178         }
179         return Y.Selector._test(node, selector);
180     },
181
182     _test: function(node, selector, token, deDupe) {
183         token = token || Y.Selector._tokenize(selector).pop() || {};
184
185         if (!node.tagName ||
186             (token.tag !== '*' && node.tagName !== token.tag) ||
187             (deDupe && node._found) ) {
188             return false;
189         }
190
191         if (token.attributes.length) {
192             var val,
193                 ieFlag,
194                 re_urls = Y.Selector._re.urls;
195
196             if (!node.attributes || !node.attributes.length) {
197                 return false;
198             }
199             for (var i = 0, attr; attr = token.attributes[i++];) {
200                 ieFlag = (re_urls.test(attr[0])) ? 2 : 0;
201                 val = node.getAttribute(attr[0], ieFlag);
202                 if (val === null || val === undefined) {
203                     return false;
204                 }
205                 if ( Y.Selector.operators[attr[1]] &&
206                         !Y.Selector.operators[attr[1]](val, attr[2])) {
207                     return false;
208                 }
209             }
210         }
211
212         if (token.pseudos.length) {
213             for (var i = 0, len = token.pseudos.length; i < len; ++i) {
214                 if (Y.Selector.pseudos[token.pseudos[i][0]] &&
215                         !Y.Selector.pseudos[token.pseudos[i][0]](node, token.pseudos[i][1])) {
216                     return false;
217                 }
218             }
219         }
220
221         return (token.previous && token.previous.combinator !== ',') ?
222                 Y.Selector._combinators[token.previous.combinator](node, token) :
223                 true;
224     },
225
226     /**
227      * Filters a set of nodes based on a given CSS selector. 
228      * @method filter
229      *
230      * @param {array} nodes A set of nodes/ids to filter. 
231      * @param {string} selector The selector used to test each node.
232      * @return{array} An array of nodes from the supplied array that match the given selector.
233      * @static
234      */
235     filter: function(nodes, selector) {
236         nodes = nodes || [];
237
238         var node,
239             result = [],
240             tokens = Y.Selector._tokenize(selector);
241
242         if (!nodes.item) { // if not HTMLCollection, handle arrays of ids and/or nodes
243             YAHOO.log('filter: scanning input for HTMLElements/IDs', 'info', 'Selector');
244             for (var i = 0, len = nodes.length; i < len; ++i) {
245                 if (!nodes[i].tagName) { // tagName limits to HTMLElements 
246                     node = Y.Selector.document.getElementById(nodes[i]);
247                     if (node) { // skip IDs that return null 
248                         nodes[i] = node;
249                     } else {
250                         YAHOO.log('filter: skipping invalid node', 'warn', 'Selector');
251                     }
252                 }
253             }
254         }
255         result = Y.Selector._filter(nodes, Y.Selector._tokenize(selector)[0]);
256         YAHOO.log('filter: returning:' + result.length, 'info', 'Selector');
257         return result;
258     },
259
260     _filter: function(nodes, token, firstOnly, deDupe) {
261         var result = firstOnly ? null : [],
262             foundCache = Y.Selector._foundCache;
263
264         for (var i = 0, len = nodes.length; i < len; i++) {
265             if (! Y.Selector._test(nodes[i], '', token, deDupe)) {
266                 continue;
267             }
268
269             if (firstOnly) {
270                 return nodes[i];
271             }
272             if (deDupe) {
273                 if (nodes[i]._found) {
274                     continue;
275                 }
276                 nodes[i]._found = true;
277                 foundCache[foundCache.length] = nodes[i];
278             }
279
280             result[result.length] = nodes[i];
281         }
282
283         return result;
284     },
285
286     /**
287      * Retrieves a set of nodes based on a given CSS selector. 
288      * @method query
289      *
290      * @param {string} selector The CSS Selector to test the node against.
291      * @param {HTMLElement | String} root optional An id or HTMLElement to start the query from. Defaults to Selector.document.
292      * @param {Boolean} firstOnly optional Whether or not to return only the first match.
293      * @return {Array} An array of nodes that match the given selector.
294      * @static
295      */
296     query: function(selector, root, firstOnly) {
297         var result = Y.Selector._query(selector, root, firstOnly);
298         YAHOO.log('query: returning ' + result, 'info', 'Selector');
299         return result;
300     },
301
302
303     _query: function(selector, root, firstOnly, deDupe) {
304         var result =  (firstOnly) ? null : [],
305             node;
306
307         if (!selector) {
308             return result;
309         }
310
311         var groups = selector.split(','); // TODO: handle comma in attribute/pseudo
312
313         if (groups.length > 1) {
314             var found;
315             for (var i = 0, len = groups.length; i < len; ++i) {
316                 found = arguments.callee(groups[i], root, firstOnly, true);
317                 result = firstOnly ? found : result.concat(found); 
318             }
319             Y.Selector._clearFoundCache();
320             return result;
321         }
322
323         if (root && !root.nodeName) { // assume ID
324             root = Y.Selector.document.getElementById(root);
325             if (!root) {
326                 YAHOO.log('invalid root node provided', 'warn', 'Selector');
327                 return result;
328             }
329         }
330
331         root = root || Y.Selector.document;
332
333         if (root.nodeName !== '#document') { // prepend with root selector
334             Y.Dom.generateId(root); // TODO: cleanup after?
335             selector = root.tagName + '#' + root.id + ' ' + selector;
336             node = root;
337             root = root.ownerDocument;
338         }
339
340         var tokens = Y.Selector._tokenize(selector);
341         var idToken = tokens[Y.Selector._getIdTokenIndex(tokens)],
342             nodes = [],
343             id,
344             token = tokens.pop() || {};
345             
346         if (idToken) {
347             id = Y.Selector._getId(idToken.attributes);
348         }
349
350         // use id shortcut when possible
351         if (id) {
352             node = node || Y.Selector.document.getElementById(id);
353
354             if (node && (root.nodeName === '#document' || Y.Dom.isAncestor(root, node))) {
355                 if ( Y.Selector._test(node, null, idToken) ) {
356                     if (idToken === token) {
357                         nodes = [node]; // simple selector
358                     } else if (idToken.combinator === ' ' || idToken.combinator === '>') {
359                         root = node; // start from here
360                     }
361                 }
362             } else {
363                 return result;
364             }
365         }
366
367         if (root && !nodes.length) {
368             nodes = root.getElementsByTagName(token.tag);
369         }
370
371         if (nodes.length) {
372             result = Y.Selector._filter(nodes, token, firstOnly, deDupe); 
373         }
374
375         return result;
376     },
377
378
379     _clearFoundCache: function() {
380         var foundCache = Y.Selector._foundCache;
381         YAHOO.log('getBySelector: clearing found cache of ' + foundCache.length + ' elements');
382         for (var i = 0, len = foundCache.length; i < len; ++i) {
383             try { // IE no like delete
384                 delete foundCache[i]._found;
385             } catch(e) {
386                 foundCache[i].removeAttribute('_found');
387             }
388         }
389         foundCache = [];
390         YAHOO.log('getBySelector: done clearing foundCache');
391     },
392
393
394     _getRegExp: function(str, flags) {
395         var regexCache = Y.Selector._regexCache;
396         flags = flags || '';
397         if (!regexCache[str + flags]) {
398             regexCache[str + flags] = new RegExp(str, flags);
399         }
400         return regexCache[str + flags];
401     },
402
403     _getChildren: function() {
404         if (document.documentElement.children) { // document for capability test
405             return function(node, tag) {
406                 return (tag) ? node.children.tags(tag) : node.children || [];
407             };
408         } else {
409             return function(node, tag) {
410                 if (node._children) {
411                     return node._children;
412                 }
413                 var children = [],
414                     childNodes = node.childNodes;
415
416                 for (var i = 0, len = childNodes.length; i < len; ++i) {
417                     if (childNodes[i].tagName) {
418                         if (!tag || childNodes[i].tagName === tag) {
419                             children[children.length] = childNodes[i];
420                         }
421                     }
422                 }
423                 node._children = children;
424                 return children;
425             };
426         }
427     }(),
428
429     _combinators: {
430         ' ': function(node, token) {
431             while ( (node = node.parentNode) ) {
432                 if (Y.Selector._test(node, '', token.previous)) {
433                     return true;
434                 }
435             }  
436             return false;
437         },
438
439         '>': function(node, token) {
440             return Y.Selector._test(node.parentNode, null, token.previous);
441         },
442
443         '+': function(node, token) {
444             var sib = node.previousSibling;
445             while (sib && sib.nodeType !== 1) {
446                 sib = sib.previousSibling;
447             }
448
449             if (sib && Y.Selector._test(sib, null, token.previous)) {
450                 return true; 
451             }
452             return false;
453         },
454
455         '~': function(node, token) {
456             var sib = node.previousSibling;
457             while (sib) {
458                 if (sib.nodeType === 1 && Y.Selector._test(sib, null, token.previous)) {
459                     return true;
460                 }
461                 sib = sib.previousSibling;
462             }
463
464             return false;
465         }
466     },
467
468
469     /*
470         an+b = get every _a_th node starting at the _b_th
471         0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
472         1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
473         an+0 = get every _a_th element, "0" may be omitted 
474     */
475     _getNth: function(node, expr, tag, reverse) {
476         Y.Selector._re.nth.test(expr);
477         var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
478             n = RegExp.$2, // "n"
479             oddeven = RegExp.$3, // "odd" or "even"
480             b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
481             result = [],
482             op;
483
484         var siblings = Y.Selector._getChildren(node.parentNode, tag);
485
486         if (oddeven) {
487             a = 2; // always every other
488             op = '+';
489             n = 'n';
490             b = (oddeven === 'odd') ? 1 : 0;
491         } else if ( isNaN(a) ) {
492             a = (n) ? 1 : 0; // start from the first or no repeat
493         }
494
495         if (a === 0) { // just the first
496             if (reverse) {
497                 b = siblings.length - b + 1; 
498             }
499
500             if (siblings[b - 1] === node) {
501                 return true;
502             } else {
503                 return false;
504             }
505
506         } else if (a < 0) {
507             reverse = !!reverse;
508             a = Math.abs(a);
509         }
510
511         if (!reverse) {
512             for (var i = b - 1, len = siblings.length; i < len; i += a) {
513                 if ( i >= 0 && siblings[i] === node ) {
514                     return true;
515                 }
516             }
517         } else {
518             for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
519                 if ( i < len && siblings[i] === node ) {
520                     return true;
521                 }
522             }
523         }
524         return false;
525     },
526
527     _getId: function(attr) {
528         for (var i = 0, len = attr.length; i < len; ++i) {
529             if (attr[i][0] == 'id' && attr[i][1] === '=') {
530                 return attr[i][2];
531             }
532         }
533     },
534
535     _getIdTokenIndex: function(tokens) {
536         for (var i = 0, len = tokens.length; i < len; ++i) {
537             if (Y.Selector._getId(tokens[i].attributes)) {
538                 return i;
539             }
540         }
541         return -1;
542     },
543
544     _patterns: {
545         tag: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
546         attributes: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
547         pseudos: /^:([-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
548         combinator: /^\s*([>+~]|\s)\s*/
549     },
550
551     /**
552         Break selector into token units per simple selector.
553         Combinator is attached to left-hand selector.
554      */
555     _tokenize: function(selector) {
556         var token = {},     // one token per simple selector (left selector holds combinator)
557             tokens = [],    // array of tokens
558             id,             // unique id for the simple selector (if found)
559             found = false,  // whether or not any matches were found this pass
560             patterns = Y.Selector._patterns,
561             match;          // the regex match
562
563         selector = Y.Selector._replaceShorthand(selector); // convert ID and CLASS shortcuts to attributes
564
565         /*
566             Search for selector patterns, store, and strip them from the selector string
567             until no patterns match (invalid selector) or we run out of chars.
568
569             Multiple attributes and pseudos are allowed, in any order.
570             for example:
571                 'form:first-child[type=button]:not(button)[lang|=en]'
572         */
573         do {
574             found = false; // reset after full pass
575             for (var re in patterns) {
576                 if (YAHOO.lang.hasOwnProperty(patterns, re)) {
577                     if (re != 'tag' && re != 'combinator') { // only one allowed
578                         token[re] = token[re] || [];
579                     }
580                     if ( (match = patterns[re].exec(selector)) ) { // note assignment
581                         found = true;
582                         if (re != 'tag' && re != 'combinator') { // only one allowed
583                             // capture ID for fast path to element
584                             if (re === 'attributes' && match[1] === 'id') {
585                                 token.id = match[3];
586                             }
587
588                             token[re].push(match.slice(1));
589                         } else { // single selector (tag, combinator)
590                             token[re] = match[1];
591                         }
592                         selector = selector.replace(match[0], ''); // strip current match from selector
593                         if (re === 'combinator' || !selector.length) { // next token or done
594                             token.attributes = Y.Selector._fixAttributes(token.attributes);
595                             token.pseudos = token.pseudos || [];
596                             token.tag = token.tag ? token.tag.toUpperCase() : '*';
597                             tokens.push(token);
598
599                             token = { // prep next token
600                                 previous: token
601                             };
602                         }
603                     }
604                 }
605             }
606         } while (found);
607
608         return tokens;
609     },
610
611
612     _fixAttributes: function(attr) {
613         var aliases = Y.Selector.attrAliases;
614         attr = attr || [];
615         for (var i = 0, len = attr.length; i < len; ++i) {
616             if (aliases[attr[i][0]]) { // convert reserved words, etc
617                 attr[i][0] = aliases[attr[i][0]];
618             }
619             if (!attr[i][1]) { // use exists operator
620                 attr[i][1] = '';
621             }
622         }
623         return attr;
624     },
625
626     _replaceShorthand: function(selector) {
627         var shorthand = Y.Selector.shorthand;
628
629         //var attrs = selector.match(Y.Selector._patterns.attributes); // pull attributes to avoid false pos on "." and "#"
630         var attrs = selector.match(Y.Selector._re.attr); // pull attributes to avoid false pos on "." and "#"
631         if (attrs) {
632             selector = selector.replace(Y.Selector._re.attr, 'REPLACED_ATTRIBUTE');
633         }
634         for (var re in shorthand) {
635             if (YAHOO.lang.hasOwnProperty(shorthand, re)) {
636                 selector = selector.replace(Y.Selector._getRegExp(re, 'gi'), shorthand[re]);
637             }
638         }
639
640         if (attrs) {
641             for (var i = 0, len = attrs.length; i < len; ++i) {
642                 selector = selector.replace('REPLACED_ATTRIBUTE', attrs[i]);
643             }
644         }
645         return selector;
646     }
647 };
648
649 if (YAHOO.env.ua.ie && YAHOO.env.ua.ie < 8) { // rewrite class for IE < 8
650     Y.Selector.attrAliases['class'] = 'className';
651     Y.Selector.attrAliases['for'] = 'htmlFor';
652 }
653
654 })();
655 YAHOO.register("selector", YAHOO.util.Selector, {version: "2.7.0", build: "1799"});