1 | var fs = require('fs'),
|
2 | path = require('path'),
|
3 |
|
4 | tags = require('./tags'),
|
5 | parser = require('./parser'),
|
6 | filters = require('./filters'),
|
7 | helpers = require('./helpers'),
|
8 | dateformat = require('./dateformat'),
|
9 |
|
10 | _ = require('underscore');
|
11 |
|
12 | var config = {
|
13 | allowErrors: false,
|
14 | autoescape: true,
|
15 | cache: true,
|
16 | encoding: 'utf8',
|
17 | filters: filters,
|
18 | root: '/',
|
19 | tags: tags,
|
20 | extensions: {},
|
21 | tzOffset: 0
|
22 | },
|
23 | _config = _.extend({}, config),
|
24 | CACHE = {};
|
25 |
|
26 |
|
27 | exports.init = function (options) {
|
28 | CACHE = {};
|
29 | _config = _.extend({}, config, options);
|
30 | _config.filters = _.extend(filters, options.filters);
|
31 | _config.tags = _.extend(tags, options.tags);
|
32 |
|
33 | dateformat.defaultTZOffset = _config.tzOffset;
|
34 | };
|
35 |
|
36 | function TemplateError(error) {
|
37 | return { render: function () {
|
38 | return '<pre>' + error.stack + '</pre>';
|
39 | }};
|
40 | }
|
41 |
|
42 | function createRenderFunc(code) {
|
43 |
|
44 | return new Function('_context', '_parents', '_filters', '_', '_ext', [
|
45 | '_parents = _parents ? _parents.slice() : [];',
|
46 | '_context = _context || {};',
|
47 |
|
48 | 'var j = _parents.length,',
|
49 | ' _output = "",',
|
50 | ' _this = this;',
|
51 |
|
52 | 'while (j--) {',
|
53 | ' if (_parents[j] === this.id) {',
|
54 | ' return "Circular import of template " + this.id + " in " + _parents[_parents.length-1];',
|
55 | ' }',
|
56 | '}',
|
57 |
|
58 | '_parents.push(this.id);',
|
59 | code,
|
60 | 'return _output;'
|
61 | ].join(''));
|
62 | }
|
63 |
|
64 | function createTemplate(data, id) {
|
65 | var template = {
|
66 |
|
67 | compileFile: exports.compileFile,
|
68 |
|
69 | blocks: {},
|
70 |
|
71 | type: parser.TEMPLATE,
|
72 |
|
73 | id: id
|
74 | },
|
75 | tokens,
|
76 | code,
|
77 | render;
|
78 |
|
79 |
|
80 | if (_config.allowErrors) {
|
81 | tokens = parser.parse.call(template, data, _config.tags, _config.autoescape);
|
82 | } else {
|
83 | try {
|
84 | tokens = parser.parse.call(template, data, _config.tags, _config.autoescape);
|
85 | } catch (e) {
|
86 | return new TemplateError(e);
|
87 | }
|
88 | }
|
89 |
|
90 | template.tokens = tokens;
|
91 |
|
92 |
|
93 | code = parser.compile.call(template);
|
94 |
|
95 | if (code !== false) {
|
96 | render = createRenderFunc(code);
|
97 | } else {
|
98 | render = function (_context, _parents, _filters, _, _ext) {
|
99 | template.tokens = tokens;
|
100 | code = parser.compile.call(template, '', _context);
|
101 | var fn = createRenderFunc(code);
|
102 | return fn.call(this, _context, _parents, _filters, _, _ext);
|
103 | };
|
104 | }
|
105 |
|
106 | template.render = function (context, parents) {
|
107 | if (_config.allowErrors) {
|
108 | return render.call(this, context, parents, _config.filters, _, _config.extensions);
|
109 | }
|
110 | try {
|
111 | return render.call(this, context, parents, _config.filters, _, _config.extensions);
|
112 | } catch (e) {
|
113 | return new TemplateError(e);
|
114 | }
|
115 | };
|
116 |
|
117 | return template;
|
118 | }
|
119 |
|
120 | function getTemplate(source, options) {
|
121 | var key = options.filename || source;
|
122 | if (_config.cache || options.cache) {
|
123 | if (!CACHE.hasOwnProperty(key)) {
|
124 | CACHE[key] = createTemplate(source, key);
|
125 | }
|
126 |
|
127 | return CACHE[key];
|
128 | }
|
129 |
|
130 | return createTemplate(source, key);
|
131 | }
|
132 |
|
133 | exports.compileFile = function (filepath, forceAllowErrors) {
|
134 | var tpl, get;
|
135 |
|
136 | if (_config.cache && CACHE.hasOwnProperty(filepath)) {
|
137 | return CACHE[filepath];
|
138 | }
|
139 |
|
140 | if (typeof window !== 'undefined') {
|
141 | throw new TemplateError({ stack: 'You must pre-compile all templates in-browser. Use `swig.compile(template);`.' });
|
142 | }
|
143 |
|
144 | get = function () {
|
145 | var file = ((/^\//).test(filepath) || (/^.:/).test(filepath)) ? filepath : _config.root + '/' + filepath,
|
146 | data = fs.readFileSync(file, config.encoding);
|
147 | tpl = getTemplate(data, { filename: filepath });
|
148 | };
|
149 |
|
150 | if (_config.allowErrors || forceAllowErrors) {
|
151 | get();
|
152 | } else {
|
153 | try {
|
154 | get();
|
155 | } catch (error) {
|
156 | tpl = new TemplateError(error);
|
157 | }
|
158 | }
|
159 | return tpl;
|
160 | };
|
161 |
|
162 | exports.compile = function (source, options) {
|
163 | options = options || {};
|
164 | var tmpl = getTemplate(source, options || {});
|
165 |
|
166 | return function (source, options) {
|
167 | return tmpl.render(source, options);
|
168 | };
|
169 | };
|