Skip to content

Instantly share code, notes, and snippets.

@markiz
Created December 14, 2010 13:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markiz/740386 to your computer and use it in GitHub Desktop.
Save markiz/740386 to your computer and use it in GitHub Desktop.
Proof of concept: Slick can do it even faster
<html>
<head>
<title>Slick Matcher Test</title>
<script src="./Slick.Parser.js" type="text/javascript"></script>
<script src="./Slick.Finder.js" type="text/javascript"></script>
</head>
<body>
<div id="head" class="foo">
<b class="foo">Foobaz</b>
<i class="baz">Foobar</i>
<b id="bar" class="baz"></b>
</div>
<!-- Here goes EXCESS DOM -->
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
</div>
</div>
</div>
</div>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
<div id="wtf">
<b class="omg">Woot</b>
<a href="#" class="baz">Link</a>
<i id="uhm">I am excess</i>
<b>I am too</b>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
</div>
</div>
</div>
</div>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
<span style="background: black"><i>spoiler</i>: Dumbledore dies</span>
</div>
<!-- -->
<script>
function assert(name, statement) {
if (statement) {
//console.log(name, "PASS");
} else {
console.log(name, "FAIL");
}
}
var divs = document.getElementsByTagName("div");
var bs = document.getElementsByTagName("b");
var is = document.getElementsByTagName("i");
console.profile("Slick#match");
console.log("OLD MATCH");
var iterationsTotal = 1;
for (var i = 0; i < iterationsTotal; i++){
assert("basic tag", Slick.match(divs[0], "div"));
assert("basic tag", Slick.match(bs[0], "b"));
assert("basic tag", Slick.match(is[0], "i"));
assert("child", Slick.match(bs[0], "div b"));
assert("child", Slick.match(bs[1], "div b"));
assert("child", !Slick.match(bs[1], "div i"));
assert("grandchild", Slick.match(bs[0], "body b"));
assert("grandchild", Slick.match(bs[1], "body b"));
assert("direct child", Slick.match(bs[0], "div > b"));
assert("direct child", Slick.match(divs[0], "body > div"));
assert("direct child", !Slick.match(divs[0], "html > div"));
assert("direct child", !Slick.match(bs[0], "body > b"));
assert("id", Slick.match(divs[0], "div#head"));
assert("id", Slick.match(bs[1], "b#bar"));
assert("class", Slick.match(divs[0], "div.foo"));
assert("class", Slick.match(bs[1], "b.baz"));
assert("sibling", Slick.match(bs[1], "i + b"));
assert("sibling", !Slick.match(bs[1], "b + b"));
assert("cousin", Slick.match(bs[1], "b + i + b"));
assert("cousin", !Slick.match(bs[1], "i + b + b"));
assert("non-existant bro", !Slick.match(bs[1], "i + i + i + i + i + i + i + i + b + b"));
}
console.profileEnd("Slick#match");
console.profile("Slick#matchNew");
console.log("NEW MATCH");
for (var i = 0; i < iterationsTotal; i++) {
assert("basic tag", Slick.matchNew(divs[0], "div"));
assert("basic tag", Slick.matchNew(bs[0], "b"));
assert("basic tag", Slick.matchNew(is[0], "i"));
assert("child", Slick.matchNew(bs[0], "div b"));
assert("child", Slick.matchNew(bs[1], "div b"));
assert("child", !Slick.matchNew(bs[1], "div i"));
assert("grandchild", Slick.matchNew(bs[0], "body b"));
assert("grandchild", Slick.matchNew(bs[1], "body b"));
assert("direct child", Slick.matchNew(bs[0], "div > b"));
assert("direct child", Slick.matchNew(divs[0], "body > div"));
assert("direct child", !Slick.matchNew(divs[0], "html > div"));
assert("direct child", !Slick.matchNew(bs[0], "body > b"));
assert("id", Slick.matchNew(divs[0], "div#head"));
assert("id", Slick.matchNew(bs[1], "b#bar"));
assert("class", Slick.matchNew(divs[0], "div.foo"));
assert("class", Slick.matchNew(bs[1], "b.baz"));
assert("sibling", Slick.matchNew(bs[1], "i + b"));
assert("sibling", !Slick.matchNew(bs[1], "b + b"));
assert("cousin", Slick.matchNew(bs[1], "b + i + b"));
assert("cousin", !Slick.matchNew(bs[1], "i + b + b"));
assert("non-existant bro", !Slick.matchNew(bs[1], "i + i + i + i + i + i + i + i + b + b"));
}
console.profileEnd("Slick#matchNew");
console.profile("Slick#matchNew2");
console.log("NEW MATCH2");
for (var i = 0; i < iterationsTotal; i++) {
assert("basic tag", Slick.matchNew2(divs[0], "div"));
assert("basic tag", Slick.matchNew2(bs[0], "b"));
assert("basic tag", Slick.matchNew2(is[0], "i"));
assert("child", Slick.matchNew2(bs[0], "div b"));
assert("child", Slick.matchNew2(bs[1], "div b"));
assert("child", !Slick.matchNew2(bs[1], "div i"));
assert("grandchild", Slick.matchNew2(bs[0], "body b"));
assert("grandchild", Slick.matchNew2(bs[1], "body b"));
assert("direct child", Slick.matchNew2(bs[0], "div > b"));
assert("direct child", Slick.matchNew2(divs[0], "body > div"));
assert("direct child", !Slick.matchNew2(divs[0], "html > div"));
assert("direct child", !Slick.matchNew2(bs[0], "body > b"));
assert("id", Slick.matchNew2(divs[0], "div#head"));
assert("id", Slick.matchNew2(bs[1], "b#bar"));
assert("class", Slick.matchNew2(divs[0], "div.foo"));
assert("class", Slick.matchNew2(bs[1], "b.baz"));
assert("sibling", Slick.matchNew2(bs[1], "i + b"));
assert("sibling", !Slick.matchNew2(bs[1], "b + b"));
assert("cousin", Slick.matchNew2(bs[1], "b + i + b"));
assert("cousin", !Slick.matchNew2(bs[1], "i + b + b"));
assert("non-existant bro", !Slick.matchNew2(bs[1], "i + i + i + i + i + i + i + i + b + b"));
}
console.profileEnd("Slick#matchNew2");
</script>
</body>
diff --git a/Source/Slick/Slick.Finder.js b/Source/Slick/Slick.Finder.js
index 3034d81..2fb6cf8 100644
--- a/Source/Slick/Slick.Finder.js
+++ b/Source/Slick/Slick.Finder.js
@@ -64,9 +64,9 @@ local.setDocument = function(document){
} catch(e){};
if (this.isHTMLDocument){
-
+
testNode.style.display = 'none';
-
+
// IE returns comment nodes for getElementsByTagName('*') for some documents
testNode.appendChild(document.createComment(''));
starSelectsComments = (testNode.getElementsByTagName('*').length > 0);
@@ -114,19 +114,19 @@ local.setDocument = function(document){
} catch(e){};
this.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
-
+
// Webkit dont return selected options on querySelectorAll
try {
testNode.innerHTML = '<select><option selected="selected">a</option></select>';
this.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
} catch(e){};
-
+
// IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
try {
testNode.innerHTML = '<a class=""></a>';
this.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
} catch(e){};
-
+
}
root.removeChild(testNode);
@@ -408,6 +408,76 @@ local.matchNode = function(node, selector){
return false;
};
+local.matchNodeNew = function(node, selector){
+ var parsed = this.Slick.parse(selector);
+ if (!parsed) return true;
+ // node, tag, id, classes, attributes, pseudos
+ var lambda = function(result) { return (function() { return result }); };
+
+ var hierarchy = [];
+ for (var n = node; n && n.nodeType != 9; n = n.parentNode) hierarchy.push(n);
+ var hierarchySize = hierarchy.length;
+
+ for (var i = 0; i < parsed.expressions.length; i++) {
+ var expression = parsed.expressions[i];
+ hierarchy[0].matched = expression.length - 1;
+ for (var j = expression.length - 1; j >= 0; j--) {
+ var selector = expression[j];
+ for (var k = 0; k < hierarchySize; k++) {
+ if (hierarchy[k].matched == j) {
+ hierarchy[k].matched = undefined;
+ var toCheck = [];
+ var matcher = function(node, selector){ return local.matchSelector(node, selector.tag.toUpperCase(),
+ selector.id, selector.classes, selector.attributes, selector.pseudos)};
+ var doMatch = function(node) { node.matched = j - 1 };
+ var combinator = expression[j + 1] ? expression[j + 1].combinator : false;
+ switch (combinator) {
+ case false: toCheck = [hierarchy[k]]; break;
+ case " ": toCheck = hierarchy.slice(k + 1); break;
+ case ">": toCheck = [hierarchy[k + 1]]; break;
+ case "+": do {
+ hierarchy[k] = hierarchy[k].previousSibling;
+ } while (hierarchy[k] && hierarchy[k].nodeType != 1);
+ if (hierarchy[k]) toCheck = [hierarchy[k]];
+ break;
+ }
+ for (var kk = 0; kk < toCheck.length; kk++) {
+ if (matcher(toCheck[kk], selector)) {
+ doMatch(toCheck[kk]);
+ }
+ }
+ }
+ }
+ };
+ var result = false;
+ for (var k = 0; k < hierarchySize; k++) if (hierarchy[k].matched != undefined) { hierarchy[k].matched = undefined; result = true };
+ if (result) return true;
+ }
+ return false;
+};
+
+local.matchNodeNew2 = function(node, selector){
+ var parsed = this.Slick.parse(selector);
+ if (!parsed) return true;
+ parsed = parsed.reverse();
+ for (var i = 0; i < parsed.expressions.length; i++) {
+ var exp = parsed.expressions[i][0];
+ if (local.matchSelector(node, exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) { // matching first selector against element
+ if (parsed.expressions[i].length == 2) {
+ // simple selector
+ return true;
+ } else {
+ // slicing the fat from expressions
+ parsed.expressions[i] = parsed.expressions[i].slice(1, -1);
+ }
+ } else {
+ parsed.expressions[i] = []; // empty expressions array doesn't match anything
+ }
+ }
+ return Slick.find(node, parsed);
+}
+
+
local.matchPseudo = function(node, name, argument){
var pseudoName = 'pseudo:' + name;
if (this[pseudoName]) return this[pseudoName](node, argument);
@@ -460,7 +530,7 @@ var combinators = {
for (i = 0; item = children[i++];) if (item.getAttributeNode('id').nodeValue == id){
this.push(item, tag, null, classes, attributes, pseudos);
break;
- }
+ }
return;
}
if (!item){
@@ -669,7 +739,7 @@ var pseudos = {
'root': function(node){
return (node === this.root);
},
-
+
'selected': function(node){
return node.selected;
}
@@ -864,6 +934,23 @@ Slick.match = function(node, selector){
return local.matchNode(node, selector);
};
+Slick.matchNew = function(node, selector){
+ if (!(node && selector)) return false;
+ if (!selector || selector === node) return true;
+ if (typeof selector != 'string') return false;
+ local.setDocument(node);
+ return local.matchNodeNew(node, selector);
+};
+
+
+Slick.matchNew2 = function(node, selector){
+ if (!(node && selector)) return false;
+ if (!selector || selector === node) return true;
+ if (typeof selector != 'string') return false;
+ local.setDocument(node);
+ return local.matchNodeNew2(node, selector);
+};
+
// Slick attribute accessor
Slick.defineAttributeGetter = function(name, fn){
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment