cryptpad/www/common/flat-dom.js
2017-12-04 10:16:17 +01:00

83 lines
2.4 KiB
JavaScript

define([], function () {
var Flat = {};
var slice = function (coll) {
return Array.prototype.slice.call(coll);
};
var getAttrs = function (el) {
var i = 0;
var l = el.attributes.length;
var attr;
var data = {};
for (;i < l;i++) {
attr = el.attributes[i];
if (attr.name && attr.value) { data[attr.name] = attr.value; }
}
return data;
};
var identity = function (x) { return x; };
Flat.fromDOM = function (dom) {
var data = {
map: {},
};
var i = 1; // start from 1 so we're always truthey
var uid = function () { return i++; };
var process = function (el) {
var id;
if (!el.tagName && el.nodeType === Node.TEXT_NODE) {
id = uid();
data.map[id] = el.textContent;
return id;
}
if (!el || !el.attributes) { return; }
id = uid();
data.map[id] = [
el.tagName,
getAttrs(el),
slice(el.childNodes).map(function (e) {
return process(e);
}).filter(identity)
];
return id;
};
data.root = process(dom);
return data;
};
Flat.toDOM = function (data) {
var visited = {};
var process = function (key) {
if (!key) { return; } // ignore falsey keys
if (visited[key]) {
// TODO handle this more gracefully.
throw new Error('duplicate id or loop detected');
}
visited[key] = true; // mark paths as visited.
var hj = data.map[key];
if (typeof(hj) === 'string') { return document.createTextNode(hj); }
if (typeof(hj) === 'undefined') { return; }
if (!Array.isArray(hj)) { console.error(hj); throw new Error('expected array'); }
var e = document.createElement(hj[0]);
for (var x in hj[1]) { e.setAttribute(x, hj[1][x]); }
var child;
for (var i = 0; i < hj[2].length; i++) {
child = process(hj[2][i]);
if (child) {
e.appendChild(child);
}
}
return e;
};
return process(data.root);
};
return Flat;
});