luca 0.7.6 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,649 @@
1
+ // JQuery Console 1.0
2
+ // Sun Feb 21 20:28:47 GMT 2010
3
+ //
4
+ // Copyright 2010 Chris Done, Simon David Pratt. All rights reserved.
5
+ //
6
+ // Redistribution and use in source and binary forms, with or without
7
+ // modification, are permitted provided that the following conditions
8
+ // are met:
9
+ //
10
+ // 1. Redistributions of source code must retain the above
11
+ // copyright notice, this list of conditions and the following
12
+ // disclaimer.
13
+ //
14
+ // 2. Redistributions in binary form must reproduce the above
15
+ // copyright notice, this list of conditions and the following
16
+ // disclaimer in the documentation and/or other materials
17
+ // provided with the distribution.
18
+ //
19
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
+ // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
+ // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24
+ // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25
+ // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26
+ // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
+ // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29
+ // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ // POSSIBILITY OF SUCH DAMAGE.
31
+
32
+ // TESTED ON
33
+ // Internet Explorer 6
34
+ // Opera 10.01
35
+ // Chromium 4.0.237.0 (Ubuntu build 31094)
36
+ // Firefox 3.5.8, 3.6.2 (Mac)
37
+ // Safari 4.0.5 (6531.22.7) (Mac)
38
+ // Google Chrome 5.0.375.55 (Mac)
39
+
40
+ (function($){
41
+ $.fn.console = function(config){
42
+ ////////////////////////////////////////////////////////////////////////
43
+ // Constants
44
+ // Some are enums, data types, others just for optimisation
45
+ var keyCodes = {
46
+ // left
47
+ 37: moveBackward,
48
+ // right
49
+ 39: moveForward,
50
+ // up
51
+ 38: previousHistory,
52
+ // down
53
+ 40: nextHistory,
54
+ // backspace
55
+ 8: backDelete,
56
+ // delete
57
+ 46: forwardDelete,
58
+ // end
59
+ 35: moveToEnd,
60
+ // start
61
+ 36: moveToStart,
62
+ // return
63
+ 13: commandTrigger,
64
+ // tab
65
+ 18: doNothing
66
+ };
67
+ var ctrlCodes = {
68
+ // C-a
69
+ 65: moveToStart,
70
+ // C-e
71
+ 69: moveToEnd,
72
+ // C-d
73
+ 68: forwardDelete,
74
+ // C-n
75
+ 78: nextHistory,
76
+ // C-p
77
+ 80: previousHistory,
78
+ // C-b
79
+ 66: moveBackward,
80
+ // C-f
81
+ 70: moveForward,
82
+ // C-k
83
+ 75: deleteUntilEnd
84
+ };
85
+ var altCodes = {
86
+ // M-f
87
+ 70: moveToNextWord,
88
+ // M-b
89
+ 66: moveToPreviousWord,
90
+ // M-d
91
+ 68: deleteNextWord
92
+ };
93
+ var cursor = '<span class="jquery-console-cursor">&nbsp;</span>';
94
+
95
+ ////////////////////////////////////////////////////////////////////////
96
+ // Globals
97
+ var container = $(this);
98
+ var inner = $('<div class="jquery-console-inner"></div>');
99
+ // erjiang: changed this from a text input to a textarea so we
100
+ // can get pasted newlines
101
+ var typer = $('<textarea class="jquery-console-typer"></textarea>');
102
+ // Prompt
103
+ var promptBox;
104
+ var prompt;
105
+ var promptLabel = config && config.promptLabel? config.promptLabel : "> ";
106
+ var continuedPromptLabel = config && config.continuedPromptLabel?
107
+ config.continuedPromptLabel : "> ";
108
+ var column = 0;
109
+ var promptText = '';
110
+ var restoreText = '';
111
+ var continuedText = '';
112
+ // Prompt history stack
113
+ var history = [];
114
+ var ringn = 0;
115
+ // For reasons unknown to The Sword of Michael himself, Opera
116
+ // triggers and sends a key character when you hit various
117
+ // keys like PgUp, End, etc. So there is no way of knowing
118
+ // when a user has typed '#' or End. My solution is in the
119
+ // typer.keydown and typer.keypress functions; I use the
120
+ // variable below to ignore the keypress event if the keydown
121
+ // event succeeds.
122
+ var cancelKeyPress = 0;
123
+ // When this value is false, the prompt will not respond to input
124
+ var acceptInput = true;
125
+ // When this value is true, the command has been canceled
126
+ var cancelCommand = false;
127
+
128
+ // External exports object
129
+ var extern = {};
130
+
131
+ ////////////////////////////////////////////////////////////////////////
132
+ // Main entry point
133
+ (function(){
134
+ container.append(inner);
135
+ inner.append(typer);
136
+ typer.css({position:'absolute',top:0,left:'-9999px'});
137
+ if (config.welcomeMessage)
138
+ message(config.welcomeMessage,'jquery-console-welcome');
139
+ newPromptBox();
140
+ if (config.autofocus) {
141
+ inner.addClass('jquery-console-focus');
142
+ typer.focus();
143
+ setTimeout(function(){
144
+ inner.addClass('jquery-console-focus');
145
+ typer.focus();
146
+ },100);
147
+ }
148
+ extern.inner = inner;
149
+ extern.typer = typer;
150
+ extern.scrollToBottom = scrollToBottom;
151
+ })();
152
+
153
+ ////////////////////////////////////////////////////////////////////////
154
+ // Reset terminal
155
+ extern.reset = function(){
156
+ var welcome = (typeof config.welcomeMessage != 'undefined');
157
+ inner.parent().fadeOut(function(){
158
+ inner.find('div').each(function(){
159
+ if (!welcome) {
160
+ $(this).remove();
161
+ } else {
162
+ welcome = false;
163
+ }
164
+ });
165
+ newPromptBox();
166
+ inner.parent().fadeIn(function(){
167
+ inner.addClass('jquery-console-focus');
168
+ typer.focus();
169
+ });
170
+ });
171
+ };
172
+
173
+ ////////////////////////////////////////////////////////////////////////
174
+ // Reset terminal
175
+ extern.notice = function(msg,style){
176
+ var n = $('<div class="notice"></div>').append($('<div></div>').text(msg))
177
+ .css({visibility:'hidden'});
178
+ container.append(n);
179
+ var focused = true;
180
+ if (style=='fadeout')
181
+ setTimeout(function(){
182
+ n.fadeOut(function(){
183
+ n.remove();
184
+ });
185
+ },4000);
186
+ else if (style=='prompt') {
187
+ var a = $('<br/><div class="action"><a href="javascript:">OK</a><div class="clear"></div></div>');
188
+ n.append(a);
189
+ focused = false;
190
+ a.click(function(){ n.fadeOut(function(){ n.remove();inner.css({opacity:1}) }); });
191
+ }
192
+ var h = n.height();
193
+ n.css({height:'0px',visibility:'visible'})
194
+ .animate({height:h+'px'},function(){
195
+ if (!focused) inner.css({opacity:0.5});
196
+ });
197
+ n.css('cursor','default');
198
+ return n;
199
+ };
200
+
201
+ ////////////////////////////////////////////////////////////////////////
202
+ // Make a new prompt box
203
+ function newPromptBox() {
204
+ column = 0;
205
+ promptText = '';
206
+ ringn = 0; // Reset the position of the history ring
207
+ enableInput();
208
+ promptBox = $('<div class="jquery-console-prompt-box"></div>');
209
+ var label = $('<span class="jquery-console-prompt-label"></span>');
210
+ var labelText = extern.continuedPrompt? continuedPromptLabel : promptLabel;
211
+ promptBox.append(label.text(labelText).show());
212
+ label.html(label.html().replace(' ','&nbsp;'));
213
+ prompt = $('<span class="jquery-console-prompt"></span>');
214
+ promptBox.append(prompt);
215
+ inner.append(promptBox);
216
+ updatePromptDisplay();
217
+ };
218
+
219
+ ////////////////////////////////////////////////////////////////////////
220
+ // Handle setting focus
221
+ container.click(function(){
222
+ inner.addClass('jquery-console-focus');
223
+ inner.removeClass('jquery-console-nofocus');
224
+ typer.focus();
225
+ scrollToBottom();
226
+ return false;
227
+ });
228
+
229
+ ////////////////////////////////////////////////////////////////////////
230
+ // Handle losing focus
231
+ typer.blur(function(){
232
+ inner.removeClass('jquery-console-focus');
233
+ inner.addClass('jquery-console-nofocus');
234
+ });
235
+
236
+ ////////////////////////////////////////////////////////////////////////
237
+ // Bind to the paste event of the input box so we know when we
238
+ // get pasted data
239
+ typer.bind('paste', function(e) {
240
+ // wipe typer input clean just in case
241
+ typer.val("");
242
+ // this timeout is required because the onpaste event is
243
+ // fired *before* the text is actually pasted
244
+ setTimeout(function() {
245
+ typer.consoleInsert(typer.val());
246
+ typer.val("");
247
+ }, 0);
248
+ });
249
+
250
+ ////////////////////////////////////////////////////////////////////////
251
+ // Handle key hit before translation
252
+ // For picking up control characters like up/left/down/right
253
+
254
+ typer.keydown(function(e){
255
+ cancelKeyPress = 0;
256
+ var keyCode = e.keyCode;
257
+ // C-c: cancel the execution
258
+ if(e.ctrlKey && keyCode == 67) {
259
+ cancelKeyPress = keyCode;
260
+ cancelExecution();
261
+ return false;
262
+ }
263
+ if (acceptInput) {
264
+ if (keyCode in keyCodes) {
265
+ cancelKeyPress = keyCode;
266
+ (keyCodes[keyCode])();
267
+ return false;
268
+ } else if (e.ctrlKey && keyCode in ctrlCodes) {
269
+ cancelKeyPress = keyCode;
270
+ (ctrlCodes[keyCode])();
271
+ return false;
272
+ } else if (e.altKey && keyCode in altCodes) {
273
+ cancelKeyPress = keyCode;
274
+ (altCodes[keyCode])();
275
+ return false;
276
+ }
277
+ }
278
+ });
279
+
280
+ ////////////////////////////////////////////////////////////////////////
281
+ // Handle key press
282
+ typer.keypress(function(e){
283
+ var keyCode = e.keyCode || e.which;
284
+ if (isIgnorableKey(e)) {
285
+ return false;
286
+ }
287
+ // // C-v: don't insert on paste event
288
+ if ((e.ctrlKey || e.metaKey) && String.fromCharCode(keyCode).toLowerCase() == 'v') {
289
+ return true;
290
+ }
291
+ if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){
292
+ if (cancelKeyPress) return false;
293
+ if (typeof config.charInsertTrigger == 'undefined' ||
294
+ (typeof config.charInsertTrigger == 'function' &&
295
+ config.charInsertTrigger(keyCode,promptText)))
296
+ typer.consoleInsert(keyCode);
297
+ }
298
+ if ($.browser.webkit) return false;
299
+ });
300
+
301
+ function isIgnorableKey(e) {
302
+ // for now just filter alt+tab that we receive on some platforms when
303
+ // user switches windows (goes away from the browser)
304
+ return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey);
305
+ };
306
+
307
+ ////////////////////////////////////////////////////////////////////////
308
+ // Rotate through the command history
309
+ function rotateHistory(n){
310
+ if (history.length == 0) return;
311
+ ringn += n;
312
+ if (ringn < 0) ringn = history.length;
313
+ else if (ringn > history.length) ringn = 0;
314
+ var prevText = promptText;
315
+ if (ringn == 0) {
316
+ promptText = restoreText;
317
+ } else {
318
+ promptText = history[ringn - 1];
319
+ }
320
+ if (config.historyPreserveColumn) {
321
+ if (promptText.length < column + 1) {
322
+ column = promptText.length;
323
+ } else if (column == 0) {
324
+ column = promptText.length;
325
+ }
326
+ } else {
327
+ column = promptText.length;
328
+ }
329
+ updatePromptDisplay();
330
+ };
331
+
332
+ function previousHistory() {
333
+ rotateHistory(-1);
334
+ };
335
+
336
+ function nextHistory() {
337
+ rotateHistory(1);
338
+ };
339
+
340
+ // Add something to the history ring
341
+ function addToHistory(line){
342
+ history.push(line);
343
+ restoreText = '';
344
+ };
345
+
346
+ // Delete the character at the current position
347
+ function deleteCharAtPos(){
348
+ if (column < promptText.length){
349
+ promptText =
350
+ promptText.substring(0,column) +
351
+ promptText.substring(column+1);
352
+ restoreText = promptText;
353
+ return true;
354
+ } else return false;
355
+ };
356
+
357
+ function backDelete() {
358
+ if (moveColumn(-1)){
359
+ deleteCharAtPos();
360
+ updatePromptDisplay();
361
+ }
362
+ };
363
+
364
+ function forwardDelete() {
365
+ if (deleteCharAtPos())
366
+ updatePromptDisplay();
367
+ };
368
+
369
+ function deleteUntilEnd() {
370
+ while(deleteCharAtPos()) {
371
+ updatePromptDisplay();
372
+ }
373
+ };
374
+
375
+ function deleteNextWord() {
376
+ // A word is defined within this context as a series of alphanumeric
377
+ // characters.
378
+ // Delete up to the next alphanumeric character
379
+ while(column < promptText.length &&
380
+ !isCharAlphanumeric(promptText[column])) {
381
+ deleteCharAtPos();
382
+ updatePromptDisplay();
383
+ }
384
+ // Then, delete until the next non-alphanumeric character
385
+ while(column < promptText.length &&
386
+ isCharAlphanumeric(promptText[column])) {
387
+ deleteCharAtPos();
388
+ updatePromptDisplay();
389
+ }
390
+ };
391
+
392
+ ////////////////////////////////////////////////////////////////////////
393
+ // Validate command and trigger it if valid, or show a validation error
394
+ function commandTrigger() {
395
+ var line = promptText;
396
+ if (typeof config.commandValidate == 'function') {
397
+ var ret = config.commandValidate(line);
398
+ if (ret == true || ret == false) {
399
+ if (ret) {
400
+ handleCommand();
401
+ }
402
+ } else {
403
+ commandResult(ret,"jquery-console-message-error");
404
+ }
405
+ } else {
406
+ handleCommand();
407
+ }
408
+ };
409
+
410
+ // Scroll to the bottom of the view
411
+ function scrollToBottom() {
412
+ if (jQuery.fn.jquery > "1.6") {
413
+ inner.prop({ scrollTop: inner.prop("scrollHeight") });
414
+ }
415
+ else {
416
+ inner.attr({ scrollTop: inner.attr("scrollHeight") });
417
+ }
418
+ };
419
+
420
+ function cancelExecution() {
421
+ if(typeof config.cancelHandle == 'function') {
422
+ config.cancelHandle();
423
+ }
424
+ }
425
+
426
+ ////////////////////////////////////////////////////////////////////////
427
+ // Handle a command
428
+ function handleCommand() {
429
+ if (typeof config.commandHandle == 'function') {
430
+ disableInput();
431
+ addToHistory(promptText);
432
+ var text = promptText;
433
+ if (extern.continuedPrompt) {
434
+ if (continuedText)
435
+ continuedText += '\n' + promptText;
436
+ else continuedText = promptText;
437
+ } else continuedText = undefined;
438
+ if (continuedText) text = continuedText;
439
+ var ret = config.commandHandle(text,function(msgs){
440
+ commandResult(msgs);
441
+ });
442
+ if (extern.continuedPrompt && !continuedText)
443
+ continuedText = promptText;
444
+ if (typeof ret == 'boolean') {
445
+ if (ret) {
446
+ // Command succeeded without a result.
447
+ commandResult();
448
+ } else {
449
+ commandResult('Command failed.',
450
+ "jquery-console-message-error");
451
+ }
452
+ } else if (typeof ret == "string") {
453
+ commandResult(ret,"jquery-console-message-success");
454
+ } else if (typeof ret == 'object' && ret.length) {
455
+ commandResult(ret);
456
+ } else if (extern.continuedPrompt) {
457
+ commandResult();
458
+ }
459
+ }
460
+ };
461
+
462
+ ////////////////////////////////////////////////////////////////////////
463
+ // Disable input
464
+ function disableInput() {
465
+ acceptInput = false;
466
+ };
467
+
468
+ // Enable input
469
+ function enableInput() {
470
+ acceptInput = true;
471
+ }
472
+
473
+ ////////////////////////////////////////////////////////////////////////
474
+ // Reset the prompt in invalid command
475
+ function commandResult(msg,className) {
476
+ column = -1;
477
+ updatePromptDisplay();
478
+ if (typeof msg == 'string') {
479
+ message(msg,className);
480
+ } else if ($.isArray(msg)) {
481
+ for (var x in msg) {
482
+ var ret = msg[x];
483
+ message(ret.msg,ret.className);
484
+ }
485
+ } else { // Assume it's a DOM node or jQuery object.
486
+ inner.append(msg);
487
+ }
488
+ newPromptBox();
489
+ };
490
+
491
+ ////////////////////////////////////////////////////////////////////////
492
+ // Display a message
493
+ function message(msg,className) {
494
+ var mesg = $('<div class="jquery-console-message"></div>');
495
+ if (className) mesg.addClass(className);
496
+ mesg.filledText(msg).hide();
497
+ inner.append(mesg);
498
+ mesg.show();
499
+ };
500
+
501
+ ////////////////////////////////////////////////////////////////////////
502
+ // Handle normal character insertion
503
+ // data can either be a number, which will be interpreted as the
504
+ // numeric value of a single character, or a string
505
+ typer.consoleInsert = function(data){
506
+ // TODO: remove redundant indirection
507
+ var text = isNaN(data) ? data : String.fromCharCode(data);
508
+ var before = promptText.substring(0,column);
509
+ var after = promptText.substring(column);
510
+ promptText = before + text + after;
511
+ moveColumn(text.length);
512
+ restoreText = promptText;
513
+ updatePromptDisplay();
514
+ };
515
+
516
+ ////////////////////////////////////////////////////////////////////////
517
+ // Move to another column relative to this one
518
+ // Negative means go back, positive means go forward.
519
+ function moveColumn(n){
520
+ if (column + n >= 0 && column + n <= promptText.length){
521
+ column += n;
522
+ return true;
523
+ } else return false;
524
+ };
525
+
526
+ function moveForward() {
527
+ if(moveColumn(1)) {
528
+ updatePromptDisplay();
529
+ return true;
530
+ }
531
+ return false;
532
+ };
533
+
534
+ function moveBackward() {
535
+ if(moveColumn(-1)) {
536
+ updatePromptDisplay();
537
+ return true;
538
+ }
539
+ return false;
540
+ };
541
+
542
+ function moveToStart() {
543
+ if (moveColumn(-column))
544
+ updatePromptDisplay();
545
+ };
546
+
547
+ function moveToEnd() {
548
+ if (moveColumn(promptText.length-column))
549
+ updatePromptDisplay();
550
+ };
551
+
552
+ function moveToNextWord() {
553
+ while(column < promptText.length &&
554
+ !isCharAlphanumeric(promptText[column]) &&
555
+ moveForward()) {
556
+ }
557
+ while(column < promptText.length &&
558
+ isCharAlphanumeric(promptText[column]) &&
559
+ moveForward()) {
560
+ }
561
+ };
562
+
563
+ function moveToPreviousWord() {
564
+ // Move backward until we find the first alphanumeric
565
+ while(column -1 >= 0 &&
566
+ !isCharAlphanumeric(promptText[column-1]) &&
567
+ moveBackward()) {
568
+ }
569
+ // Move until we find the first non-alphanumeric
570
+ while(column -1 >= 0 &&
571
+ isCharAlphanumeric(promptText[column-1]) &&
572
+ moveBackward()) {
573
+ }
574
+ };
575
+
576
+ function isCharAlphanumeric(charToTest) {
577
+ if(typeof charToTest == 'string') {
578
+ var code = charToTest.charCodeAt();
579
+ return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) ||
580
+ (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) ||
581
+ (code >= '0'.charCodeAt() && code <= '9'.charCodeAt());
582
+ }
583
+ return false;
584
+ };
585
+
586
+ function doNothing() {};
587
+
588
+ extern.promptText = function(text){
589
+ if (text) {
590
+ promptText = text;
591
+ column = promptText.length;
592
+ updatePromptDisplay();
593
+ }
594
+ return promptText;
595
+ };
596
+
597
+ ////////////////////////////////////////////////////////////////////////
598
+ // Update the prompt display
599
+ function updatePromptDisplay(){
600
+ var line = promptText;
601
+ var html = '';
602
+ if (column > 0 && line == ''){
603
+ // When we have an empty line just display a cursor.
604
+ html = cursor;
605
+ } else if (column == promptText.length){
606
+ // We're at the end of the line, so we need to display
607
+ // the text *and* cursor.
608
+ html = htmlEncode(line) + cursor;
609
+ } else {
610
+ // Grab the current character, if there is one, and
611
+ // make it the current cursor.
612
+ var before = line.substring(0, column);
613
+ var current = line.substring(column,column+1);
614
+ if (current){
615
+ current =
616
+ '<span class="jquery-console-cursor">' +
617
+ htmlEncode(current) +
618
+ '</span>';
619
+ }
620
+ var after = line.substring(column+1);
621
+ html = htmlEncode(before) + current + htmlEncode(after);
622
+ }
623
+ prompt.html(html);
624
+ scrollToBottom();
625
+ };
626
+
627
+ // Simple HTML encoding
628
+ // Simply replace '<', '>' and '&'
629
+ // TODO: Use jQuery's .html() trick, or grab a proper, fast
630
+ // HTML encoder.
631
+ function htmlEncode(text){
632
+ return (
633
+ text.replace(/&/g,'&amp;')
634
+ .replace(/</g,'&lt;')
635
+ .replace(/</g,'&lt;')
636
+ .replace(/ /g,'&nbsp;')
637
+ .replace(/\n/g,'<br />')
638
+ );
639
+ };
640
+
641
+ return extern;
642
+ };
643
+ // Simple utility for printing messages
644
+ $.fn.filledText = function(txt){
645
+ $(this).text(txt);
646
+ $(this).html($(this).html().replace(/\n/g,'<br/>'));
647
+ return this;
648
+ };
649
+ })(jQuery);
@@ -3,7 +3,7 @@
3
3
  _.mixin(_.string);
4
4
 
5
5
  window.Luca = {
6
- VERSION: "0.7.6",
6
+ VERSION: "0.7.7",
7
7
  core: {},
8
8
  containers: {},
9
9
  components: {},