JSON Template Engine //Repositório JavaScript
Descrição
Engine de template simples, extensível e eficaz. É capaz de interpretar tags de template aninhadas e montar sua respectiva estrutura usando JSON. O modo como foi implementado permite uma melhor organização e torna o preenchimento dos templates mais fácil.
Criado: 2006.02.04
Criado: 2006.02.04
Código (Download)
//+ Carlos R. L. Rodrigues
//@ http://jsfromhell.com/classes/template [rev. #2]
Template = function(s, oo, oc, co, cc){
var o = this; o.oo = oo || "<js:", o.oc = oc || ">", o.co = co || "</js:", o.cc = cc || ">";
if(o.oo == o.co || o.oo == o.oc || o.oo == o.cc || o.co == o.oc || o.co == o.cc) throw Error("Tag definition not allowed");
o.tags = [], o.root = o.node("_root", s, null), o.parse(s);
}
Template.prototype.node = function(n, v, p){
var _ = this;
return {
_default: v, _value: [v], _parent: p, _children: [], _name: n,
_render: function(_o){
var o = this._children, s = (v = this._value)[v.length - 1], p = [], i = -1,
t = _.oo, c = _.co, tc = _.oc, cc = _.cc, y, v, a, f, j, to;
while((y = o[++i]) && (v = s.slice(0, j = s.indexOf(to = t + (a = y._name) + tc)), f = s.indexOf(a = c + a + cc, j))){
while((j = s.indexOf(to, j + 1)) + 1 && j < f) f = s.indexOf(a, f + 1);
if(f + 1 && (s = v + y._value.join("") + s.slice(a.length + f)), y._children[0]) p.push(i, o), o = y._children, i = -1;
else if(!o[i + 1]) while(p.length && !(o = p.pop())[(i = p.pop()) + 1]);
}
return _o ? s : (i = (v = this._value).length, v[i] = v[--i], v[i] = s);
},
_set: function(s){return this._value[this._value.length - 1] = s;},
_get: function(){
var r = /([(){}|*+?.,^$\[\]\\])/g, f = function(s){return s.replace(r, "\\\$1")};
return this._value.join("").replace(new RegExp((f(_.oo) + ".*?" + f(_.oc) + "|" + f(_.co) + ".*?" + f(_.cc)), "gm"), "");
},
_reset: function(c){
if(c)
for(var a, o = this, t = _.tags, i = 0, l = t.length; i < l; i++)
((a = t[i]._parent) == o || a == this) && ((o = t[i])._value = [o._default]);
return this._value = [this._default];
},
_output: function(){return this._render(1);}
};
}
Template.prototype.parse = function(s){
var _ = this, p = 0, r = _.root, l = [[-1, s.length, r]], y = _.tags, i = _.oo,
e = _.co, $ = _.oc, d = _.cc, h = $.length, g, a, f, j, c, t, v;
while((p = s.indexOf(i, p)) > -1){
if(p == s.indexOf(e, p) && ++p) continue;
a = (a = p + i.length) + (t = s.slice(a, j = s.indexOf($, p))).length + h, f = s.indexOf(g = e + t + d, p);
while((j = s.indexOf(i + t + d, j + 1)) + 1 && j < f) f = s.indexOf(g, f + 1);
if(t.charAt() == "_") throw Error("Tag name not allowed [" + t + "]");
if(f < 0) throw Error("End of tag \"" + i + t + $ + "\" expected");
for(v = s.slice(a, f), j = l.length; j--;)
if((c = l[j])[2][t] && p > c[0] && f < c[1]) throw Error("Ambiguous tag name \"" + t + "\"");
else if(p > c[0] && f < c[1] && l.push([p++, f, (v = c[2][t] = _.node(t, v, c[2]))]) &&
(!(a = c[2]._parent) ? c[2] : a[c[2]._name])._children.push(v) && y.push(v)) break;
}
}
Exemplo (Exemplo)
<script type="text/javascript">
//<![CDATA[
s =
"<html>\n" +
"<head>[header]\n" +
'[js]<script src="[src][/src]" type="text/javascript"><\/script>\n[/js]' +
"[/header]</head>\n" +
"<body>\n" +
"[content]Default content[/content]\n" +
"</body>\n" +
"</html>";
var t = new Template(s, "[", "]", "[/", "]");
var js = t.root.header.js, content = t.root.content;
js.src._set("file.js");
js._render();
js.src._set("file2.js");
content._set("I Changed the content");
alert(t.root._output());
content._reset();
alert(t.root._output());
js._reset(true);
alert(t.root._output());
js._set("");
content._set("");
alert(t.root._output());
//]]>
</script>
Ajuda
Funcionamento
Esta classe é semelhante a um interpretador de xml, ela procura por tags de template no texto e as deixa facilmente acessíveis através da classe, adicionando propriedades e métodos especiais em cada nó.
Por default são reconhecidas como tag de template as tags que seguem esse padrão:
<js:NomeDaTag>conteúdo qualquer</js:NomeDaTag>
.
Mas isso é configurável, exemplo: [NomeDaTag]conteúdo[/NomeDaTag]
.
Exemplo de template:
<js:a>
<js:b>conteúdo</js:b>
</js:a>
Após carregar o template acima, você poderia acessar o texto "conteúdo" usando instanciaDeTemplate.root.a.b._get()
Há duas restrições para a nomeação das tags:
-
O nome da tag não pode começar com "_".
Errado:[_minhaTag][/_minhaTag]
-
Não pode haver tags com o mesmo nome no mesmo nível de indentação.
Errado:[a][/a][a][/a]
.
Correto:[a][/a][b][/b]
.
Errado:[a][b][/b][b][/b][/a]
.
Correto:[a][b][/b][c][/c][/a]
.
Correto:[a][b][/b][a][b][/b]
(o segundo [b] está em outro nível de indentação).
Para mais detalhes sobre o funcionamento, olhe o exemplo.
Construtor
- Template(content: String, [beginOpen: String = "<js:"], [beginClose: String = ">"], [endOpen: String = "</js:"], [endClose: String = ">"])
-
Gera uma instância de Template.
Restrições:- beginOpen não pode ser igual ao endOpen
- beginOpen não pode ser igual ao beginClose
- beginOpen não pode ser igual ao endClose
- endOpen não pode ser igual ao beginClose
- endOpen não pode ser igual ao endClose
- content
- texto a ser interpretado pela classe
- beginOpen
- define o prefixo da tag de abertura
- beginClose
- define o sufixo da tag de abertura
- endOpen
- define o prefixo da tag de fechamento
- endClose
- define o sufixo da tag de fechamento
Propriedades
- Template.root: Node
- É o nó inicial, deve ser usado para obter acesso aos demais nós via "JSON".
- Template.tags: Array
- Array contendo referência para todos os nós.
Métodos do Node
- Node._get(void): String
- Retorna o valor do nó. Se houverem tags de template no meio desse texto, estas serão removidas, mantendo apenas o texto padrão contido dentro delas.
- Node._set(value: String): String
-
Atribui um novo valor para o nó e, retorna o próprio texto atribuido. Se o nó tiver filhos, estes serão ignorados (não realmente removidos) no output.
- value
- texto a ser atribuido
- Node._reset([deep: Boolean = false]): void
-
Desfaz todas as alterações feitas no nó pelos métodos "_render" ou "_set".
- deep
- se true, os nós filhos também serão resetados
- Node._render(void): String
-
A função retorna o valor atual do nó e implementa uma espécie de "fila de conteúdo".
O valor atual do nó é empilhado (você tem acesso a ele através da propriedade "_value"), e o nó passa a operar sobre uma cópia dele.
Obs: O desenfileiramento ocorrerá automaticamente no output. - Node._output(void): String
- Retorna o texto do nó e de seus nós filhos. É semelhante ao "_get", porém diferentemente do "_get", que só retorna o texto do nó, ele também pega o texto dos nós filhos.
Propriedades do Node
- Node._default: String
- Mantém o valor inicial do nó.
- Node._parent: Node
- Referência para o nó pai.
- Node._name: String
- Nome da tag.
- Node._children: Array
- Array contendo os nós filhos.
- Node._value: Array
- Mantém o texto do nó em forma de uma estrutura de fila.
Ranque (Votos: 44)
3.00