ブログのデザイン変更に伴い、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; }
};
}());