-
Notifications
You must be signed in to change notification settings - Fork 4
/
mooml.js
234 lines (204 loc) · 6.56 KB
/
mooml.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
---
script: mooml.js
version: 1.3.0
description: Mooml is a javasctript templating engine for HTML generation, powered by Mootools.
license: MIT-style
download: http://mootools.net/forge/p/mooml
source: http://github.com/eneko/mooml
htmltags: http://www.w3schools.com/html5/html5_reference.asp
authors:
- Eneko Alonso: (http://enekoalonso.com)
credits:
- Ed Spencer: Mooml is based on Ed Spencer's Jaml (http://edspencer.github.com/jaml)
- Tim Schmidt: contributed with function and number argument types
- Josh Cohen: helped with node stacks for nested templates
- Vasili Sviridov: for the mixin idea
provides:
- Mooml
- Mooml.Template
- Mooml.Templates
requires:
- core/1.3.0:Class
- core/1.3.0:Elements
- core/1.3.0:Array
...
*/
var Mooml = {
version: '1.2.4',
templates: {},
engine: { callstack: [], tags: {} },
htmlTags: [
"a", "abbr", "address", "area", "article", "aside", "audio",
"b", "base", "bdo", "blockquote", "body", "br", "button",
"canvas", "caption", "cite", "col", "colgroup", "command",
"datalist", "dd", "del", "details", "dialog", "dfn", "div", "dl", "dt",
"em", "embed",
"fieldset", "figure",
"footer", "form",
"h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html",
"i", "iframe", "img", "input", "ins",
"keygen", "kbd",
"label", "legend", "li", "link",
"map", "mark", "menu", "meta", "meter",
"nav", "noscript",
"object", "ol", "optgroup", "option", "output",
"p", "param", "pre", "progress",
"q",
"rp", "rt", "ruby",
"samp", "script", "section", "select", "small", "source", "span", "strong", "style", "sub", "sup",
"table", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "tr",
"ul",
"var", "video",
// Deprecated in HTML 5
"acronym", "applet", "basefont", "big", "center", "dir", "font", "frame", "frameset", "noframes", "s", "strike", "tt", "u", "xmp"
// Not supported tags
// "code"
],
/**
* Evaluates a Mooml template supporting nested templates
* @param {Mooml.Template} template The template function
* @param {Object|Array} data Optional data object or array of objects
*/
evaluate: function(template, data) {
var elements = [];
this.engine.callstack.push(template);
if (template.prepared == false) {
template.code = this.prepare(template.code);
template.prepared = true;
}
Array.from([data, {}].pick()).each(function(params, index) {
template.code(params, index);
elements.append(template.nodes.filter(function(node) {
return node.getParent() === null;
}));
template.nodes.empty();
});
this.engine.callstack.pop();
if (this.engine.callstack.length) {
if (template.elementRefs) {
Array.extend(this.engine.callstack.getLast().elementRefs, template.elementRefs);
}
}
return (elements.length > 1) ? elements : elements.shift();
},
/**
* Initializes the engine generating a javascript function for every html
* tag that can be used on the template.
* Template tag functions can receive options for the element, child
* elements and html code as parameters.
* initialize can be called by the user in case of adding additional tags.
*/
initEngine: function() {
this.htmlTags.each(function(tag) {
Mooml.engine.tags[tag] = function() {
var template = Mooml.engine.callstack.getLast();
var el = new Element(tag);
for (var i=0, l=arguments.length; i<l; i++) {
var argument = arguments[i];
if (typeOf(argument) === "function") argument = argument();
switch (typeOf(argument)) {
case "array":
case "element":
case "collection": {
el.adopt(argument);
break;
}
case "string": {
if (template) {
el.getChildren().each(function(child) {
template.nodes.erase(child);
});
}
el.set('html', el.get('html') + argument);
break;
}
case "number": {
el.appendText(argument.toString());
break;
}
case "object": {
if (i === 0) {
if (template && template.elementRefs && argument.id) {
template.elementRefs[argument.id] = el;
}
el.set(argument);
} else if (typeOf(argument.toElement) == "function") {
el.adopt(argument.toElement());
}
break;
}
}
}
if (template) template.nodes.push(el);
return el;
}
});
window.addEvent('domready', function() {
document.getElements('script[type=text/mooml]').each(function(template) {
Mooml.register(template.get('name'), new Function(['data', 'index'], template.get('text')));
});
});
},
/**
* Prepares a template function so it can be called directly without using eval
* @param {Function} code The template function to prepare
*/
prepare: function(code) {
var codeStr = code.toString();
var args = codeStr.match(/\(([a-zA-Z0-9,\s]*)\)/)[1].replace(/\s/g, '').split(',');
var body = codeStr.match(/\{([\s\S]*)\}/m)[1];
for (var i=this.htmlTags.length; --i >= 0; ) {
body = body.replace(new RegExp('(^|[^\\w.])(' + this.htmlTags[i] + ')([\\s]*(?=\\())', 'g'), '$1Mooml.engine.tags.$2$3')
}
return new Function(args, body);
}
};
/**
* Template class for Mooml templates
*/
Mooml.Template = new Class({
nodes: [],
initialize: function(name, code, options) {
if (options && options.elementRefs && typeof(options.elementRefs) === "object") {
this.elementRefs = options.elementRefs;
}
this.name = name;
this.code = code;
this.prepared = false;
},
render: function(data) {
return Mooml.evaluate(this, data);
}
});
/**
* Mixin for implemenation in Mootools classes: Implements: [Mooml.Templates, Options, ...]
*/
Mooml.Templates = new Class({
templates: {},
/**
* Registers a new template for later use or returns an existing template with that name
* @param {String} name The name of the template
* @param {Function} code The code function of the template
*/
registerTemplate: function(name, code, options) {
var template = this.templates[name];
return (template)? template : this.templates[name] = new Mooml.Template(name, code, options);
},
/**
* Evaluates a registered template or returns null if template not registered
* @param {String} name The name of the template to evaluate
* @param {Object|Array} data Optional data object or array of objects
*/
renderTemplate: function(name, data) {
var template = this.templates[name];
return (template)? template.render(data) : null;
}
});
/**
* Implement Mooml.Templates into Mooml and alias for backwards compatibility
*/
Object.append(Mooml, new Mooml.Templates());
Mooml.register = Mooml.registerTemplate;
Mooml.render = Mooml.renderTemplate;
Mooml.initEngine();