crazy_ivan 1.1.1 → 1.2.0
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/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
|
-
}();
|