diff --git a/lib/client/editor.js b/lib/client/editor.js index 5af07bbf..7f3ffefc 100644 --- a/lib/client/editor.js +++ b/lib/client/editor.js @@ -3,7 +3,7 @@ var CloudCommander, CloudFunc, CodeMirror; * and later will be Ace */ CloudCommander.Editor = {}; -CloudCommander.Editor.CodeMirror = { +CloudCommander.Editor.CodeMirror = { load: (function(pParent){ /* function loads CodeMirror js and css files */ /* function shows editor */ diff --git a/lib/client/terminal.js b/lib/client/terminal.js new file mode 100644 index 00000000..b5744602 --- /dev/null +++ b/lib/client/terminal.js @@ -0,0 +1,76 @@ +var CloudCommander, jqconsole; +/* object contains terminal jqconsole + */ +CloudCommander.Terminal = {}; +CloudCommander.Terminal.jqconsole = { + load: function(pParent){ + CloudCommander.cssLoad({ + src : 'lib/client/terminal/ansi.css' + }); + + CloudCommander.jsload({ + src : 'lib/client/terminal/jqconsole-2.7.min.js', + func : function(){ + pParent.init(); + } + }); + }, + + init: function(){ + $(function() { + // Creating the console. + var header = 'Welcome to JQConsole!\n' + + 'Use jqconsole.Write() to write and ' + + 'jqconsole.Input() to read.\n'; + window.jqconsole = $('#console').jqconsole(header, 'JS> '); + + // Abort prompt on Ctrl+Z. + jqconsole.RegisterShortcut('Z', function() { + jqconsole.AbortPrompt(); + handler(); + }); + + // Move to line start Ctrl+A. + jqconsole.RegisterShortcut('A', function() { + jqconsole.MoveToStart(); + handler(); + }); + + // Move to line end Ctrl+E. + jqconsole.RegisterShortcut('E', function() { + jqconsole.MoveToEnd(); + handler(); + }); + + jqconsole.RegisterMatching('{', '}', 'brace'); + jqconsole.RegisterMatching('(', ')', 'paran'); + jqconsole.RegisterMatching('[', ']', 'bracket'); + // Handle a command. + var handler = function(command) { + if (command) { + try { + jqconsole.Write('==> ' + window.eval(command) + '\n'); + } catch (e) { + jqconsole.Write('ERROR: ' + e.message + '\n'); + } + } + jqconsole.Prompt(true, handler, function(command) { + // Continue line if can't compile the command. + try { + Function(command); + } catch (e) { + if (/[\[\{\(]$/.test(command)) { + return 1; + } else { + return 0; + } + } + return false; + }); + }; + + // Initiate the first prompt. + handler(); + }); + } +}; \ No newline at end of file diff --git a/lib/client/terminal/README-v1.md b/lib/client/terminal/README-v1.md new file mode 100644 index 00000000..e9380537 --- /dev/null +++ b/lib/client/terminal/README-v1.md @@ -0,0 +1,107 @@ +#jq-console + +A simple jQuery terminal plugin written in CoffeeScript. + +This project was spawned because of our need for a simple web terminal plugin +for the jsREPL project. It +tries to simulate a low level terminal by providing (almost) raw input/output +streams as well as input and output states. + +##Tested Browsers + +The plugin has been tested on the following browsers: + +* IE 8 +* Chrome 10 +* Firefox 3.6 +* Safari 4 +* Opera 10 + +##Getting Started + +###Instantiating + + var jqconsole = $(div).jqconsole(welcomeString); + +* `div` is the div element or selector. +* `welcomeString` is the string to be shown when the terminal is first rendered. + +###Configuration + +There isn't much initial configuration needed, because the user must supply +options and callbacks with each state change. The only config method is used to +create custom shortcuts: + +* `jqconsole.RegisterShortcut`: Registers a callback for a keyboard shortcut. + It takes two arguments: + + * `int keyCode`: The code of the key pressing which (when Ctrl is held) will + trigger this shortcut. + + * `function callback`: A function called when the shortcut is pressed; + "this" will point to the JQConsole object. + + Example: + + // Ctrl+R: resets the console. + jqconsole.RegisterShortCut(82, function() { + this.Reset(); + }); + +##Usage + +Unlike most terminal plugins, jq-console gives you complete low-level control +over the execution; you have to call the appropriate methods to start input +or output: + +* `jqconsole.Input`: Asks user for input. It takes three arguments: + + * `bool history_enabled`: Whether this input should use history. If true, + the user can select the input from history, and their input will also be + added as a new history item. + + * `function result_callback`: A function called with the user's input when + the user presses Enter and the input operation is complete. + + * `function multiline_callback`: If specified, this function is called when + the user presses Enter to check whether the input should continue to the + next line. If this function returns a falsy value, the input operation + is completed. Otherwise, input continues and the cursor moves to the next + line. + + Example: + + jqconsole.Input(true, function(input) { + alert(input); + }, function (input) { + return /\\$/.test('asdasd \\'); + }); + +* `jqconsole.Write`: Writes the given text to the console in a ``, with an + optional class. This is used for output and writing prompt labels. It takes + two arguments: + + * `string text`: The text to write. + + * `string cls`: The class to give the span containing the text. Optional. + + Examples: + + jqconsole.Write('>>>', 'prompt') + jqconsole.Write(output, 'output') + jqconsole.Write(err.message, 'error') + +* `jqconsole.SetPromptText` Sets the text currently in the input prompt. Takes + only one parameter: + + * `string text`: The text to put in the prompt. + + Examples: + + jqconsole.SetPromptText('ls') + jqconsole.SetPromptText('print [i ** 2 for i in range(10)]') + +##Contributors + +[Max Shawabkeh](http://max99x.com/) +[Amjad Masad](http://twitter.com/amjad_masad) diff --git a/lib/client/terminal/README.md b/lib/client/terminal/README.md new file mode 100644 index 00000000..cf486c85 --- /dev/null +++ b/lib/client/terminal/README.md @@ -0,0 +1,678 @@ +#jq-console + +A jQuery terminal plugin written in CoffeeScript. + +This project was spawned because of our need for a simple web terminal plugin +for the repl.it project. It tries to simulate a low level terminal by providing (almost) +raw input/output streams as well as input and output states. + +Version 2.0 adds baked-in support for rich multi-line prompting and operation +queueing. + +NOTE: This info is for jq-console v2.0. For jq-console v1.0 see README-v1.md. + + +##Tested Browsers + +The plugin has been tested on the following browsers: + +* IE 9-10 +* Chrome 10-14 +* Firefox 3.6-6 +* Opera 11 + + +##Getting Started + +###Echo example + +```css + /* The console container element */ + #console { + position: absolute; + width: 400px; + height: 500px; + background-color:black; + } + /* The inner console element. */ + .jqconsole { + padding: 10px; + } + /* The cursor. */ + .jqconsole-cursor { + background-color: gray; + } + /* The cursor color when the console looses focus. */ + .jqconsole-blurred .jqconsole-cursor { + background-color: #666; + } + /* The current prompt text color */ + .jqconsole-prompt { + color: #0d0; + } + /* The command history */ + .jqconsole-old-prompt { + color: #0b0; + font-weight: normal; + } + /* The text color when in input mode. */ + .jqconsole-input { + color: #dd0; + } + /* Previously entered input. */ + .jqconsole-old-input { + color: #bb0; + font-weight: normal; + } + /* The text color of the output. */ + .jqconsole-output { + color: white; + } +``` + +```html +
+ + + +``` + + +###Instantiating + +```javascript + $(div).jqconsole(welcomeString, promptLabel, continueLabel); +``` + +* `div` is the div element or selector. Note that this element must be + explicity sized and positioned `absolute` or `relative`. +* `welcomeString` is the string to be shown when the terminal is first rendered. +* `promptLabel` is the label to be shown before the input when using Prompt(). +* `continueLabel` is the label to be shown before the continued lines of the + input when using Prompt(). + +##Configuration + +There isn't much initial configuration needed, because the user must supply +options and callbacks with each state change. There are a few config methods +provided to create custom shortcuts and change indentation width: + +###jqconsole.RegisterShortcut +Registers a callback for a keyboard shortcut. +Takes two arguments: + + * __(int|string)__ *keyCode*: The code of the key pressing which (when Ctrl is + held) will trigger this shortcut. If a string is provided, the ASCII code + of the first character is taken. + + * __function__ *callback*: A function called when the shortcut is pressed; + "this" will point to the JQConsole object. + + + Example: + + // Ctrl+R: resets the console. + jqconsole.RegisterShortcut('R', function() { + this.Reset(); + }); + +###jqconsole.SetIndentWidth +Sets the number of spaces inserted when indenting and removed when unindenting. +Takes one argument: + + * __int__ *width*: The code of the key pressing which (when Ctrl is held) will + trigger this shortcut. + + + Example: + + // Sets the indent width to 4 spaces. + jqconsole.SetIndentWidth(4); + +###jqconsole.RegisterMatching +Registers an opening and closing characters to match and wraps each of the +opening and closing characters with a span with the specified class. +Takes one parameters: + + * __char__ *open*: The opening character of a "block". + * __char__ *close*: The closing character of a "block". + * __string__ *class*: The css class that is applied to the matched characters. + + + Example: + + jqconsole.RegisterMatching('{', '}', 'brackets'); + +##Usage + +Unlike most terminal plugins, jq-console gives you complete low-level control +over the execution; you have to call the appropriate methods to start input +or output: + +###jqconsole.Input: +Asks user for input. If another input or prompt operation is currently underway, +the new input operation is enqueued and will be called when the current +operation and all previously enqueued operations finish. Takes one argument: + + * __function__ *input_callback*: A function called with the user's input when + the user presses Enter and the input operation is complete. + + + Example: + + // Echo the input. + jqconsole.Input(function(input) { + jqconsole.Write(input); + }); + + +###jqconsole.Prompt +Asks user for input. If another input or prompt operation is currently underway +the new prompt operation is enqueued and will be called when the current +peration and all previously enqueued operations finish. Takes three arguments: + + * __bool__ *history_enabled*: Whether this input should use history. If true, + the user can select the input from history, and their input will also be + added as a new history item. + + * __function__ *result_callback*: A function called with the user's input when + the user presses Enter and the prompt operation is complete. + + * __function__ *multiline_callback*: If specified, this function is called when + the user presses Enter to check whether the input should continue to the + next line. The function must return one of the following values: + + * `false`: the input operation is completed. + + * `0`: the input continues to the next line with the current indent. + + * `N` (int): the input continues to the next line, and the current + indent is adjusted by `N`, e.g. `-2` to unindent two levels. + + + * __bool__ *async_multiline*: Whether the multiline callback function should + be treated as an asynchronous operation and be passed a continuation + function that should be called with one of the return values mentioned + above: `false`/`0`/`N`. + + + Example: + + jqconsole.Prompt(true, function(input) { + // Alert the user with the command. + alert(input); + }, function (input) { + // Continue if the last character is a backslash. + return /\\$/.test(input); + }); + +###jqconsole.AbortPrompt +Aborts the current prompt operation and returns to output mode or the next +queued input/prompt operation. Takes no arguments. + + Example: + + jqconsole.Prompt(true, function(input) { + alert(input); + }); + // Give the user 2 seconds to enter the command. + setTimeout(function() { + jqconsole.AbortPrompt(); + }, 2000); + +###jqconsole.Write +Writes the given text to the console in a ``, with an +optional class. If a prompt is currently being shown, the text is inserted +before it. Takes two arguments: + + * __string__ *text*: The text to write. + + * __string__ *cls*: The class to give the span containing the text. Optional. + + * __bool__ *escape*: Whether the text to write should be html escaped. + Optional, defaults to true. + + + Examples: + + jqconsole.Write(output, 'my-output-class') + jqconsole.Write(err.message, 'my-error-class') + + +###jqconsole.SetPromptText +Sets the text currently in the input prompt. Takes one parameter: + + * __string__ *text*: The text to put in the prompt. + + Examples: + + jqconsole.SetPromptText('ls') + jqconsole.SetPromptText('print [i ** 2 for i in range(10)]') + + +###jqconsole.ClearPromptText +Clears all the text currently in the input prompt. Takes one parameter: + + * __bool__ *clear_label*: If specified and true, also clears the main prompt + label (e.g. ">>>"). + + + Example: + + jqconsole.ClearPromptText() + + +###jqconsole.GetPromptText +Returns the contents of the prompt. Takes one parameter: + + * __bool__ *full*: If specified and true, also includes the prompt labels + (e.g. ">>>"). + + + Examples: + + var currentCommand = jqconsole.GetPromptText() + var logEntry = jqconsole.GetPromptText(true) + + +###jqconsole.Reset +Resets the console to its initial state, cancelling all current and pending +operations. Takes no parameters. + + Example: + + jqconsole.Reset() + + +###jqconsole.GetColumn +Returns the 0-based number of the column on which the cursor currently is. +Takes no parameters. + + Example: + + // Show the current line and column in a status area. + $('#status').text(jqconsole.GetLine() + ', ' + jqconsole.GetColumn()) + + +###jqconsole.GetLine +Returns the 0-based number of the line on which the cursor currently is. +Takes no parameters. + + Example: + + // Show the current line and column in a status area. + $('#status').text(jqconsole.GetLine() + ', ' + jqconsole.GetColumn()) + +###jqconsole.Focus +Forces the focus onto the console so events can be captured. +Takes no parameters. + + Example: + + // Redirect focus to the console whenever the user clicks anywhere. + $(window).click(function() { + jqconsole.Focus(); + }) + + +###jqconsole.GetIndentWidth +Returns the number of spaces inserted when indenting. Takes no parameters. + + Example: + + jqconsole.SetIndentWidth(4); + console.assert(jqconsole.GetIndentWidth() == 4); + + +###jqconsole.UnRegisterMatching +Deletes a certain matching settings set by `jqconsole.RegisterMatching`. +Takes two paramaters: + + * __char__ *open*: The opening character of a "block". + * __char__ *close*: The closing character of a "block". + + + Example: + + jqconsole.UnRegisterMatching('{', '}'); + + +###jqconsole.Dump +Returns the text content of the console. + +###jqconsole.GetState +Returns the current state of the console. Could be one of the following: + + * Input: `"input"` + * Output: `"output"` + * Prompt: `"prompt"` + + + Example: + + jqconsole.GetState(); //output + + +###jqconsole.MoveToStart +Moves the cursor to the start of the current line. +Takes one parameter: + + * __bool__ *all_lines*: If true moves the cursor to the beginning of the first + line in the current prompt. Defaults to false. + + + Example: + + // Move to line start Ctrl+A. + jqconsole.RegisterShortcut('A', function() { + jqconsole.MoveToStart(); + handler(); + }); + + +###jqconsole.MoveToEnd +Moves the cursor to the end of the current line. +Takes one parameter: + + * __bool__ *all_lines*: If true moves the cursor to the end of the first + line in the current prompt. Defaults to false. + + Example: + + // Move to line end Ctrl+E. + jqconsole.RegisterShortcut('E', function() { + jqconsole.MoveToEnd(); + handler(); + }); + +###jqconsole.Disable +Disables input and focus on the console. + + +###jqconsole.Enable +Enables input and focus on the console. + + +###jqconsole.IsDisabled +Returns true if the console is disabled. + + +###jqconsole.ResetHistory +Resets the console history. + + +###jqconsole.ResetMatchings +Resets the character matching configuration. + + +###jqconsole.ResetShortcuts +Resets the shortcut configuration. + + +##Default Key Config + +The console responds to the followind keys and key combinations by default: + +* `Delete`: Delete the following character. +* `Ctrl+Delete`: Delete the following word. +* `Backspace`: Delete the preceding character. +* `Ctrl+Backspace`: Delete the preceding word. +* `Ctrl+Left`: Move one word to the left. +* `Ctrl+Right`: Move one word to the right. +* `Home`: Move to the beginning of the current line. +* `Ctrl+Home`: Move to the beginnig of the first line. +* `End`: Move to the end of the current line. +* `Ctrl+End`: Move to the end of the last line. +* `Shift+Up`, `Ctrl+Up`: Move cursor to the line above the current one. +* `Shift+Down`, `Ctrl+Down`: Move cursor to the line below the current one. +* `Tab`: Indent. +* `Shift+Tab`: Unindent. +* `Up`: Previous history item. +* `Down`: Next history item. +* `Enter`: Finish input/prompt operation. See Input() and Prompt() for details. +* `Shift+Enter`: New line. +* `Page Up`: Scroll console one page up. +* `Page Down`: Scroll console one page down. + +##ANSI escape code SGR support + +jq-console implements a large subset of the ANSI escape code graphics. +Using the `.Write` method you could add style to the console using +the following syntax: + +`ASCII 27 (decimal) or 0x1b (hex)` `[` `SGR code` `m` + +Example: + + jqconsole.Write('\033[31mRed Text'); + +Note that the third parameter `escape` must be true which defaults to it. + +You'll need to include the `ansi.css` file for default effects or create your +own using the css classes from the table below. + +###SGR +[Reference](http://en.wikipedia.org/wiki/ANSI_escape_code#graphics). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CodeEffectClass
0Reset / Normal
1Bold`jqconsole-ansi-bold`
2Faint`jqconsole-ansi-lighter`
3Italic`jqconsole-ansi-italic`
4Line below text`jqconsole-ansi-underline`
5Blink: 1s delay`jqconsole-ansi-blink`
6Blink: 0.5s delay`jqconsole-ansi-blink-rapid`
8Hide text`jqconsole-ansi-hidden`
9Line through text`jqconsole-ansi-line-through`
10Remove all fonts
11-19Add custom font`jqconsole-ansi-fonts-{N}` where N is code - 10
20Add Fraktur font (not implemented in ansi.css)`jqconsole-ansi-fraktur`
21Remove Bold and Faint effects
22Same as 21
23Remove italic and fraktur effects
24Remove underline effect
25Remove blinking effect(s).
28Reveal text
29Remove line-through effect
30-37Set foreground color to color from the color table belowjqconsole-ansi-color-{COLOR} where {COLOR} is the color name
39Restore default foreground color
40-47Set background color to color from the color table below`jqconsole-ansi-background-color-{COLOR}` where {COLOR} is the color name
49Restore default background color
51Adds a frame around the text`jqconsole-ansi-framed`
53Line above textjqconsole-ansi-overline
54Remove frame effect
55Remove over-line effect
+ +###Colors +[Reference](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Code offsetColor
0Black
1Red
2Green
3Yellow
4Blue
5Magenta
6Cyan
7White
+ +##CSS Classes + +Several CSS classes are provided to help stylize the console: + +* `jqconsole`: The main console container. +* `jqconsole, jqconsole-blurred`: The main console container, when not in focus. +* `jqconsole-cursor`: The cursor. +* `jqconsole-header`: The welcome message at the top of the console. +* `jqconsole-input`: The prompt area during input. May have multiple lines. +* `jqconsole-old-input`: Previously-entered inputs. +* `jqconsole-prompt`: The prompt area during prompting. May have multiple lines. +* `jqconsole-old-prompt`: Previously-entered prompts. +* `jqconsole-composition`: The div encapsulating the composition of multi-byte + characters. + + +Of course, custom classes may be specified when using `jqconsole.Write()` for +further customization. + + +##Contributors + +[Max Shawabkeh](http://max99x.com/) +[Amjad Masad](http://twitter.com/amjad_masad) diff --git a/lib/client/terminal/ansi.css b/lib/client/terminal/ansi.css new file mode 100644 index 00000000..296cfc60 --- /dev/null +++ b/lib/client/terminal/ansi.css @@ -0,0 +1,172 @@ +.jqconsole-ansi-bold { + font-weight: bold!important; +} + +.jqconsole-ansi-lighter { + font-weight: lighter!important; +} + +.jqconsole-ansi-italic { + font-style: italic!important; +} + +.jqconsole-ansi-underline { + text-decoration: underline!important; +} + +@-webkit-keyframes blinker { + from { opacity: 1.0; } + to { opacity: 0.0; } +} + +@-moz-keyframes blinker { + from { opacity: 1.0; } + to { opacity: 0.0; } +} + +@-ms-keyframes blinker { + from { opacity: 1.0; } + to { opacity: 0.0; } +} + +@-o-keyframes blinker { + from { opacity: 1.0; } + to { opacity: 0.0; } +} + +.jqconsole-ansi-blink { + -webkit-animation-name: blinker; + -moz-animation-name: blinker; + -ms-animation-name: blinker; + -o-animation-name: blinker; + -webkit-animation-iteration-count: infinite; + -moz-animation-iteration-count: infinite; + -ms-animation-iteration-count: infinite; + -o-animation-iteration-count: infinite; + -webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -o-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -webkit-animation-duration: 1s; + -moz-animation-duration: 1s; + -o-animation-duration: 1s; + -ms-animation-duration: 1s; +} + +.jqconsole-ansi-blink-rapid { + -webkit-animation-name: blinker; + -moz-animation-name: blinker; + -ms-animation-name: blinker; + -o-animation-name: blinker; + -webkit-animation-iteration-count: infinite; + -moz-animation-iteration-count: infinite; + -ms-animation-iteration-count: infinite; + -o-animation-iteration-count: infinite; + -webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -o-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0); + -webkit-animation-duration: 0.5s; + -moz-animation-duration: 0.5s; + -o-animation-duration: 0.5s; + -ms-animation-duration: 0.5s; +} + + +.jqconsole-ansi-hidden { + visibility:hidden!important; +} + +.jqconsole-ansi-line-through { + text-decoration: line-through; +} + +.jqconsole-ansi-fonts-1 { + +} +.jqconsole-ansi-fonts-2 { + +} +.jqconsole-ansi-fonts-3 { + +} +.jqconsole-ansi-fonts-4 { + +} +.jqconsole-ansi-fonts-5 { + +} +.jqconsole-ansi-fonts-6 { + +} +.jqconsole-ansi-fonts-7 { + +} +.jqconsole-ansi-fonts-8 { + +} +.jqconsole-ansi-fonts-9 { + +} + +.jqconsole-ansi-fraktur { + +} + +.jqconsole-ansi-color-black { + color: black!important; +} +.jqconsole-ansi-color-red { + color: red!important; +} +.jqconsole-ansi-color-green { + color: green!important; +} +.jqconsole-ansi-color-yellow { + color: yellow!important; +} +.jqconsole-ansi-color-blue { + color: blue!important; +} +.jqconsole-ansi-color-magenta { + color: magenta!important; +} +.jqconsole-ansi-color-cyan { + color: cyan!important; +} +.jqconsole-ansi-color-white { + color: white!important; +} + +.jqconsole-ansi-background-color-black { + background-color: black!important; +} +.jqconsole-ansi-background-color-red { + background-color: red!important; +} +.jqconsole-ansi-background-color-green { + background-color: green!important; +} +.jqconsole-ansi-background-color-yellow { + background-color: yellow!important; +} +.jqconsole-ansi-background-color-blue { + background-color: blue!important; +} +.jqconsole-ansi-background-color-magenta { + background-color: magenta!important; +} +.jqconsole-ansi-background-color-cyan { + background-color: cyan!important; +} +.jqconsole-ansi-background-color-white { + background-color: white!important; +} + +.jqconsole-ansi-framed { + border: 1px solid!important; +} +.jqconsole-ansi-overline { + text-decoration: overline!important; +} + diff --git a/lib/client/terminal/jqconsole.coffee b/lib/client/terminal/jqconsole.coffee new file mode 100644 index 00000000..c0a76c16 --- /dev/null +++ b/lib/client/terminal/jqconsole.coffee @@ -0,0 +1,1279 @@ +### +Copyrights 2011, the repl.it project. +Licensed under the MIT license +### + +# Shorthand for jQuery. +$ = jQuery + +# The states in which the console can be. +STATE_INPUT = 0 +STATE_OUTPUT = 1 +STATE_PROMPT = 2 + +# Key code values. +KEY_ENTER = 13 +KEY_TAB = 9 +KEY_DELETE = 46 +KEY_BACKSPACE = 8 +KEY_LEFT = 37 +KEY_RIGHT = 39 +KEY_UP = 38 +KEY_DOWN = 40 +KEY_HOME = 36 +KEY_END = 35 +KEY_PAGE_UP = 33 +KEY_PAGE_DOWN = 34 + +# CSS classes +CLASS_PREFIX = 'jqconsole-' +CLASS_CURSOR = "#{CLASS_PREFIX}cursor" +CLASS_HEADER = "#{CLASS_PREFIX}header" +CLASS_PROMPT = "#{CLASS_PREFIX}prompt" +CLASS_OLD_PROMPT = "#{CLASS_PREFIX}old-prompt" +CLASS_INPUT = "#{CLASS_PREFIX}input" +CLASS_BLURRED = "#{CLASS_PREFIX}blurred" + +# Frequently used string literals +E_KEYPRESS = 'keypress' +EMPTY_SPAN = '' +EMPTY_DIV = '
' +EMPTY_SELECTOR = ':empty' +NEWLINE = '\n' + +# Default prompt text for main and continuation prompts. +DEFAULT_PROMPT_LABEL = '>>> ' +DEFAULT_PROMPT_CONINUE_LABEL = '... ' + +# The default number of spaces inserted when indenting. +DEFAULT_INDENT_WIDTH = 2 + +CLASS_ANSI = "#{CLASS_PREFIX}ansi-" +ESCAPE_CHAR = '\033' +ESCAPE_SYNTAX = /\[(\d*)(?:;(\d*))*m/ + +class Ansi + COLORS: ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'] + + constructor: -> + @klasses = []; + + _append: (klass) => + klass = "#{CLASS_ANSI}#{klass}" + if @klasses.indexOf(klass) is -1 + @klasses.push klass + + _remove: (klasses...) => + for klass in klasses + if klass in ['fonts', 'color', 'background-color'] + @klasses = (cls for cls in @klasses when cls.indexOf(klass) isnt CLASS_ANSI.length) + else + klass = "#{CLASS_ANSI}#{klass}" + @klasses = (cls for cls in @klasses when cls isnt klass) + + _color: (i) => @COLORS[i] + + _style: (code) => + code = 0 if code == '' + code = parseInt code + + return if isNaN code + + switch code + when 0 then @klasses = [] + when 1 then @_append 'bold' + when 2 then @_append 'lighter' + when 3 then @_append 'italic' + when 4 then @_append 'underline' + when 5 then @_append 'blink' + when 6 then @_append 'blink-rapid' + when 8 then @_append 'hidden' + when 9 then @_append 'line-through' + when 10 then @_remove 'fonts' + when 11,12,13,14,15,16,17,18,19 + @_append "fonts-#{code - 10}" + when 20 then @_append 'fraktur' + when 21 then @_remove 'bold', 'lighter' + when 22 then @_remove 'bold', 'lighter' + when 23 then @_remove 'italic', 'fraktur' + when 24 then @_remove 'underline' + when 25 then @_remove 'blink', 'blink-rapid' + when 28 then @_remove 'hidden' + when 29 then @_remove 'line-through' + when 30,31,32,33,34,35,36,37 + @_append 'color-' + @_color code - 30 + when 39 then @_remove 'color' + when 40,41,42,43,44,45,46,47 + @_append 'background-color-' + @_color code - 40 + when 49 then @_remove 'background-color' + when 51 then @_append 'framed' + when 53 then @_append 'overline' + when 54 then @_remove 'framed' + when 55 then @_remove 'overline' + + getClasses: => @klasses.join ' ' + + _openSpan: (text) => "#{text}" + _closeSpan: (text) => "#{text}" + + stylize: (text) => + text = @_openSpan text + + i = 0 + while (i = text.indexOf(ESCAPE_CHAR ,i)) and i isnt -1 + if d = text[i...].match ESCAPE_SYNTAX + @_style code for code in d[1...] + text = @_closeSpan(text[0...i]) + @_openSpan text[i + 1 + d[0].length...] + else i++ + + return @_closeSpan text + +# Helper functions +spanHtml = (klass, content) -> "#{content or ''}" + +class JQConsole + # Creates a console. + # @arg container: The DOM element into which the console is inserted. + # @arg header: Text to print at the top of the console on reset. Optional. + # Defaults to an empty string. + # @arg prompt_label: The label to show before the command prompt. Optional. + # Defaults to DEFAULT_PROMPT_LABEL. + # @arg prompt_continue: The label to show before continuation lines of the + # command prompt. Optional. Defaults to DEFAULT_PROMPT_CONINUE_LABEL. + constructor: (@container, header, prompt_label, prompt_continue_label) -> + # Mobile devices supported sniff. + @isMobile = !!navigator.userAgent.match /iPhone|iPad|iPod|Android/i + @isIos = !!navigator.userAgent.match /iPhone|iPad|iPod/i + @isAndroid = !!navigator.userAgent.match /Android/i + + @$window = $(window) + + # The header written when the console is reset. + @header = header or '' + + # The prompt label used by Prompt(). + @prompt_label_main = prompt_label or DEFAULT_PROMPT_LABEL + @prompt_label_continue = ' \n' + (prompt_continue_label or + DEFAULT_PROMPT_CONINUE_LABEL) + + # How many spaces are inserted when a tab character is pressed. + @indent_width = DEFAULT_INDENT_WIDTH + + # By default, the console is in the output state. + @state = STATE_OUTPUT + + # A queue of input/prompt operations waiting to be called. The items are + # bound functions ready to be called. + @input_queue = [] + + # The function to call when input is accepted. Valid only in + # input/prompt mode. + @input_callback = null + # The function to call to determine whether the input should continue to the + # next line. + @multiline_callback = null + + # A table of all "recorded" inputs given so far. + @history = [] + # The index of the currently selected history item. If this is past the end + # of @history, then the user has not selected a history item. + @history_index = 0 + # The command which the user was typing before browsing history. Keeping + # track of this allows us to restore the user's command if they browse the + # history then decide to go back to what they were typing. + @history_new = '' + # Whether the current input operation is using history. + @history_active = false + + # A table of custom shortcuts, mapping character codes to callbacks. + @shortcuts = {} + + # The main console area. Everything else happens inside this. + @$console = $('
').appendTo @container
+    @$console.css 
+      position: 'absolute'
+      top: 0
+      bottom: 0
+      right: 0
+      left: 0
+      margin: 0
+      overflow: 'auto'
+
+    # Whether the console currently has focus.
+    @$console_focused = true
+    
+    # On screen somehow invisible textbox for input.
+    # Copied from codemirror2, this works for both mobile and desktop browsers.
+    @$input_container = $(EMPTY_DIV).appendTo @container
+    @$input_container.css
+      position: 'relative'
+      width: 1
+      height: 0
+      overflow: 'hidden'
+    @$input_source = $('