631 lines
22 KiB
JavaScript
631 lines
22 KiB
JavaScript
/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
|
||
* part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
|
||
* license. Please see http://www.yui-ext.com/ for more information. */
|
||
|
||
var Selector = Class.create({
|
||
initialize: function(expression) {
|
||
this.expression = expression.strip();
|
||
this.compileMatcher();
|
||
},
|
||
|
||
compileMatcher: function() {
|
||
// Selectors with namespaced attributes can't use the XPath version
|
||
if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
|
||
return this.compileXPathMatcher();
|
||
|
||
var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
|
||
c = Selector.criteria, le, p, m;
|
||
|
||
if (Selector._cache[e]) {
|
||
this.matcher = Selector._cache[e];
|
||
return;
|
||
}
|
||
|
||
this.matcher = ["this.matcher = function(root) {",
|
||
"var r = root, h = Selector.handlers, c = false, n;"];
|
||
|
||
while (e && le != e && (/\S/).test(e)) {
|
||
le = e;
|
||
for (var i in ps) {
|
||
p = ps[i];
|
||
if (m = e.match(p)) {
|
||
this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
|
||
new Template(c[i]).evaluate(m));
|
||
e = e.replace(m[0], '');
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
this.matcher.push("return h.unique(n);\n}");
|
||
eval(this.matcher.join('\n'));
|
||
Selector._cache[this.expression] = this.matcher;
|
||
},
|
||
|
||
compileXPathMatcher: function() {
|
||
var e = this.expression, ps = Selector.patterns,
|
||
x = Selector.xpath, le, m;
|
||
|
||
if (Selector._cache[e]) {
|
||
this.xpath = Selector._cache[e]; return;
|
||
}
|
||
|
||
this.matcher = ['.//*'];
|
||
while (e && le != e && (/\S/).test(e)) {
|
||
le = e;
|
||
for (var i in ps) {
|
||
if (m = e.match(ps[i])) {
|
||
this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
|
||
new Template(x[i]).evaluate(m));
|
||
e = e.replace(m[0], '');
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
this.xpath = this.matcher.join('');
|
||
Selector._cache[this.expression] = this.xpath;
|
||
},
|
||
|
||
findElements: function(root) {
|
||
root = root || document;
|
||
if (this.xpath) return document._getElementsByXPath(this.xpath, root);
|
||
return this.matcher(root);
|
||
},
|
||
|
||
match: function(element) {
|
||
this.tokens = [];
|
||
|
||
var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
|
||
var le, p, m;
|
||
|
||
while (e && le !== e && (/\S/).test(e)) {
|
||
le = e;
|
||
for (var i in ps) {
|
||
p = ps[i];
|
||
if (m = e.match(p)) {
|
||
// use the Selector.assertions methods unless the selector
|
||
// is too complex.
|
||
if (as[i]) {
|
||
this.tokens.push([i, Object.clone(m)]);
|
||
e = e.replace(m[0], '');
|
||
} else {
|
||
// reluctantly do a document-wide search
|
||
// and look for a match in the array
|
||
return this.findElements(document).include(element);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
var match = true, name, matches;
|
||
for (var i = 0, token; token = this.tokens[i]; i++) {
|
||
name = token[0], matches = token[1];
|
||
if (!Selector.assertions[name](element, matches)) {
|
||
match = false; break;
|
||
}
|
||
}
|
||
|
||
return match;
|
||
},
|
||
|
||
toString: function() {
|
||
return this.expression;
|
||
},
|
||
|
||
inspect: function() {
|
||
return "#<Selector:" + this.expression.inspect() + ">";
|
||
}
|
||
});
|
||
|
||
Object.extend(Selector, {
|
||
_cache: { },
|
||
|
||
xpath: {
|
||
descendant: "//*",
|
||
child: "/*",
|
||
adjacent: "/following-sibling::*[1]",
|
||
laterSibling: '/following-sibling::*',
|
||
tagName: function(m) {
|
||
if (m[1] == '*') return '';
|
||
return "[local-name()='" + m[1].toLowerCase() +
|
||
"' or local-name()='" + m[1].toUpperCase() + "']";
|
||
},
|
||
className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
|
||
id: "[@id='#{1}']",
|
||
attrPresence: "[@#{1}]",
|
||
attr: function(m) {
|
||
m[3] = m[5] || m[6];
|
||
return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
|
||
},
|
||
pseudo: function(m) {
|
||
var h = Selector.xpath.pseudos[m[1]];
|
||
if (!h) return '';
|
||
if (Object.isFunction(h)) return h(m);
|
||
return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
|
||
},
|
||
operators: {
|
||
'=': "[@#{1}='#{3}']",
|
||
'!=': "[@#{1}!='#{3}']",
|
||
'^=': "[starts-with(@#{1}, '#{3}')]",
|
||
'$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
|
||
'*=': "[contains(@#{1}, '#{3}')]",
|
||
'~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
|
||
'|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
|
||
},
|
||
pseudos: {
|
||
'first-child': '[not(preceding-sibling::*)]',
|
||
'last-child': '[not(following-sibling::*)]',
|
||
'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
|
||
'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
|
||
'checked': "[@checked]",
|
||
'disabled': "[@disabled]",
|
||
'enabled': "[not(@disabled)]",
|
||
'not': function(m) {
|
||
var e = m[6], p = Selector.patterns,
|
||
x = Selector.xpath, le, m, v;
|
||
|
||
var exclusion = [];
|
||
while (e && le != e && (/\S/).test(e)) {
|
||
le = e;
|
||
for (var i in p) {
|
||
if (m = e.match(p[i])) {
|
||
v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
|
||
exclusion.push("(" + v.substring(1, v.length - 1) + ")");
|
||
e = e.replace(m[0], '');
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return "[not(" + exclusion.join(" and ") + ")]";
|
||
},
|
||
'nth-child': function(m) {
|
||
return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
|
||
},
|
||
'nth-last-child': function(m) {
|
||
return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
|
||
},
|
||
'nth-of-type': function(m) {
|
||
return Selector.xpath.pseudos.nth("position() ", m);
|
||
},
|
||
'nth-last-of-type': function(m) {
|
||
return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
|
||
},
|
||
'first-of-type': function(m) {
|
||
m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
|
||
},
|
||
'last-of-type': function(m) {
|
||
m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
|
||
},
|
||
'only-of-type': function(m) {
|
||
var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
|
||
},
|
||
nth: function(fragment, m) {
|
||
var mm, formula = m[6], predicate;
|
||
if (formula == 'even') formula = '2n+0';
|
||
if (formula == 'odd') formula = '2n+1';
|
||
if (mm = formula.match(/^(\d+)$/)) // digit only
|
||
return '[' + fragment + "= " + mm[1] + ']';
|
||
if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
|
||
if (mm[1] == "-") mm[1] = -1;
|
||
var a = mm[1] ? Number(mm[1]) : 1;
|
||
var b = mm[2] ? Number(mm[2]) : 0;
|
||
predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
|
||
"((#{fragment} - #{b}) div #{a} >= 0)]";
|
||
return new Template(predicate).evaluate({
|
||
fragment: fragment, a: a, b: b });
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
criteria: {
|
||
tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
|
||
className: 'n = h.className(n, r, "#{1}", c); c = false;',
|
||
id: 'n = h.id(n, r, "#{1}", c); c = false;',
|
||
attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
|
||
attr: function(m) {
|
||
m[3] = (m[5] || m[6]);
|
||
return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
|
||
},
|
||
pseudo: function(m) {
|
||
if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
|
||
return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
|
||
},
|
||
descendant: 'c = "descendant";',
|
||
child: 'c = "child";',
|
||
adjacent: 'c = "adjacent";',
|
||
laterSibling: 'c = "laterSibling";'
|
||
},
|
||
|
||
patterns: {
|
||
// combinators must be listed first
|
||
// (and descendant needs to be last combinator)
|
||
laterSibling: /^\s*~\s*/,
|
||
child: /^\s*>\s*/,
|
||
adjacent: /^\s*\+\s*/,
|
||
descendant: /^\s/,
|
||
|
||
// selectors follow
|
||
tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
|
||
id: /^#([\w\-\*]+)(\b|$)/,
|
||
className: /^\.([\w\-\*]+)(\b|$)/,
|
||
pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
|
||
attrPresence: /^\[([\w]+)\]/,
|
||
attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
|
||
},
|
||
|
||
// for Selector.match and Element#match
|
||
assertions: {
|
||
tagName: function(element, matches) {
|
||
return matches[1].toUpperCase() == element.tagName.toUpperCase();
|
||
},
|
||
|
||
className: function(element, matches) {
|
||
return Element.hasClassName(element, matches[1]);
|
||
},
|
||
|
||
id: function(element, matches) {
|
||
return element.id === matches[1];
|
||
},
|
||
|
||
attrPresence: function(element, matches) {
|
||
return Element.hasAttribute(element, matches[1]);
|
||
},
|
||
|
||
attr: function(element, matches) {
|
||
var nodeValue = Element.readAttribute(element, matches[1]);
|
||
return Selector.operators[matches[2]](nodeValue, matches[3]);
|
||
}
|
||
},
|
||
|
||
handlers: {
|
||
// UTILITY FUNCTIONS
|
||
// joins two collections
|
||
concat: function(a, b) {
|
||
for (var i = 0, node; node = b[i]; i++)
|
||
a.push(node);
|
||
return a;
|
||
},
|
||
|
||
// marks an array of nodes for counting
|
||
mark: function(nodes) {
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
node._counted = true;
|
||
return nodes;
|
||
},
|
||
|
||
unmark: function(nodes) {
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
node._counted = undefined;
|
||
return nodes;
|
||
},
|
||
|
||
// mark each child node with its position (for nth calls)
|
||
// "ofType" flag indicates whether we're indexing for nth-of-type
|
||
// rather than nth-child
|
||
index: function(parentNode, reverse, ofType) {
|
||
parentNode._counted = true;
|
||
if (reverse) {
|
||
for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
|
||
var node = nodes[i];
|
||
if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
|
||
}
|
||
} else {
|
||
for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
|
||
if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
|
||
}
|
||
},
|
||
|
||
// filters out duplicates and extends all nodes
|
||
unique: function(nodes) {
|
||
if (nodes.length == 0) return nodes;
|
||
var results = [], n;
|
||
for (var i = 0, l = nodes.length; i < l; i++)
|
||
if (!(n = nodes[i])._counted) {
|
||
n._counted = true;
|
||
results.push(Element.extend(n));
|
||
}
|
||
return Selector.handlers.unmark(results);
|
||
},
|
||
|
||
// COMBINATOR FUNCTIONS
|
||
descendant: function(nodes) {
|
||
var h = Selector.handlers;
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++)
|
||
h.concat(results, node.getElementsByTagName('*'));
|
||
return results;
|
||
},
|
||
|
||
child: function(nodes) {
|
||
var h = Selector.handlers;
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++) {
|
||
for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
|
||
if (child.nodeType == 1 && child.tagName != '!') results.push(child);
|
||
}
|
||
return results;
|
||
},
|
||
|
||
adjacent: function(nodes) {
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++) {
|
||
var next = this.nextElementSibling(node);
|
||
if (next) results.push(next);
|
||
}
|
||
return results;
|
||
},
|
||
|
||
laterSibling: function(nodes) {
|
||
var h = Selector.handlers;
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++)
|
||
h.concat(results, Element.nextSiblings(node));
|
||
return results;
|
||
},
|
||
|
||
nextElementSibling: function(node) {
|
||
while (node = node.nextSibling)
|
||
if (node.nodeType == 1) return node;
|
||
return null;
|
||
},
|
||
|
||
previousElementSibling: function(node) {
|
||
while (node = node.previousSibling)
|
||
if (node.nodeType == 1) return node;
|
||
return null;
|
||
},
|
||
|
||
// TOKEN FUNCTIONS
|
||
tagName: function(nodes, root, tagName, combinator) {
|
||
tagName = tagName.toUpperCase();
|
||
var results = [], h = Selector.handlers;
|
||
if (nodes) {
|
||
if (combinator) {
|
||
// fastlane for ordinary descendant combinators
|
||
if (combinator == "descendant") {
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
h.concat(results, node.getElementsByTagName(tagName));
|
||
return results;
|
||
} else nodes = this[combinator](nodes);
|
||
if (tagName == "*") return nodes;
|
||
}
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
if (node.tagName.toUpperCase() == tagName) results.push(node);
|
||
return results;
|
||
} else return root.getElementsByTagName(tagName);
|
||
},
|
||
|
||
id: function(nodes, root, id, combinator) {
|
||
var targetNode = $(id), h = Selector.handlers;
|
||
if (!targetNode) return [];
|
||
if (!nodes && root == document) return [targetNode];
|
||
if (nodes) {
|
||
if (combinator) {
|
||
if (combinator == 'child') {
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
if (targetNode.parentNode == node) return [targetNode];
|
||
} else if (combinator == 'descendant') {
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
if (Element.descendantOf(targetNode, node)) return [targetNode];
|
||
} else if (combinator == 'adjacent') {
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
if (Selector.handlers.previousElementSibling(targetNode) == node)
|
||
return [targetNode];
|
||
} else nodes = h[combinator](nodes);
|
||
}
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
if (node == targetNode) return [targetNode];
|
||
return [];
|
||
}
|
||
return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
|
||
},
|
||
|
||
className: function(nodes, root, className, combinator) {
|
||
if (nodes && combinator) nodes = this[combinator](nodes);
|
||
return Selector.handlers.byClassName(nodes, root, className);
|
||
},
|
||
|
||
byClassName: function(nodes, root, className) {
|
||
if (!nodes) nodes = Selector.handlers.descendant([root]);
|
||
var needle = ' ' + className + ' ';
|
||
for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
|
||
nodeClassName = node.className;
|
||
if (nodeClassName.length == 0) continue;
|
||
if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
|
||
results.push(node);
|
||
}
|
||
return results;
|
||
},
|
||
|
||
attrPresence: function(nodes, root, attr) {
|
||
if (!nodes) nodes = root.getElementsByTagName("*");
|
||
var results = [];
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
if (Element.hasAttribute(node, attr)) results.push(node);
|
||
return results;
|
||
},
|
||
|
||
attr: function(nodes, root, attr, value, operator) {
|
||
if (!nodes) nodes = root.getElementsByTagName("*");
|
||
var handler = Selector.operators[operator], results = [];
|
||
for (var i = 0, node; node = nodes[i]; i++) {
|
||
var nodeValue = Element.readAttribute(node, attr);
|
||
if (nodeValue === null) continue;
|
||
if (handler(nodeValue, value)) results.push(node);
|
||
}
|
||
return results;
|
||
},
|
||
|
||
pseudo: function(nodes, name, value, root, combinator) {
|
||
if (nodes && combinator) nodes = this[combinator](nodes);
|
||
if (!nodes) nodes = root.getElementsByTagName("*");
|
||
return Selector.pseudos[name](nodes, value, root);
|
||
}
|
||
},
|
||
|
||
pseudos: {
|
||
'first-child': function(nodes, value, root) {
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++) {
|
||
if (Selector.handlers.previousElementSibling(node)) continue;
|
||
results.push(node);
|
||
}
|
||
return results;
|
||
},
|
||
'last-child': function(nodes, value, root) {
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++) {
|
||
if (Selector.handlers.nextElementSibling(node)) continue;
|
||
results.push(node);
|
||
}
|
||
return results;
|
||
},
|
||
'only-child': function(nodes, value, root) {
|
||
var h = Selector.handlers;
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++)
|
||
if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
|
||
results.push(node);
|
||
return results;
|
||
},
|
||
'nth-child': function(nodes, formula, root) {
|
||
return Selector.pseudos.nth(nodes, formula, root);
|
||
},
|
||
'nth-last-child': function(nodes, formula, root) {
|
||
return Selector.pseudos.nth(nodes, formula, root, true);
|
||
},
|
||
'nth-of-type': function(nodes, formula, root) {
|
||
return Selector.pseudos.nth(nodes, formula, root, false, true);
|
||
},
|
||
'nth-last-of-type': function(nodes, formula, root) {
|
||
return Selector.pseudos.nth(nodes, formula, root, true, true);
|
||
},
|
||
'first-of-type': function(nodes, formula, root) {
|
||
return Selector.pseudos.nth(nodes, "1", root, false, true);
|
||
},
|
||
'last-of-type': function(nodes, formula, root) {
|
||
return Selector.pseudos.nth(nodes, "1", root, true, true);
|
||
},
|
||
'only-of-type': function(nodes, formula, root) {
|
||
var p = Selector.pseudos;
|
||
return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
|
||
},
|
||
|
||
// handles the an+b logic
|
||
getIndices: function(a, b, total) {
|
||
if (a == 0) return b > 0 ? [b] : [];
|
||
return $R(1, total).inject([], function(memo, i) {
|
||
if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
|
||
return memo;
|
||
});
|
||
},
|
||
|
||
// handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
|
||
nth: function(nodes, formula, root, reverse, ofType) {
|
||
if (nodes.length == 0) return [];
|
||
if (formula == 'even') formula = '2n+0';
|
||
if (formula == 'odd') formula = '2n+1';
|
||
var h = Selector.handlers, results = [], indexed = [], m;
|
||
h.mark(nodes);
|
||
for (var i = 0, node; node = nodes[i]; i++) {
|
||
if (!node.parentNode._counted) {
|
||
h.index(node.parentNode, reverse, ofType);
|
||
indexed.push(node.parentNode);
|
||
}
|
||
}
|
||
if (formula.match(/^\d+$/)) { // just a number
|
||
formula = Number(formula);
|
||
for (var i = 0, node; node = nodes[i]; i++)
|
||
if (node.nodeIndex == formula) results.push(node);
|
||
} else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
|
||
if (m[1] == "-") m[1] = -1;
|
||
var a = m[1] ? Number(m[1]) : 1;
|
||
var b = m[2] ? Number(m[2]) : 0;
|
||
var indices = Selector.pseudos.getIndices(a, b, nodes.length);
|
||
for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
|
||
for (var j = 0; j < l; j++)
|
||
if (node.nodeIndex == indices[j]) results.push(node);
|
||
}
|
||
}
|
||
h.unmark(nodes);
|
||
h.unmark(indexed);
|
||
return results;
|
||
},
|
||
|
||
'empty': function(nodes, value, root) {
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++) {
|
||
// IE treats comments as element nodes
|
||
if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
|
||
results.push(node);
|
||
}
|
||
return results;
|
||
},
|
||
|
||
'not': function(nodes, selector, root) {
|
||
var h = Selector.handlers, selectorType, m;
|
||
var exclusions = new Selector(selector).findElements(root);
|
||
h.mark(exclusions);
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++)
|
||
if (!node._counted) results.push(node);
|
||
h.unmark(exclusions);
|
||
return results;
|
||
},
|
||
|
||
'enabled': function(nodes, value, root) {
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++)
|
||
if (!node.disabled) results.push(node);
|
||
return results;
|
||
},
|
||
|
||
'disabled': function(nodes, value, root) {
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++)
|
||
if (node.disabled) results.push(node);
|
||
return results;
|
||
},
|
||
|
||
'checked': function(nodes, value, root) {
|
||
for (var i = 0, results = [], node; node = nodes[i]; i++)
|
||
if (node.checked) results.push(node);
|
||
return results;
|
||
}
|
||
},
|
||
|
||
operators: {
|
||
'=': function(nv, v) { return nv == v; },
|
||
'!=': function(nv, v) { return nv != v; },
|
||
'^=': function(nv, v) { return nv.startsWith(v); },
|
||
'$=': function(nv, v) { return nv.endsWith(v); },
|
||
'*=': function(nv, v) { return nv.include(v); },
|
||
'~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
|
||
'|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
|
||
},
|
||
|
||
matchElements: function(elements, expression) {
|
||
var matches = new Selector(expression).findElements(), h = Selector.handlers;
|
||
h.mark(matches);
|
||
for (var i = 0, results = [], element; element = elements[i]; i++)
|
||
if (element._counted) results.push(element);
|
||
h.unmark(matches);
|
||
return results;
|
||
},
|
||
|
||
findElement: function(elements, expression, index) {
|
||
if (Object.isNumber(expression)) {
|
||
index = expression; expression = false;
|
||
}
|
||
return Selector.matchElements(elements, expression || '*')[index || 0];
|
||
},
|
||
|
||
findChildElements: function(element, expressions) {
|
||
var exprs = expressions.join(','), expressions = [];
|
||
exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
|
||
expressions.push(m[1].strip());
|
||
});
|
||
var results = [], h = Selector.handlers;
|
||
for (var i = 0, l = expressions.length, selector; i < l; i++) {
|
||
selector = new Selector(expressions[i].strip());
|
||
h.concat(results, selector.findElements(element));
|
||
}
|
||
return (l > 1) ? h.unique(results) : results;
|
||
}
|
||
});
|
||
|
||
function $$() {
|
||
return Selector.findChildElements(document, $A(arguments));
|
||
}
|