Minibufferの$Xと$Nを勝手に拡張

まずは$X。

const Single = {};

function $exp(exp, ownerDocument) {
    if(!ownerDocument) ownerDocument = document;
    var resolver = document.createNSResolver(ownerDocument);
    var def = (document.contentType == 'application/xhtml+xml') ? 'http://www.w3.org/1999/xhtml' : '';
    return ownerDocument.createExpression(exp, function(prefix) {
            return resolver.lookupNamespaceURI(prefix) || def;
        });
}

function $X(exp, context, type /* want type */) {
    if (typeof context == 'function') {
        type    = context;
        context = null;
    }
    if(typeof exp == 'string' || exp instanceof String)
        exp = $exp(exp, context.ownerDocument || context);

    switch (type) {
    case String:  return exp.evaluate(context, XPathResult.STRING_TYPE,  null).stringValue;
    case Number:  return exp.evaluate(context, XPathResult.NUMBER_TYPE,  null).numberValue;
    case Boolean: return exp.evaluate(context, XPathResult.BOOLEAN_TYPE, null).booleanValue;
    case Single:  return exp.evaluate(context, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    case Array:
        var result = exp.evaluate( context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        var ret = [];
        for (var i = 0, len = result.snapshotLength; i < len; i++)
            ret.push(result.snapshotItem(i));
        return ret;
    case undefined:
        var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
        switch (result.resultType) {
        case XPathResult.STRING_TYPE : return result.stringValue;
        case XPathResult.NUMBER_TYPE : return result.numberValue;
        case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
        case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
            // not ensure the order.
            var ret = [];
            var i = null;
            while (i = result.iterateNext())
                ret.push(i);
            return ret;
        }
        return null;
    default:
        throw(TypeError('$X: specified type is not valid type.'));
    }
}

変更点

  • typeにSingleを追加。マッチする最初の要素だけ欲しい時でも配列作るのはなんか嫌だった。
  • $exp関数を追加。何度も使うXPathを何回も生成し直すのは嫌だった。

てきとーに書き換えたから正しい動作をするのかは不明。偉い人が突っ込みいれてくれないかなw

次に$N

function $N(elem, attr, children) {
    if(!elem) return null;
    if(typeof elem == 'string' || elem instanceof String)
        elem = document.createElement(elem);
    else
        elem = elem.cloneNode(!children);
    for (key in attr) {
        if (!attr.hasOwnProperty(key)) continue;
        elem.setAttribute(key, attr[key]);
    }
    function recAppend(child) {
        if(typeof child == 'string' || child instanceof String)
            elem.appendChild(document.createTextNode(child));
        else if(child instanceof Array)
            child.forEach(recAppend);
        else if(child)
            elem.appendChild(child);
    }
    recAppend(children);
    return elem;
}

変更点

  • 第1引数に要素を渡すとコピーしてくれる。
    • 既存の要素を拡張したいときとかに使える。
  • 第3引数に入れ子になった配列を渡せるようにした。

2つめの変更点はこんな時に便利。

$N('div', {}, [1, 2, 3].map(function(num) { return [$N('span', {}, num), ' ']; }));
=> <div><span>1</span> <span>2</span> <span>3</span> </div>

map一発で非常に楽ちん。