JavaScript: $g
var $g = function(xexp, context, type, thisp){
if (!context) context = document;
var doc = context.ownerDocument || context, defaultNamespaceURI = (/xml$/).test(doc.contentType) ? doc.documentElement.getAttribute("xmlns") : "";
var resolver = function(prefix){
return doc.createNSResolver(context).lookupNamespaceURI(prefix) || defaultNamespaceURI;
};
switch (typeof(type)) {
case "number":
return doc.evaluate(xexp, context, resolver, XPathResult.NUMBER_TYPE, null).numberValue;
case "string":
return doc.evaluate(xexp, context, resolver, XPathResult.STRING_TYPE, null).stringValue;
case "boolean":
return doc.evaluate(xexp, context, resolver, XPathResult.BOOLEAN_TYPE, null).booleanValue;
case "function":
var ret = [], xp = doc.evaluate(xexp, context, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i=0,l=xp.snapshotLength,node; i<l; i++) {
node = type.apply(thisp, [xp.snapshotItem(i), i, xp]);
if (typeof(node) !== "undefined") ret.push(node);
}
return ret;
case "object":
var ret = [], xp = doc.evaluate(xexp, context, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i=0,l=xp.snapshotLength,node; i<l; i++) {
node = xp.snapshotItem(i);
ret[i] = (node.nodeType === 2) ? (node.nodeName === "href" || node.nodeName === "src") ? node.ownerElement[node.nodeName]
: node.nodeValue
: node.textContent;
}
return ret;
default:
var ret = [], xp = doc.evaluate(xexp, context, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i=0,l=xp.snapshotLength; i<l; i++) {
ret[i] = xp.snapshotItem(i);
}
return ret;
}
};
$g('//a')
// -> [a note, ...]
$g('count(//a)', null, 0)
// -> 149
$g('/descendant::a[1]/@href', null, "")
// -> "http://mayokara.info/note/"
$g('//a', null, function(v){return {href: v.href, textContent: v.textContent};})
// -> [Object href=http://mayokara.info/note/, ...]
$g('//a', null, function(v){if (v.href.indexOf("mayokara.info") === -1) return v;})
// -> [a 1214557471, ...]
第3引数のtypeofに応じて挙動を変えるdocument.evaluateラッパー。
1パスでmap/filterできるのが特長。
gはgetのg。