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