Created
December 14, 2010 13:11
-
-
Save markiz/740386 to your computer and use it in GitHub Desktop.
Proof of concept: Slick can do it even faster
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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