rails-hamljs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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);