Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 29 additions & 16 deletions src/nwsapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -1820,17 +1820,23 @@
},

// equivalent of w3c 'querySelector' method
// A stable identity for the common no-callback case. A cached plan is
// only reused when the callback matches, and a closure allocated per call
// never does, so every querySelector() rebuilt the plan it had just cached.
firstMatch =
function firstMatch() {
return false;
},

first =
function _querySelector(selectors, context, callback) {
return select(selectors, context,
typeof callback == 'function' ?
function firstMatch(element) {
function firstMatchCallback(element) {
callback(element);
return false;
} :
function firstMatch() {
return false;
}
firstMatch
)[0] || null;
},

Expand All @@ -1849,11 +1855,9 @@

if (selectors) {
if ((resolver = selectResolvers.get(selectors))) {
if (resolver.context === context &&
resolver.callback === callback) {
if (resolver.callback === callback) {
var i, l, list,
f = resolver.factory,
h = resolver.htmlset,
n = resolver.nodeset;
if (n.length > 1) {
for (i = 0, l = n.length; l > i; ++i) {
Expand All @@ -1869,11 +1873,8 @@
hasDupes && (nodes = unique(nodes));
}
} else {
if (f[0]) {
nodes = f[0](h[0](), callback, context, nodes);
} else {
nodes = h[0]();
}
list = compat[n[0][0]](context, n[0].slice(1))();
nodes = f[0] ? f[0](list, callback, context, nodes) : list;
}
if (typeof callback == 'function') {
nodes = concatCall(nodes, callback);
Expand All @@ -1885,10 +1886,20 @@
}
}

// save/reuse factory and closure collection
selectResolvers.set(selectors, collect(parse(selectors, true), context, callback));
resolver = collect(parse(selectors, true), context, callback);
nodes = resolver.results;

nodes = selectResolvers.get(selectors).results;
// Cache the query plan, never the answer. 'results' is a live list of
// matched elements and 'htmlset' closes over the context, so caching
// the whole collection kept a removed subtree alive for as long as its
// selector stayed in the cache. What is kept here is context-free,
// which also lets a plan be reused across contexts instead of only for
// the one it was built against.
selectResolvers.set(selectors, {
callback: callback,
factory: resolver.factory,
nodeset: resolver.nodeset
});

if (typeof callback == 'function') {
nodes = concatCall(nodes, callback);
Expand Down Expand Up @@ -1928,8 +1939,10 @@
}
}

nodeset[i] = token[1] + token[2];
// unescape before recording the token: 'nodeset' is what a later
// run rebuilds its candidate list from, so the two must agree
token[2] = unescapeIdentifier(token[2]);
nodeset[i] = token[1] + token[2];
htmlset[i] = compat[token[1]](context, token[2]);
factory[i] = compile(optimized[i], true, null);

Expand Down