gusto 1.0.0.beta2

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.
data/public/jsdiff.js ADDED
@@ -0,0 +1,161 @@
1
+ /*
2
+ * Javascript Diff Algorithm
3
+ * By John Resig (http://ejohn.org/)
4
+ * Modified by Chu Alan "sprite"
5
+ *
6
+ * Released under the MIT license.
7
+ *
8
+ * More Info:
9
+ * http://ejohn.org/projects/javascript-diff-algorithm/
10
+ */
11
+
12
+ function escape(s) {
13
+ var n = s;
14
+ n = n.replace(/&/g, "&");
15
+ n = n.replace(/</g, "&lt;");
16
+ n = n.replace(/>/g, "&gt;");
17
+ n = n.replace(/"/g, "&quot;");
18
+
19
+ return n;
20
+ }
21
+
22
+ function diffString( o, n ) {
23
+ o = o.replace(/\s+$/, '');
24
+ n = n.replace(/\s+$/, '');
25
+
26
+ var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
27
+ var str = "";
28
+
29
+ var oSpace = o.match(/\s+/g);
30
+ if (oSpace == null) {
31
+ oSpace = ["\n"];
32
+ } else {
33
+ oSpace.push("\n");
34
+ }
35
+ var nSpace = n.match(/\s+/g);
36
+ if (nSpace == null) {
37
+ nSpace = ["\n"];
38
+ } else {
39
+ nSpace.push("\n");
40
+ }
41
+
42
+ if (out.n.length == 0) {
43
+ for (var i = 0; i < out.o.length; i++) {
44
+ str += '<del>' + escape(out.o[i]) + oSpace[i] + "</del>";
45
+ }
46
+ } else {
47
+ if (out.n[0].text == null) {
48
+ for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
49
+ str += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
50
+ }
51
+ }
52
+
53
+ for ( var i = 0; i < out.n.length; i++ ) {
54
+ if (out.n[i].text == null) {
55
+ str += '<ins>' + escape(out.n[i]) + nSpace[i] + "</ins>";
56
+ } else {
57
+ var pre = "";
58
+
59
+ for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
60
+ pre += '<del>' + escape(out.o[n]) + oSpace[n] + "</del>";
61
+ }
62
+ str += " " + out.n[i].text + nSpace[i] + pre;
63
+ }
64
+ }
65
+ }
66
+
67
+ return str;
68
+ }
69
+
70
+ function randomColor() {
71
+ return "rgb(" + (Math.random() * 100) + "%, " +
72
+ (Math.random() * 100) + "%, " +
73
+ (Math.random() * 100) + "%)";
74
+ }
75
+ function diffString2( o, n ) {
76
+ o = o.replace(/\s+$/, '');
77
+ n = n.replace(/\s+$/, '');
78
+
79
+ var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
80
+
81
+ var oSpace = o.match(/\s+/g);
82
+ if (oSpace == null) {
83
+ oSpace = ["\n"];
84
+ } else {
85
+ oSpace.push("\n");
86
+ }
87
+ var nSpace = n.match(/\s+/g);
88
+ if (nSpace == null) {
89
+ nSpace = ["\n"];
90
+ } else {
91
+ nSpace.push("\n");
92
+ }
93
+
94
+ var os = "";
95
+ var colors = new Array();
96
+ for (var i = 0; i < out.o.length; i++) {
97
+ colors[i] = randomColor();
98
+
99
+ if (out.o[i].text != null) {
100
+ os += '<span style="background-color: ' +colors[i]+ '">' +
101
+ escape(out.o[i].text) + oSpace[i] + "</span>";
102
+ } else {
103
+ os += "<del>" + escape(out.o[i]) + oSpace[i] + "</del>";
104
+ }
105
+ }
106
+
107
+ var ns = "";
108
+ for (var i = 0; i < out.n.length; i++) {
109
+ if (out.n[i].text != null) {
110
+ ns += '<span style="background-color: ' +colors[out.n[i].row]+ '">' +
111
+ escape(out.n[i].text) + nSpace[i] + "</span>";
112
+ } else {
113
+ ns += "<ins>" + escape(out.n[i]) + nSpace[i] + "</ins>";
114
+ }
115
+ }
116
+
117
+ return { o : os , n : ns };
118
+ }
119
+
120
+ function diff( o, n ) {
121
+ var ns = new Object();
122
+ var os = new Object();
123
+
124
+ for ( var i = 0; i < n.length; i++ ) {
125
+ if ( ns[ n[i] ] == null )
126
+ ns[ n[i] ] = { rows: new Array(), o: null };
127
+ ns[ n[i] ].rows.push( i );
128
+ }
129
+
130
+ for ( var i = 0; i < o.length; i++ ) {
131
+ if ( os[ o[i] ] == null )
132
+ os[ o[i] ] = { rows: new Array(), n: null };
133
+ os[ o[i] ].rows.push( i );
134
+ }
135
+
136
+ for ( var i in ns ) {
137
+ if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) {
138
+ n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };
139
+ o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };
140
+ }
141
+ }
142
+
143
+ for ( var i = 0; i < n.length - 1; i++ ) {
144
+ if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
145
+ n[i+1] == o[ n[i].row + 1 ] ) {
146
+ n[i+1] = { text: n[i+1], row: n[i].row + 1 };
147
+ o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 };
148
+ }
149
+ }
150
+
151
+ for ( var i = n.length - 1; i > 0; i-- ) {
152
+ if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
153
+ n[i-1] == o[ n[i].row - 1 ] ) {
154
+ n[i-1] = { text: n[i-1], row: n[i].row - 1 };
155
+ o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };
156
+ }
157
+ }
158
+
159
+ return { o: o, n: n };
160
+ }
161
+
@@ -0,0 +1,446 @@
1
+ // Domain Public by Eric Wendelin http://eriwen.com/ (2008)
2
+ // Luke Smith http://lucassmith.name/ (2008)
3
+ // Loic Dachary <loic@dachary.org> (2008)
4
+ // Johan Euphrosine <proppy@aminche.com> (2008)
5
+ // Oyvind Sean Kinsey http://kinsey.no/blog (2010)
6
+ // Victor Homyakov <victor-homyakov@users.sourceforge.net> (2010)
7
+
8
+ /**
9
+ * Main function giving a function stack trace with a forced or passed in Error
10
+ *
11
+ * @cfg {Error} e The error to create a stacktrace from (optional)
12
+ * @cfg {Boolean} guess If we should try to resolve the names of anonymous functions
13
+ * @return {Array} of Strings with functions, lines, files, and arguments where possible
14
+ */
15
+ function printStackTrace(options) {
16
+ options = options || {guess: true};
17
+ var ex = options.e || null, guess = !!options.guess;
18
+ var p = new printStackTrace.implementation(), result = p.run(ex);
19
+ return (guess) ? p.guessAnonymousFunctions(result) : result;
20
+ }
21
+
22
+ if (typeof module !== "undefined" && module.exports) {
23
+ module.exports = printStackTrace;
24
+ }
25
+
26
+ printStackTrace.implementation = function() {
27
+ };
28
+
29
+ printStackTrace.implementation.prototype = {
30
+ /**
31
+ * @param {Error} ex The error to create a stacktrace from (optional)
32
+ * @param {String} mode Forced mode (optional, mostly for unit tests)
33
+ */
34
+ run: function(ex, mode) {
35
+ ex = ex || this.createException();
36
+ // examine exception properties w/o debugger
37
+ //for (var prop in ex) {alert("Ex['" + prop + "']=" + ex[prop]);}
38
+ mode = mode || this.mode(ex);
39
+ if (mode === 'other') {
40
+ return this.other(arguments.callee);
41
+ } else {
42
+ return this[mode](ex);
43
+ }
44
+ },
45
+
46
+ createException: function() {
47
+ try {
48
+ this.undef();
49
+ } catch (e) {
50
+ return e;
51
+ }
52
+ },
53
+
54
+ /**
55
+ * Mode could differ for different exception, e.g.
56
+ * exceptions in Chrome may or may not have arguments or stack.
57
+ *
58
+ * @return {String} mode of operation for the exception
59
+ */
60
+ mode: function(e) {
61
+ if (e['arguments'] && e.stack) {
62
+ return 'chrome';
63
+ } else if (e.stack && e.sourceURL) {
64
+ return 'safari';
65
+ } else if (e.stack && e.number) {
66
+ return 'ie';
67
+ } else if (typeof e.message === 'string' && typeof window !== 'undefined' && window.opera) {
68
+ // e.message.indexOf("Backtrace:") > -1 -> opera
69
+ // !e.stacktrace -> opera
70
+ if (!e.stacktrace) {
71
+ return 'opera9'; // use e.message
72
+ }
73
+ // 'opera#sourceloc' in e -> opera9, opera10a
74
+ if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) {
75
+ return 'opera9'; // use e.message
76
+ }
77
+ // e.stacktrace && !e.stack -> opera10a
78
+ if (!e.stack) {
79
+ return 'opera10a'; // use e.stacktrace
80
+ }
81
+ // e.stacktrace && e.stack -> opera10b
82
+ if (e.stacktrace.indexOf("called from line") < 0) {
83
+ return 'opera10b'; // use e.stacktrace, format differs from 'opera10a'
84
+ }
85
+ // e.stacktrace && e.stack -> opera11
86
+ return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b'
87
+ } else if (e.stack) {
88
+ return 'firefox';
89
+ }
90
+ return 'other';
91
+ },
92
+
93
+ /**
94
+ * Given a context, function name, and callback function, overwrite it so that it calls
95
+ * printStackTrace() first with a callback and then runs the rest of the body.
96
+ *
97
+ * @param {Object} context of execution (e.g. window)
98
+ * @param {String} functionName to instrument
99
+ * @param {Function} function to call with a stack trace on invocation
100
+ */
101
+ instrumentFunction: function(context, functionName, callback) {
102
+ context = context || window;
103
+ var original = context[functionName];
104
+ context[functionName] = function instrumented() {
105
+ callback.call(this, printStackTrace().slice(4));
106
+ return context[functionName]._instrumented.apply(this, arguments);
107
+ };
108
+ context[functionName]._instrumented = original;
109
+ },
110
+
111
+ /**
112
+ * Given a context and function name of a function that has been
113
+ * instrumented, revert the function to it's original (non-instrumented)
114
+ * state.
115
+ *
116
+ * @param {Object} context of execution (e.g. window)
117
+ * @param {String} functionName to de-instrument
118
+ */
119
+ deinstrumentFunction: function(context, functionName) {
120
+ if (context[functionName].constructor === Function &&
121
+ context[functionName]._instrumented &&
122
+ context[functionName]._instrumented.constructor === Function) {
123
+ context[functionName] = context[functionName]._instrumented;
124
+ }
125
+ },
126
+
127
+ /**
128
+ * Given an Error object, return a formatted Array based on Chrome's stack string.
129
+ *
130
+ * @param e - Error object to inspect
131
+ * @return Array<String> of function calls, files and line numbers
132
+ */
133
+ chrome: function(e) {
134
+ var stack = (e.stack + '\n').replace(/^\S[^\(]+?[\n$]/gm, '').
135
+ replace(/^\s+(at eval )?at\s+/gm, '').
136
+ replace(/^([^\(]+?)([\n$])/gm, '{anonymous}()@$1$2').
137
+ replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}()@$1').split('\n');
138
+ stack.pop();
139
+ return stack;
140
+ },
141
+
142
+ /**
143
+ * Given an Error object, return a formatted Array based on Safari's stack string.
144
+ *
145
+ * @param e - Error object to inspect
146
+ * @return Array<String> of function calls, files and line numbers
147
+ */
148
+ safari: function(e) {
149
+ return e.stack.replace(/\[native code\]\n/m, '')
150
+ .replace(/^(?=\w+Error\:).*$\n/m, '')
151
+ .replace(/^@/gm, '{anonymous}()@')
152
+ .split('\n');
153
+ },
154
+
155
+ /**
156
+ * Given an Error object, return a formatted Array based on IE's stack string.
157
+ *
158
+ * @param e - Error object to inspect
159
+ * @return Array<String> of function calls, files and line numbers
160
+ */
161
+ ie: function(e) {
162
+ var lineRE = /^.*at (\w+) \(([^\)]+)\)$/gm;
163
+ return e.stack.replace(/at Anonymous function /gm, '{anonymous}()@')
164
+ .replace(/^(?=\w+Error\:).*$\n/m, '')
165
+ .replace(lineRE, '$1@$2')
166
+ .split('\n');
167
+ },
168
+
169
+ /**
170
+ * Given an Error object, return a formatted Array based on Firefox's stack string.
171
+ *
172
+ * @param e - Error object to inspect
173
+ * @return Array<String> of function calls, files and line numbers
174
+ */
175
+ firefox: function(e) {
176
+ return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^[\(@]/gm, '{anonymous}()@').split('\n');
177
+ },
178
+
179
+ opera11: function(e) {
180
+ var ANON = '{anonymous}', lineRE = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/;
181
+ var lines = e.stacktrace.split('\n'), result = [];
182
+
183
+ for (var i = 0, len = lines.length; i < len; i += 2) {
184
+ var match = lineRE.exec(lines[i]);
185
+ if (match) {
186
+ var location = match[4] + ':' + match[1] + ':' + match[2];
187
+ var fnName = match[3] || "global code";
188
+ fnName = fnName.replace(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, ANON);
189
+ result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
190
+ }
191
+ }
192
+
193
+ return result;
194
+ },
195
+
196
+ opera10b: function(e) {
197
+ // "<anonymous function: run>([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" +
198
+ // "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" +
199
+ // "@file://localhost/G:/js/test/functional/testcase1.html:15"
200
+ var lineRE = /^(.*)@(.+):(\d+)$/;
201
+ var lines = e.stacktrace.split('\n'), result = [];
202
+
203
+ for (var i = 0, len = lines.length; i < len; i++) {
204
+ var match = lineRE.exec(lines[i]);
205
+ if (match) {
206
+ var fnName = match[1]? (match[1] + '()') : "global code";
207
+ result.push(fnName + '@' + match[2] + ':' + match[3]);
208
+ }
209
+ }
210
+
211
+ return result;
212
+ },
213
+
214
+ /**
215
+ * Given an Error object, return a formatted Array based on Opera 10's stacktrace string.
216
+ *
217
+ * @param e - Error object to inspect
218
+ * @return Array<String> of function calls, files and line numbers
219
+ */
220
+ opera10a: function(e) {
221
+ // " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n"
222
+ // " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n"
223
+ var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
224
+ var lines = e.stacktrace.split('\n'), result = [];
225
+
226
+ for (var i = 0, len = lines.length; i < len; i += 2) {
227
+ var match = lineRE.exec(lines[i]);
228
+ if (match) {
229
+ var fnName = match[3] || ANON;
230
+ result.push(fnName + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
231
+ }
232
+ }
233
+
234
+ return result;
235
+ },
236
+
237
+ // Opera 7.x-9.2x only!
238
+ opera9: function(e) {
239
+ // " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n"
240
+ // " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n"
241
+ var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
242
+ var lines = e.message.split('\n'), result = [];
243
+
244
+ for (var i = 2, len = lines.length; i < len; i += 2) {
245
+ var match = lineRE.exec(lines[i]);
246
+ if (match) {
247
+ result.push(ANON + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
248
+ }
249
+ }
250
+
251
+ return result;
252
+ },
253
+
254
+ // Safari 5-, IE 9-, and others
255
+ other: function(curr) {
256
+ var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10;
257
+ while (curr && curr['arguments'] && stack.length < maxStackSize) {
258
+ fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
259
+ args = Array.prototype.slice.call(curr['arguments'] || []);
260
+ stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')';
261
+ curr = curr.caller;
262
+ }
263
+ return stack;
264
+ },
265
+
266
+ /**
267
+ * Given arguments array as a String, subsituting type names for non-string types.
268
+ *
269
+ * @param {Arguments} args
270
+ * @return {Array} of Strings with stringified arguments
271
+ */
272
+ stringifyArguments: function(args) {
273
+ var result = [];
274
+ var slice = Array.prototype.slice;
275
+ for (var i = 0; i < args.length; ++i) {
276
+ var arg = args[i];
277
+ if (arg === undefined) {
278
+ result[i] = 'undefined';
279
+ } else if (arg === null) {
280
+ result[i] = 'null';
281
+ } else if (arg.constructor) {
282
+ if (arg.constructor === Array) {
283
+ if (arg.length < 3) {
284
+ result[i] = '[' + this.stringifyArguments(arg) + ']';
285
+ } else {
286
+ result[i] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']';
287
+ }
288
+ } else if (arg.constructor === Object) {
289
+ result[i] = '#object';
290
+ } else if (arg.constructor === Function) {
291
+ result[i] = '#function';
292
+ } else if (arg.constructor === String) {
293
+ result[i] = '"' + arg + '"';
294
+ } else if (arg.constructor === Number) {
295
+ result[i] = arg;
296
+ }
297
+ }
298
+ }
299
+ return result.join(',');
300
+ },
301
+
302
+ sourceCache: {},
303
+
304
+ /**
305
+ * @return the text from a given URL
306
+ */
307
+ ajax: function(url) {
308
+ var req = this.createXMLHTTPObject();
309
+ if (req) {
310
+ try {
311
+ req.open('GET', url, false);
312
+ //req.overrideMimeType('text/plain');
313
+ //req.overrideMimeType('text/javascript');
314
+ req.send(null);
315
+ //return req.status == 200 ? req.responseText : '';
316
+ return req.responseText;
317
+ } catch (e) {
318
+ }
319
+ }
320
+ return '';
321
+ },
322
+
323
+ /**
324
+ * Try XHR methods in order and store XHR factory.
325
+ *
326
+ * @return <Function> XHR function or equivalent
327
+ */
328
+ createXMLHTTPObject: function() {
329
+ var xmlhttp, XMLHttpFactories = [
330
+ function() {
331
+ return new XMLHttpRequest();
332
+ }, function() {
333
+ return new ActiveXObject('Msxml2.XMLHTTP');
334
+ }, function() {
335
+ return new ActiveXObject('Msxml3.XMLHTTP');
336
+ }, function() {
337
+ return new ActiveXObject('Microsoft.XMLHTTP');
338
+ }
339
+ ];
340
+ for (var i = 0; i < XMLHttpFactories.length; i++) {
341
+ try {
342
+ xmlhttp = XMLHttpFactories[i]();
343
+ // Use memoization to cache the factory
344
+ this.createXMLHTTPObject = XMLHttpFactories[i];
345
+ return xmlhttp;
346
+ } catch (e) {
347
+ }
348
+ }
349
+ },
350
+
351
+ /**
352
+ * Given a URL, check if it is in the same domain (so we can get the source
353
+ * via Ajax).
354
+ *
355
+ * @param url <String> source url
356
+ * @return False if we need a cross-domain request
357
+ */
358
+ isSameDomain: function(url) {
359
+ return typeof location !== "undefined" && url.indexOf(location.hostname) !== -1; // location may not be defined, e.g. when running from nodejs.
360
+ },
361
+
362
+ /**
363
+ * Get source code from given URL if in the same domain.
364
+ *
365
+ * @param url <String> JS source URL
366
+ * @return <Array> Array of source code lines
367
+ */
368
+ getSource: function(url) {
369
+ // TODO reuse source from script tags?
370
+ if (!(url in this.sourceCache)) {
371
+ this.sourceCache[url] = this.ajax(url).split('\n');
372
+ }
373
+ return this.sourceCache[url];
374
+ },
375
+
376
+ guessAnonymousFunctions: function(stack) {
377
+ for (var i = 0; i < stack.length; ++i) {
378
+ var reStack = /\{anonymous\}\(.*\)@(.*)/,
379
+ reRef = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/,
380
+ frame = stack[i], ref = reStack.exec(frame);
381
+
382
+ if (ref) {
383
+ var m = reRef.exec(ref[1]);
384
+ if (m) { // If falsey, we did not get any file/line information
385
+ var file = m[1], lineno = m[2], charno = m[3] || 0;
386
+ if (file && this.isSameDomain(file) && lineno) {
387
+ var functionName = this.guessAnonymousFunction(file, lineno, charno);
388
+ stack[i] = frame.replace('{anonymous}', functionName);
389
+ }
390
+ }
391
+ }
392
+ }
393
+ return stack;
394
+ },
395
+
396
+ guessAnonymousFunction: function(url, lineNo, charNo) {
397
+ var ret;
398
+ try {
399
+ ret = this.findFunctionName(this.getSource(url), lineNo);
400
+ } catch (e) {
401
+ ret = 'getSource failed with url: ' + url + ', exception: ' + e.toString();
402
+ }
403
+ return ret;
404
+ },
405
+
406
+ findFunctionName: function(source, lineNo) {
407
+ // FIXME findFunctionName fails for compressed source
408
+ // (more than one function on the same line)
409
+ // function {name}({args}) m[1]=name m[2]=args
410
+ var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
411
+ // {name} = function ({args}) TODO args capture
412
+ // /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function(?:[^(]*)/
413
+ var reFunctionExpression = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/;
414
+ // {name} = eval()
415
+ var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
416
+ // Walk backwards in the source lines until we find
417
+ // the line which matches one of the patterns above
418
+ var code = "", line, maxLines = Math.min(lineNo, 20), m, commentPos;
419
+ for (var i = 0; i < maxLines; ++i) {
420
+ // lineNo is 1-based, source[] is 0-based
421
+ line = source[lineNo - i - 1];
422
+ commentPos = line.indexOf('//');
423
+ if (commentPos >= 0) {
424
+ line = line.substr(0, commentPos);
425
+ }
426
+ // TODO check other types of comments? Commented code may lead to false positive
427
+ if (line) {
428
+ code = line + code;
429
+ m = reFunctionExpression.exec(code);
430
+ if (m && m[1]) {
431
+ return m[1];
432
+ }
433
+ m = reFunctionDeclaration.exec(code);
434
+ if (m && m[1]) {
435
+ //return m[1] + "(" + (m[2] || "") + ")";
436
+ return m[1];
437
+ }
438
+ m = reFunctionEvaluation.exec(code);
439
+ if (m && m[1]) {
440
+ return m[1];
441
+ }
442
+ }
443
+ }
444
+ return '(?)';
445
+ }
446
+ };
data/views/index.slim ADDED
@@ -0,0 +1,19 @@
1
+ doctype html
2
+ head
3
+ script type="text/javascript" src="ie.js"
4
+ script type="text/javascript" src="jquery-1.5.2.js"
5
+ script type="text/javascript" src="stacktrace.js"
6
+ script type="text/javascript" src="jsdiff.js"
7
+ link rel="stylesheet" href="/assets/spec.css" type="text/css"
8
+ script type="text/javascript" src="/assets/Spec.js"
9
+ script type="text/javascript" src="/assets/HtmlReport.js"
10
+
11
+ body
12
+ javascript:
13
+ - @scripts.each do |file_name|
14
+ script type="text/javascript" src="/assets/#{{file_name.sub /\.coffee$/, '.js'}}"
15
+ javascript:
16
+ $(function() {
17
+ window.report = new HtmlReport(document.body);
18
+ report.run();
19
+ });