dynamictemplate
Version:
Δt - async & dynamic templating engine
391 lines (352 loc) • 11.5 kB
JavaScript
(function() {
var DOMElementType, HTMLCompiler, Tag, Template, copy_structure, deep_merge, example, example2, fs, hook, isArray, jQuery, jsonify, mask, match, new_tag, slim, slim_attrs, suitup, traverse, trim, util, _ref;
var __slice = Array.prototype.slice;
fs = require('fs');
jQuery = require('jquery');
isArray = Array.isArray;
DOMElementType = {
NORMAL: 1,
TEXT: 3,
CDATA: 4,
FRAGMENT: 11
};
deep_merge = function() {
var k, obj, objs, res, v, _i, _len;
objs = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
if (isArray(objs[0])) objs = objs[0];
res = {};
for (_i = 0, _len = objs.length; _i < _len; _i++) {
obj = objs[_i];
for (k in obj) {
v = obj[k];
if (typeof v === 'object' && !isArray(v)) {
res[k] = deep_merge(res[k] || {}, v);
} else {
res[k] = v;
}
}
}
return res;
};
trim = function(str) {
return str.replace(/^\s+|\s+$/g, "");
};
slim_attrs = function(el) {
var attr, attrs, _i, _len, _ref, _ref2;
attrs = {};
_ref2 = (_ref = el.attributes) != null ? _ref : [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
attr = _ref2[_i];
attrs[attr.name] = attr.value;
}
return attrs;
};
slim = function(el) {
return {
name: el.nodeName.toLowerCase(),
attrs: slim_attrs(el),
children: traverse(el.childNodes)
};
};
traverse = function(elems) {
var el, res, _i, _len;
if (elems == null) return [];
res = [];
for (_i = 0, _len = elems.length; _i < _len; _i++) {
el = elems[_i];
if (el.nodeType === DOMElementType.NORMAL) {
res.push(slim(el));
} else if (el.nodeType === DOMElementType.TEXT) {
if (trim(el.value).length) res.push(el.value);
} else {
continue;
}
}
return res;
};
jsonify = function(elems) {
var el, _i, _len, _ref, _results;
_ref = elems != null ? elems : [];
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
el = _ref[_i];
_results.push(slim(el));
}
return _results;
};
copy_structure = function(tree) {
var el, res, _i, _len, _ref;
res = [];
_ref = tree != null ? tree : [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
el = _ref[_i];
if (typeof el === 'string' || typeof el === 'number') {
res.push(el);
continue;
}
res.push({
name: el.name,
attrs: el.attrs,
children: copy_structure(el.children)
});
}
return res;
};
match = function(tag, el) {
var key, value, _ref, _ref2;
if (el == null) return true;
if (tag.name !== el.name) return false;
_ref = tag.attrs;
for (key in _ref) {
value = _ref[key];
if (el.attrs[key] !== value) {
if (typeof value !== 'string' || (((_ref2 = el.attrs[key]) != null ? _ref2.indexOf(value) : void 0) || -1) === -1) {
return false;
}
}
}
return true;
};
new_tag = function(parent, el, callback) {
var attrs;
attrs = deep_merge(el.attrs);
return parent.tag(el.name, attrs, function() {
var child, _i, _len, _ref, _ref2;
_ref2 = (_ref = el.children.slice()) != null ? _ref : [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
child = _ref2[_i];
if (typeof child === 'string' || typeof child === 'number') {
this.text("" + child, {
append: true
});
} else {
new_tag(this, child, function() {
if (typeof callback === "function") callback();
return callback = null;
});
}
}
this.end();
return typeof callback === "function" ? callback() : void 0;
});
};
mask = function(tag, el) {
if (el == null) return;
tag.attr(el.attrs);
return tag._elems = el.children;
};
hook = function(tpl) {
return tpl.xml.use(function(parent, tag, next) {
var elems, repeat;
elems = parent._elems;
if (elems == null) return next(tag);
repeat = function() {
var el;
el = elems[0];
if (typeof el === 'string' || typeof el === 'number') {
console.log("text".blue, el);
elems.shift();
if (typeof parent.text === "function") {
parent.text(el, {
append: true
});
}
return repeat();
} else if (match(tag, el)) {
console.log("match".yellow, el != null ? el.name : void 0, el != null ? el.attrs : void 0);
elems.shift();
mask(tag, el);
return next(tag);
} else {
console.log("new".green, el != null ? el.name : void 0, el != null ? el.attrs : void 0, "(", tag.name, tag.attrs, ")");
console.log("WTF WTF WTF WTF".bold.red, tag.name, tag.attrs);
return new_tag(parent, el, function() {
console.log("repeat".red, tag.name, tag.attrs, "(", el != null ? el.name : void 0, el != null ? el.attrs : void 0, ")");
return repeat();
});
}
};
return repeat();
});
};
suitup = function(rawtemplate, tree) {
return function() {
var args, elems, tpl;
args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
tpl = rawtemplate.apply(null, args);
elems = copy_structure(tree);
tpl.xml._elems = elems;
hook(tpl);
return tpl;
};
};
HTMLCompiler = (function() {
function HTMLCompiler() {
this.$ = jQuery.create();
this.loadSync = this.open;
}
HTMLCompiler.prototype.read = function(filename, callback) {
var _this = this;
return fs.readFile(filename, function(err, data) {
return callback != null ? callback.call(_this, err, data != null ? data.toString() : void 0) : void 0;
});
};
HTMLCompiler.prototype.readSync = function(filename) {
var _ref;
return (_ref = fs.readFileSync(filename)) != null ? _ref.toString() : void 0;
};
HTMLCompiler.prototype.parse = function(data) {
return this.el = this.$(data);
};
HTMLCompiler.prototype.select = function(from, to) {
var el;
el = this.el.find(from);
el = el.clone();
el.find(to).remove();
return el;
};
HTMLCompiler.prototype.use = function(data) {
this.loaded = true;
return this.parse(data);
};
HTMLCompiler.prototype.load = function(filename, callback) {
return this.read(filename, function(err, data) {
if (err) return callback.call(this, err);
this.use(data);
return callback(null, this.el);
});
};
HTMLCompiler.prototype.open = function(filename) {
var data;
data = this.readSync(filename);
return this.use(data);
};
HTMLCompiler.prototype.compile = function(rawtemplate, el) {
var r, tree;
if (!this.loaded) {
throw new Error("no html file loaded or html string used.");
}
console.log("building data structure ...");
if (el == null) el = this.el;
tree = jsonify(el);
console.log("design is ready loaded. suiting up the template ...");
r = suitup(rawtemplate, tree);
r.tree = tree;
console.log("done.");
return r;
};
/*`<div id="channels" class="antiscroll-wrap">
<div class="antiscroll-inner scrollHolder">
<div class="channel selected">
<div class="avatar" style="background-image: url(public/avatars/user2.jpg)">
<span class="channelpost counter">2</span>
</div>
<div class="info">
<span class="owner">vera<span class="domain">@buddycloud.com</span></span>
<span class="status">What a wonderful day</span>
</div>
</div>`
*/
return HTMLCompiler;
})();
require('colors');
util = require('util');
util.orginspect = util.inspect;
util.inspect = require('eyes').inspector({
stream: null,
hexy: {
format: 'fours'
}
});
_ref = require('./dynamictemplate'), Template = _ref.Template, Tag = _ref.Tag;
example = function() {
var channelsdesign, design, rawtemplate, streamshtml, template, _ref2;
design = new HTMLCompiler;
console.log("* loading html ...");
streamshtml = design.open('/home/dodo/code/arbyt/buddycloud/webclient/brunch/build/streams.html');
console.log("* selecting part of the html ...");
channelsdesign = streamshtml.select('#channels', '.channel');
console.log("S", (_ref2 = streamshtml.select('.channel')) != null ? _ref2.length : void 0);
rawtemplate = function(ee) {
return new Template({
schema: 5
}, function() {
return this.$div({
id: 'channels'
}, function() {
return this.$div(function() {
var _this = this;
return ee != null ? ee.on('new:channel', function(channel) {
return _this.$div({
"class": 'channel'
}, function() {
this.$div({
"class": 'avatar',
style: "background-image:url(" + channel.avatar + ")"
}, function() {
return this.$span({
"class": 'counter'
}, channel.counter);
});
return this.$div({
"class": 'info'
}, function() {
var _ref3;
this.$span({
"class": 'owner'
}, function() {
var jid, _ref3;
jid = ((_ref3 = channel.get('jid')) != null ? _ref3.split('@') : void 0) || [];
this.text("" + jid[0]);
return this.$span({
"class": 'domain'
}, "" + jid[1]);
});
return this.$span({
"class": 'status'
}, ((_ref3 = channel.nodes.get('status')) != null ? _ref3.last() : void 0) || "");
});
});
}) : void 0;
});
});
});
};
console.log("* start compiling ...");
return template = design.compile(rawtemplate, channelsdesign);
};
example2 = function() {
var design, rawtemplate, template;
design = new HTMLCompiler;
console.log("* using html ...");
design.use("<div id=\"main\">\n <div class=\"logo\" id=\"big\">some <a href=\"#\">linked</a> logo</div>\n <div class=\"tor list\" id=\"zwiebel\">\n <div class=\"entry\">some random entry</div>\n </div>\n <span>impressum</span>\n</div>\n<script>alert('evil');</script>");
rawtemplate = function() {
return new Template({
schema: 5
}, function() {
return this.$div({
id: 'main'
}, function() {
this.$div({
"class": 'list'
}, function() {
return this.$div({
"class": 'entry'
}, "specific text");
});
return this.$footer("stuff");
});
});
};
console.log("* start compiling ...");
template = design.compile(rawtemplate);
template.design = design;
return template;
};
module.exports = {
example: example,
example2: example2,
HTMLCompiler: HTMLCompiler,
copy_structure: copy_structure
};
}).call(this);