src/cm/media/js/lib/yui/yui3.0.0/build/dom/selector-css2.js
changeset 0 40c8f766c9b8
equal deleted inserted replaced
-1:000000000000 0:40c8f766c9b8
       
     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: 3.0.0
       
     6 build: 1549
       
     7 */
       
     8 YUI.add('selector-css2', function(Y) {
       
     9 
       
    10 /**
       
    11  * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
       
    12  * @module dom
       
    13  * @submodule selector-css2
       
    14  * @for Selector
       
    15  */
       
    16 
       
    17 /**
       
    18  * Provides helper methods for collecting and filtering DOM elements.
       
    19  */
       
    20 
       
    21 var PARENT_NODE = 'parentNode',
       
    22     TAG_NAME = 'tagName',
       
    23     ATTRIBUTES = 'attributes',
       
    24     COMBINATOR = 'combinator',
       
    25     PSEUDOS = 'pseudos',
       
    26 
       
    27     Selector = Y.Selector,
       
    28 
       
    29     SelectorCSS2 = {
       
    30         SORT_RESULTS: true,
       
    31         _children: function(node, tag) {
       
    32             var ret = node.children,
       
    33                 i,
       
    34                 children = [],
       
    35                 childNodes,
       
    36                 child;
       
    37 
       
    38             if (node.children && tag && node.children.tags) {
       
    39                 children = node.children.tags(tag);
       
    40             } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
       
    41                 childNodes = ret || node.childNodes;
       
    42                 ret = [];
       
    43                 for (i = 0; (child = childNodes[i++]);) {
       
    44                     if (child.tagName) {
       
    45                         if (!tag || tag === child.tagName) {
       
    46                             ret.push(child);
       
    47                         }
       
    48                     }
       
    49                 }
       
    50             }
       
    51 
       
    52             return ret || [];
       
    53         },
       
    54 
       
    55         _regexCache: {},
       
    56 
       
    57         _re: {
       
    58             attr: /(\[.*\])/g,
       
    59             pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
       
    60         },
       
    61 
       
    62         /**
       
    63          * Mapping of shorthand tokens to corresponding attribute selector 
       
    64          * @property shorthand
       
    65          * @type object
       
    66          */
       
    67         shorthand: {
       
    68             '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
       
    69             '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
       
    70         },
       
    71 
       
    72         /**
       
    73          * List of operators and corresponding boolean functions. 
       
    74          * These functions are passed the attribute and the current node's value of the attribute.
       
    75          * @property operators
       
    76          * @type object
       
    77          */
       
    78         operators: {
       
    79             '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
       
    80             //'': '.+',
       
    81             //'=': '^{val}$', // equality
       
    82             '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
       
    83             '|=': '^{val}-?' // optional hyphen-delimited
       
    84         },
       
    85 
       
    86         pseudos: {
       
    87            'first-child': function(node) { 
       
    88                 return Y.Selector._children(node[PARENT_NODE])[0] === node; 
       
    89             } 
       
    90         },
       
    91 
       
    92         _bruteQuery: function(selector, root, firstOnly) {
       
    93             var ret = [],
       
    94                 nodes = [],
       
    95                 tokens = Selector._tokenize(selector),
       
    96                 token = tokens[tokens.length - 1],
       
    97                 rootDoc = Y.DOM._getDoc(root),
       
    98                 id,
       
    99                 className,
       
   100                 tagName;
       
   101 
       
   102 
       
   103             // if we have an initial ID, set to root when in document
       
   104             if (tokens[0] && rootDoc === root &&  
       
   105                     (id = tokens[0].id) &&
       
   106                     rootDoc.getElementById(id)) {
       
   107                 root = rootDoc.getElementById(id);
       
   108             }
       
   109 
       
   110             if (token) {
       
   111                 // prefilter nodes
       
   112                 id = token.id;
       
   113                 className = token.className;
       
   114                 tagName = token.tagName || '*';
       
   115 
       
   116                 // try ID first
       
   117                 if (id) {
       
   118                     if (rootDoc.getElementById(id)) { // if in document
       
   119                     nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
       
   120                 }
       
   121                 // try className if supported
       
   122                 } else if (className) {
       
   123                     nodes = root.getElementsByClassName(className);
       
   124                 } else if (tagName) { // default to tagName
       
   125                     nodes = root.getElementsByTagName(tagName || '*');
       
   126                 }
       
   127 
       
   128                 if (nodes.length) {
       
   129                     ret = Selector._filterNodes(nodes, tokens, firstOnly);
       
   130                 }
       
   131             }
       
   132 
       
   133             return ret;
       
   134         },
       
   135         
       
   136         _filterNodes: function(nodes, tokens, firstOnly) {
       
   137             var i = 0,
       
   138                 j,
       
   139                 len = tokens.length,
       
   140                 n = len - 1,
       
   141                 result = [],
       
   142                 node = nodes[0],
       
   143                 tmpNode = node,
       
   144                 getters = Y.Selector.getters,
       
   145                 operator,
       
   146                 combinator,
       
   147                 token,
       
   148                 path,
       
   149                 pass,
       
   150                 //FUNCTION = 'function',
       
   151                 value,
       
   152                 tests,
       
   153                 test;
       
   154 
       
   155             //do {
       
   156             for (i = 0; (tmpNode = node = nodes[i++]);) {
       
   157                 n = len - 1;
       
   158                 path = null;
       
   159                 
       
   160                 testLoop:
       
   161                 while (tmpNode && tmpNode.tagName) {
       
   162                     token = tokens[n];
       
   163                     tests = token.tests;
       
   164                     j = tests.length;
       
   165                     if (j && !pass) {
       
   166                         while ((test = tests[--j])) {
       
   167                             operator = test[1];
       
   168                             if (getters[test[0]]) {
       
   169                                 value = getters[test[0]](tmpNode, test[0]);
       
   170                             } else {
       
   171                                 value = tmpNode[test[0]];
       
   172                                 // use getAttribute for non-standard attributes
       
   173                                 if (value === undefined && tmpNode.getAttribute) {
       
   174                                     value = tmpNode.getAttribute(test[0]);
       
   175                                 }
       
   176                             }
       
   177 
       
   178                             if ((operator === '=' && value !== test[2]) ||  // fast path for equality
       
   179                                 (operator.test && !operator.test(value)) ||  // regex test
       
   180                                 (operator.call && !operator(tmpNode, test[0]))) { // function test
       
   181 
       
   182                                 // skip non element nodes or non-matching tags
       
   183                                 if ((tmpNode = tmpNode[path])) {
       
   184                                     while (tmpNode &&
       
   185                                         (!tmpNode.tagName ||
       
   186                                             (token.tagName && token.tagName !== tmpNode.tagName))
       
   187                                     ) {
       
   188                                         tmpNode = tmpNode[path]; 
       
   189                                     }
       
   190                                 }
       
   191                                 continue testLoop;
       
   192                             }
       
   193                         }
       
   194                     }
       
   195 
       
   196                     n--; // move to next token
       
   197                     // now that we've passed the test, move up the tree by combinator
       
   198                     if (!pass && (combinator = token.combinator)) {
       
   199                         path = combinator.axis;
       
   200                         tmpNode = tmpNode[path];
       
   201 
       
   202                         // skip non element nodes
       
   203                         while (tmpNode && !tmpNode.tagName) {
       
   204                             tmpNode = tmpNode[path]; 
       
   205                         }
       
   206 
       
   207                         if (combinator.direct) { // one pass only
       
   208                             path = null; 
       
   209                         }
       
   210 
       
   211                     } else { // success if we made it this far
       
   212                         result.push(node);
       
   213                         if (firstOnly) {
       
   214                             return result;
       
   215                         }
       
   216                         break;
       
   217                     }
       
   218                 }
       
   219             }// while (tmpNode = node = nodes[++i]);
       
   220             node = tmpNode = null;
       
   221             return result;
       
   222         },
       
   223 
       
   224         _getRegExp: function(str, flags) {
       
   225             var regexCache = Selector._regexCache;
       
   226             flags = flags || '';
       
   227             if (!regexCache[str + flags]) {
       
   228                 regexCache[str + flags] = new RegExp(str, flags);
       
   229             }
       
   230             return regexCache[str + flags];
       
   231         },
       
   232 
       
   233         combinators: {
       
   234             ' ': {
       
   235                 axis: 'parentNode'
       
   236             },
       
   237 
       
   238             '>': {
       
   239                 axis: 'parentNode',
       
   240                 direct: true
       
   241             },
       
   242 
       
   243 
       
   244             '+': {
       
   245                 axis: 'previousSibling',
       
   246                 direct: true
       
   247             }
       
   248         },
       
   249 
       
   250         _parsers: [
       
   251             {
       
   252                 name: ATTRIBUTES,
       
   253                 re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
       
   254                 fn: function(match, token) {
       
   255                     var operator = match[2] || '',
       
   256                         operators = Y.Selector.operators,
       
   257                         test;
       
   258 
       
   259                     // add prefiltering for ID and CLASS
       
   260                     if ((match[1] === 'id' && operator === '=') ||
       
   261                             (match[1] === 'className' &&
       
   262                             document.getElementsByClassName &&
       
   263                             (operator === '~=' || operator === '='))) {
       
   264                         token.prefilter = match[1];
       
   265                         token[match[1]] = match[3];
       
   266                     }
       
   267 
       
   268                     // add tests
       
   269                     if (operator in operators) {
       
   270                         test = operators[operator];
       
   271                         if (typeof test === 'string') {
       
   272                             test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
       
   273                         }
       
   274                         match[2] = test;
       
   275                     }
       
   276                     if (!token.last || token.prefilter !== match[1]) {
       
   277                         return match.slice(1);
       
   278                     }
       
   279                 }
       
   280 
       
   281             },
       
   282             {
       
   283                 name: TAG_NAME,
       
   284                 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
       
   285                 fn: function(match, token) {
       
   286                     var tag = match[1].toUpperCase();
       
   287                     token.tagName = tag;
       
   288 
       
   289                     if (tag !== '*' && (!token.last || token.prefilter)) {
       
   290                         return [TAG_NAME, '=', tag];
       
   291                     }
       
   292                     if (!token.prefilter) {
       
   293                         token.prefilter = 'tagName';
       
   294                     }
       
   295                 }
       
   296             },
       
   297             {
       
   298                 name: COMBINATOR,
       
   299                 re: /^\s*([>+~]|\s)\s*/,
       
   300                 fn: function(match, token) {
       
   301                 }
       
   302             },
       
   303             {
       
   304                 name: PSEUDOS,
       
   305                 re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
       
   306                 fn: function(match, token) {
       
   307                     var test = Selector[PSEUDOS][match[1]];
       
   308                     if (test) { // reorder match array
       
   309                         return [match[2], test];
       
   310                     } else { // selector token not supported (possibly missing CSS3 module)
       
   311                         return false;
       
   312                     }
       
   313                 }
       
   314             }
       
   315             ],
       
   316 
       
   317         _getToken: function(token) {
       
   318             return {
       
   319                 tagName: null,
       
   320                 id: null,
       
   321                 className: null,
       
   322                 attributes: {},
       
   323                 combinator: null,
       
   324                 tests: []
       
   325             };
       
   326         },
       
   327 
       
   328         /**
       
   329             Break selector into token units per simple selector.
       
   330             Combinator is attached to the previous token.
       
   331          */
       
   332         _tokenize: function(selector) {
       
   333             selector = selector || '';
       
   334             selector = Selector._replaceShorthand(Y.Lang.trim(selector)); 
       
   335             var token = Selector._getToken(),     // one token per simple selector (left selector holds combinator)
       
   336                 query = selector, // original query for debug report
       
   337                 tokens = [],    // array of tokens
       
   338                 found = false,  // whether or not any matches were found this pass
       
   339                 match,         // the regex match
       
   340                 test,
       
   341                 i, parser;
       
   342 
       
   343             /*
       
   344                 Search for selector patterns, store, and strip them from the selector string
       
   345                 until no patterns match (invalid selector) or we run out of chars.
       
   346 
       
   347                 Multiple attributes and pseudos are allowed, in any order.
       
   348                 for example:
       
   349                     'form:first-child[type=button]:not(button)[lang|=en]'
       
   350             */
       
   351             outer:
       
   352             do {
       
   353                 found = false; // reset after full pass
       
   354                 for (i = 0; (parser = Selector._parsers[i++]);) {
       
   355                     if ( (match = parser.re.exec(selector)) ) { // note assignment
       
   356                         if (parser !== COMBINATOR ) {
       
   357                             token.selector = selector;
       
   358                         }
       
   359                         selector = selector.replace(match[0], ''); // strip current match from selector
       
   360                         if (!selector.length) {
       
   361                             token.last = true;
       
   362                         }
       
   363 
       
   364                         if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
       
   365                             match[1] = Selector._attrFilters[match[1]];
       
   366                         }
       
   367 
       
   368                         test = parser.fn(match, token);
       
   369                         if (test === false) { // selector not supported
       
   370                             found = false;
       
   371                             break outer;
       
   372                         } else if (test) {
       
   373                             token.tests.push(test);
       
   374                         }
       
   375 
       
   376                         if (!selector.length || parser.name === COMBINATOR) {
       
   377                             tokens.push(token);
       
   378                             token = Selector._getToken(token);
       
   379                             if (parser.name === COMBINATOR) {
       
   380                                 token.combinator = Y.Selector.combinators[match[1]];
       
   381                             }
       
   382                         }
       
   383                         found = true;
       
   384                     }
       
   385                 }
       
   386             } while (found && selector.length);
       
   387 
       
   388             if (!found || selector.length) { // not fully parsed
       
   389                 tokens = [];
       
   390             }
       
   391             return tokens;
       
   392         },
       
   393 
       
   394         _replaceShorthand: function(selector) {
       
   395             var shorthand = Selector.shorthand,
       
   396                 attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
       
   397                 pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
       
   398                 re, i, len;
       
   399 
       
   400             if (pseudos) {
       
   401                 selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
       
   402             }
       
   403 
       
   404             if (attrs) {
       
   405                 selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
       
   406             }
       
   407 
       
   408             for (re in shorthand) {
       
   409                 if (shorthand.hasOwnProperty(re)) {
       
   410                     selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
       
   411                 }
       
   412             }
       
   413 
       
   414             if (attrs) {
       
   415                 for (i = 0, len = attrs.length; i < len; ++i) {
       
   416                     selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
       
   417                 }
       
   418             }
       
   419             if (pseudos) {
       
   420                 for (i = 0, len = pseudos.length; i < len; ++i) {
       
   421                     selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
       
   422                 }
       
   423             }
       
   424             return selector;
       
   425         },
       
   426 
       
   427         _attrFilters: {
       
   428             'class': 'className',
       
   429             'for': 'htmlFor'
       
   430         },
       
   431 
       
   432         getters: {
       
   433             href: function(node, attr) {
       
   434                 return Y.DOM.getAttribute(node, attr);
       
   435             }
       
   436         }
       
   437     };
       
   438 
       
   439 Y.mix(Y.Selector, SelectorCSS2, true);
       
   440 Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
       
   441 
       
   442 // IE wants class with native queries
       
   443 if (Y.Selector.useNative && document.querySelector) {
       
   444     Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
       
   445 }
       
   446 
       
   447 
       
   448 
       
   449 }, '3.0.0' ,{requires:['selector-native']});