2
YUI 3.10.3 (build 2fb5187)
3
Copyright 2013 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
8
YUI.add('selector-css2', function (Y, NAME) {
11
* The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
13
* @submodule selector-css2
18
* Provides helper methods for collecting and filtering DOM elements.
21
var PARENT_NODE = 'parentNode',
23
ATTRIBUTES = 'attributes',
24
COMBINATOR = 'combinator',
27
Selector = Y.Selector,
30
_reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/,
33
// TODO: better detection, document specific
35
var isXML = (Y.config.doc.createElement('div').tagName !== 'DIV');
40
* Mapping of shorthand tokens to corresponding attribute selector
45
'\\#(-?[_a-z0-9]+[-\\w\\uE000]*)': '[id=$1]',
46
'\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
50
* List of operators and corresponding boolean functions.
51
* These functions are passed the attribute and the current node's value of the attribute.
56
'': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
57
'~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
58
'|=': '^{val}-?' // optional hyphen-delimited
62
'first-child': function(node) {
63
return Y.DOM._children(node[PARENT_NODE])[0] === node;
67
_bruteQuery: function(selector, root, firstOnly) {
70
tokens = Selector._tokenize(selector),
71
token = tokens[tokens.length - 1],
72
rootDoc = Y.DOM._getDoc(root),
81
className = token.className;
82
tagName = token.tagName || '*';
84
if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
85
// try ID first, unless no root.all && root not in document
86
// (root.all works off document, but not getElementById)
87
if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) {
88
nodes = Y.DOM.allById(id, root);
90
} else if (className) {
91
nodes = root.getElementsByClassName(className);
92
} else { // default to tagName
93
nodes = root.getElementsByTagName(tagName);
96
} else { // brute getElementsByTagName()
97
child = root.firstChild;
99
// only collect HTMLElements
100
// match tag to supplement missing getElementsByTagName
101
if (child.tagName && (tagName === '*' || child.tagName === tagName)) {
104
child = child.nextSibling || child.firstChild;
108
ret = Selector._filterNodes(nodes, tokens, firstOnly);
115
_filterNodes: function(nodes, tokens, firstOnly) {
123
getters = Y.Selector.getters,
133
for (i = 0; (tmpNode = node = nodes[i++]);) {
138
while (tmpNode && tmpNode.tagName) {
143
while ((test = tests[--j])) {
145
if (getters[test[0]]) {
146
value = getters[test[0]](tmpNode, test[0]);
148
value = tmpNode[test[0]];
149
if (test[0] === 'tagName' && !Selector._isXML) {
150
value = value.toUpperCase();
152
if (typeof value != 'string' && value !== undefined && value.toString) {
153
value = value.toString(); // coerce for comparison
154
} else if (value === undefined && tmpNode.getAttribute) {
155
// use getAttribute for non-standard attributes
156
value = tmpNode.getAttribute(test[0], 2); // 2 === force string for IE
160
if ((operator === '=' && value !== test[2]) || // fast path for equality
161
(typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
162
operator.test && !operator.test(value)) || // regex test
163
(!operator.test && // protect against RegExp as function (webkit)
164
typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
166
// skip non element nodes or non-matching tags
167
if ((tmpNode = tmpNode[path])) {
170
(token.tagName && token.tagName !== tmpNode.tagName))
172
tmpNode = tmpNode[path];
180
n--; // move to next token
181
// now that we've passed the test, move up the tree by combinator
182
if (!pass && (combinator = token.combinator)) {
183
path = combinator.axis;
184
tmpNode = tmpNode[path];
186
// skip non element nodes
187
while (tmpNode && !tmpNode.tagName) {
188
tmpNode = tmpNode[path];
191
if (combinator.direct) { // one pass only
195
} else { // success if we made it this far
204
node = tmpNode = null;
220
axis: 'previousSibling',
228
re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
229
fn: function(match, token) {
230
var operator = match[2] || '',
231
operators = Selector.operators,
232
escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
235
// add prefiltering for ID and CLASS
236
if ((match[1] === 'id' && operator === '=') ||
237
(match[1] === 'className' &&
238
Y.config.doc.documentElement.getElementsByClassName &&
239
(operator === '~=' || operator === '='))) {
240
token.prefilter = match[1];
245
// escape all but ID for prefilter, which may run through QSA (via Dom.allById)
246
token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
251
if (operator in operators) {
252
test = operators[operator];
253
if (typeof test === 'string') {
254
match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
255
test = new RegExp(test.replace('{val}', match[3]));
259
if (!token.last || token.prefilter !== match[1]) {
260
return match.slice(1);
266
re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
267
fn: function(match, token) {
270
if (!Selector._isXML) {
271
tag = tag.toUpperCase();
276
if (tag !== '*' && (!token.last || token.prefilter)) {
277
return [TAG_NAME, '=', tag];
279
if (!token.prefilter) {
280
token.prefilter = 'tagName';
286
re: /^\s*([>+~]|\s)\s*/,
287
fn: function(match, token) {
292
re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
293
fn: function(match, token) {
294
var test = Selector[PSEUDOS][match[1]];
295
if (test) { // reorder match array and unescape special chars for tests
297
match[2] = match[2].replace(/\\/g, '');
299
return [match[2], test];
300
} else { // selector token not supported (possibly missing CSS3 module)
307
_getToken: function(token) {
319
Break selector into token units per simple selector.
320
Combinator is attached to the previous token.
322
_tokenize: function(selector) {
323
selector = selector || '';
324
selector = Selector._parseSelector(Y.Lang.trim(selector));
325
var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
326
query = selector, // original query for debug report
327
tokens = [], // array of tokens
328
found = false, // whether or not any matches were found this pass
329
match, // the regex match
334
Search for selector patterns, store, and strip them from the selector string
335
until no patterns match (invalid selector) or we run out of chars.
337
Multiple attributes and pseudos are allowed, in any order.
339
'form:first-child[type=button]:not(button)[lang|=en]'
343
found = false; // reset after full pass
344
for (i = 0; (parser = Selector._parsers[i++]);) {
345
if ( (match = parser.re.exec(selector)) ) { // note assignment
346
if (parser.name !== COMBINATOR ) {
347
token.selector = selector;
349
selector = selector.replace(match[0], ''); // strip current match from selector
350
if (!selector.length) {
354
if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
355
match[1] = Selector._attrFilters[match[1]];
358
test = parser.fn(match, token);
359
if (test === false) { // selector not supported
363
token.tests.push(test);
366
if (!selector.length || parser.name === COMBINATOR) {
368
token = Selector._getToken(token);
369
if (parser.name === COMBINATOR) {
370
token.combinator = Y.Selector.combinators[match[1]];
376
} while (found && selector.length);
378
if (!found || selector.length) { // not fully parsed
379
Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
385
_replaceMarkers: function(selector) {
386
selector = selector.replace(/\[/g, '\uE003');
387
selector = selector.replace(/\]/g, '\uE004');
389
selector = selector.replace(/\(/g, '\uE005');
390
selector = selector.replace(/\)/g, '\uE006');
394
_replaceShorthand: function(selector) {
395
var shorthand = Y.Selector.shorthand,
398
for (re in shorthand) {
399
if (shorthand.hasOwnProperty(re)) {
400
selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
407
_parseSelector: function(selector) {
408
var replaced = Y.Selector._replaceSelector(selector),
409
selector = replaced.selector;
411
// replace shorthand (".foo, #bar") after pseudos and attrs
412
// to avoid replacing unescaped chars
413
selector = Y.Selector._replaceShorthand(selector);
415
selector = Y.Selector._restore('attr', selector, replaced.attrs);
416
selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
418
// replace braces and parens before restoring escaped chars
419
// to avoid replacing ecaped markers
420
selector = Y.Selector._replaceMarkers(selector);
421
selector = Y.Selector._restore('esc', selector, replaced.esc);
427
'class': 'className',
432
href: function(node, attr) {
433
return Y.DOM.getAttribute(node, attr);
436
id: function(node, attr) {
437
return Y.DOM.getId(node);
442
Y.mix(Y.Selector, SelectorCSS2, true);
443
Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
445
// IE wants class with native queries
446
if (Y.Selector.useNative && Y.config.doc.querySelector) {
447
Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
452
}, '3.10.3', {"requires": ["selector-native"]});