Skip to content

Instantly share code, notes, and snippets.

@kyo-ago
Created September 21, 2012 09:27
Show Gist options
  • Save kyo-ago/3760572 to your computer and use it in GitHub Desktop.
Save kyo-ago/3760572 to your computer and use it in GitHub Desktop.
body { background-color: #DDDDDD; font: 30px sans-serif; }
<pre>
TestCase('TemplateTest', {
'setUp' : function () {
this.tt = window.Template;
this.tt.baseParam = {};
},
'test_Template' : function () {
assertFunction(this.tt);
},
'test_result' : function () {
assertEquals(this.tt({
'template' : 'test'
}), 'test');
assertEquals(this.tt({
'template' : ''
}), '');
},
'test_var' : function () {
assertEquals(this.tt({
'template' : '{!hoge!}',
'param' : {
'hoge' : 'huga'
}
}), 'huga');
assertEquals(this.tt({
'template' : 'foo{!hoge!}bar',
'param' : {
'hoge' : 'huga'
}
}), 'foohugabar');
assertEquals(this.tt({
'template' : '{!zero!}',
'param' : {
'zero' : 0
}
}), '0');
assertEquals(this.tt({
'template' : '{!hoge!}',
'param' : {
'hoge' : '<a href="#">hoge<\/a>'
}
}), 'a href=#hoge/a');
assertEquals(this.tt({
'template' : '{!hoge filter="noescape"!}',
'param' : {
'hoge' : '<a href="#">hoge<\/a>'
}
}), '<a href="#">hoge</a>');
assertEquals(this.tt({
'template' : '{!hoge pad="2"!}',
'param' : {
'hoge' : '1'
}
}), '01');
assertEquals(this.tt({
'template' : '{!hoge regexp="\\d"!}',
'param' : {
'hoge' : '1'
}
}), '1');
assertEquals(this.tt({
'template' : '{!hoge regexp="\\d"!}',
'param' : {
'hoge' : 'a'
}
}), '');
assertEquals(this.tt({
'template' : '{!hoge regexp="\\w+(\\d+)\\w+" replace="$1"!}',
'param' : {
'hoge' : 'a1b'
}
}), '1');
assertEquals(this.tt({
'template' : '{!hoge regexp="\\w" replace=""!}',
'param' : {
'hoge' : 'a1b'
}
}), '1b');
assertEquals(this.tt({
'template' : '{!hoge regexp="\\w" regexp_flag="g" replace=""!}',
'param' : {
'hoge' : 'a!b'
}
}), '!');
this.tt.baseParam = {
'hogehoge' : 'hugahuga'
};
assertEquals(this.tt({
'template' : '{!hogehoge!}'
}), 'hugahuga');
},
'test_if' : function () {
assertEquals(this.tt({
'template' : '{!if hoge!}{!hoge!}{!/if!}',
'param' : {
'hoge' : 'huga'
}
}), 'huga');
assertEquals(this.tt({
'template' : '{!if hoge!}{!hoge!}{!/if!}',
'param' : {}
}), '');
assertEquals(this.tt({
'template' : '{!if name="hoge"!}{!hoge!}{!/if!}',
'param' : {}
}), '');
assertEquals(this.tt({
'template' : '{!if hoge!}{if}false{!/if!}',
'param' : {}
}), 'false');
assertEquals(this.tt({
'template' : '{!if name="hoge"!}{if}false{!/if!}',
'param' : {}
}), 'false');
assertEquals(this.tt({
'template' : '{!if name="hoge" op="not"!}not{if}{!/if!}',
'param' : {}
}), 'not');
assertEquals(this.tt({
'template' : '{!if hoge.huga!}{!hoge.huga!}{!/if!}',
'param' : {
'hoge' : {
'huga' : 'foo'
}
}
}), 'foo');
assertEquals(this.tt({
'template' : '{!if name="hoge" eq="$huga"!}foo{!/if!}',
'param' : {
'hoge' : '1',
'huga' : 1
}
}), 'foo');
assertEquals(this.tt({
'template' : '{!if name="hoge" eq="1"!}1{if name="hoge" eq="2"}2{if}3{!/if!}',
'param' : {
'hoge' : '2'
}
}), '2');
},
'test_for' : function () {
assertEquals(this.tt({
'template' : '{!for name="hoge"!}{!huga!}{!/for!}',
'param' : {
'hoge' : [
{
'huga' : 1
},
{
'huga' : 2
},
{
'huga' : 3
}
]
}
}), '123');
assertEquals(this.tt({
'template' : '{!for hoge!}{!_counter_!}{!/for!}',
'param' : {
'hoge' : [
{},
{},
{}
]
}
}), '012');
assertEquals(this.tt({
'template' : '{!for hoge!}{!_counter_ add="1"!}{!/for!}',
'param' : {
'hoge' : [
{},
{},
{}
]
}
}), '123');
assertEquals(this.tt({
'template' : '{!for name="hoge" minLoop="5"!}{!_counter_!}{!/for!}',
'param' : {
'hoge' : [
{},
{}
]
}
}), '01234');
},
'test_toJSON' : function () {
assertEquals(this.tt({
'template' : '{!toJSON hoge!}',
'param' : {
'hoge' : {
'huga' : '1'
}
}
}), '%7B%22huga%22%3A%221%22%7D');
},
'test_local' : function () {
assertEquals(this.tt({
'template' : '{!local name="hoge"!}{!huga!}{!/local!}',
'param' : {
'hoge' : {
'huga' : 'foo'
}
}
}), 'foo');
},
'test_set' : function () {
assertEquals(this.tt({
'template' : '{!set hoge="huga"!}{!hoge!}'
}), 'huga');
assertEquals(this.tt({
'template' : '{!set huga="$hoge"!}{!huga!}',
'param' : {
'hoge' : 'foo'
}
}), 'foo');
}
});
</pre>
"use strict";
(function () {
var name = 'Template';
var self = function (option) {
option.result = option.result || '';
option.template = option.template || '';
option.param = option.param || {};
if (!~option.template.indexOf('{!')) {
return option.template;
}
while (true) {
var match;
match = option.template.match(/([\s\S]*?)\{!(.+?)!\}([\s\S]*)/);
if (!match) {
break;
}
option.result += match[1];
var elem = match[2];
option.template = match[3];
if (self._getObject(option, elem)) {
var value = self._getValue(option, elem);
value = (value === 0) ? '0' : value;
option.result += ((value || '') + '').replace(/[<>'"`]/g, '');
continue;
}
// 'aaa.bbb hoge' -> ["aaa.bbb hoge", "aaa", ".bbb", "hoge"]
// 'aaa hoge' -> ["aaa hoge", "aaa", undefined, "hoge"]
var param_match = elem.match(/^(\w+)(\.[\w\.]+)?(?:\s+(.+))?/);
var param_name = param_match[1];
var elem_name = param_name + (param_match[2] || '');
var attr = param_match[3] || '';
if (self._getObject(option, param_name)) {
self.fn['val'](option, elem_name, attr);
continue;
}
var func = self.fn[param_name.split('_')[0]];
func && func(option, elem_name, attr);
}
option.result += option.template;
return option.result;
};
// external global vars
self.baseParam = {};
// internal global vars
self.setParam = {};
// file
self.fileCache = {};
/*
{{{template functions
*/
self.fn = {};
self.fn['val'] = function (option, elem_name, attr) {
var param = self._parseAttrs(option, attr);
elem_name = param['name'] || elem_name;
var value = self._getValue(option, elem_name);
value = (value === 0) ? '0' : value;
if (param['add']) {
value = ((value | 0) + (param['add'] | 0)) + '';
}
if (param['pad']) {
var pad = param['pad'] | 0;
value = ((new Array(pad + 1).join('0')) + value).slice(-pad);
}
if (param['regexp']) {
var regexp = new RegExp(param['regexp'], param['regexp_flag']);
if ('undefined' !== typeof param['replace']) {
value = value.replace(regexp, param['replace']);
} else if (!regexp.test(value)) {
return;
}
}
if (param['filter'] !== 'noescape') {
value = ((value || '') + '').replace(/[<>'"`]/g, '');
}
option.result += value;
};
self.fn['include'] = function (option, elem_name, attr) {
var file = self.fileCache[attr];
option.result += self({
'template':file,
'param':option.param
});
};
self.fn['set'] = function (option, elem_name, attr) {
var attrs = attr.split('=');
attrs[1] = attrs[1].replace(/^["']|['"]$/g, '');
// first character === '$'?
if (attrs[1].indexOf('$') === 0) {
attrs[1] = self._getValue(option, attrs[1].slice(1));
}
self.setParam[attrs[0]] = attrs[1];
};
self.fn['if'] = function (option, elem_name, attr) {
var template = self._getBlock(option, elem_name);
if (self._if_check_attrs(option, attr)) {
return output(template.split('{' + elem_name)[0]);
}
var tmps = template.split('{' + elem_name + '}');
var elseif_block = tmps[0];
var else_block = tmps[1];
var blocks = elseif_block.split('{' + elem_name + ' ');
blocks.shift();
for (var i = 0, l = blocks.length; i < l; ++i) {
var block = blocks[i];
if (!block) {
continue;
}
var match = block.match(/^(.+?)}([\s\S]+)/);
if (!match || match.length === 1) {
throw new Error('template syntax error\n' + block);
}
if (self._if_check_attrs(option, match[1])) {
output(match[2]);
return;
}
}
output(else_block);
function output (text) {
if (!text) {
return;
}
option.result += self({
'template':text,
'param':option.param
});
}
};
self.fn['local'] = function (option, elem_name, attr) {
self.fn["for"](option, elem_name, attr);
};
self.fn['dump'] = function (option, elem_name, attr) {
var param = self._parseAttrs(option, attr);
if (!window.console) {
return;
}
window.console.log([option.param, self.baseParam, self.setParam]);
if (param['debugger']) {
debugger;
}
};
self.fn['for'] = function (option, elem_name, attr) {
var template = self._getBlock(option, elem_name);
var param = self._parseAttrs(option, attr);
var targets = self._getValue(option, param['name']);
if (!targets) {
return '';
}
if (!self._isArray(targets)) {
option.result += self({
'template':template,
'param':targets
});
return '';
}
var result = '';
if (param.minLoop) {
var min = param.minLoop - targets.length;
for (var n = 0; n < min; ++n) {
targets.push({});
}
}
for (var i = 0, l = targets.length; i < l; ++i) {
targets[i]['_counter_'] = i + '';
result += self({
'template':template,
'param':targets[i]
});
}
option.result += result;
};
self.fn['toJSON'] = function (option, elem_name, attr) {
var param = self._parseAttrs(option, attr);
var value = self._getValue(option, param['name']);
option.result += escape(JSON.stringify(value));
};
/*
}}}
*/
self._if_check_attrs = function (option, attr) {
var param = self._parseAttrs(option, attr);
var value = self._getValue(option, param['name']);
// for no eq in param
var judge = !!value;
if ('eq' in param) {
// first character === '$'?
if ((param['eq'] || '').indexOf('$') === 0) {
param['eq'] = self._getValue(option, param['eq'].slice(1));
}
judge = (value == (param['eq'] || ''));
}
if (self._isArray(value) && value.length === 0) {
judge = false;
}
if (param['op'] === 'not') {
judge = !judge;
}
return judge;
};
self._getBlock = function (option, elem_name) {
var match = option.template.match(new RegExp('([\\s\\S]*?){!/' + elem_name + '!}([\\s\\S]*)'));
if (!match) {
throw new Error('template syntax error' + option.template);
}
option.template = match[2];
return match[1];
};
self._parseAttrs = function (option, attr) {
var param = {};
if (attr === '') {
return param;
}
if ((attr in option.param) && !option.param[attr]) {
param['name'] = attr;
return param;
}
var attrs = attr.split(/\s+/);
// attr === 'var_name' ?
if (attrs.length === 1 && !~attrs[0].indexOf('=')) {
param['name'] = attrs[0];
return param;
}
for (var i = 0, l = attrs.length; i < l; ++i) {
var sets = attrs[i].split(/=/);
param[sets[0].replace(/^['"]|["']$/g, '')] = sets[1].replace(/^['"]|["']$/g, '');
}
return param;
};
self._getObject = function (option, name) {
return name in option.param
? option.param
: name in self.baseParam
? self.baseParam
: name in self.setParam
? self.setParam
: undefined
;
};
self._getValue = function (option, name) {
var value = (self._getObject(option, name) || {})[name];
if (value === 0) {
value = '0';
}
value = value || '';
if ('string' !== typeof value) {
return value;
}
// /\./.test(name)?
if (!~name.indexOf('.')) {
return value;
}
var names = name.split('.');
value = self._getObject(option, names[0]) || {};
for (var i = 0, l = names.length; i < l; ++i) {
value = value[names[i]];
}
return value;
};
// from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
self._isArray = Array.isArray || function (arg) {
return Object.prototype.toString.call(arg) == '[object Array]';
};
window[name] = self;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment