selector-debug.js revision 744aec5933ba55f438dc42a241edb3c95df5abff
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * Provides helper methods for collecting and filtering DOM elements.
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @module dom
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @submodule selector
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * Provides helper methods for collecting and filtering DOM elements.
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @class Selector
aca118b711d5dc86653e0b3c1a122a6b93a0112dlucy wang - Sun Microsystems - Beijing China PSEUDOS = 'pseudos',
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amorevar reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
945c212afec9d02b63fcd6b175ab711ffb55bf3bseb attributes: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb combinator: /^\s*([>+~]|\s)\s*/
ba2e4443695ee6a6f420a35cd4fc3d3346d22932sebvar Selector = {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * Default document for use queries
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @property document
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @type object
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @default window.document
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb document: Y.config.doc,
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * Mapping of attributes to aliases, normally to work around HTMLAttributes
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * that conflict with JS reserved words.
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @property attrAliases
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @type object
107875b08504ad7f74a2abc64d3862ee8845d5efsangeeta attrAliases: {},
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * Mapping of shorthand tokens to corresponding attribute selector
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @property shorthand
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * @type object
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb shorthand: {
9b14cf1dc76dad8f2d9a74310e0a0200af96d217gd * List of operators and corresponding boolean functions.
0d2a8e5eea8ac6ea0f5c517f0c481329b57d5459gd * These functions are passed the attribute and the current node's value of the attribute.
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb '=': function(attr, val) { return attr === val; }, // Equality
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore '!=': function(attr, val) { return attr !== val; }, // Inequality
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore '~=': function(attr, val) { // Match one of space seperated words
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore return (s + attr + s).indexOf((s + val + s)) > -1;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb '|=': function(attr, val) { return Y.DOM._getRegExp('^' + val + '[-]?').test(attr); }, // Match start with value followed by optional hyphen
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb '^=': function(attr, val) { return attr.indexOf(val) === 0; }, // Match starts with value
0d2a8e5eea8ac6ea0f5c517f0c481329b57d5459gd '$=': function(attr, val) { return attr.lastIndexOf(val) === attr[LENGTH] - val[LENGTH]; }, // Match ends with value
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb '*=': function(attr, val) { return attr.indexOf(val) > -1; }, // Match contains value as substring
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb '': function(attr, val) { return attr; } // Just test for existence of attribute
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * List of pseudo-classes and corresponding boolean functions.
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb * These functions are called with the current node, and any value that was parsed with the pseudo regex.
9b14cf1dc76dad8f2d9a74310e0a0200af96d217gd * @property pseudos
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * @type object
aca118b711d5dc86653e0b3c1a122a6b93a0112dlucy wang - Sun Microsystems - Beijing China return node === node.ownerDocument.documentElement;
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore return Selector.getNth(node, val, node[TAG_NAME], true);
9b14cf1dc76dad8f2d9a74310e0a0200af96d217gd return Y.DOM.firstChildByTag(node[PARENT_NODE], node[TAG_NAME]) === node;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return Y.DOM.lastChildByTag(node[PARENT_NODE], node[TAG_NAME]) === node;
39b361b2ebefcef5612a54ae5cbd2179e19be296Richard Bean return children[LENGTH] === 1 && children[0] === node;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return Y.DOM.childrenByTag(node[PARENT_NODE], node[TAG_NAME])[LENGTH] === 1;
aca118b711d5dc86653e0b3c1a122a6b93a0112dlucy wang - Sun Microsystems - Beijing China return !Selector.test(node, simple);
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore var text = node.innerText || node.textContent || '';
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * Test if the supplied node matches the supplied selector.
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * @method test
aca118b711d5dc86653e0b3c1a122a6b93a0112dlucy wang - Sun Microsystems - Beijing China * @param {HTMLElement | String} node An id or node reference to the HTMLElement being tested.
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini * @param {string} selector The CSS Selector to test the node against.
3fd94f8c011031b38162a1db3b554de4371c167fam * @return{boolean} Whether or not the node matches the selector.
3fd94f8c011031b38162a1db3b554de4371c167fam return false;
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini for (var i = 0, len = groups[LENGTH]; i < len; ++i) {
3fd94f8c011031b38162a1db3b554de4371c167fam if ( Selector._testNode(node, groups[i]) ) { // passes if ANY group matches
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini return true;
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini return false;
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini * Filters a set of nodes based on a given CSS selector.
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini * @method filter
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini * @param {array} nodes A set of nodes/ids to filter.
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini * @param {string} selector The selector used to test each node.
3fd94f8c011031b38162a1db3b554de4371c167fam * @return{array} An array of nodes from the supplied array that match the given selector.
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini var result = Selector._filter(nodes, Selector._tokenize(selector)[0]);
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini Y.log('filter: returning:' + result[LENGTH], 'info', 'Selector');
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * Retrieves a set of nodes based on a given CSS selector.
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * @method query
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * @param {string} selector The CSS Selector to test the node against.
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * @param {HTMLElement | String} root optional An id or HTMLElement to start the query from. Defaults to Selector.document.
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * @param {Boolean} firstOnly optional Whether or not to return only the first match.
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore * @return {Array} An array of nodes that match the given selector.
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore query: function(selector, root, firstOnly) {
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore var result = Selector._query(selector, root, firstOnly);
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore //Y.log('query: ' + selector + ' returning ' + result, 'info', 'Selector');
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini _query: function(selector, root, firstOnly, deDupe) {
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini var groups = selector.split(','); // TODO: handle comma in attribute/pseudo
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini for (var i = 0, len = groups[LENGTH]; i < len; ++i) {
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini found = arguments.callee(groups[i], root, firstOnly, true);
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini var idToken = tokens[Selector._getIdTokenIndex(tokens)],
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore // use id shortcut when possible
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore node = Selector.document.getElementById(id);
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore if (node && (root[NODE_TYPE] === 9 || Y.DOM.contains(root, node))) {
3bc4925d580ab95c16f1ddd5eb387655cbd16e65Garrett D'Amore if ( Selector._testNode(node, null, idToken) ) {
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini result = Selector._filter(nodes, token, firstOnly, deDupe);
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini _filter: function(nodes, token, firstOnly, deDupe) {
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini result = Y.DOM.filterElementsBy(nodes, function(node) {
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini if (! Selector._testNode(node, '', token, deDupe)) {
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini return false;
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini return false;
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini Selector._foundCache[Selector._foundCache[LENGTH]] = node;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return true;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb token = token || Selector._tokenize(selector).pop() || {};
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb (token[TAG] !== '*' && node[TAG_NAME].toUpperCase() !== token[TAG]) ||
4045d94132614e1de2073685a6cdd4fbd86bec33sowmini return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb for (i = 0, len = token[ATTRIBUTES][LENGTH]; i < len; ++i) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb attribute = node.getAttribute(token[ATTRIBUTES][i][0], 2);
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb !ops[token[ATTRIBUTES][i][1]](attribute, token[ATTRIBUTES][i][2])) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb for (i = 0, len = token[PSEUDOS][LENGTH]; i < len; ++i) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb !pseudos[token[PSEUDOS][i][0]](node, token[PSEUDOS][i][1])) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb _clearFoundCache: function() {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb Y.log('getBySelector: clearing found cache of ' + Selector._foundCache[LENGTH] + ' elements');
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb for (var i = 0, len = Selector._foundCache[LENGTH]; i < len; ++i) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb try { // IE no like delete
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb } catch(e) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb Y.log('getBySelector: done clearing Selector._foundCache');
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return true;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return Selector._testNode(node[PARENT_NODE], null, token.previous);
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb if (sib && Selector._testNode(sib, null, token.previous)) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return true;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb if (sib[NODE_TYPE] === 1 && Selector._testNode(sib, null, token.previous)) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return true;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb an+b = get every _a_th node starting at the _b_th
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb 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
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb an+0 = get every _a_th element, "0" may be omitted
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb } else if ( isNaN(a) ) {
22eb7cb54d8a6bcf6fe2674cb4b1f0cf2d85cfb6gd return true;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return false;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb } else if (a < 0) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb a = Math.abs(a);
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb for (i = b - 1, len = siblings[LENGTH]; i < len; i += a) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return true;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb for (i = siblings[LENGTH] - b, len = siblings[LENGTH]; i >= 0; i -= a) {
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb return true;
0d2a8e5eea8ac6ea0f5c517f0c481329b57d5459gd return false;
0d2a8e5eea8ac6ea0f5c517f0c481329b57d5459gd return -1;
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb Break selector into token units per simple selector.
ba2e4443695ee6a6f420a35cd4fc3d3346d22932seb Combinator is attached to left-hand selector.
0d2a8e5eea8ac6ea0f5c517f0c481329b57d5459gd var token = {}, // one token per simple selector (left selector holds combinator)
0d2a8e5eea8ac6ea0f5c517f0c481329b57d5459gd found = false, // whether or not any matches were found this pass
found = true;
} while (found);
return tokens;
return attr;
var attrs = selector.match(patterns[ATTRIBUTES]); // pull attributes to avoid false pos on "." and "#"
if (attrs) {
if (attrs) {
return selector;