omljs
Version:
Template engine built on top of the Oli language
399 lines • 9.68 kB
JavaScript
var fs, oli, tags, ht, ref$, isObject, isArray, isString, isUndef, isArrayStrings, extend, clone, has, cwd, exports, Engine, mixinNameRegex, mixinCallRegex, fileExt, getMixinName, getMixinCall, getMixinArgs, isValid, isSelfClosed, normalize, isDoctype, isMixinNode, isMixinDefinition, isMixinCall;
fs = require('fs');
oli = require('oli');
tags = require('./tags');
ht = require('htgen');
ref$ = require('./helpers'), isObject = ref$.isObject, isArray = ref$.isArray, isString = ref$.isString, isUndef = ref$.isUndef, isArrayStrings = ref$.isArrayStrings, extend = ref$.extend, clone = ref$.clone, has = ref$.has, cwd = ref$.cwd;
exports = module.exports = Engine = (function(){
Engine.displayName = 'Engine';
var prototype = Engine.prototype, constructor = Engine;
Engine.render = function(code, options){
return new Engine(oli.parse(String(code), options), options).compile();
};
Engine.options = {
basePath: cwd(),
pretty: false,
indent: 2
};
function Engine(data, options){
this.data = data;
this.mixins = {};
this.buf = [];
this.result = null;
this.setOptions(
options);
}
prototype.setOptions = function(it){
return this.options = extend(clone(
Engine.options), it);
};
prototype.compile = function(data){
data == null && (data = this.data);
if (isObject(
data)) {
return this.compileObject(
data);
} else if (isArray(
data)) {
return this.compileArray(
data);
} else {
return this.compileRaw(
data);
}
};
prototype.compileObject = function(it){
var this$ = this;
this.buf = this.processMixins(
this.visitor(
it)).filter(isValid).map(function(it){
return this$.processNode(
it);
});
return this.buf.join(
this.separator());
};
prototype.compileArray = function(data){
var buf, iterate, this$ = this;
buf = [];
(iterate = function(){
var i$, ref$, len$, index, item, results$ = [];
for (i$ = 0, len$ = (ref$ = data).length; i$ < len$; ++i$) {
index = i$;
item = ref$[i$];
if (item) {
if (isArray(
item)) {
if (item = iterate(
item)) {
results$.push(buf.push(
item));
}
} else {
if (item = this$.compile(
item)) {
results$.push(buf.push(
item));
}
}
}
}
return results$;
})();
return this.processMixins(
buf).join(this.separator());
};
prototype.compileRaw = function(it){
if (isString(
it)) {
if (isDoctype(
it)) {
return ht(
it).render(this.options);
} else if (isSelfClosed(
it)) {
return ht(
"!" + it).render(this.options);
} else if (it.charAt(0) === '!') {
return ht(
it).render(this.options);
} else {
return it;
}
} else {
return it;
}
};
prototype.visitor = function(node){
var buf, name, child, own$ = {}.hasOwnProperty;
buf = [];
for (name in node) if (own$.call(node, name)) {
child = node[name];
if (child !== undefined) {
name = normalize(
name);
if ((name === 'include' || name === 'require') && isString(
child)) {
buf.push(
Engine.render(this.readFile(
child), this.options));
} else if (isArray(
child)) {
buf = buf.concat(
this.processArray(name, child));
} else if (isMixinDefinition(
name)) {
if (!has(child, '$$attributes')) {
child = {
$$attributes: null,
$$body: child
};
}
child.$$body = this.visitMixin(
child.$$body);
this.registerMixin(name, child);
} else {
buf.push(
this.process(name, child));
}
}
}
return buf;
};
prototype.visitMixin = function(it){
var buf, name, node, i$, len$, item, own$ = {}.hasOwnProperty;
buf = [];
if (isObject(
it)) {
for (name in it) if (own$.call(it, name)) {
node = it[name];
buf.push(
this.process(name, node));
}
} else if (isArray(
it)) {
for (i$ = 0, len$ = it.length; i$ < len$; ++i$) {
item = it[i$];
buf.push(
this.visitor(
item));
}
} else {
buf.push(
it);
}
return buf.join(
this.separator());
};
prototype.process = function(name, node){
var attrs;
if (node === null) {
name = "!" + name;
} else if (has(node, '$$attributes')) {
attrs = node.$$attributes;
node = node.$$body;
if (isArray(
node)) {
node = node.map(function(it){
return this.visitor(
it);
});
} else if (isObject(
node)) {
node = this.visitor(
node);
}
} else if (isObject(
node)) {
node = this.visitor(
node);
}
if (isSelfClosed(
name)) {
name = "!" + name;
}
return ht(name, attrs, node);
};
prototype.processNode = function(it){
if (isObject(
it)) {
if (it.mixin) {
if (isArray(
it.body)) {
return it.body.join(
this.separator());
} else {
return it.body;
}
} else {
return it.render(
this.options);
}
} else {
return it;
}
};
prototype.processArray = function(name, node){
var buf, i$, len$, item;
buf = [];
if (isArrayStrings(
node)) {
buf.push(
ht.apply(null, [name, node.join(' ')]));
} else {
for (i$ = 0, len$ = node.length; i$ < len$; ++i$) {
item = node[i$];
if (isObject(
item)) {
if (item.$$name && item.$$attributes && isUndef(
item.$$body)) {
buf.push(
this.process(item.$$name, item));
} else {
buf.push(
ht(name, this.visitor(
item)));
}
} else {
buf.push(
ht(name, item));
}
}
}
return buf;
};
prototype.processMixins = function(it){
var i$, len$, index, child, name, mixin, callArgs;
if (!isArray(
it)) {
return it;
}
for (i$ = 0, len$ = it.length; i$ < len$; ++i$) {
index = i$;
child = it[i$];
if (name = isMixinNode(
child)) {
if (!(mixin = this.mixins[name])) {
throw new Error("Missing required mixin: " + name);
}
if (child.attributes) {
callArgs = Object.keys(
child.attributes);
}
it[index] = this.mixinArguments(mixin, callArgs);
} else if (isObject(
child) && child.childNodes) {
it[index]['childNodes'] = this.processMixins(
child.childNodes);
}
}
return it;
};
prototype.mixinArguments = function(mixin, callArgs){
var body, args, defValue, i$, len$, aindex, arg, key;
callArgs == null && (callArgs = []);
body = mixin.body, args = mixin.args;
if (!args) {
return body;
}
defValue = '';
for (i$ = 0, len$ = args.length; i$ < len$; ++i$) {
aindex = i$;
arg = args[i$];
if (isObject(
arg)) {
key = Object.keys(
arg)[0];
defValue = arg[key] || defValue;
arg = key;
}
body = body.replace("$" + arg, callArgs[aindex] || defValue);
}
return body;
};
prototype.registerMixin = function(name, node){
var ref$;
name = getMixinName(
name);
if (!name) {
throw new SyntaxError('Missing mixin name identifier');
}
return ref$ = this.mixins, ref$[name] = {
args: getMixinArgs(
node),
body: node.$$body
}, ref$;
};
prototype.separator = function(){
if (this.options.pretty) {
return '\n';
} else {
return '';
}
};
prototype.readFile = function(it){
it = fileExt(
it);
if (it.charAt(0) !== '/') {
it = this.options.basePath + "/" + it;
}
return fs.readFileSync(
it).toString();
};
return Engine;
}());
mixinNameRegex = /^mixin ([a-z0-9\_\-\.]+)[\s+]?\(?/i;
mixinCallRegex = /^\+[\s+]?([a-z0-9\_\-\.]+)[\s+]?\(?/i;
fileExt = function(it){
if (!/\.([a-z\-\_0-9]){0,10}$/i.test(it)) {
return it += '.oli';
} else {
return it;
}
};
getMixinName = function(it){
var name;
if (name = it.match(mixinNameRegex)) {
return name[1];
}
};
getMixinCall = function(it){
var name;
if (name = it.match(mixinCallRegex)) {
return name[1];
}
};
getMixinArgs = function(it){
var args, attrs;
args = null;
if (isObject(
it) && isObject(
attrs = it.$$attributes)) {
args = Object.keys(
attrs).map(function(it){
var ref$;
if (isUndef(
attrs[it])) {
return it;
} else {
return ref$ = {}, ref$[it] = attrs[it], ref$;
}
});
}
return args;
};
isValid = function(it){
return isObject(
it) || (isString(
it) && it.length);
};
isSelfClosed = function(it){
return tags.indexOf(
it) !== -1;
};
normalize = function(it){
return it.replace('@', '#');
};
isDoctype = function(it){
return /^doctype/i.test(it);
};
isMixinNode = function(it){
if (isObject(
it) && isMixinCall(
it.tag)) {
return getMixinCall(
it.tag);
} else if (isMixinCall(
it)) {
return getMixinCall(
it);
} else {
return false;
}
};
isMixinDefinition = function(it){
return /^mixin/i.test(it);
};
isMixinCall = function(it){
return /^\+/i.test(it);
};