Created
September 21, 2012 09:27
-
-
Save kyo-ago/3760572 to your computer and use it in GitHub Desktop.
http://jsdo.it/kyo_ago/51Ru Template.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { background-color: #DDDDDD; font: 30px sans-serif; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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