crazy_ivan 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +4 -5
- data/bin/crazy_ivan +5 -32
- data/bin/test_report2campfire +6 -1
- data/lib/crazy_ivan/process_manager.rb +48 -0
- data/lib/crazy_ivan/report_assembler.rb +35 -22
- data/lib/crazy_ivan/templates/index.html +200 -154
- data/lib/crazy_ivan/test_runner.rb +57 -9
- data/lib/crazy_ivan/vendor/open4.rb +1 -1
- data/lib/crazy_ivan/version.rb +1 -1
- data/lib/crazy_ivan.rb +18 -15
- metadata +3 -12
- data/lib/crazy_ivan/html_asset_crush.rb +0 -56
- data/lib/crazy_ivan/templates/css/ci.css +0 -34
- data/lib/crazy_ivan/templates/javascript/builder.js +0 -136
- data/lib/crazy_ivan/templates/javascript/controls.js +0 -965
- data/lib/crazy_ivan/templates/javascript/dragdrop.js +0 -974
- data/lib/crazy_ivan/templates/javascript/effects.js +0 -1123
- data/lib/crazy_ivan/templates/javascript/json-template.js +0 -544
- data/lib/crazy_ivan/templates/javascript/prototype.js +0 -4917
- data/lib/crazy_ivan/templates/javascript/scriptaculous.js +0 -68
- data/lib/crazy_ivan/templates/javascript/slider.js +0 -275
@@ -1,544 +0,0 @@
|
|
1
|
-
// Copyright (C) 2009 Andy Chu
|
2
|
-
//
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
// you may not use this file except in compliance with the License.
|
5
|
-
// You may obtain a copy of the License at
|
6
|
-
//
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
//
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
// See the License for the specific language governing permissions and
|
13
|
-
// limitations under the License.
|
14
|
-
|
15
|
-
// $Id: json-template.js 271 2009-05-31 19:35:43Z andy@chubot.org $
|
16
|
-
|
17
|
-
//
|
18
|
-
// JavaScript implementation of json-template.
|
19
|
-
//
|
20
|
-
|
21
|
-
// This is predefined in tests, shouldn't be defined anywhere else. TODO: Do
|
22
|
-
// something nicer.
|
23
|
-
var log = log || function() {};
|
24
|
-
var repr = repr || function() {};
|
25
|
-
|
26
|
-
|
27
|
-
// The "module" exported by this script is called "jsontemplate":
|
28
|
-
|
29
|
-
var jsontemplate = function() {
|
30
|
-
|
31
|
-
|
32
|
-
// Regex escaping for common metacharacters (note that JavaScript needs 2 \\ --
|
33
|
-
// no raw strings!
|
34
|
-
var META_ESCAPE = {
|
35
|
-
'{': '\\{',
|
36
|
-
'}': '\\}',
|
37
|
-
'{{': '\\{\\{',
|
38
|
-
'}}': '\\}\\}',
|
39
|
-
'[': '\\[',
|
40
|
-
']': '\\]'
|
41
|
-
};
|
42
|
-
|
43
|
-
function _MakeTokenRegex(meta_left, meta_right) {
|
44
|
-
// TODO: check errors
|
45
|
-
return new RegExp(
|
46
|
-
'(' +
|
47
|
-
META_ESCAPE[meta_left] +
|
48
|
-
'.+?' +
|
49
|
-
META_ESCAPE[meta_right] +
|
50
|
-
'\n?)', 'g'); // global for use with .exec()
|
51
|
-
}
|
52
|
-
|
53
|
-
//
|
54
|
-
// Formatters
|
55
|
-
//
|
56
|
-
|
57
|
-
function HtmlEscape(s) {
|
58
|
-
return s.replace(/&/g,'&').
|
59
|
-
replace(/>/g,'>').
|
60
|
-
replace(/</g,'<');
|
61
|
-
}
|
62
|
-
|
63
|
-
function HtmlTagEscape(s) {
|
64
|
-
return s.replace(/&/g,'&').
|
65
|
-
replace(/>/g,'>').
|
66
|
-
replace(/</g,'<').
|
67
|
-
replace(/"/g,'"');
|
68
|
-
}
|
69
|
-
|
70
|
-
// Default ToString can be changed
|
71
|
-
function ToString(s) {
|
72
|
-
return s.toString();
|
73
|
-
}
|
74
|
-
|
75
|
-
var DEFAULT_FORMATTERS = {
|
76
|
-
'html': HtmlEscape,
|
77
|
-
'htmltag': HtmlTagEscape,
|
78
|
-
'html-attr-value': HtmlTagEscape,
|
79
|
-
'str': ToString,
|
80
|
-
'raw': function(x) {return x;}
|
81
|
-
};
|
82
|
-
|
83
|
-
|
84
|
-
//
|
85
|
-
// Template implementation
|
86
|
-
//
|
87
|
-
|
88
|
-
function _ScopedContext(context, undefined_str) {
|
89
|
-
// The stack contains:
|
90
|
-
// The current context (an object).
|
91
|
-
// An iteration index. -1 means we're NOT iterating.
|
92
|
-
var stack = [{context: context, index: -1}];
|
93
|
-
|
94
|
-
return {
|
95
|
-
PushSection: function(name) {
|
96
|
-
log('PushSection '+name);
|
97
|
-
if (name === undefined || name === null) {
|
98
|
-
return null;
|
99
|
-
}
|
100
|
-
var new_context = stack[stack.length-1].context[name] || null;
|
101
|
-
stack.push({context: new_context, index: -1});
|
102
|
-
return new_context;
|
103
|
-
},
|
104
|
-
|
105
|
-
Pop: function() {
|
106
|
-
stack.pop();
|
107
|
-
},
|
108
|
-
|
109
|
-
next: function() {
|
110
|
-
var stacktop = stack[stack.length-1];
|
111
|
-
|
112
|
-
// Now we're iterating -- push a new mutable object onto the stack
|
113
|
-
if (stacktop.index == -1) {
|
114
|
-
stacktop = {context: null, index: 0};
|
115
|
-
stack.push(stacktop);
|
116
|
-
}
|
117
|
-
|
118
|
-
// The thing we're iterating over
|
119
|
-
var context_array = stack[stack.length - 2].context;
|
120
|
-
|
121
|
-
// We're already done
|
122
|
-
if (stacktop.index == context_array.length) {
|
123
|
-
stack.pop();
|
124
|
-
log('next: null');
|
125
|
-
return null; // sentinel to say that we're done
|
126
|
-
}
|
127
|
-
|
128
|
-
log('next: ' + stacktop.index);
|
129
|
-
|
130
|
-
stacktop.context = context_array[stacktop.index++];
|
131
|
-
|
132
|
-
log('next: true');
|
133
|
-
return true; // OK, we mutated the stack
|
134
|
-
},
|
135
|
-
|
136
|
-
CursorValue: function() {
|
137
|
-
return stack[stack.length - 1].context;
|
138
|
-
},
|
139
|
-
|
140
|
-
_Undefined: function(name) {
|
141
|
-
if (undefined_str === undefined) {
|
142
|
-
throw {
|
143
|
-
name: 'UndefinedVariable', message: name + ' is not defined'
|
144
|
-
};
|
145
|
-
} else {
|
146
|
-
return undefined_str;
|
147
|
-
}
|
148
|
-
},
|
149
|
-
|
150
|
-
_LookUpStack: function(name) {
|
151
|
-
var i = stack.length - 1;
|
152
|
-
while (true) {
|
153
|
-
var context = stack[i].context;
|
154
|
-
log('context '+repr(context));
|
155
|
-
|
156
|
-
if (typeof context !== 'object') {
|
157
|
-
i--;
|
158
|
-
} else {
|
159
|
-
var value = context[name];
|
160
|
-
if (value === undefined || value === null) {
|
161
|
-
i--;
|
162
|
-
} else {
|
163
|
-
return value;
|
164
|
-
}
|
165
|
-
}
|
166
|
-
if (i <= -1) {
|
167
|
-
return this._Undefined(name);
|
168
|
-
}
|
169
|
-
}
|
170
|
-
},
|
171
|
-
|
172
|
-
Lookup: function(name) {
|
173
|
-
var parts = name.split('.');
|
174
|
-
var value = this._LookUpStack(parts[0]);
|
175
|
-
if (parts.length > 1) {
|
176
|
-
for (var i=1; i<parts.length; i++) {
|
177
|
-
value = value[parts[i]];
|
178
|
-
if (value === undefined) {
|
179
|
-
return this._Undefined(parts[i]);
|
180
|
-
}
|
181
|
-
}
|
182
|
-
}
|
183
|
-
return value;
|
184
|
-
}
|
185
|
-
|
186
|
-
};
|
187
|
-
}
|
188
|
-
|
189
|
-
|
190
|
-
function _Section(section_name) {
|
191
|
-
var current_clause = [];
|
192
|
-
var statements = {'default': current_clause};
|
193
|
-
|
194
|
-
return {
|
195
|
-
section_name: section_name, // public attribute
|
196
|
-
|
197
|
-
Statements: function(clause) {
|
198
|
-
clause = clause || 'default';
|
199
|
-
return statements[clause] || [];
|
200
|
-
},
|
201
|
-
|
202
|
-
NewClause: function(clause_name) {
|
203
|
-
var new_clause = [];
|
204
|
-
statements[clause_name] = new_clause;
|
205
|
-
current_clause = new_clause;
|
206
|
-
},
|
207
|
-
|
208
|
-
Append: function(statement) {
|
209
|
-
current_clause.push(statement);
|
210
|
-
}
|
211
|
-
};
|
212
|
-
}
|
213
|
-
|
214
|
-
|
215
|
-
function _Execute(statements, context, callback) {
|
216
|
-
var i;
|
217
|
-
for (i=0; i<statements.length; i++) {
|
218
|
-
statement = statements[i];
|
219
|
-
|
220
|
-
//log('Executing ' + statement);
|
221
|
-
|
222
|
-
if (typeof(statement) == 'string') {
|
223
|
-
callback(statement);
|
224
|
-
} else {
|
225
|
-
var func = statement[0];
|
226
|
-
var args = statement[1];
|
227
|
-
func(args, context, callback);
|
228
|
-
}
|
229
|
-
}
|
230
|
-
}
|
231
|
-
|
232
|
-
|
233
|
-
function _DoSubstitute(statement, context, callback) {
|
234
|
-
log('Substituting: '+ statement.name);
|
235
|
-
var value;
|
236
|
-
if (statement.name == '@') {
|
237
|
-
value = context.CursorValue();
|
238
|
-
} else {
|
239
|
-
value = context.Lookup(statement.name);
|
240
|
-
}
|
241
|
-
|
242
|
-
// Format values
|
243
|
-
for (i=0; i<statement.formatters.length; i++) {
|
244
|
-
value = statement.formatters[i](value);
|
245
|
-
}
|
246
|
-
|
247
|
-
callback(value);
|
248
|
-
}
|
249
|
-
|
250
|
-
|
251
|
-
// for [section foo]
|
252
|
-
function _DoSection(args, context, callback) {
|
253
|
-
|
254
|
-
var block = args;
|
255
|
-
var value = context.PushSection(block.section_name);
|
256
|
-
var do_section = false;
|
257
|
-
|
258
|
-
// "truthy" values should have their sections executed.
|
259
|
-
if (value) {
|
260
|
-
do_section = true;
|
261
|
-
}
|
262
|
-
// Except: if the value is a zero-length array (which is "truthy")
|
263
|
-
if (value && value.length === 0) {
|
264
|
-
do_section = false;
|
265
|
-
}
|
266
|
-
|
267
|
-
if (do_section) {
|
268
|
-
_Execute(block.Statements(), context, callback);
|
269
|
-
context.Pop();
|
270
|
-
} else { // Empty list, None, False, etc.
|
271
|
-
context.Pop();
|
272
|
-
_Execute(block.Statements('or'), context, callback);
|
273
|
-
}
|
274
|
-
}
|
275
|
-
|
276
|
-
|
277
|
-
function _DoRepeatedSection(args, context, callback) {
|
278
|
-
var block = args;
|
279
|
-
var pushed;
|
280
|
-
|
281
|
-
if (block.section_name == '@') {
|
282
|
-
// If the name is @, we stay in the enclosing context, but assume it's a
|
283
|
-
// list, and repeat this block many times.
|
284
|
-
items = context.CursorValue();
|
285
|
-
// TODO: check that items is an array; apparently this is hard in JavaScript
|
286
|
-
//if type(items) is not list:
|
287
|
-
// raise EvaluationError('Expected a list; got %s' % type(items))
|
288
|
-
pushed = false;
|
289
|
-
} else {
|
290
|
-
items = context.PushSection(block.section_name);
|
291
|
-
pushed = true;
|
292
|
-
}
|
293
|
-
|
294
|
-
//log('ITEMS: '+showArray(items));
|
295
|
-
if (items && items.length > 0) {
|
296
|
-
// Execute the statements in the block for every item in the list.
|
297
|
-
// Execute the alternate block on every iteration except the last. Each
|
298
|
-
// item could be an atom (string, integer, etc.) or a dictionary.
|
299
|
-
|
300
|
-
var last_index = items.length - 1;
|
301
|
-
var statements = block.Statements();
|
302
|
-
var alt_statements = block.Statements('alternate');
|
303
|
-
|
304
|
-
for (var i=0; context.next() !== null; i++) {
|
305
|
-
log('_DoRepeatedSection i: ' +i);
|
306
|
-
_Execute(statements, context, callback);
|
307
|
-
if (i != last_index) {
|
308
|
-
log('ALTERNATE');
|
309
|
-
_Execute(alt_statements, context, callback);
|
310
|
-
}
|
311
|
-
}
|
312
|
-
} else {
|
313
|
-
log('OR: '+block.Statements('or'));
|
314
|
-
_Execute(block.Statements('or'), context, callback);
|
315
|
-
}
|
316
|
-
|
317
|
-
if (pushed) {
|
318
|
-
context.Pop();
|
319
|
-
}
|
320
|
-
}
|
321
|
-
|
322
|
-
|
323
|
-
var _SECTION_RE = /(repeated)?\s*(section)\s+(\S+)?/;
|
324
|
-
|
325
|
-
|
326
|
-
// TODO: The compile function could be in a different module, in case we want to
|
327
|
-
// compile on the server side.
|
328
|
-
function _Compile(template_str, options) {
|
329
|
-
var more_formatters = options.more_formatters ||
|
330
|
-
function (x) { return null; };
|
331
|
-
|
332
|
-
// We want to allow an explicit null value for default_formatter, which means
|
333
|
-
// that an error is raised if no formatter is specified.
|
334
|
-
var default_formatter;
|
335
|
-
if (options.default_formatter === undefined) {
|
336
|
-
default_formatter = 'str';
|
337
|
-
} else {
|
338
|
-
default_formatter = options.default_formatter;
|
339
|
-
}
|
340
|
-
|
341
|
-
function GetFormatter(format_str) {
|
342
|
-
var formatter = more_formatters(format_str) ||
|
343
|
-
DEFAULT_FORMATTERS[format_str];
|
344
|
-
if (formatter === undefined) {
|
345
|
-
throw {
|
346
|
-
name: 'BadFormatter',
|
347
|
-
message: format_str + ' is not a valid formatter'
|
348
|
-
};
|
349
|
-
}
|
350
|
-
return formatter;
|
351
|
-
}
|
352
|
-
|
353
|
-
var format_char = options.format_char || '|';
|
354
|
-
if (format_char != ':' && format_char != '|') {
|
355
|
-
throw {
|
356
|
-
name: 'ConfigurationError',
|
357
|
-
message: 'Only format characters : and | are accepted'
|
358
|
-
};
|
359
|
-
}
|
360
|
-
|
361
|
-
var meta = options.meta || '{}';
|
362
|
-
var n = meta.length;
|
363
|
-
if (n % 2 == 1) {
|
364
|
-
throw {
|
365
|
-
name: 'ConfigurationError',
|
366
|
-
message: meta + ' has an odd number of metacharacters'
|
367
|
-
};
|
368
|
-
}
|
369
|
-
var meta_left = meta.substring(0, n/2);
|
370
|
-
var meta_right = meta.substring(n/2, n);
|
371
|
-
|
372
|
-
var token_re = _MakeTokenRegex(meta_left, meta_right);
|
373
|
-
var current_block = _Section();
|
374
|
-
var stack = [current_block];
|
375
|
-
|
376
|
-
var strip_num = meta_left.length; // assume they're the same length
|
377
|
-
|
378
|
-
var token_match;
|
379
|
-
var last_index = 0;
|
380
|
-
|
381
|
-
while (true) {
|
382
|
-
token_match = token_re.exec(template_str);
|
383
|
-
log('match:', token_match);
|
384
|
-
if (token_match === null) {
|
385
|
-
break;
|
386
|
-
} else {
|
387
|
-
var token = token_match[0];
|
388
|
-
}
|
389
|
-
log('last_index: '+ last_index);
|
390
|
-
log('token_match.index: '+ token_match.index);
|
391
|
-
|
392
|
-
// Add the previous literal to the program
|
393
|
-
if (token_match.index > last_index) {
|
394
|
-
var tok = template_str.slice(last_index, token_match.index);
|
395
|
-
current_block.Append(tok);
|
396
|
-
log('tok: "'+ tok+'"');
|
397
|
-
}
|
398
|
-
last_index = token_re.lastIndex;
|
399
|
-
|
400
|
-
log('token0: "'+ token+'"');
|
401
|
-
|
402
|
-
var had_newline = false;
|
403
|
-
if (token.slice(-1) == '\n') {
|
404
|
-
token = token.slice(null, -1);
|
405
|
-
had_newline = true;
|
406
|
-
}
|
407
|
-
|
408
|
-
token = token.slice(strip_num, -strip_num);
|
409
|
-
|
410
|
-
if (token.charAt(0) == '#') {
|
411
|
-
continue; // comment
|
412
|
-
}
|
413
|
-
|
414
|
-
if (token.charAt(0) == '.') { // Keyword
|
415
|
-
token = token.substring(1, token.length);
|
416
|
-
|
417
|
-
var literal = {
|
418
|
-
'meta-left': meta_left,
|
419
|
-
'meta-right': meta_right,
|
420
|
-
'space': ' ',
|
421
|
-
'tab': '\t',
|
422
|
-
'newline': '\n'
|
423
|
-
}[token];
|
424
|
-
|
425
|
-
if (literal !== undefined) {
|
426
|
-
current_block.Append(literal);
|
427
|
-
continue;
|
428
|
-
}
|
429
|
-
|
430
|
-
var section_match = token.match(_SECTION_RE);
|
431
|
-
|
432
|
-
if (section_match) {
|
433
|
-
var repeated = section_match[1];
|
434
|
-
var section_name = section_match[3];
|
435
|
-
var func = repeated ? _DoRepeatedSection : _DoSection;
|
436
|
-
log('repeated ' + repeated + ' section_name ' + section_name);
|
437
|
-
|
438
|
-
var new_block = _Section(section_name);
|
439
|
-
current_block.Append([func, new_block]);
|
440
|
-
stack.push(new_block);
|
441
|
-
current_block = new_block;
|
442
|
-
continue;
|
443
|
-
}
|
444
|
-
|
445
|
-
if (token == 'alternates with') {
|
446
|
-
current_block.NewClause('alternate');
|
447
|
-
continue;
|
448
|
-
}
|
449
|
-
|
450
|
-
if (token == 'or') {
|
451
|
-
current_block.NewClause('or');
|
452
|
-
continue;
|
453
|
-
}
|
454
|
-
|
455
|
-
if (token == 'end') {
|
456
|
-
// End the block
|
457
|
-
stack.pop();
|
458
|
-
if (stack.length > 0) {
|
459
|
-
current_block = stack[stack.length-1];
|
460
|
-
} else {
|
461
|
-
throw {
|
462
|
-
name: 'TemplateSyntaxError',
|
463
|
-
message: 'Got too many {end} statements'
|
464
|
-
};
|
465
|
-
}
|
466
|
-
continue;
|
467
|
-
}
|
468
|
-
}
|
469
|
-
|
470
|
-
// A variable substitution
|
471
|
-
var parts = token.split(format_char);
|
472
|
-
var formatters;
|
473
|
-
var name;
|
474
|
-
if (parts.length == 1) {
|
475
|
-
if (default_formatter === null) {
|
476
|
-
throw {
|
477
|
-
name: 'MissingFormatter',
|
478
|
-
message: 'This template requires explicit formatters.'
|
479
|
-
};
|
480
|
-
}
|
481
|
-
// If no formatter is specified, the default is the 'str' formatter,
|
482
|
-
// which the user can define however they desire.
|
483
|
-
formatters = [GetFormatter(default_formatter)];
|
484
|
-
name = token;
|
485
|
-
} else {
|
486
|
-
formatters = [];
|
487
|
-
for (var j=1; j<parts.length; j++) {
|
488
|
-
formatters.push(GetFormatter(parts[j]));
|
489
|
-
}
|
490
|
-
name = parts[0];
|
491
|
-
}
|
492
|
-
current_block.Append(
|
493
|
-
[_DoSubstitute, { name: name, formatters: formatters}]);
|
494
|
-
if (had_newline) {
|
495
|
-
current_block.Append('\n');
|
496
|
-
}
|
497
|
-
}
|
498
|
-
|
499
|
-
// Add the trailing literal
|
500
|
-
current_block.Append(template_str.slice(last_index));
|
501
|
-
|
502
|
-
if (stack.length !== 1) {
|
503
|
-
throw {
|
504
|
-
name: 'TemplateSyntaxError',
|
505
|
-
message: 'Got too few {end} statements'
|
506
|
-
};
|
507
|
-
}
|
508
|
-
return current_block;
|
509
|
-
}
|
510
|
-
|
511
|
-
// The Template class is defined in the traditional style so that users can add
|
512
|
-
// methods by mutating the prototype attribute. TODO: Need a good idiom for
|
513
|
-
// inheritance without mutating globals.
|
514
|
-
|
515
|
-
function Template(template_str, options) {
|
516
|
-
|
517
|
-
// Add 'new' if we were not called with 'new', so prototyping works.
|
518
|
-
if(!(this instanceof Template)) {
|
519
|
-
return new Template(template_str, options);
|
520
|
-
}
|
521
|
-
|
522
|
-
this._options = options || {};
|
523
|
-
this._program = _Compile(template_str, this._options);
|
524
|
-
}
|
525
|
-
|
526
|
-
Template.prototype.render = function(data_dict, callback) {
|
527
|
-
// options.undefined_str can either be a string or undefined
|
528
|
-
var context = _ScopedContext(data_dict, this._options.undefined_str);
|
529
|
-
_Execute(this._program.Statements(), context, callback);
|
530
|
-
};
|
531
|
-
|
532
|
-
Template.prototype.expand = function(data_dict) {
|
533
|
-
var tokens = [];
|
534
|
-
this.render(data_dict, function(x) { tokens.push(x); });
|
535
|
-
return tokens.join('');
|
536
|
-
};
|
537
|
-
|
538
|
-
|
539
|
-
// We just export one name for now, the Template "class".
|
540
|
-
// We need HtmlEscape in the browser tests, so might as well export it.
|
541
|
-
|
542
|
-
return {Template: Template, HtmlEscape: HtmlEscape};
|
543
|
-
|
544
|
-
}();
|