rails-hamljs 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,331 @@
1
+ // Underscore.string
2
+ // (c) 2010 Esa-Matti Suuronen <esa-matti aet suuronen dot org>
3
+ // Underscore.strings is freely distributable under the terms of the MIT license.
4
+ // Documentation: https://github.com/edtsech/underscore.string
5
+ // Some code is borrowed from MooTools and Alexandru Marasteanu.
6
+
7
+ // Version 1.1.4
8
+
9
+ (function(){
10
+ // ------------------------- Baseline setup ---------------------------------
11
+
12
+ // Establish the root object, "window" in the browser, or "global" on the server.
13
+ var root = this;
14
+
15
+ var nativeTrim = String.prototype.trim;
16
+
17
+ var parseNumber = function(source) { return source * 1 || 0; };
18
+
19
+ function str_repeat(i, m) {
20
+ for (var o = []; m > 0; o[--m] = i);
21
+ return o.join('');
22
+ }
23
+
24
+ function defaultToWhiteSpace(characters){
25
+ if (characters) {
26
+ return _s.escapeRegExp(characters);
27
+ }
28
+ return '\\s';
29
+ }
30
+
31
+ var _s = {
32
+
33
+ isBlank: function(str){
34
+ return !!str.match(/^\s*$/);
35
+ },
36
+
37
+ capitalize : function(str) {
38
+ return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
39
+ },
40
+
41
+ chop: function(str, step){
42
+ step = step || str.length;
43
+ var arr = [];
44
+ for (var i = 0; i < str.length;) {
45
+ arr.push(str.slice(i,i + step));
46
+ i = i + step;
47
+ }
48
+ return arr;
49
+ },
50
+
51
+ clean: function(str){
52
+ return _s.strip(str.replace(/\s+/g, ' '));
53
+ },
54
+
55
+ count: function(str, substr){
56
+ var count = 0, index;
57
+ for (var i=0; i < str.length;) {
58
+ index = str.indexOf(substr, i);
59
+ index >= 0 && count++;
60
+ i = i + (index >= 0 ? index : 0) + substr.length;
61
+ }
62
+ return count;
63
+ },
64
+
65
+ chars: function(str) {
66
+ return str.split('');
67
+ },
68
+
69
+ escapeHTML: function(str) {
70
+ return String(str||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
71
+ .replace(/"/g, '&quot;').replace(/'/g, "&apos;");
72
+ },
73
+
74
+ unescapeHTML: function(str) {
75
+ return String(str||'').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
76
+ .replace(/&quot;/g, '"').replace(/&apos;/g, "'");
77
+ },
78
+
79
+ escapeRegExp: function(str){
80
+ // From MooTools core 1.2.4
81
+ return String(str||'').replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
82
+ },
83
+
84
+ insert: function(str, i, substr){
85
+ var arr = str.split('');
86
+ arr.splice(i, 0, substr);
87
+ return arr.join('');
88
+ },
89
+
90
+ includes: function(str, needle){
91
+ return str.indexOf(needle) !== -1;
92
+ },
93
+
94
+ join: function(sep) {
95
+ // TODO: Could this be faster by converting
96
+ // arguments to Array and using array.join(sep)?
97
+ sep = String(sep);
98
+ var str = "";
99
+ for (var i=1; i < arguments.length; i += 1) {
100
+ str += String(arguments[i]);
101
+ if ( i !== arguments.length-1 ) {
102
+ str += sep;
103
+ }
104
+ }
105
+ return str;
106
+ },
107
+
108
+ lines: function(str) {
109
+ return str.split("\n");
110
+ },
111
+
112
+ // reverse: function(str){
113
+ // return Array.prototype.reverse.apply(str.split('')).join('');
114
+ // },
115
+
116
+ splice: function(str, i, howmany, substr){
117
+ var arr = str.split('');
118
+ arr.splice(i, howmany, substr);
119
+ return arr.join('');
120
+ },
121
+
122
+ startsWith: function(str, starts){
123
+ return str.length >= starts.length && str.substring(0, starts.length) === starts;
124
+ },
125
+
126
+ endsWith: function(str, ends){
127
+ return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
128
+ },
129
+
130
+ succ: function(str){
131
+ var arr = str.split('');
132
+ arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
133
+ return arr.join('');
134
+ },
135
+
136
+ titleize: function(str){
137
+ var arr = str.split(' '),
138
+ word;
139
+ for (var i=0; i < arr.length; i++) {
140
+ word = arr[i].split('');
141
+ if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase();
142
+ i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' ';
143
+ }
144
+ return arr.join('');
145
+ },
146
+
147
+ camelize: function(str){
148
+ return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
149
+ return chr ? chr.toUpperCase() : '';
150
+ });
151
+ },
152
+
153
+ underscored: function(str){
154
+ return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
155
+ },
156
+
157
+ dasherize: function(str){
158
+ return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase();
159
+ },
160
+
161
+ trim: function(str, characters){
162
+ if (!characters && nativeTrim) {
163
+ return nativeTrim.call(str);
164
+ }
165
+ characters = defaultToWhiteSpace(characters);
166
+ return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
167
+ },
168
+
169
+ ltrim: function(str, characters){
170
+ characters = defaultToWhiteSpace(characters);
171
+ return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
172
+ },
173
+
174
+ rtrim: function(str, characters){
175
+ characters = defaultToWhiteSpace(characters);
176
+ return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
177
+ },
178
+
179
+ truncate: function(str, length, truncateStr){
180
+ truncateStr = truncateStr || '...';
181
+ return str.slice(0,length) + truncateStr;
182
+ },
183
+
184
+ words: function(str, delimiter) {
185
+ delimiter = delimiter || " ";
186
+ return str.split(delimiter);
187
+ },
188
+
189
+
190
+ pad: function(str, length, padStr, type) {
191
+
192
+ var padding = '';
193
+ var padlen = 0;
194
+
195
+ if (!padStr) { padStr = ' '; }
196
+ else if (padStr.length > 1) { padStr = padStr[0]; }
197
+ switch(type) {
198
+ case "right":
199
+ padlen = (length - str.length);
200
+ padding = str_repeat(padStr, padlen);
201
+ str = str+padding;
202
+ break;
203
+ case "both":
204
+ padlen = (length - str.length);
205
+ padding = {
206
+ 'left' : str_repeat(padStr, Math.ceil(padlen/2)),
207
+ 'right': str_repeat(padStr, Math.floor(padlen/2))
208
+ };
209
+ str = padding.left+str+padding.right;
210
+ break;
211
+ default: // "left"
212
+ padlen = (length - str.length);
213
+ padding = str_repeat(padStr, padlen);;
214
+ str = padding+str;
215
+ }
216
+ return str;
217
+ },
218
+
219
+ lpad: function(str, length, padStr) {
220
+ return _s.pad(str, length, padStr);
221
+ },
222
+
223
+ rpad: function(str, length, padStr) {
224
+ return _s.pad(str, length, padStr, 'right');
225
+ },
226
+
227
+ lrpad: function(str, length, padStr) {
228
+ return _s.pad(str, length, padStr, 'both');
229
+ },
230
+
231
+
232
+ /**
233
+ * Credits for this function goes to
234
+ * http://www.diveintojavascript.com/projects/sprintf-for-javascript
235
+ *
236
+ * Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
237
+ * All rights reserved.
238
+ * */
239
+ sprintf: function(){
240
+
241
+ var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
242
+ while (f) {
243
+ if (m = /^[^\x25]+/.exec(f)) {
244
+ o.push(m[0]);
245
+ }
246
+ else if (m = /^\x25{2}/.exec(f)) {
247
+ o.push('%');
248
+ }
249
+ else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
250
+ if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
251
+ throw('Too few arguments.');
252
+ }
253
+ if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) {
254
+ throw('Expecting number but found ' + typeof(a));
255
+ }
256
+ switch (m[7]) {
257
+ case 'b': a = a.toString(2); break;
258
+ case 'c': a = String.fromCharCode(a); break;
259
+ case 'd': a = parseInt(a); break;
260
+ case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
261
+ case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
262
+ case 'o': a = a.toString(8); break;
263
+ case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
264
+ case 'u': a = Math.abs(a); break;
265
+ case 'x': a = a.toString(16); break;
266
+ case 'X': a = a.toString(16).toUpperCase(); break;
267
+ }
268
+ a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+'+ a : a);
269
+ c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
270
+ x = m[5] - String(a).length - s.length;
271
+ p = m[5] ? str_repeat(c, x) : '';
272
+ o.push(s + (m[4] ? a + p : p + a));
273
+ }
274
+ else {
275
+ throw('Huh ?!');
276
+ }
277
+ f = f.substring(m[0].length);
278
+ }
279
+ return o.join('');
280
+ },
281
+
282
+ toNumber: function(str, decimals) {
283
+ return parseNumber(parseNumber(str).toFixed(parseNumber(decimals)));
284
+ },
285
+
286
+ strRight: function(sourceStr, sep){
287
+ var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
288
+ return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
289
+ },
290
+
291
+ strRightBack: function(sourceStr, sep){
292
+ var pos = (!sep) ? -1 : sourceStr.lastIndexOf(sep);
293
+ return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
294
+ },
295
+
296
+ strLeft: function(sourceStr, sep){
297
+ var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
298
+ return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
299
+ },
300
+
301
+ strLeftBack: function(sourceStr, sep){
302
+ var pos = sourceStr.lastIndexOf(sep);
303
+ return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
304
+ }
305
+
306
+ };
307
+
308
+ // Aliases
309
+
310
+ _s.strip = _s.trim;
311
+ _s.lstrip = _s.ltrim;
312
+ _s.rstrip = _s.rtrim;
313
+ _s.center = _s.lrpad;
314
+ _s.ljust = _s.lpad;
315
+ _s.rjust = _s.rpad;
316
+
317
+ // CommonJS module is defined
318
+ if (typeof window === 'undefined' && typeof module !== 'undefined') {
319
+ // Export module
320
+ module.exports = _s;
321
+
322
+ // Integrate with Underscore.js
323
+ } else if (typeof root._ !== 'undefined') {
324
+ root._.mixin(_s);
325
+
326
+ // Or define it
327
+ } else {
328
+ root._ = _s;
329
+ }
330
+
331
+ }());
@@ -0,0 +1,2058 @@
1
+ // Generated by CoffeeScript 1.3.3
2
+
3
+ /*
4
+ clientside HAML compiler for Javascript and Coffeescript (Version 5)
5
+
6
+ Copyright 2011-12, Ronald Holshausen (https://github.com/uglyog)
7
+ Released under the MIT License (http://www.opensource.org/licenses/MIT)
8
+ */
9
+
10
+
11
+ (function() {
12
+ var Buffer, CodeGenerator, CoffeeCodeGenerator, HamlRuntime, JsCodeGenerator, ProductionJsCodeGenerator, Tokeniser, filters, root,
13
+ __hasProp = {}.hasOwnProperty,
14
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
15
+
16
+ root = this;
17
+
18
+ /*
19
+ Haml runtime functions. These are used both by the compiler and the generated template functions
20
+ */
21
+
22
+
23
+ HamlRuntime = {
24
+ /*
25
+ Taken from underscore.string.js escapeHTML, and replace the apos entity with character 39 so that it renders
26
+ correctly in IE7
27
+ */
28
+
29
+ escapeHTML: function(str) {
30
+ return String(str || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, "&#39;");
31
+ },
32
+ /*
33
+ Provides the implementation to preserve the whitespace as per the HAML reference
34
+ */
35
+
36
+ perserveWhitespace: function(str) {
37
+ var i, out, re, result;
38
+ re = /<[a-zA-Z]+>[^<]*<\/[a-zA-Z]+>/g;
39
+ out = '';
40
+ i = 0;
41
+ result = re.exec(str);
42
+ if (result) {
43
+ while (result) {
44
+ out += str.substring(i, result.index);
45
+ out += result[0].replace(/\n/g, '&#x000A;');
46
+ i = result.index + result[0].length;
47
+ result = re.exec(str);
48
+ }
49
+ out += str.substring(i);
50
+ } else {
51
+ out = str;
52
+ }
53
+ return out;
54
+ },
55
+ /*
56
+ Generates a error message including the current line in the source where the error occurred
57
+ */
58
+
59
+ templateError: function(lineNumber, characterNumber, currentLine, error) {
60
+ var i, message;
61
+ message = error + " at line " + lineNumber + " and character " + characterNumber + ":\n" + currentLine + '\n';
62
+ i = 0;
63
+ while (i < characterNumber - 1) {
64
+ message += '-';
65
+ i++;
66
+ }
67
+ message += '^';
68
+ return message;
69
+ },
70
+ /*
71
+ Generates the attributes for the element by combining all the various sources together
72
+ */
73
+
74
+ generateElementAttributes: function(context, id, classes, objRefFn, attrList, attrFunction, lineNumber, characterNumber, currentLine) {
75
+ var attr, attributes, className, dataAttr, dataAttributes, hash, html, object, objectId;
76
+ attributes = {};
77
+ attributes = this.combineAttributes(attributes, 'id', id);
78
+ if (classes.length > 0 && classes[0].length > 0) {
79
+ attributes = this.combineAttributes(attributes, 'class', classes);
80
+ }
81
+ if (attrList) {
82
+ for (attr in attrList) {
83
+ if (!__hasProp.call(attrList, attr)) continue;
84
+ attributes = this.combineAttributes(attributes, attr, attrList[attr]);
85
+ }
86
+ }
87
+ if (objRefFn) {
88
+ try {
89
+ object = objRefFn.call(context, context);
90
+ if (object) {
91
+ objectId = null;
92
+ if (object.id) {
93
+ objectId = object.id;
94
+ } else if (object.get) {
95
+ objectId = object.get('id');
96
+ }
97
+ attributes = this.combineAttributes(attributes, 'id', objectId);
98
+ className = null;
99
+ if (object['class']) {
100
+ className = object['class'];
101
+ } else if (object.get) {
102
+ className = object.get('class');
103
+ }
104
+ attributes = this.combineAttributes(attributes, 'class', className);
105
+ }
106
+ } catch (e) {
107
+ throw haml.HamlRuntime.templateError(lineNumber, characterNumber, currentLine, "Error evaluating object reference - " + e);
108
+ }
109
+ }
110
+ if (attrFunction) {
111
+ try {
112
+ hash = attrFunction.call(context, context);
113
+ if (hash) {
114
+ for (attr in hash) {
115
+ if (!__hasProp.call(hash, attr)) continue;
116
+ if (attr === 'data') {
117
+ dataAttributes = hash[attr];
118
+ for (dataAttr in dataAttributes) {
119
+ if (!__hasProp.call(dataAttributes, dataAttr)) continue;
120
+ attributes = this.combineAttributes(attributes, 'data-' + dataAttr, dataAttributes[dataAttr]);
121
+ }
122
+ } else {
123
+ attributes = this.combineAttributes(attributes, attr, hash[attr]);
124
+ }
125
+ }
126
+ }
127
+ } catch (ex) {
128
+ throw haml.HamlRuntime.templateError(lineNumber, characterNumber, currentLine, "Error evaluating attribute hash - " + ex);
129
+ }
130
+ }
131
+ html = '';
132
+ if (attributes) {
133
+ for (attr in attributes) {
134
+ if (!__hasProp.call(attributes, attr)) continue;
135
+ if (haml.hasValue(attributes[attr])) {
136
+ if ((attr === 'id' || attr === 'for') && attributes[attr] instanceof Array) {
137
+ html += ' ' + attr + '="' + _(attributes[attr]).flatten().join('-') + '"';
138
+ } else if (attr === 'class' && attributes[attr] instanceof Array) {
139
+ html += ' ' + attr + '="' + _(attributes[attr]).flatten().join(' ') + '"';
140
+ } else {
141
+ html += ' ' + attr + '="' + haml.attrValue(attr, attributes[attr]) + '"';
142
+ }
143
+ }
144
+ }
145
+ }
146
+ return html;
147
+ },
148
+ /*
149
+ Returns a white space string with a length of indent * 2
150
+ */
151
+
152
+ indentText: function(indent) {
153
+ var i, text;
154
+ text = '';
155
+ i = 0;
156
+ while (i < indent) {
157
+ text += ' ';
158
+ i++;
159
+ }
160
+ return text;
161
+ },
162
+ /*
163
+ Combines the attributes in the attributres hash with the given attribute and value
164
+ ID, FOR and CLASS attributes will expand to arrays when multiple values are provided
165
+ */
166
+
167
+ combineAttributes: function(attributes, attrName, attrValue) {
168
+ var classes;
169
+ if (haml.hasValue(attrValue)) {
170
+ if (attrName === 'id' && attrValue.toString().length > 0) {
171
+ if (attributes && attributes.id instanceof Array) {
172
+ attributes.id.unshift(attrValue);
173
+ } else if (attributes && attributes.id) {
174
+ attributes.id = [attributes.id, attrValue];
175
+ } else if (attributes) {
176
+ attributes.id = attrValue;
177
+ } else {
178
+ attributes = {
179
+ id: attrValue
180
+ };
181
+ }
182
+ } else if (attrName === 'for' && attrValue.toString().length > 0) {
183
+ if (attributes && attributes['for'] instanceof Array) {
184
+ attributes['for'].unshift(attrValue);
185
+ } else if (attributes && attributes['for']) {
186
+ attributes['for'] = [attributes['for'], attrValue];
187
+ } else if (attributes) {
188
+ attributes['for'] = attrValue;
189
+ } else {
190
+ attributes = {
191
+ 'for': attrValue
192
+ };
193
+ }
194
+ } else if (attrName === 'class') {
195
+ classes = [];
196
+ if (attrValue instanceof Array) {
197
+ classes = classes.concat(attrValue);
198
+ } else {
199
+ classes.push(attrValue);
200
+ }
201
+ if (attributes && attributes['class']) {
202
+ attributes['class'] = attributes['class'].concat(classes);
203
+ } else if (attributes) {
204
+ attributes['class'] = classes;
205
+ } else {
206
+ attributes = {
207
+ 'class': classes
208
+ };
209
+ }
210
+ } else if (attrName !== 'id') {
211
+ attributes || (attributes = {});
212
+ attributes[attrName] = attrValue;
213
+ }
214
+ }
215
+ return attributes;
216
+ }
217
+ };
218
+
219
+ /*
220
+ HAML Tokiniser: This class is responsible for parsing the haml source into tokens
221
+ */
222
+
223
+
224
+ Tokeniser = (function() {
225
+
226
+ Tokeniser.prototype.currentLineMatcher = /[^\n]*/g;
227
+
228
+ Tokeniser.prototype.tokenMatchers = {
229
+ whitespace: /[ \t]+/g,
230
+ element: /%[a-zA-Z][a-zA-Z0-9]*/g,
231
+ idSelector: /#[a-zA-Z_\-][a-zA-Z0-9_\-]*/g,
232
+ classSelector: /\.[a-zA-Z0-9_\-]+/g,
233
+ identifier: /[a-zA-Z][a-zA-Z0-9\-]*/g,
234
+ quotedString: /[\'][^\'\n]*[\']/g,
235
+ quotedString2: /[\"][^\"\n]*[\"]/g,
236
+ comment: /\-#/g,
237
+ escapeHtml: /\&=/g,
238
+ unescapeHtml: /\!=/g,
239
+ objectReference: /\[[a-zA-Z_@][a-zA-Z0-9_]*\]/g,
240
+ doctype: /!!!/g,
241
+ continueLine: /\|\s*\n/g,
242
+ filter: /:\w+/g
243
+ };
244
+
245
+ function Tokeniser(options) {
246
+ var errorFn, successFn, template,
247
+ _this = this;
248
+ this.buffer = null;
249
+ this.bufferIndex = null;
250
+ this.prevToken = null;
251
+ this.token = null;
252
+ if (options.templateId != null) {
253
+ template = document.getElementById(options.templateId);
254
+ if (template) {
255
+ this.buffer = template.text;
256
+ this.bufferIndex = 0;
257
+ } else {
258
+ throw "Did not find a template with ID '" + options.templateId + "'";
259
+ }
260
+ } else if (options.template != null) {
261
+ this.buffer = options.template;
262
+ this.bufferIndex = 0;
263
+ } else if (options.templateUrl != null) {
264
+ errorFn = function(jqXHR, textStatus, errorThrown) {
265
+ throw "Failed to fetch haml template at URL " + options.templateUrl + ": " + textStatus + " " + errorThrown;
266
+ };
267
+ successFn = function(data) {
268
+ _this.buffer = data;
269
+ return _this.bufferIndex = 0;
270
+ };
271
+ jQuery.ajax({
272
+ url: options.templateUrl,
273
+ success: successFn,
274
+ error: errorFn,
275
+ dataType: 'text',
276
+ async: false,
277
+ beforeSend: function(xhr) {
278
+ return xhr.withCredentials = true;
279
+ }
280
+ });
281
+ }
282
+ }
283
+
284
+ /*
285
+ Try to match a token with the given regexp
286
+ */
287
+
288
+
289
+ Tokeniser.prototype.matchToken = function(matcher) {
290
+ var result;
291
+ matcher.lastIndex = this.bufferIndex;
292
+ result = matcher.exec(this.buffer);
293
+ if ((result != null ? result.index : void 0) === this.bufferIndex) {
294
+ return result[0];
295
+ }
296
+ };
297
+
298
+ /*
299
+ Match a multi-character token
300
+ */
301
+
302
+
303
+ Tokeniser.prototype.matchMultiCharToken = function(matcher, token, tokenStr) {
304
+ var matched, _ref;
305
+ if (!this.token) {
306
+ matched = this.matchToken(matcher);
307
+ if (matched) {
308
+ this.token = token;
309
+ this.token.tokenString = (_ref = typeof tokenStr === "function" ? tokenStr(matched) : void 0) != null ? _ref : matched;
310
+ this.token.matched = matched;
311
+ return this.advanceCharsInBuffer(matched.length);
312
+ }
313
+ }
314
+ };
315
+
316
+ /*
317
+ Match a single character token
318
+ */
319
+
320
+
321
+ Tokeniser.prototype.matchSingleCharToken = function(ch, token) {
322
+ if (!this.token && this.buffer.charAt(this.bufferIndex) === ch) {
323
+ this.token = token;
324
+ this.token.tokenString = ch;
325
+ this.token.matched = ch;
326
+ return this.advanceCharsInBuffer(1);
327
+ }
328
+ };
329
+
330
+ /*
331
+ Match and return the next token in the input buffer
332
+ */
333
+
334
+
335
+ Tokeniser.prototype.getNextToken = function() {
336
+ var braceCount, ch, ch1, characterNumberStart, i, lineNumberStart, str;
337
+ if (isNaN(this.bufferIndex)) {
338
+ throw haml.HamlRuntime.templateError(this.lineNumber, this.characterNumber, this.currentLine, "An internal parser error has occurred in the HAML parser");
339
+ }
340
+ this.prevToken = this.token;
341
+ this.token = null;
342
+ if (this.buffer === null || this.buffer.length === this.bufferIndex) {
343
+ this.token = {
344
+ eof: true,
345
+ token: 'EOF'
346
+ };
347
+ } else {
348
+ this.initLine();
349
+ if (!this.token) {
350
+ ch = this.buffer.charCodeAt(this.bufferIndex);
351
+ ch1 = this.buffer.charCodeAt(this.bufferIndex + 1);
352
+ if (ch === 10 || (ch === 13 && ch1 === 10)) {
353
+ this.token = {
354
+ eol: true,
355
+ token: 'EOL'
356
+ };
357
+ if (ch === 13 && ch1 === 10) {
358
+ this.advanceCharsInBuffer(2);
359
+ this.token.matched = String.fromCharCode(ch) + String.fromCharCode(ch1);
360
+ } else {
361
+ this.advanceCharsInBuffer(1);
362
+ this.token.matched = String.fromCharCode(ch);
363
+ }
364
+ this.characterNumber = 0;
365
+ this.currentLine = this.getCurrentLine();
366
+ }
367
+ }
368
+ this.matchMultiCharToken(this.tokenMatchers.whitespace, {
369
+ ws: true,
370
+ token: 'WS'
371
+ });
372
+ this.matchMultiCharToken(this.tokenMatchers.continueLine, {
373
+ continueLine: true,
374
+ token: 'CONTINUELINE'
375
+ });
376
+ this.matchMultiCharToken(this.tokenMatchers.element, {
377
+ element: true,
378
+ token: 'ELEMENT'
379
+ }, function(matched) {
380
+ return matched.substring(1);
381
+ });
382
+ this.matchMultiCharToken(this.tokenMatchers.idSelector, {
383
+ idSelector: true,
384
+ token: 'ID'
385
+ }, function(matched) {
386
+ return matched.substring(1);
387
+ });
388
+ this.matchMultiCharToken(this.tokenMatchers.classSelector, {
389
+ classSelector: true,
390
+ token: 'CLASS'
391
+ }, function(matched) {
392
+ return matched.substring(1);
393
+ });
394
+ this.matchMultiCharToken(this.tokenMatchers.identifier, {
395
+ identifier: true,
396
+ token: 'IDENTIFIER'
397
+ });
398
+ this.matchMultiCharToken(this.tokenMatchers.doctype, {
399
+ doctype: true,
400
+ token: 'DOCTYPE'
401
+ });
402
+ this.matchMultiCharToken(this.tokenMatchers.filter, {
403
+ filter: true,
404
+ token: 'FILTER'
405
+ }, function(matched) {
406
+ return matched.substring(1);
407
+ });
408
+ if (!this.token) {
409
+ str = this.matchToken(this.tokenMatchers.quotedString);
410
+ if (!str) {
411
+ str = this.matchToken(this.tokenMatchers.quotedString2);
412
+ }
413
+ if (str) {
414
+ this.token = {
415
+ string: true,
416
+ token: 'STRING',
417
+ tokenString: str.substring(1, str.length - 1),
418
+ matched: str
419
+ };
420
+ this.advanceCharsInBuffer(str.length);
421
+ }
422
+ }
423
+ this.matchMultiCharToken(this.tokenMatchers.comment, {
424
+ comment: true,
425
+ token: 'COMMENT'
426
+ });
427
+ this.matchMultiCharToken(this.tokenMatchers.escapeHtml, {
428
+ escapeHtml: true,
429
+ token: 'ESCAPEHTML'
430
+ });
431
+ this.matchMultiCharToken(this.tokenMatchers.unescapeHtml, {
432
+ unescapeHtml: true,
433
+ token: 'UNESCAPEHTML'
434
+ });
435
+ this.matchMultiCharToken(this.tokenMatchers.objectReference, {
436
+ objectReference: true,
437
+ token: 'OBJECTREFERENCE'
438
+ }, function(matched) {
439
+ return matched.substring(1, matched.length - 1);
440
+ });
441
+ if (!this.token) {
442
+ if (this.buffer && this.buffer.charAt(this.bufferIndex) === '{') {
443
+ i = this.bufferIndex + 1;
444
+ characterNumberStart = this.characterNumber;
445
+ lineNumberStart = this.lineNumber;
446
+ braceCount = 1;
447
+ while (i < this.buffer.length && (braceCount > 1 || this.buffer.charAt(i) !== '}')) {
448
+ if (this.buffer.charAt(i) === '{') {
449
+ braceCount++;
450
+ } else if (this.buffer.charAt(i) === '}') {
451
+ braceCount--;
452
+ }
453
+ i++;
454
+ }
455
+ if (i === this.buffer.length) {
456
+ this.characterNumber = characterNumberStart + 1;
457
+ this.lineNumber = lineNumberStart;
458
+ throw this.parseError('Error parsing attribute hash - Did not find a terminating "}"');
459
+ } else {
460
+ this.token = {
461
+ attributeHash: true,
462
+ token: 'ATTRHASH',
463
+ tokenString: this.buffer.substring(this.bufferIndex, i + 1),
464
+ matched: this.buffer.substring(this.bufferIndex, i + 1)
465
+ };
466
+ this.advanceCharsInBuffer(i - this.bufferIndex + 1);
467
+ }
468
+ }
469
+ }
470
+ this.matchSingleCharToken('(', {
471
+ openBracket: true,
472
+ token: 'OPENBRACKET'
473
+ });
474
+ this.matchSingleCharToken(')', {
475
+ closeBracket: true,
476
+ token: 'CLOSEBRACKET'
477
+ });
478
+ this.matchSingleCharToken('=', {
479
+ equal: true,
480
+ token: 'EQUAL'
481
+ });
482
+ this.matchSingleCharToken('/', {
483
+ slash: true,
484
+ token: 'SLASH'
485
+ });
486
+ this.matchSingleCharToken('!', {
487
+ exclamation: true,
488
+ token: 'EXCLAMATION'
489
+ });
490
+ this.matchSingleCharToken('-', {
491
+ minus: true,
492
+ token: 'MINUS'
493
+ });
494
+ this.matchSingleCharToken('&', {
495
+ amp: true,
496
+ token: 'AMP'
497
+ });
498
+ this.matchSingleCharToken('<', {
499
+ lt: true,
500
+ token: 'LT'
501
+ });
502
+ this.matchSingleCharToken('>', {
503
+ gt: true,
504
+ token: 'GT'
505
+ });
506
+ this.matchSingleCharToken('~', {
507
+ tilde: true,
508
+ token: 'TILDE'
509
+ });
510
+ if (this.token === null) {
511
+ this.token = {
512
+ unknown: true,
513
+ token: 'UNKNOWN'
514
+ };
515
+ }
516
+ }
517
+ return this.token;
518
+ };
519
+
520
+ /*
521
+ Look ahead a number of tokens and return the token found
522
+ */
523
+
524
+
525
+ Tokeniser.prototype.lookAhead = function(numberOfTokens) {
526
+ var bufferIndex, characterNumber, currentLine, currentToken, i, lineNumber, prevToken, token;
527
+ token = null;
528
+ if (numberOfTokens > 0) {
529
+ currentToken = this.token;
530
+ prevToken = this.prevToken;
531
+ currentLine = this.currentLine;
532
+ lineNumber = this.lineNumber;
533
+ characterNumber = this.characterNumber;
534
+ bufferIndex = this.bufferIndex;
535
+ i = 0;
536
+ while (i++ < numberOfTokens) {
537
+ token = this.getNextToken();
538
+ }
539
+ this.token = currentToken;
540
+ this.prevToken = prevToken;
541
+ this.currentLine = currentLine;
542
+ this.lineNumber = lineNumber;
543
+ this.characterNumber = characterNumber;
544
+ this.bufferIndex = bufferIndex;
545
+ }
546
+ return token;
547
+ };
548
+
549
+ /*
550
+ Initilise the line and character counters
551
+ */
552
+
553
+
554
+ Tokeniser.prototype.initLine = function() {
555
+ if (!this.currentLine && this.currentLine !== "") {
556
+ this.currentLine = this.getCurrentLine();
557
+ this.lineNumber = 1;
558
+ return this.characterNumber = 0;
559
+ }
560
+ };
561
+
562
+ /*
563
+ Returns the current line in the input buffer
564
+ */
565
+
566
+
567
+ Tokeniser.prototype.getCurrentLine = function(index) {
568
+ var line;
569
+ this.currentLineMatcher.lastIndex = this.bufferIndex + (index != null ? index : 0);
570
+ line = this.currentLineMatcher.exec(this.buffer);
571
+ if (line) {
572
+ return line[0];
573
+ } else {
574
+ return '';
575
+ }
576
+ };
577
+
578
+ /*
579
+ Returns an error string filled out with the line and character counters
580
+ */
581
+
582
+
583
+ Tokeniser.prototype.parseError = function(error) {
584
+ return haml.HamlRuntime.templateError(this.lineNumber, this.characterNumber, this.currentLine, error);
585
+ };
586
+
587
+ /*
588
+ Skips to the end of the line and returns the string that was skipped
589
+ */
590
+
591
+
592
+ Tokeniser.prototype.skipToEOLorEOF = function() {
593
+ var contents, line, text;
594
+ text = '';
595
+ if (!(this.token.eof || this.token.eol)) {
596
+ if (!this.token.unknown) {
597
+ text += this.token.matched;
598
+ }
599
+ this.currentLineMatcher.lastIndex = this.bufferIndex;
600
+ line = this.currentLineMatcher.exec(this.buffer);
601
+ if (line && line.index === this.bufferIndex) {
602
+ contents = (_.str || _).rtrim(line[0]);
603
+ if ((_.str || _).endsWith(contents, '|')) {
604
+ text += contents.substring(0, contents.length - 1);
605
+ this.advanceCharsInBuffer(contents.length - 1);
606
+ this.getNextToken();
607
+ text += this.parseMultiLine();
608
+ } else {
609
+ text += line[0];
610
+ this.advanceCharsInBuffer(line[0].length);
611
+ this.getNextToken();
612
+ }
613
+ }
614
+ }
615
+ return text;
616
+ };
617
+
618
+ /*
619
+ Parses a multiline code block and returns the parsed text
620
+ */
621
+
622
+
623
+ Tokeniser.prototype.parseMultiLine = function() {
624
+ var contents, line, text;
625
+ text = '';
626
+ while (this.token.continueLine) {
627
+ this.currentLineMatcher.lastIndex = this.bufferIndex;
628
+ line = this.currentLineMatcher.exec(this.buffer);
629
+ if (line && line.index === this.bufferIndex) {
630
+ contents = (_.str || _).rtrim(line[0]);
631
+ if ((_.str || _).endsWith(contents, '|')) {
632
+ text += contents.substring(0, contents.length - 1);
633
+ this.advanceCharsInBuffer(contents.length - 1);
634
+ }
635
+ this.getNextToken();
636
+ }
637
+ }
638
+ return text;
639
+ };
640
+
641
+ /*
642
+ Advances the input buffer pointer by a number of characters, updating the line and character counters
643
+ */
644
+
645
+
646
+ Tokeniser.prototype.advanceCharsInBuffer = function(numChars) {
647
+ var ch, ch1, i;
648
+ i = 0;
649
+ while (i < numChars) {
650
+ ch = this.buffer.charCodeAt(this.bufferIndex + i);
651
+ ch1 = this.buffer.charCodeAt(this.bufferIndex + i + 1);
652
+ if (ch === 13 && ch1 === 10) {
653
+ this.lineNumber++;
654
+ this.characterNumber = 0;
655
+ this.currentLine = this.getCurrentLine(i);
656
+ i++;
657
+ } else if (ch === 10) {
658
+ this.lineNumber++;
659
+ this.characterNumber = 0;
660
+ this.currentLine = this.getCurrentLine(i);
661
+ } else {
662
+ this.characterNumber++;
663
+ }
664
+ i++;
665
+ }
666
+ return this.bufferIndex += numChars;
667
+ };
668
+
669
+ /*
670
+ Returns the current line and character counters
671
+ */
672
+
673
+
674
+ Tokeniser.prototype.currentParsePoint = function() {
675
+ return {
676
+ lineNumber: this.lineNumber,
677
+ characterNumber: this.characterNumber,
678
+ currentLine: this.currentLine
679
+ };
680
+ };
681
+
682
+ /*
683
+ Pushes back the current token onto the front of the input buffer
684
+ */
685
+
686
+
687
+ Tokeniser.prototype.pushBackToken = function() {
688
+ if (!this.token.unknown && !this.token.eof) {
689
+ this.bufferIndex -= this.token.matched.length;
690
+ return this.token = this.prevToken;
691
+ }
692
+ };
693
+
694
+ /*
695
+ Is the current token an end of line or end of input buffer
696
+ */
697
+
698
+
699
+ Tokeniser.prototype.isEolOrEof = function() {
700
+ return this.token.eol || this.token.eof;
701
+ };
702
+
703
+ return Tokeniser;
704
+
705
+ })();
706
+
707
+ /*
708
+ Provides buffering between the generated javascript and html contents
709
+ */
710
+
711
+
712
+ Buffer = (function() {
713
+
714
+ function Buffer(generator) {
715
+ this.generator = generator;
716
+ this.buffer = '';
717
+ this.outputBuffer = '';
718
+ }
719
+
720
+ Buffer.prototype.append = function(str) {
721
+ if (this.buffer.length === 0) {
722
+ this.generator.mark();
723
+ }
724
+ if (str && str.length > 0) {
725
+ return this.buffer += str;
726
+ }
727
+ };
728
+
729
+ Buffer.prototype.appendToOutputBuffer = function(str) {
730
+ if (str && str.length > 0) {
731
+ this.flush();
732
+ return this.outputBuffer += str;
733
+ }
734
+ };
735
+
736
+ Buffer.prototype.flush = function() {
737
+ if (this.buffer && this.buffer.length > 0) {
738
+ this.outputBuffer += this.generator.generateFlush(this.buffer);
739
+ }
740
+ return this.buffer = '';
741
+ };
742
+
743
+ Buffer.prototype.output = function() {
744
+ return this.outputBuffer;
745
+ };
746
+
747
+ Buffer.prototype.trimWhitespace = function() {
748
+ var ch, i;
749
+ if (this.buffer.length > 0) {
750
+ i = this.buffer.length - 1;
751
+ while (i > 0) {
752
+ ch = this.buffer.charAt(i);
753
+ if (ch === ' ' || ch === '\t' || ch === '\n') {
754
+ i--;
755
+ } else if (i > 1 && (ch === 'n' || ch === 't') && (this.buffer.charAt(i - 1) === '\\')) {
756
+ i -= 2;
757
+ } else {
758
+ break;
759
+ }
760
+ }
761
+ if (i > 0 && i < this.buffer.length - 1) {
762
+ return this.buffer = this.buffer.substring(0, i + 1);
763
+ } else if (i === 0) {
764
+ return this.buffer = '';
765
+ }
766
+ }
767
+ };
768
+
769
+ return Buffer;
770
+
771
+ })();
772
+
773
+ /*
774
+ Common code shared across all code generators
775
+ */
776
+
777
+
778
+ CodeGenerator = (function() {
779
+
780
+ function CodeGenerator() {}
781
+
782
+ CodeGenerator.prototype.embeddedCodeBlockMatcher = /#{([^}]*)}/g;
783
+
784
+ return CodeGenerator;
785
+
786
+ })();
787
+
788
+ /*
789
+ Code generator that generates a Javascript function body
790
+ */
791
+
792
+
793
+ JsCodeGenerator = (function(_super) {
794
+
795
+ __extends(JsCodeGenerator, _super);
796
+
797
+ function JsCodeGenerator() {
798
+ this.outputBuffer = new haml.Buffer(this);
799
+ }
800
+
801
+ /*
802
+ Append a line with embedded javascript code
803
+ */
804
+
805
+
806
+ JsCodeGenerator.prototype.appendEmbeddedCode = function(indentText, expression, escapeContents, perserveWhitespace, currentParsePoint) {
807
+ this.outputBuffer.flush();
808
+ this.outputBuffer.appendToOutputBuffer(indentText + 'try {\n');
809
+ this.outputBuffer.appendToOutputBuffer(indentText + ' var value = eval("' + (_.str || _).trim(expression).replace(/"/g, '\\"').replace(/\\n/g, '\\\\n') + '");\n');
810
+ this.outputBuffer.appendToOutputBuffer(indentText + ' value = value === null ? "" : value;');
811
+ if (escapeContents) {
812
+ this.outputBuffer.appendToOutputBuffer(indentText + ' html.push(haml.HamlRuntime.escapeHTML(String(value)));\n');
813
+ } else if (perserveWhitespace) {
814
+ this.outputBuffer.appendToOutputBuffer(indentText + ' html.push(haml.HamlRuntime.perserveWhitespace(String(value)));\n');
815
+ } else {
816
+ this.outputBuffer.appendToOutputBuffer(indentText + ' html.push(String(value));\n');
817
+ }
818
+ this.outputBuffer.appendToOutputBuffer(indentText + '} catch (e) {\n');
819
+ this.outputBuffer.appendToOutputBuffer(indentText + ' throw new Error(haml.HamlRuntime.templateError(' + currentParsePoint.lineNumber + ', ' + currentParsePoint.characterNumber + ', "' + this.escapeCode(currentParsePoint.currentLine) + '",\n');
820
+ this.outputBuffer.appendToOutputBuffer(indentText + ' "Error evaluating expression - " + e));\n');
821
+ return this.outputBuffer.appendToOutputBuffer(indentText + '}\n');
822
+ };
823
+
824
+ /*
825
+ Initilising the output buffer with any variables or code
826
+ */
827
+
828
+
829
+ JsCodeGenerator.prototype.initOutput = function() {
830
+ return this.outputBuffer.appendToOutputBuffer(' var html = [];\n' + ' var hashFunction = null, hashObject = null, objRef = null, objRefFn = null;\n with (context || {}) {\n');
831
+ };
832
+
833
+ /*
834
+ Flush and close the output buffer and return the contents
835
+ */
836
+
837
+
838
+ JsCodeGenerator.prototype.closeAndReturnOutput = function() {
839
+ this.outputBuffer.flush();
840
+ return this.outputBuffer.output() + ' }\n return html.join("");\n';
841
+ };
842
+
843
+ /*
844
+ Append a line of code to the output buffer
845
+ */
846
+
847
+
848
+ JsCodeGenerator.prototype.appendCodeLine = function(line, eol) {
849
+ this.outputBuffer.flush();
850
+ this.outputBuffer.appendToOutputBuffer(HamlRuntime.indentText(this.indent));
851
+ this.outputBuffer.appendToOutputBuffer(line);
852
+ return this.outputBuffer.appendToOutputBuffer(eol);
853
+ };
854
+
855
+ /*
856
+ Does the current line end with a function declaration?
857
+ */
858
+
859
+
860
+ JsCodeGenerator.prototype.lineMatchesStartFunctionBlock = function(line) {
861
+ return line.match(/function\s*\((,?\s*\w+)*\)\s*\{\s*$/);
862
+ };
863
+
864
+ /*
865
+ Does the current line end with a starting code block
866
+ */
867
+
868
+
869
+ JsCodeGenerator.prototype.lineMatchesStartBlock = function(line) {
870
+ return line.match(/\{\s*$/);
871
+ };
872
+
873
+ /*
874
+ Generate the code to close off a code block
875
+ */
876
+
877
+
878
+ JsCodeGenerator.prototype.closeOffCodeBlock = function(tokeniser) {
879
+ if (!(tokeniser.token.minus && tokeniser.matchToken(/\s*\}/g))) {
880
+ this.outputBuffer.flush();
881
+ return this.outputBuffer.appendToOutputBuffer(HamlRuntime.indentText(this.indent) + '}\n');
882
+ }
883
+ };
884
+
885
+ /*
886
+ Generate the code to close off a function parameter
887
+ */
888
+
889
+
890
+ JsCodeGenerator.prototype.closeOffFunctionBlock = function(tokeniser) {
891
+ if (!(tokeniser.token.minus && tokeniser.matchToken(/\s*\}/g))) {
892
+ this.outputBuffer.flush();
893
+ return this.outputBuffer.appendToOutputBuffer(HamlRuntime.indentText(this.indent) + '});\n');
894
+ }
895
+ };
896
+
897
+ /*
898
+ Generate the code for dynamic attributes ({} form)
899
+ */
900
+
901
+
902
+ JsCodeGenerator.prototype.generateCodeForDynamicAttributes = function(id, classes, attributeList, attributeHash, objectRef, currentParsePoint) {
903
+ this.outputBuffer.flush();
904
+ if (attributeHash.length > 0) {
905
+ attributeHash = this.replaceReservedWordsInHash(attributeHash);
906
+ this.outputBuffer.appendToOutputBuffer(' hashFunction = function () { return eval("hashObject = ' + attributeHash.replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"); };\n');
907
+ }
908
+ if (objectRef.length > 0) {
909
+ this.outputBuffer.appendToOutputBuffer(' objRefFn = function () { return eval("objRef = ' + objectRef.replace(/"/g, '\\"') + '"); };\n');
910
+ }
911
+ return this.outputBuffer.appendToOutputBuffer(' html.push(haml.HamlRuntime.generateElementAttributes(context, "' + id + '", ["' + classes.join('","') + '"], objRefFn, ' + JSON.stringify(attributeList) + ', hashFunction, ' + currentParsePoint.lineNumber + ', ' + currentParsePoint.characterNumber + ', "' + this.escapeCode(currentParsePoint.currentLine) + '"));\n');
912
+ };
913
+
914
+ /*
915
+ Clean any reserved words in the given hash
916
+ */
917
+
918
+
919
+ JsCodeGenerator.prototype.replaceReservedWordsInHash = function(hash) {
920
+ var reservedWord, resultHash, _i, _len, _ref;
921
+ resultHash = hash;
922
+ _ref = ['class', 'for'];
923
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
924
+ reservedWord = _ref[_i];
925
+ resultHash = resultHash.replace(reservedWord + ':', '"' + reservedWord + '":');
926
+ }
927
+ return resultHash;
928
+ };
929
+
930
+ /*
931
+ Escape the line so it is safe to put into a javascript string
932
+ */
933
+
934
+
935
+ JsCodeGenerator.prototype.escapeCode = function(jsStr) {
936
+ return jsStr.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r');
937
+ };
938
+
939
+ /*
940
+ Generate a function from the function body
941
+ */
942
+
943
+
944
+ JsCodeGenerator.prototype.generateJsFunction = function(functionBody) {
945
+ try {
946
+ return new Function('context', functionBody);
947
+ } catch (e) {
948
+ throw "Incorrect embedded code has resulted in an invalid Haml function - " + e + "\nGenerated Function:\n" + functionBody;
949
+ }
950
+ };
951
+
952
+ /*
953
+ Generate the code required to support a buffer flush
954
+ */
955
+
956
+
957
+ JsCodeGenerator.prototype.generateFlush = function(bufferStr) {
958
+ return ' html.push("' + this.escapeCode(bufferStr) + '");\n';
959
+ };
960
+
961
+ /*
962
+ Set the current indent level
963
+ */
964
+
965
+
966
+ JsCodeGenerator.prototype.setIndent = function(indent) {
967
+ return this.indent = indent;
968
+ };
969
+
970
+ /*
971
+ Save the current indent level if required
972
+ */
973
+
974
+
975
+ JsCodeGenerator.prototype.mark = function() {};
976
+
977
+ /*
978
+ Append the text contents to the buffer, expanding any embedded code
979
+ */
980
+
981
+
982
+ JsCodeGenerator.prototype.appendTextContents = function(text, shouldInterpolate, currentParsePoint, options) {
983
+ if (options == null) {
984
+ options = {};
985
+ }
986
+ if (shouldInterpolate && text.match(/#{[^}]*}/)) {
987
+ return this.interpolateString(text, currentParsePoint, options);
988
+ } else {
989
+ return this.outputBuffer.append(this.processText(text, options));
990
+ }
991
+ };
992
+
993
+ /*
994
+ Interpolate any embedded code in the text
995
+ */
996
+
997
+
998
+ JsCodeGenerator.prototype.interpolateString = function(text, currentParsePoint, options) {
999
+ var index, precheedingChar, precheedingChar2, result;
1000
+ index = 0;
1001
+ result = this.embeddedCodeBlockMatcher.exec(text);
1002
+ while (result) {
1003
+ if (result.index > 0) {
1004
+ precheedingChar = text.charAt(result.index - 1);
1005
+ }
1006
+ if (result.index > 1) {
1007
+ precheedingChar2 = text.charAt(result.index - 2);
1008
+ }
1009
+ if (precheedingChar === '\\' && precheedingChar2 !== '\\') {
1010
+ if (result.index !== 0) {
1011
+ this.outputBuffer.append(this.processText(text.substring(index, result.index - 1), options));
1012
+ }
1013
+ this.outputBuffer.append(this.processText(result[0]), options);
1014
+ } else {
1015
+ this.outputBuffer.append(this.processText(text.substring(index, result.index)), options);
1016
+ this.appendEmbeddedCode(HamlRuntime.indentText(this.indent + 1), result[1], options.escapeHTML, options.perserveWhitespace, currentParsePoint);
1017
+ }
1018
+ index = this.embeddedCodeBlockMatcher.lastIndex;
1019
+ result = this.embeddedCodeBlockMatcher.exec(text);
1020
+ }
1021
+ if (index < text.length) {
1022
+ return this.outputBuffer.append(this.processText(text.substring(index), options));
1023
+ }
1024
+ };
1025
+
1026
+ /*
1027
+ process text based on escape and preserve flags
1028
+ */
1029
+
1030
+
1031
+ JsCodeGenerator.prototype.processText = function(text, options) {
1032
+ if (options != null ? options.escapeHTML : void 0) {
1033
+ return haml.HamlRuntime.escapeHTML(text);
1034
+ } else if (options != null ? options.perserveWhitespace : void 0) {
1035
+ return haml.HamlRuntime.perserveWhitespace(text);
1036
+ } else {
1037
+ return text;
1038
+ }
1039
+ };
1040
+
1041
+ return JsCodeGenerator;
1042
+
1043
+ })(CodeGenerator);
1044
+
1045
+ /*
1046
+ Code generator that generates javascript code without runtime evaluation
1047
+ */
1048
+
1049
+
1050
+ ProductionJsCodeGenerator = (function(_super) {
1051
+
1052
+ __extends(ProductionJsCodeGenerator, _super);
1053
+
1054
+ function ProductionJsCodeGenerator() {
1055
+ return ProductionJsCodeGenerator.__super__.constructor.apply(this, arguments);
1056
+ }
1057
+
1058
+ /*
1059
+ Append a line with embedded javascript code
1060
+ */
1061
+
1062
+
1063
+ ProductionJsCodeGenerator.prototype.appendEmbeddedCode = function(indentText, expression, escapeContents, perserveWhitespace, currentParsePoint) {
1064
+ this.outputBuffer.flush();
1065
+ this.outputBuffer.appendToOutputBuffer(indentText + ' value = ' + (_.str || _).trim(expression) + ';\n');
1066
+ this.outputBuffer.appendToOutputBuffer(indentText + ' value = value === null ? "" : value;');
1067
+ if (escapeContents) {
1068
+ return this.outputBuffer.appendToOutputBuffer(indentText + ' html.push(haml.HamlRuntime.escapeHTML(String(value)));\n');
1069
+ } else if (perserveWhitespace) {
1070
+ return this.outputBuffer.appendToOutputBuffer(indentText + ' html.push(haml.HamlRuntime.perserveWhitespace(String(value)));\n');
1071
+ } else {
1072
+ return this.outputBuffer.appendToOutputBuffer(indentText + ' html.push(String(value));\n');
1073
+ }
1074
+ };
1075
+
1076
+ /*
1077
+ Generate the code for dynamic attributes ({} form)
1078
+ */
1079
+
1080
+
1081
+ ProductionJsCodeGenerator.prototype.generateCodeForDynamicAttributes = function(id, classes, attributeList, attributeHash, objectRef, currentParsePoint) {
1082
+ this.outputBuffer.flush();
1083
+ if (attributeHash.length > 0) {
1084
+ attributeHash = this.replaceReservedWordsInHash(attributeHash);
1085
+ this.outputBuffer.appendToOutputBuffer(' hashFunction = function () { return ' + attributeHash + '; };\n');
1086
+ }
1087
+ if (objectRef.length > 0) {
1088
+ this.outputBuffer.appendToOutputBuffer(' objRefFn = function () { return ' + objectRef + '; };\n');
1089
+ }
1090
+ return this.outputBuffer.appendToOutputBuffer(' html.push(haml.HamlRuntime.generateElementAttributes(context, "' + id + '", ["' + classes.join('","') + '"], objRefFn, ' + JSON.stringify(attributeList) + ', hashFunction, ' + currentParsePoint.lineNumber + ', ' + currentParsePoint.characterNumber + ', "' + this.escapeCode(currentParsePoint.currentLine) + '"));\n');
1091
+ };
1092
+
1093
+ /*
1094
+ Initilising the output buffer with any variables or code
1095
+ */
1096
+
1097
+
1098
+ ProductionJsCodeGenerator.prototype.initOutput = function() {
1099
+ return this.outputBuffer.appendToOutputBuffer(' var html = [];\n' + ' var hashFunction = null, hashObject = null, objRef = null, objRefFn = null, value= null;\n with (context || {}) {\n');
1100
+ };
1101
+
1102
+ return ProductionJsCodeGenerator;
1103
+
1104
+ })(JsCodeGenerator);
1105
+
1106
+ /*
1107
+ Code generator that generates a coffeescript function body
1108
+ */
1109
+
1110
+
1111
+ CoffeeCodeGenerator = (function(_super) {
1112
+
1113
+ __extends(CoffeeCodeGenerator, _super);
1114
+
1115
+ function CoffeeCodeGenerator() {
1116
+ this.outputBuffer = new haml.Buffer(this);
1117
+ }
1118
+
1119
+ CoffeeCodeGenerator.prototype.appendEmbeddedCode = function(indentText, expression, escapeContents, perserveWhitespace, currentParsePoint) {
1120
+ var indent;
1121
+ this.outputBuffer.flush();
1122
+ indent = this.calcCodeIndent();
1123
+ this.outputBuffer.appendToOutputBuffer(indent + "try\n");
1124
+ this.outputBuffer.appendToOutputBuffer(indent + " exp = CoffeeScript.compile('" + expression.replace(/'/g, "\\'").replace(/\\n/g, '\\\\n') + "', bare: true)\n");
1125
+ this.outputBuffer.appendToOutputBuffer(indent + " value = eval(exp)\n");
1126
+ this.outputBuffer.appendToOutputBuffer(indent + " value ?= ''\n");
1127
+ if (escapeContents) {
1128
+ this.outputBuffer.appendToOutputBuffer(indent + " html.push(haml.HamlRuntime.escapeHTML(String(value)))\n");
1129
+ } else if (perserveWhitespace) {
1130
+ this.outputBuffer.appendToOutputBuffer(indent + " html.push(haml.HamlRuntime.perserveWhitespace(String(value)))\n");
1131
+ } else {
1132
+ this.outputBuffer.appendToOutputBuffer(indent + " html.push(String(value))\n");
1133
+ }
1134
+ this.outputBuffer.appendToOutputBuffer(indent + "catch e \n");
1135
+ this.outputBuffer.appendToOutputBuffer(indent + " throw new Error(haml.HamlRuntime.templateError(" + currentParsePoint.lineNumber + ", " + currentParsePoint.characterNumber + ", '" + this.escapeCode(currentParsePoint.currentLine) + "',\n");
1136
+ return this.outputBuffer.appendToOutputBuffer(indent + " 'Error evaluating expression - ' + e))\n");
1137
+ };
1138
+
1139
+ CoffeeCodeGenerator.prototype.initOutput = function() {
1140
+ return this.outputBuffer.appendToOutputBuffer('html = []\n');
1141
+ };
1142
+
1143
+ CoffeeCodeGenerator.prototype.closeAndReturnOutput = function() {
1144
+ this.outputBuffer.flush();
1145
+ return this.outputBuffer.output() + 'return html.join("")\n';
1146
+ };
1147
+
1148
+ CoffeeCodeGenerator.prototype.appendCodeLine = function(line, eol) {
1149
+ this.outputBuffer.flush();
1150
+ this.outputBuffer.appendToOutputBuffer(this.calcCodeIndent());
1151
+ this.outputBuffer.appendToOutputBuffer((_.str || _).trim(line));
1152
+ this.outputBuffer.appendToOutputBuffer(eol);
1153
+ return this.prevCodeIndent = this.indent;
1154
+ };
1155
+
1156
+ CoffeeCodeGenerator.prototype.lineMatchesStartFunctionBlock = function(line) {
1157
+ return line.match(/\) [\-=]>\s*$/);
1158
+ };
1159
+
1160
+ CoffeeCodeGenerator.prototype.lineMatchesStartBlock = function(line) {
1161
+ return true;
1162
+ };
1163
+
1164
+ CoffeeCodeGenerator.prototype.closeOffCodeBlock = function(tokeniser) {
1165
+ return this.outputBuffer.flush();
1166
+ };
1167
+
1168
+ CoffeeCodeGenerator.prototype.closeOffFunctionBlock = function(tokeniser) {
1169
+ return this.outputBuffer.flush();
1170
+ };
1171
+
1172
+ CoffeeCodeGenerator.prototype.generateCodeForDynamicAttributes = function(id, classes, attributeList, attributeHash, objectRef, currentParsePoint) {
1173
+ var indent;
1174
+ this.outputBuffer.flush();
1175
+ indent = this.calcCodeIndent();
1176
+ if (attributeHash.length > 0) {
1177
+ attributeHash = this.replaceReservedWordsInHash(attributeHash);
1178
+ this.outputBuffer.appendToOutputBuffer(indent + "hashFunction = () -> s = CoffeeScript.compile('" + attributeHash.replace(/'/g, "\\'").replace(/\n/g, '\\n') + "', bare: true); eval 'hashObject = ' + s\n");
1179
+ }
1180
+ if (objectRef.length > 0) {
1181
+ this.outputBuffer.appendToOutputBuffer(indent + "objRefFn = () -> s = CoffeeScript.compile('" + objectRef.replace(/'/g, "\\'") + "', bare: true); eval 'objRef = ' + s\n");
1182
+ }
1183
+ return this.outputBuffer.appendToOutputBuffer(indent + "html.push(haml.HamlRuntime.generateElementAttributes(this, '" + id + "', ['" + classes.join("','") + "'], objRefFn ? null, " + JSON.stringify(attributeList) + ", hashFunction ? null, " + currentParsePoint.lineNumber + ", " + currentParsePoint.characterNumber + ", '" + this.escapeCode(currentParsePoint.currentLine) + "'))\n");
1184
+ };
1185
+
1186
+ CoffeeCodeGenerator.prototype.replaceReservedWordsInHash = function(hash) {
1187
+ var reservedWord, resultHash, _i, _len, _ref;
1188
+ resultHash = hash;
1189
+ _ref = ['class', 'for'];
1190
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1191
+ reservedWord = _ref[_i];
1192
+ resultHash = resultHash.replace(reservedWord + ':', "'" + reservedWord + "':");
1193
+ }
1194
+ return resultHash;
1195
+ };
1196
+
1197
+ /*
1198
+ Escapes the string for insertion into the generated code. Embedded code blocks in strings must not be escaped
1199
+ */
1200
+
1201
+
1202
+ CoffeeCodeGenerator.prototype.escapeCode = function(str) {
1203
+ var index, outString, precheedingChar, precheedingChar2, result;
1204
+ outString = '';
1205
+ index = 0;
1206
+ result = this.embeddedCodeBlockMatcher.exec(str);
1207
+ while (result) {
1208
+ if (result.index > 0) {
1209
+ precheedingChar = str.charAt(result.index - 1);
1210
+ }
1211
+ if (result.index > 1) {
1212
+ precheedingChar2 = str.charAt(result.index - 2);
1213
+ }
1214
+ if (precheedingChar === '\\' && precheedingChar2 !== '\\') {
1215
+ if (result.index !== 0) {
1216
+ outString += this._escapeText(str.substring(index, result.index - 1));
1217
+ }
1218
+ outString += this._escapeText('\\' + result[0]);
1219
+ } else {
1220
+ outString += this._escapeText(str.substring(index, result.index));
1221
+ outString += result[0];
1222
+ }
1223
+ index = this.embeddedCodeBlockMatcher.lastIndex;
1224
+ result = this.embeddedCodeBlockMatcher.exec(str);
1225
+ }
1226
+ if (index < str.length) {
1227
+ outString += this._escapeText(str.substring(index));
1228
+ }
1229
+ return outString;
1230
+ };
1231
+
1232
+ CoffeeCodeGenerator.prototype._escapeText = function(text) {
1233
+ return text.replace(/\\/g, '\\\\').replace(/'/g, '\\\'').replace(/"/g, '\\\"').replace(/\n/g, '\\n').replace(/(^|[^\\]{2})\\\\#{/g, '$1\\#{');
1234
+ };
1235
+
1236
+ /*
1237
+ Generates the javascript function by compiling the given code with coffeescript compiler
1238
+ */
1239
+
1240
+
1241
+ CoffeeCodeGenerator.prototype.generateJsFunction = function(functionBody) {
1242
+ var fn;
1243
+ try {
1244
+ fn = CoffeeScript.compile(functionBody, {
1245
+ bare: true
1246
+ });
1247
+ return new Function(fn);
1248
+ } catch (e) {
1249
+ throw "Incorrect embedded code has resulted in an invalid Haml function - " + e + "\nGenerated Function:\n" + fn;
1250
+ }
1251
+ };
1252
+
1253
+ CoffeeCodeGenerator.prototype.generateFlush = function(bufferStr) {
1254
+ return this.calcCodeIndent() + "html.push('" + this.escapeCode(bufferStr) + "')\n";
1255
+ };
1256
+
1257
+ CoffeeCodeGenerator.prototype.setIndent = function(indent) {
1258
+ return this.indent = indent;
1259
+ };
1260
+
1261
+ CoffeeCodeGenerator.prototype.mark = function() {
1262
+ return this.prevIndent = this.indent;
1263
+ };
1264
+
1265
+ CoffeeCodeGenerator.prototype.calcCodeIndent = function() {
1266
+ var codeIndent, i, _i, _ref, _ref1, _ref2;
1267
+ codeIndent = 0;
1268
+ for (i = _i = 0, _ref = this.indent; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
1269
+ if (((_ref1 = this.elementStack[i]) != null ? _ref1.block : void 0) || ((_ref2 = this.elementStack[i]) != null ? _ref2.fnBlock : void 0)) {
1270
+ codeIndent += 1;
1271
+ }
1272
+ }
1273
+ return HamlRuntime.indentText(codeIndent);
1274
+ };
1275
+
1276
+ /*
1277
+ Append the text contents to the buffer (interpolating embedded code not required for coffeescript)
1278
+ */
1279
+
1280
+
1281
+ CoffeeCodeGenerator.prototype.appendTextContents = function(text, shouldInterpolate, currentParsePoint, options) {
1282
+ var prefix, suffix;
1283
+ if (shouldInterpolate && text.match(/#{[^}]*}/)) {
1284
+ this.outputBuffer.flush();
1285
+ prefix = suffix = '';
1286
+ if (options != null ? options.escapeHTML : void 0) {
1287
+ prefix = 'haml.HamlRuntime.escapeHTML(';
1288
+ suffix = ')';
1289
+ } else if (options != null ? options.perserveWhitespace : void 0) {
1290
+ prefix = 'haml.HamlRuntime.perserveWhitespace(';
1291
+ suffix = ')';
1292
+ }
1293
+ return this.outputBuffer.appendToOutputBuffer(this.calcCodeIndent() + 'html.push(' + prefix + '"' + this.escapeCode(text) + '"' + suffix + ')\n');
1294
+ } else {
1295
+ if (options != null ? options.escapeHTML : void 0) {
1296
+ text = haml.HamlRuntime.escapeHTML(text);
1297
+ }
1298
+ if (options != null ? options.perserveWhitespace : void 0) {
1299
+ text = haml.HamlRuntime.perserveWhitespace(text);
1300
+ }
1301
+ return this.outputBuffer.append(text);
1302
+ }
1303
+ };
1304
+
1305
+ return CoffeeCodeGenerator;
1306
+
1307
+ })(CodeGenerator);
1308
+
1309
+ /*
1310
+ HAML filters are functions that take 3 parameters
1311
+ contents: The contents block for the filter an array of lines of text
1312
+ generator: The current generator for the compiled function
1313
+ indentText: A whitespace string specifying the current indent level
1314
+ currentParsePoint: line and character counters for the current parse point in the input buffer
1315
+ */
1316
+
1317
+
1318
+ filters = {
1319
+ /*
1320
+ Plain filter, just renders the text in the block
1321
+ */
1322
+
1323
+ plain: function(contents, generator, indentText, currentParsePoint) {
1324
+ var line, _i, _len;
1325
+ for (_i = 0, _len = contents.length; _i < _len; _i++) {
1326
+ line = contents[_i];
1327
+ generator.appendTextContents(indentText + line + '\n', true, currentParsePoint);
1328
+ }
1329
+ return true;
1330
+ },
1331
+ /*
1332
+ Wraps the filter block in a javascript tag
1333
+ */
1334
+
1335
+ javascript: function(contents, generator, indentText, currentParsePoint) {
1336
+ var line, _i, _len;
1337
+ generator.outputBuffer.append(indentText + "<script type=\"text/javascript\">\n");
1338
+ generator.outputBuffer.append(indentText + "//<![CDATA[\n");
1339
+ for (_i = 0, _len = contents.length; _i < _len; _i++) {
1340
+ line = contents[_i];
1341
+ generator.appendTextContents(indentText + line + '\n', true, currentParsePoint);
1342
+ }
1343
+ generator.outputBuffer.append(indentText + "//]]>\n");
1344
+ return generator.outputBuffer.append(indentText + "</script>\n");
1345
+ },
1346
+ /*
1347
+ Wraps the filter block in a style tag
1348
+ */
1349
+
1350
+ css: function(contents, generator, indentText, currentParsePoint) {
1351
+ var line, _i, _len;
1352
+ generator.outputBuffer.append(indentText + "<style type=\"text/css\">\n");
1353
+ generator.outputBuffer.append(indentText + "/*<![CDATA[*/\n");
1354
+ for (_i = 0, _len = contents.length; _i < _len; _i++) {
1355
+ line = contents[_i];
1356
+ generator.appendTextContents(indentText + line + '\n', true, currentParsePoint);
1357
+ }
1358
+ generator.outputBuffer.append(indentText + "/*]]>*/\n");
1359
+ return generator.outputBuffer.append(indentText + "</style>\n");
1360
+ },
1361
+ /*
1362
+ Wraps the filter block in a CDATA tag
1363
+ */
1364
+
1365
+ cdata: function(contents, generator, indentText, currentParsePoint) {
1366
+ var line, _i, _len;
1367
+ generator.outputBuffer.append(indentText + "<![CDATA[\n");
1368
+ for (_i = 0, _len = contents.length; _i < _len; _i++) {
1369
+ line = contents[_i];
1370
+ generator.appendTextContents(indentText + line + '\n', true, currentParsePoint);
1371
+ }
1372
+ return generator.outputBuffer.append(indentText + "]]>\n");
1373
+ },
1374
+ /*
1375
+ Preserve filter, preserved blocks of text aren't indented, and newlines are replaced with the HTML escape code for newlines
1376
+ */
1377
+
1378
+ preserve: function(contents, generator, indentText, currentParsePoint) {
1379
+ return generator.appendTextContents(contents.join('\n') + '\n', true, currentParsePoint, {
1380
+ perserveWhitespace: true
1381
+ });
1382
+ },
1383
+ /*
1384
+ Escape filter, renders the text in the block with html escaped
1385
+ */
1386
+
1387
+ escape: function(contents, generator, indentText, currentParsePoint) {
1388
+ var line, _i, _len;
1389
+ for (_i = 0, _len = contents.length; _i < _len; _i++) {
1390
+ line = contents[_i];
1391
+ generator.appendTextContents(indentText + line + '\n', true, currentParsePoint, {
1392
+ escapeHTML: true
1393
+ });
1394
+ }
1395
+ return true;
1396
+ }
1397
+ };
1398
+
1399
+ /*
1400
+ Main haml compiler implemtation
1401
+ */
1402
+
1403
+
1404
+ root.haml = {
1405
+ /*
1406
+ Compiles the haml provided in the parameters to a Javascipt function
1407
+
1408
+ Parameter:
1409
+ String: Looks for a haml template in dom with this ID
1410
+ Option Hash: The following options determines how haml sources and compiles the template
1411
+ source - This contains the template in string form
1412
+ sourceId - This contains the element ID in the dom which contains the haml source
1413
+ sourceUrl - This contains the URL where the template can be fetched from
1414
+ outputFormat - This determines what is returned, and has the following values:
1415
+ string - The javascript source code
1416
+ function - A javascript function (default)
1417
+ generator - Which code generator to use
1418
+ javascript (default)
1419
+ coffeescript
1420
+ productionjavascript
1421
+
1422
+ Returns a javascript function
1423
+ */
1424
+
1425
+ compileHaml: function(options) {
1426
+ var codeGenerator, result, tokinser;
1427
+ if (typeof options === 'string') {
1428
+ return this._compileHamlTemplate(options, new haml.JsCodeGenerator());
1429
+ } else {
1430
+ codeGenerator = (function() {
1431
+ switch (options.generator) {
1432
+ case 'coffeescript':
1433
+ return new haml.CoffeeCodeGenerator();
1434
+ case 'productionjavascript':
1435
+ return new haml.ProductionJsCodeGenerator();
1436
+ default:
1437
+ return new haml.JsCodeGenerator();
1438
+ }
1439
+ })();
1440
+ if (options.source != null) {
1441
+ tokinser = new haml.Tokeniser({
1442
+ template: options.source
1443
+ });
1444
+ } else if (options.sourceId != null) {
1445
+ tokinser = new haml.Tokeniser({
1446
+ templateId: options.sourceId
1447
+ });
1448
+ } else if (options.sourceUrl != null) {
1449
+ tokinser = new haml.Tokeniser({
1450
+ templateUrl: options.sourceUrl
1451
+ });
1452
+ } else {
1453
+ throw "No template source specified for compileHaml. You need to provide a source, sourceId or sourceUrl option";
1454
+ }
1455
+ result = this._compileHamlToJs(tokinser, codeGenerator);
1456
+ if (options.outputFormat !== 'string') {
1457
+ return codeGenerator.generateJsFunction(result);
1458
+ } else {
1459
+ return "function (context) {\n" + result + "}\n";
1460
+ }
1461
+ }
1462
+ },
1463
+ /*
1464
+ Compiles the haml in the script block with ID templateId using the coffeescript generator
1465
+ Returns a javascript function
1466
+ */
1467
+
1468
+ compileCoffeeHaml: function(templateId) {
1469
+ return this._compileHamlTemplate(templateId, new haml.CoffeeCodeGenerator());
1470
+ },
1471
+ /*
1472
+ Compiles the haml in the passed in string
1473
+ Returns a javascript function
1474
+ */
1475
+
1476
+ compileStringToJs: function(string) {
1477
+ var codeGenerator, result;
1478
+ codeGenerator = new haml.JsCodeGenerator();
1479
+ result = this._compileHamlToJs(new haml.Tokeniser({
1480
+ template: string
1481
+ }), codeGenerator);
1482
+ return codeGenerator.generateJsFunction(result);
1483
+ },
1484
+ /*
1485
+ Compiles the haml in the passed in string using the coffeescript generator
1486
+ Returns a javascript function
1487
+ */
1488
+
1489
+ compileCoffeeHamlFromString: function(string) {
1490
+ var codeGenerator, result;
1491
+ codeGenerator = new haml.CoffeeCodeGenerator();
1492
+ result = this._compileHamlToJs(new haml.Tokeniser({
1493
+ template: string
1494
+ }), codeGenerator);
1495
+ return codeGenerator.generateJsFunction(result);
1496
+ },
1497
+ /*
1498
+ Compiles the haml in the passed in string
1499
+ Returns the javascript function source
1500
+
1501
+ This is mainly used for precompiling the haml templates so they can be packaged.
1502
+ */
1503
+
1504
+ compileHamlToJsString: function(string) {
1505
+ var result;
1506
+ result = 'function (context) {\n';
1507
+ result += this._compileHamlToJs(new haml.Tokeniser({
1508
+ template: string
1509
+ }), new haml.JsCodeGenerator());
1510
+ return result += '}\n';
1511
+ },
1512
+ _compileHamlTemplate: function(templateId, codeGenerator) {
1513
+ var fn, result;
1514
+ haml.cache || (haml.cache = {});
1515
+ if (haml.cache[templateId]) {
1516
+ return haml.cache[templateId];
1517
+ }
1518
+ result = this._compileHamlToJs(new haml.Tokeniser({
1519
+ templateId: templateId
1520
+ }), codeGenerator);
1521
+ fn = codeGenerator.generateJsFunction(result);
1522
+ haml.cache[templateId] = fn;
1523
+ return fn;
1524
+ },
1525
+ _compileHamlToJs: function(tokeniser, generator) {
1526
+ var indent;
1527
+ generator.elementStack = [];
1528
+ generator.initOutput();
1529
+ tokeniser.getNextToken();
1530
+ while (!tokeniser.token.eof) {
1531
+ if (!tokeniser.token.eol) {
1532
+ indent = this._whitespace(tokeniser);
1533
+ generator.setIndent(indent);
1534
+ if (tokeniser.token.eol) {
1535
+ generator.outputBuffer.append(HamlRuntime.indentText(indent) + tokeniser.token.matched);
1536
+ tokeniser.getNextToken();
1537
+ } else if (tokeniser.token.doctype) {
1538
+ this._doctype(tokeniser, indent, generator);
1539
+ } else if (tokeniser.token.exclamation) {
1540
+ this._ignoredLine(tokeniser, indent, generator.elementStack, generator);
1541
+ } else if (tokeniser.token.equal || tokeniser.token.escapeHtml || tokeniser.token.unescapeHtml || tokeniser.token.tilde) {
1542
+ this._embeddedJs(tokeniser, indent, generator.elementStack, {
1543
+ innerWhitespace: true
1544
+ }, generator);
1545
+ } else if (tokeniser.token.minus) {
1546
+ this._jsLine(tokeniser, indent, generator.elementStack, generator);
1547
+ } else if (tokeniser.token.comment || tokeniser.token.slash) {
1548
+ this._commentLine(tokeniser, indent, generator.elementStack, generator);
1549
+ } else if (tokeniser.token.amp) {
1550
+ this._escapedLine(tokeniser, indent, generator.elementStack, generator);
1551
+ } else if (tokeniser.token.filter) {
1552
+ this._filter(tokeniser, indent, generator);
1553
+ } else {
1554
+ this._templateLine(tokeniser, generator.elementStack, indent, generator);
1555
+ }
1556
+ } else {
1557
+ generator.outputBuffer.append(tokeniser.token.matched);
1558
+ tokeniser.getNextToken();
1559
+ }
1560
+ }
1561
+ this._closeElements(0, generator.elementStack, tokeniser, generator);
1562
+ return generator.closeAndReturnOutput();
1563
+ },
1564
+ _doctype: function(tokeniser, indent, generator) {
1565
+ var contents, params;
1566
+ if (tokeniser.token.doctype) {
1567
+ generator.outputBuffer.append(HamlRuntime.indentText(indent));
1568
+ tokeniser.getNextToken();
1569
+ if (tokeniser.token.ws) {
1570
+ tokeniser.getNextToken();
1571
+ }
1572
+ contents = tokeniser.skipToEOLorEOF();
1573
+ if (contents && contents.length > 0) {
1574
+ params = contents.split(/\s+/);
1575
+ switch (params[0]) {
1576
+ case 'XML':
1577
+ if (params.length > 1) {
1578
+ generator.outputBuffer.append("<?xml version='1.0' encoding='" + params[1] + "' ?>");
1579
+ } else {
1580
+ generator.outputBuffer.append("<?xml version='1.0' encoding='utf-8' ?>");
1581
+ }
1582
+ break;
1583
+ case 'Strict':
1584
+ generator.outputBuffer.append('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">');
1585
+ break;
1586
+ case 'Frameset':
1587
+ generator.outputBuffer.append('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">');
1588
+ break;
1589
+ case '5':
1590
+ generator.outputBuffer.append('<!DOCTYPE html>');
1591
+ break;
1592
+ case '1.1':
1593
+ generator.outputBuffer.append('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">');
1594
+ break;
1595
+ case 'Basic':
1596
+ generator.outputBuffer.append('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">');
1597
+ break;
1598
+ case 'Mobile':
1599
+ generator.outputBuffer.append('<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">');
1600
+ break;
1601
+ case 'RDFa':
1602
+ generator.outputBuffer.append('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">');
1603
+ }
1604
+ } else {
1605
+ generator.outputBuffer.append('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">');
1606
+ }
1607
+ generator.outputBuffer.append(this._newline(tokeniser));
1608
+ return tokeniser.getNextToken();
1609
+ }
1610
+ },
1611
+ _filter: function(tokeniser, indent, generator) {
1612
+ var filter, filterBlock, i, line;
1613
+ if (tokeniser.token.filter) {
1614
+ filter = tokeniser.token.tokenString;
1615
+ if (!haml.filters[filter]) {
1616
+ throw tokeniser.parseError("Filter '" + filter + "' not registered. Filter functions need to be added to 'haml.filters'.");
1617
+ }
1618
+ tokeniser.skipToEOLorEOF();
1619
+ tokeniser.getNextToken();
1620
+ i = haml._whitespace(tokeniser);
1621
+ filterBlock = [];
1622
+ while (!tokeniser.token.eof && i > indent) {
1623
+ line = tokeniser.skipToEOLorEOF();
1624
+ filterBlock.push(haml.HamlRuntime.indentText(i - indent - 1) + line);
1625
+ tokeniser.getNextToken();
1626
+ i = haml._whitespace(tokeniser);
1627
+ }
1628
+ haml.filters[filter](filterBlock, generator, haml.HamlRuntime.indentText(indent), tokeniser.currentParsePoint());
1629
+ return tokeniser.pushBackToken();
1630
+ }
1631
+ },
1632
+ _commentLine: function(tokeniser, indent, elementStack, generator) {
1633
+ var contents, i;
1634
+ if (tokeniser.token.comment) {
1635
+ tokeniser.skipToEOLorEOF();
1636
+ tokeniser.getNextToken();
1637
+ i = this._whitespace(tokeniser);
1638
+ while (!tokeniser.token.eof && i > indent) {
1639
+ tokeniser.skipToEOLorEOF();
1640
+ tokeniser.getNextToken();
1641
+ i = this._whitespace(tokeniser);
1642
+ }
1643
+ if (i > 0) {
1644
+ return tokeniser.pushBackToken();
1645
+ }
1646
+ } else if (tokeniser.token.slash) {
1647
+ haml._closeElements(indent, elementStack, tokeniser, generator);
1648
+ generator.outputBuffer.append(HamlRuntime.indentText(indent));
1649
+ generator.outputBuffer.append("<!--");
1650
+ tokeniser.getNextToken();
1651
+ contents = tokeniser.skipToEOLorEOF();
1652
+ if (contents && contents.length > 0) {
1653
+ generator.outputBuffer.append(contents);
1654
+ }
1655
+ if (contents && (_.str || _).startsWith(contents, '[') && contents.match(/\]\s*$/)) {
1656
+ elementStack[indent] = {
1657
+ htmlConditionalComment: true,
1658
+ eol: this._newline(tokeniser)
1659
+ };
1660
+ generator.outputBuffer.append(">");
1661
+ } else {
1662
+ elementStack[indent] = {
1663
+ htmlComment: true,
1664
+ eol: this._newline(tokeniser)
1665
+ };
1666
+ }
1667
+ if (haml._tagHasContents(indent, tokeniser)) {
1668
+ generator.outputBuffer.append("\n");
1669
+ }
1670
+ return tokeniser.getNextToken();
1671
+ }
1672
+ },
1673
+ _escapedLine: function(tokeniser, indent, elementStack, generator) {
1674
+ var contents;
1675
+ if (tokeniser.token.amp) {
1676
+ haml._closeElements(indent, elementStack, tokeniser, generator);
1677
+ generator.outputBuffer.append(HamlRuntime.indentText(indent));
1678
+ tokeniser.getNextToken();
1679
+ contents = tokeniser.skipToEOLorEOF();
1680
+ if (contents && contents.length > 0) {
1681
+ generator.outputBuffer.append(haml.HamlRuntime.escapeHTML(contents));
1682
+ }
1683
+ generator.outputBuffer.append(this._newline(tokeniser));
1684
+ return tokeniser.getNextToken();
1685
+ }
1686
+ },
1687
+ _ignoredLine: function(tokeniser, indent, elementStack, generator) {
1688
+ var contents;
1689
+ if (tokeniser.token.exclamation) {
1690
+ tokeniser.getNextToken();
1691
+ if (tokeniser.token.ws) {
1692
+ indent += haml._whitespace(tokeniser);
1693
+ }
1694
+ haml._closeElements(indent, elementStack, tokeniser, generator);
1695
+ contents = tokeniser.skipToEOLorEOF();
1696
+ return generator.outputBuffer.append(HamlRuntime.indentText(indent) + contents);
1697
+ }
1698
+ },
1699
+ _embeddedJs: function(tokeniser, indent, elementStack, tagOptions, generator) {
1700
+ var currentParsePoint, escapeHtml, expression, indentText, perserveWhitespace;
1701
+ if (elementStack) {
1702
+ haml._closeElements(indent, elementStack, tokeniser, generator);
1703
+ }
1704
+ if (tokeniser.token.equal || tokeniser.token.escapeHtml || tokeniser.token.unescapeHtml || tokeniser.token.tilde) {
1705
+ escapeHtml = tokeniser.token.escapeHtml || tokeniser.token.equal;
1706
+ perserveWhitespace = tokeniser.token.tilde;
1707
+ currentParsePoint = tokeniser.currentParsePoint();
1708
+ tokeniser.getNextToken();
1709
+ expression = tokeniser.skipToEOLorEOF();
1710
+ indentText = HamlRuntime.indentText(indent);
1711
+ if (!tagOptions || tagOptions.innerWhitespace) {
1712
+ generator.outputBuffer.append(indentText);
1713
+ }
1714
+ generator.appendEmbeddedCode(indentText, expression, escapeHtml, perserveWhitespace, currentParsePoint);
1715
+ if (!tagOptions || tagOptions.innerWhitespace) {
1716
+ generator.outputBuffer.append(this._newline(tokeniser));
1717
+ if (tokeniser.token.eol) {
1718
+ return tokeniser.getNextToken();
1719
+ }
1720
+ }
1721
+ }
1722
+ },
1723
+ _jsLine: function(tokeniser, indent, elementStack, generator) {
1724
+ var line;
1725
+ if (tokeniser.token.minus) {
1726
+ haml._closeElements(indent, elementStack, tokeniser, generator);
1727
+ tokeniser.getNextToken();
1728
+ line = tokeniser.skipToEOLorEOF();
1729
+ generator.setIndent(indent);
1730
+ generator.appendCodeLine(line, this._newline(tokeniser));
1731
+ if (tokeniser.token.eol) {
1732
+ tokeniser.getNextToken();
1733
+ }
1734
+ if (generator.lineMatchesStartFunctionBlock(line)) {
1735
+ return elementStack[indent] = {
1736
+ fnBlock: true
1737
+ };
1738
+ } else if (generator.lineMatchesStartBlock(line)) {
1739
+ return elementStack[indent] = {
1740
+ block: true
1741
+ };
1742
+ }
1743
+ }
1744
+ },
1745
+ _templateLine: function(tokeniser, elementStack, indent, generator) {
1746
+ var attrList, attributesHash, classes, contents, currentParsePoint, hasContents, id, identifier, indentText, lineHasElement, objectRef, shouldInterpolate, tagOptions;
1747
+ if (!tokeniser.token.eol) {
1748
+ this._closeElements(indent, elementStack, tokeniser, generator);
1749
+ }
1750
+ identifier = this._element(tokeniser);
1751
+ id = this._idSelector(tokeniser);
1752
+ classes = this._classSelector(tokeniser);
1753
+ objectRef = this._objectReference(tokeniser);
1754
+ attrList = this._attributeList(tokeniser);
1755
+ currentParsePoint = tokeniser.currentParsePoint();
1756
+ attributesHash = this._attributeHash(tokeniser);
1757
+ tagOptions = {
1758
+ selfClosingTag: false,
1759
+ innerWhitespace: true,
1760
+ outerWhitespace: true
1761
+ };
1762
+ lineHasElement = this._lineHasElement(identifier, id, classes);
1763
+ if (tokeniser.token.slash) {
1764
+ tagOptions.selfClosingTag = true;
1765
+ tokeniser.getNextToken();
1766
+ }
1767
+ if (tokeniser.token.gt && lineHasElement) {
1768
+ tagOptions.outerWhitespace = false;
1769
+ tokeniser.getNextToken();
1770
+ }
1771
+ if (tokeniser.token.lt && lineHasElement) {
1772
+ tagOptions.innerWhitespace = false;
1773
+ tokeniser.getNextToken();
1774
+ }
1775
+ if (lineHasElement) {
1776
+ if (!tagOptions.selfClosingTag) {
1777
+ tagOptions.selfClosingTag = haml._isSelfClosingTag(identifier) && !haml._tagHasContents(indent, tokeniser);
1778
+ }
1779
+ this._openElement(currentParsePoint, indent, identifier, id, classes, objectRef, attrList, attributesHash, elementStack, tagOptions, generator);
1780
+ }
1781
+ hasContents = false;
1782
+ if (tokeniser.token.ws) {
1783
+ tokeniser.getNextToken();
1784
+ }
1785
+ if (tokeniser.token.equal || tokeniser.token.escapeHtml || tokeniser.token.unescapeHtml) {
1786
+ this._embeddedJs(tokeniser, indent + 1, null, tagOptions, generator);
1787
+ hasContents = true;
1788
+ } else {
1789
+ contents = '';
1790
+ shouldInterpolate = false;
1791
+ if (tokeniser.token.exclamation) {
1792
+ tokeniser.getNextToken();
1793
+ contents = tokeniser.skipToEOLorEOF();
1794
+ } else {
1795
+ contents = tokeniser.skipToEOLorEOF();
1796
+ if (contents.match(/^\\/)) {
1797
+ contents = contents.substring(1);
1798
+ }
1799
+ shouldInterpolate = true;
1800
+ }
1801
+ hasContents = contents.length > 0;
1802
+ if (hasContents) {
1803
+ if (tagOptions.innerWhitespace && lineHasElement || (!lineHasElement && haml._parentInnerWhitespace(elementStack, indent))) {
1804
+ indentText = HamlRuntime.indentText(identifier.length > 0 ? indent + 1 : indent);
1805
+ } else {
1806
+ indentText = '';
1807
+ contents = (_.str || _).trim(contents);
1808
+ }
1809
+ generator.appendTextContents(indentText + contents, shouldInterpolate, currentParsePoint);
1810
+ generator.outputBuffer.append(this._newline(tokeniser));
1811
+ }
1812
+ this._eolOrEof(tokeniser);
1813
+ }
1814
+ if (tagOptions.selfClosingTag && hasContents) {
1815
+ throw haml.HamlRuntime.templateError(currentParsePoint.lineNumber, currentParsePoint.characterNumber, currentParsePoint.currentLine, "A self-closing tag can not have any contents");
1816
+ }
1817
+ },
1818
+ _attributeHash: function(tokeniser) {
1819
+ var attr;
1820
+ attr = '';
1821
+ if (tokeniser.token.attributeHash) {
1822
+ attr = tokeniser.token.tokenString;
1823
+ tokeniser.getNextToken();
1824
+ }
1825
+ return attr;
1826
+ },
1827
+ _objectReference: function(tokeniser) {
1828
+ var attr;
1829
+ attr = '';
1830
+ if (tokeniser.token.objectReference) {
1831
+ attr = tokeniser.token.tokenString;
1832
+ tokeniser.getNextToken();
1833
+ }
1834
+ return attr;
1835
+ },
1836
+ _attributeList: function(tokeniser) {
1837
+ var attr, attrList;
1838
+ attrList = {};
1839
+ if (tokeniser.token.openBracket) {
1840
+ tokeniser.getNextToken();
1841
+ while (!tokeniser.token.closeBracket) {
1842
+ attr = haml._attribute(tokeniser);
1843
+ if (attr) {
1844
+ attrList[attr.name] = attr.value;
1845
+ } else {
1846
+ tokeniser.getNextToken();
1847
+ }
1848
+ if (tokeniser.token.ws || tokeniser.token.eol) {
1849
+ tokeniser.getNextToken();
1850
+ } else if (!tokeniser.token.closeBracket && !tokeniser.token.identifier) {
1851
+ throw tokeniser.parseError("Expecting either an attribute name to continue the attibutes or a closing " + "bracket to end");
1852
+ }
1853
+ }
1854
+ tokeniser.getNextToken();
1855
+ }
1856
+ return attrList;
1857
+ },
1858
+ _attribute: function(tokeniser) {
1859
+ var attr, name;
1860
+ attr = null;
1861
+ if (tokeniser.token.identifier) {
1862
+ name = tokeniser.token.tokenString;
1863
+ tokeniser.getNextToken();
1864
+ haml._whitespace(tokeniser);
1865
+ if (!tokeniser.token.equal) {
1866
+ throw tokeniser.parseError("Expected '=' after attribute name");
1867
+ }
1868
+ tokeniser.getNextToken();
1869
+ haml._whitespace(tokeniser);
1870
+ if (!tokeniser.token.string && !tokeniser.token.identifier) {
1871
+ throw tokeniser.parseError("Expected a quoted string or an identifier for the attribute value");
1872
+ }
1873
+ attr = {
1874
+ name: name,
1875
+ value: tokeniser.token.tokenString
1876
+ };
1877
+ tokeniser.getNextToken();
1878
+ }
1879
+ return attr;
1880
+ },
1881
+ _closeElement: function(indent, elementStack, tokeniser, generator) {
1882
+ var innerWhitespace, outerWhitespace;
1883
+ if (elementStack[indent]) {
1884
+ generator.setIndent(indent);
1885
+ if (elementStack[indent].htmlComment) {
1886
+ generator.outputBuffer.append(HamlRuntime.indentText(indent) + '-->' + elementStack[indent].eol);
1887
+ } else if (elementStack[indent].htmlConditionalComment) {
1888
+ generator.outputBuffer.append(HamlRuntime.indentText(indent) + '<![endif]-->' + elementStack[indent].eol);
1889
+ } else if (elementStack[indent].block) {
1890
+ generator.closeOffCodeBlock(tokeniser);
1891
+ } else if (elementStack[indent].fnBlock) {
1892
+ generator.closeOffFunctionBlock(tokeniser);
1893
+ } else {
1894
+ innerWhitespace = !elementStack[indent].tagOptions || elementStack[indent].tagOptions.innerWhitespace;
1895
+ if (innerWhitespace) {
1896
+ generator.outputBuffer.append(HamlRuntime.indentText(indent));
1897
+ } else {
1898
+ generator.outputBuffer.trimWhitespace();
1899
+ }
1900
+ generator.outputBuffer.append('</' + elementStack[indent].tag + '>');
1901
+ outerWhitespace = !elementStack[indent].tagOptions || elementStack[indent].tagOptions.outerWhitespace;
1902
+ if (haml._parentInnerWhitespace(elementStack, indent) && outerWhitespace) {
1903
+ generator.outputBuffer.append('\n');
1904
+ }
1905
+ }
1906
+ elementStack[indent] = null;
1907
+ return generator.mark();
1908
+ }
1909
+ },
1910
+ _closeElements: function(indent, elementStack, tokeniser, generator) {
1911
+ var i, _results;
1912
+ i = elementStack.length - 1;
1913
+ _results = [];
1914
+ while (i >= indent) {
1915
+ _results.push(this._closeElement(i--, elementStack, tokeniser, generator));
1916
+ }
1917
+ return _results;
1918
+ },
1919
+ _openElement: function(currentParsePoint, indent, identifier, id, classes, objectRef, attributeList, attributeHash, elementStack, tagOptions, generator) {
1920
+ var element, parentInnerWhitespace, tagOuterWhitespace;
1921
+ element = identifier.length === 0 ? "div" : identifier;
1922
+ parentInnerWhitespace = this._parentInnerWhitespace(elementStack, indent);
1923
+ tagOuterWhitespace = !tagOptions || tagOptions.outerWhitespace;
1924
+ if (!tagOuterWhitespace) {
1925
+ generator.outputBuffer.trimWhitespace();
1926
+ }
1927
+ if (indent > 0 && parentInnerWhitespace && tagOuterWhitespace) {
1928
+ generator.outputBuffer.append(HamlRuntime.indentText(indent));
1929
+ }
1930
+ generator.outputBuffer.append('<' + element);
1931
+ if (attributeHash.length > 0 || objectRef.length > 0) {
1932
+ generator.generateCodeForDynamicAttributes(id, classes, attributeList, attributeHash, objectRef, currentParsePoint);
1933
+ } else {
1934
+ generator.outputBuffer.append(HamlRuntime.generateElementAttributes(null, id, classes, null, attributeList, null, currentParsePoint.lineNumber, currentParsePoint.characterNumber, currentParsePoint.currentLine));
1935
+ }
1936
+ if (tagOptions.selfClosingTag) {
1937
+ generator.outputBuffer.append("/>");
1938
+ if (tagOptions.outerWhitespace) {
1939
+ return generator.outputBuffer.append("\n");
1940
+ }
1941
+ } else {
1942
+ generator.outputBuffer.append(">");
1943
+ elementStack[indent] = {
1944
+ tag: element,
1945
+ tagOptions: tagOptions
1946
+ };
1947
+ if (tagOptions.innerWhitespace) {
1948
+ return generator.outputBuffer.append("\n");
1949
+ }
1950
+ }
1951
+ },
1952
+ _isSelfClosingTag: function(tag) {
1953
+ return tag === 'meta' || tag === 'img' || tag === 'link' || tag === 'script' || tag === 'br' || tag === 'hr';
1954
+ },
1955
+ _tagHasContents: function(indent, tokeniser) {
1956
+ var nextToken;
1957
+ if (!tokeniser.isEolOrEof()) {
1958
+ return true;
1959
+ } else {
1960
+ nextToken = tokeniser.lookAhead(1);
1961
+ return nextToken.ws && nextToken.tokenString.length / 2 > indent;
1962
+ }
1963
+ },
1964
+ _parentInnerWhitespace: function(elementStack, indent) {
1965
+ return indent === 0 || (!elementStack[indent - 1] || !elementStack[indent - 1].tagOptions || elementStack[indent - 1].tagOptions.innerWhitespace);
1966
+ },
1967
+ _lineHasElement: function(identifier, id, classes) {
1968
+ return identifier.length > 0 || id.length > 0 || classes.length > 0;
1969
+ },
1970
+ hasValue: function(value) {
1971
+ return (value != null) && value !== false;
1972
+ },
1973
+ attrValue: function(attr, value) {
1974
+ if (attr === 'selected' || attr === 'checked' || attr === 'disabled') {
1975
+ return attr;
1976
+ } else {
1977
+ return value;
1978
+ }
1979
+ },
1980
+ _whitespace: function(tokeniser) {
1981
+ var i, indent, whitespace;
1982
+ indent = 0;
1983
+ if (tokeniser.token.ws) {
1984
+ i = 0;
1985
+ whitespace = tokeniser.token.tokenString;
1986
+ while (i < whitespace.length) {
1987
+ if (whitespace.charCodeAt(i) === 9 && i % 2 === 0) {
1988
+ indent += 2;
1989
+ } else {
1990
+ indent++;
1991
+ }
1992
+ i++;
1993
+ }
1994
+ indent = Math.floor((indent + 1) / 2);
1995
+ tokeniser.getNextToken();
1996
+ }
1997
+ return indent;
1998
+ },
1999
+ _element: function(tokeniser) {
2000
+ var identifier;
2001
+ identifier = '';
2002
+ if (tokeniser.token.element) {
2003
+ identifier = tokeniser.token.tokenString;
2004
+ tokeniser.getNextToken();
2005
+ }
2006
+ return identifier;
2007
+ },
2008
+ _eolOrEof: function(tokeniser) {
2009
+ if (tokeniser.token.eol || tokeniser.token.continueLine) {
2010
+ return tokeniser.getNextToken();
2011
+ } else if (!tokeniser.token.eof) {
2012
+ throw tokeniser.parseError("Expected EOL or EOF");
2013
+ }
2014
+ },
2015
+ _idSelector: function(tokeniser) {
2016
+ var id;
2017
+ id = '';
2018
+ if (tokeniser.token.idSelector) {
2019
+ id = tokeniser.token.tokenString;
2020
+ tokeniser.getNextToken();
2021
+ }
2022
+ return id;
2023
+ },
2024
+ _classSelector: function(tokeniser) {
2025
+ var classes;
2026
+ classes = [];
2027
+ while (tokeniser.token.classSelector) {
2028
+ classes.push(tokeniser.token.tokenString);
2029
+ tokeniser.getNextToken();
2030
+ }
2031
+ return classes;
2032
+ },
2033
+ _newline: function(tokeniser) {
2034
+ if (tokeniser.token.eol) {
2035
+ return tokeniser.token.matched;
2036
+ } else if (tokeniser.token.continueLine) {
2037
+ return tokeniser.token.matched.substring(1);
2038
+ } else {
2039
+ return "\n";
2040
+ }
2041
+ }
2042
+ };
2043
+
2044
+ root.haml.Tokeniser = Tokeniser;
2045
+
2046
+ root.haml.Buffer = Buffer;
2047
+
2048
+ root.haml.JsCodeGenerator = JsCodeGenerator;
2049
+
2050
+ root.haml.ProductionJsCodeGenerator = ProductionJsCodeGenerator;
2051
+
2052
+ root.haml.CoffeeCodeGenerator = CoffeeCodeGenerator;
2053
+
2054
+ root.haml.HamlRuntime = HamlRuntime;
2055
+
2056
+ root.haml.filters = filters;
2057
+
2058
+ }).call(this);