< MySQL の比較演算時の型変換 | 関数内の this が参照するもの >

September 15, 2010

簡易 XML ビルダ/パーサ

ブログのデザイン変更に伴い、jQuery で DOM を生成する部分が増えました。引数に渡す HTML ソースを作成するのに、文字列を + 演算子で連結していくと何が何やら…。ソースを見やすくしたくて、簡易的な XML ビルダ を作成してみました。"簡易" ってつけたら何を公開してもいいのか!という声が聞こえてきそうですが、でも実際このブログのあちこちでこのモジュールを活用しています。

一応、xml を渡すとパースもできますが、引用符のネストなんかにうまく対応できていません。誤りを検出するようには作ってないので、少々間違った xml を渡しても適当に解析してしまいます。なんちゃってです。

var jp;
if (!jp) jp = {};
if (!jp.raindrop) jp.raindrop = {};
if (!jp.raindrop.frog) jp.raindrop.frog = {};
jp.raindrop.frog.xmlight || (function () {

    function Element (name)
    {
        var _name = name;
        this.name = function () { return _name; };
        this.children = new Elements ();
        this.attributes = {};
        this.attr = function ()
            {
                if (!arguments.length)
                    return this.attributes;
                if (arguments.length == 1)
                    return this.attributes[arguments[0]];
                this.attributes[arguments[0]] = arguments[1];
                return this;
            }
        this.wrap = function (name) { var elem = new Element (name); elem.children.append (this); return elem; };
        this.toString = function ()
            {
                var s = '<' + _name;
                for (var key in this.attributes)
                    s += ' ' + key + '="' + this.attributes[key] + '"';
                var c = this.children.toString ();
                if (c.length)
                    s += '>' + c + '</' + _name + '>';
                else
                    s += ' />';
                return s;
            }
    };

    function TextNode (text) { this.toString = function () { return text; }; };

    function Elements ()
    {
        var _inner = [];
        this.add = function (name) { return this.append (new Element (name)); };
        this.append = function (elem) { _inner.push (elem); return elem; };
        this.addText = function (text) { return this.append (new TextNode (text)); };
        this.remove = function (elem)
            {
                var index = -1;
                for (var i = 0; i < _inner.length; i++)
                {
                    if (elem == _inner [i])
                    {
                        index = i;
                        break;
                    }
                }

                if (index < 0)
                    return;

                _inner.splice (index, 1);
            };
        this.insert = function (index, elem) { _inner.splice (index, 0, elem); return elem; };
        this.insertText = function (index, text) { return this.insert (index, new TextNode (text)); }
        this.item = function (index) { return _inner[index]; };
        this.wrap = function (name)
            {
                var elem = new Element (name);
                for (var i = 0; i < _inner.length; i ++)
                    elem.children.append (_inner[i]); 
                return elem;
            };
        this.length = function () { return _inner.length; };
        this.toString = function ()
            {
                var s = "";
                for (var i = 0; i < _inner.length; i ++)
                    s += _inner[i].toString ();
                return s;
            };
    }

    var _parse = function (str)
        {
            var elements = new Elements ();
            _parseElements (str, elements);
            return elements;
        };

    var _parseElements = function (str, elements)
        {
            var regex = /<((?:(\w+):)?(\w+))([^>]*)(?:\/|>(.*)<\/\1)>/g;
            var match = [];
            while (match = regex.exec (str))
            {
                var element = elements.add (match[1]);
                _parseAttributes (match[4], element);
                if (match.length > 5)
                    _parseElements (match[5], element.children);
            }
        };

    var _parseAttributes = function (str, element)
        {
            var regex = /((?:(\w+):)?(\w+))=(["'])(.*?)(\4)/g;
            var match = [];
            while (match = regex.exec (str))
            {
                element.attributes [match[1]] = match[5];
            }
        };

    jp.raindrop.frog.xmlight = {
        create: function (name) { return new Element (name); },
        parse: function (str) { var c = new Elements (); _parseElements (str, c); return c; }
        };
}());

こんな感じ。

$(function ()
{
    var div = jp.raindrop.frog.xmlight.create ('div');
    div.children.add ('img')
        .attr ('src', 'foo.jpg')
        .attr ('alt', 'click me !')
        .wrap ('a')
        .attr ('href', 'bar.html')
        .attr ('title', 'my page');
    // body 内に以下の HTML が追加される
    // <div><a href="bar.html" title="my page"><img src="foo.jpg" alt="click me !" /></a></div>
    $(document.body).append (div.toString ());
});

トラックバック

このエントリーにトラックバック:
http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/2547

コメント

コメントする

※ コメントスパム対策のため、コメント本文はおはよう、こんにちわ、こんばんわのいずれかより始めるようにしてください。

name:
email:

※ 必要ですが、表示しません。

url:
情報を保存する ?