**** mass:werk termlib.js - JS-WebTerminal Object v1.66 **** (c) Norbert Landsteiner 2003-2015 mass:werk - media environments ### COMPATIBILITY WARNING ### Dropped support of Netscape 4 (layers) with version 1.5! Netscape 4 is now outdated for more than 10 years. Any further support of this browser would be of academic nature. As a benefit this step allows us to include the socket extension in the main library, so there are no additional files to load anymore. For the first time there is a backward compatibility issue from version 1.3 to version 1.4: The following applies to the style vector for the `type()' method while using colors: while with version 1.3 a color was encoded using the color code times 16 (0xf), e.g.: myTerm.type( 'This is red.', 2*16 ); this changed with version 1.4 to the color code times 256 (0xff), e.g.: myTerm.type( 'This is red.', 2*256 ); All other style encodings or color API remain unchanged. Since this feature was only introduced in version 1.3 and there are no known applications that would use a statement like the above (since you would usually use the `write()' method for complex output), this seems to be good bargain for some codes for custom styles. C.f.: sect 7.5 "TermGlobals.assignStyle()" ### Mac OS X Dead-Keys ### (Dead-keys: combinations of accents and characters that are built by two consecutively pressed keys.) Mac OS X 10.5 and later doesn't fire a keyboard event for dead keys anymore. It's possible to fix this for Safari by a custom dead keys emulation, but not for Chrome or Firefox. "termlib.js" provides automatic translations of common dead-keys for Safari in German (de-de). In case you would need dead-keys for another language, please contact me via http://www.masswerk.at/. Contents: 1 About 2 Creating a new Terminal Instance 2.1 Configuration Values 3 Using the Terminal 3.1 The Default Handler 3.2 Input Modes 3.2.1 Normal Line Input (Command Line Mode) 3.2.1.2 Special Keys (ctrlHandler) 3.2.2 Raw Mode 3.2.3 Character Mode 3.3 Other Handlers 3.3.1 initHandler 3.3.2 exitHandler 3.4 Flags for Behaviour Control 4 Output Methods 4.1 Terminal.type() 4.2 Terminal.write() 4.3 Terminal.typeAt() 4.4 Terminal.setChar() 4.5 Terminal.newLine() 4.6 Terminal.clear() 4.7 Terminal.statusLine() 4.8 Terminal.printRowFromString() 4.9 Terminal.redraw() 4.10 Using Color 4.11 Text Wrap - Terminal.wrapOn(), Terminal.wrapOff() 4.12 ANSI Support 5 Cursor Methods and Editing 5.1 Terminal.cursorOn() 5.2 Terminal.cursorOff() 5.3 Terminal.cursorSet() 5.4 Terminal.cursorLeft() 5.5 Terminal.cursorRight() 5.6 Terminal.backspace() 5.7 Terminal.fwdDelete() 5.8 Terminal.isPrintable() 6 Other Methods of the Terminal Object 6.1 Terminal.prompt() 6.2 Terminal.reset() 6.3 Terminal.open() 6.4 Terminal.close() 6.5 Terminal.focus() 6.6 Terminal.moveTo() 6.7 Terminal.resizeTo() 6.8 Terminal.getDimensions() 6.9 Terminal.rebuild() 6.10 Terminal.backupScreen() 6.11 Terminal.restoreScreen() 6.12 Terminal.swapBackup() 6.13 Terminal.setTextColor() 6.14 Terminal.setTextBlur() 7 Global Static Methods (TermGlobals) 7.1 TermGlobals.setFocus() 7.2 TermGlobals.keylock (Global Locking Flag) 7.3 TermGlobals Text Methods 7.3.1 TermGlobals.normalize() 7.3.2 TermGlobals.fillLeft() 7.3.3 TermGlobals.center() 7.3.4 TermGlobals.stringReplace() 7.4 TermGlobals Import Methods 7.4.1 TermGlobals.insertText() 7.4.2 TermGlobals.importEachLine() 7.4.3 TermGlobals.importMultiLine() 7.5 TermGlobals.assignStyle() 8 Localization 9 The Socket Extension (Remote Communication) 9.1 A First Example 9.2 The send() API 9.3 Global Config Settings 9.4 The Callback (Response Handling) 9.5 Error Codes 9.6 Note on Compatibly / Browser Requirements 9.7 termlib_socket.js Version History 10 Cross Browser Functions 11 Architecture, Internals 11.1 Global Entities 11.2 I/O Architecture 11.3 Compatibility 12 History 13 Example for a Command Line Parser 14 License 15 Disclaimer 16 Donations 17 References 1 About The Terminal library "termlib.js" provides an object oriented constructor and control methods for a terminal-like DHTML interface. "termlib.js" features direct keyboard input and powerful output methods for multiple instances of the `Terminal' object (including focus control). "termlib.js" also comprises methods for a transparent handling of client-server com- munications via XMLHttpRequests (see sect. 9 "The Socket Extension"). The library was written with the aim of simple usage and a maximum of compatibility with minimal footprint in the global namespace. A simple example: // creating a terminal and using it var term = new Terminal( {handler: termHandler} ); term.open(); function termHandler() { var line = this.lineBuffer; this.newLine(); if (line == "help") { this.write(helpPage) } else if (line == "exit") { this.close(); return; } else if (line != "") { this.write("You typed: "+line); } this.prompt(); } var helpPage = [ "This is the monstrous help page for my groovy terminal.", "Commands available:", " help ... print this monstrous help page", " exit ... leave this groovy terminal", " ", "Have fun!" ]; You should provide CSS font definitions for the classes ".term" (normal video) and ".termReverse" (reverse video) in a monospaced font. A sample stylesheet "term_styles.css" comes with this library. See the sample application "multiterm_test.html" for a demo of multiple terminals. v.1.01: If you configure to use another font class (see 2.1 Configuration Values), you must provide a subclass ".termReverse" for reversed video. p.e.: .myFontClass .termReverse { /* your definitions for reverse video here */ } With the addition of `conf.fontClass' you can now create multiple instances with independend appearences. 2 Creating a new Terminal Instance Use the `new' constructor to create a new instance of the Terminal object. You will want to supply a configuration object as an argument to the constructor. If the `new' constructor is called without an object as its first argument, default values are used. p.e.: // creating a new instance of Terminal var conf= { x: 100, y: 100, cols: 80, rows: 24 } var term = new Term(conf); term.open(); `Terminal.open()' initializes the terminal and makes it visible to the user. This is handled in by separate method to allow the re-initilization of instances previously closed. NOTE: The division or HTML-element that holds the terminal must be present when calling `Terminal.open()'. So you must not call this method from the header of a HTML-document at compile time. 2.1 Configuration Values Set any of these values in your configuration object to override: LABEL DEFAULT VALUE COMMENT x 100 terminal's position x in px y 100 terminal's position y in px termDiv 'termDiv' id of terminals CSS division bgColor '#181818' background color (HTML hex value) frameColor '#555555' frame color (HTML hex value) frameWidth 1 frame border width in px fontClass 'term' class name of CSS font definition to use cols 80 number of cols per row rows 24 number of rows rowHeight 15 a row's line-height in px blinkDelay 500 delay for cursor blinking in milliseconds crsrBlinkMode false true for blinking cursor crsrBlockMode true true for block-cursor else underscore DELisBS false handle as printTab true handle as printable (prints as space) printEuro true handle unicode 0x20AC (Euro sign) as printable catchCtrlH true handle ^H as closeOnESC true close terminal on historyUnique false prevent consecutive and identical entries in history id 0 terminal id ps '>' prompt string greeting '%+r Terminal ready. %-r' string for greeting if no initHandler is used handler defaultHandler reference to handler for command interpretation ctrlHandler null reference to handler called on uncatched special keys initHandler null reference to handler called at end of init() exitHandler null reference to handler called on close() wrapping false text wrapping for `write()' on/off mapANSI false enable mapping of ANSI escape sequences (SGR only) ANSItrueBlack false force ANSI 30m to be rendered as black (default: fg color) textColor '' String, default text color (color 0), overrides any CSS rules textBlur 0 Number, if set, adds a CSS text-shadow with the given number of px ("text-shadow: 0 0 px "), use this with textColor If the value is an array of numbers, multiple text-shadows will be applied. At least you will want to specify `handler' to implement your own command parser. Note: While `id' is not used by the Termninal object, it provides an easy way to identify multiple terminals by the use of "this.id". (e.g.: "if (this.id == 1) startupterm = true;") p.e.: // creating two individual Terminal instances var term1 = new Terminal( { id: 1, x: 200, y: 10, cols: 80, rows: 12, greeting: "*** This is Terminal 1 ***", handler: myTerminalHandler } ); term1.open(); var term2 = new Terminal( { id: 2, x, 200, y: 220, cols: 80 rows: 12, greeting: "*** This is Terminal 2 ***", handler: myTerminalHandler } ); term2.open(); 3 Using the Terminal There are 4 different handlers that are called by a Terminal instance to process input and some flags to control the input mode and behaviour. 3.1 The Default Handler (a simlple example for input handling) If no handlers are defined in the configuration object, a default handler is called to handle a line of user input. The default command line handler `defaultHandler' just closes the command line with a new line and echos the input back to the user: function termDefaultHandler() { this.newLine(); if (this.lineBuffer != '') { this.type('You typed: '+this.lineBuffer); this.newLine(); } this.prompt(); } // Note: This used to be top level function. With version 1.4 `termDefaultHandler' became // a reference to the method `Terminal.prototype.defaultHandler'. First you may note that the instance is refered to as `this'. So you need not worry about which Terminal instance is calling your handler. As the handler is entered, the terminal is locked for user input and the cursor is off. The current input is available as a string value in `this.lineBuffer'. The method `type()' just does what it says and types a string at the current cursor position to the terminal screen. `newLine()' moves the cursor to a new line. The method `prompt()' adds a new line if the cursor isn't at the start of a line, outputs the prompt string (as specified in the configuration), activates the cursor, and unlocks the terminal for further input. While you're doing normal command line processing, always call `prompt()' when leaving your handler. In fact this is all you need to create your own terminal application. Please see at least the method `write()' for a more powerful output method. Below we will refer to all methods of the Terminal object as `Terminal.()'. You can call them as `this.()' in a handler or as methods of your named instance in other context (e.g.: "myTerminal.close()"). [In technical terms these methods are methods of the Terminal's prototype object, while the properties are properties of a Termninal instance. Since this doesn't make any difference to your script, we'll refer to both as `Terminal.'.] 3.2 Input Modes 3.2.1 Normal Line Input (Command Line Mode) By default the terminal is in normal input mode. Any printable characters in the range of ASCII 0x20 - 0xff are echoed to the terminal and may be edited with the use of the cursor keys and the key. The cursor keys UP and DOWN let the user browse in the command line history (the list of all commands issued previously in this Terminal instance). If the user presses or , the line is read from the terminal buffer, converted to a string, and placed in `Terminal.lineBuffer' (-> `this.lineBuffer') for further use. The terminal is then locked for further input and the specified handler (`Terminal.handler') is called. 3.2.1.2 Special Keys (ctrlHandler) If a special character (ASCII<0x20) or an according combination of and a key is pressed, which is not caught for editing or "enter", and a handler for `ctrlHandler' is specified, this handler is called. The ASCII value of the special character is available in `Terminal.inputChar'. Please note that the terminal is neither locked, nor is the cursor off - all further actions have to be controlled by `ctrlHandler'. (The tracking of - combinations as "^C" usually works but cannot be taken for granted.) A named reference of the special control values in POSIX form (as well as the values of the cursor keys [LEFT, RIGHT, UP, DOWN]) is available in the `termKey' object. Note: With version 1.4 `termKey' is a reference to `Terminal.prototype.globals.termKey'. This object is also mapped to `Terminal.prototype.termKey', so you may also access it as "this.termKey" inside handlers. p.e.: // a simple ctrlHandler function myCtrlHandler() { if (this.inputChar == termKey.ETX) { // exit on ^C (^C == ASCII 0x03 == ) this.close(); } } If no `ctrlHandler' is specified, control keys are ignored (default). 3.2.2 Raw Mode If the flag `Terminal.rawMode' is set to a value evaluating to `true', no special keys are tracked but and (and , if the flag `Terminal.closeOnESC' is set). The input is NOT echoed to the terminal. All printable key values [0x20-0xff] are transformed to characters and added to `Terminal.lineBuffer' sequentially. The command line input is NOT added to the history. This mode is especially suitable for password input. p.e.: // using raw mode for password input function myTermHandler() { this.newLine(); // we stored a flag in Terminal.env to track the status if (this.env.getpassword) { // leave raw mode this.rawMode = false; if (passwords[this.env.user] == this.lineBuffer) { // matched this.type('Welcome '+this.env.user); this.env.loggedin = true; } else { this.type('Sorry.'); } this.env.getpassword = false; } else { // simple parsing var args = this.lineBuffer.split(' '); var cmd = args[0]; if (cmd == 'login') { var user = args[1]; if (!user) { this.type('usage: login '); } else { this.env.user = user; this.env.getpassword = true; this.type('password? '); // enter raw mode this.rawMode = true; // leave without prompt so we must unlock first this.lock = false; return; } } /* other actions ... */ } this.prompt(); } In this example a handler is set up to process the command "login " and ask for a password for the given user name in raw mode. Note the use of the object `Terminal.env' which is just an empty object set up at the creation of the Terminal instance. Its only purpose is to provide an individual namespace for private data to be stored by a Terminal instance. NOTE: The flag `Terminal.lock' is used to control the keyboard locking. If we would not set this to `false' before leaving in raw mode, we would be caught in dead-lock, since no input could be entered and our handler wouldn't be called again. - A dreadful end of our terminal session. NOTE: Raw mode utilizes the property `Terminal.lastLine' to collect the input string. This is normally emty, when a handler is called. This is not the case if your script left the input process on a call of ctrlHandler. You should clear `Terminal.lastLine' in such a case, if you're going to enter raw mode immediatly after this. 3.2.3 Character Mode If the flag `Terminal.charMode' is set to a value evaluating to `true', the terminal is in character mode. In this mode the numeric ASCII value of the next key typed is stored in `Terminal.inputChar'. The input is NOT echoed to the terminal. NO locking or cursor control is performed and left to the handler. You can use this mode to implement your editor or a console game. `Terminal.charMode' takes precedence over `Terminal.rawMode'. p.e.: // using char mode function myTermHandler() { // this is the normal handler this.newLine(); // simple parsing var args = this.lineBuffer.split(' '); var cmd = args[0]; if (cmd == 'edit') { // init the editor myEditor(this); // redirect the handler to editor this.handler = myEditor; // leave in char mode this.charMode = true; // show cursor this.cursorOn(); // don't forget unlocking this.lock = false; return; } /* other actions ... */ this.prompt(); } function myEditor(initterm) { // our dummy editor (featuring modal behaviour) if (initterm) { // perform initialization tasks initterm.clear(); initterm.write('this is a simple test editor; leave with then "q"%n%n'); initterm.env.mode = ''; // store a reference of the calling handler initterm.env.handler = initterm.handler; return; } // called as handler -> lock first this.lock=true; // hide cursor this.cursorOff(); var key = this.inputChar; if (this.env.mode == 'ctrl') { // control mode if (key == 113) { // "q" => quit // leave charMode and reset the handler to normal this.charMode = false; this.handler = this.env.handler; // clear the screen this.clear(); // prompt and return this.prompt(); return; } else { // leave control mode this.env.mode = ''; } } else { // edit mode if (key == termKey.ESC) { // enter control mode // we'd better indicate this in a status line ... this.env.mode = 'ctrl'; } else if (key == termKey.LEFT) { // cursor left } else if (key == termKey.RIGHT) { // cursor right } else if (key == termKey.UP) { // cursor up } else if (key == termKey.DOWN) { // cursor down } else if (key == termKey.CR) { // cr or enter } else if (key == termKey.BS) { // backspace } else if (key == termKey.DEL) { // fwd delete // conf.DELisBS is not evaluated in charMode! } else if (this.isPrintable(key)) { // printable char - just type it var ch = String.fromCharCode(key); this.type(ch); } } // leave unlocked with cursor this.lock = false; this.cursorOn(); } Note the redirecting of the input handler to replace the command line handler by the editor. The method `Terminal.clear()' clears the terminal. `Terminal.cursorOn()' and `Terminal.cursorOff()' are used to show and hide the cursor. 3.3 Other Handlers There are two more handlers that can be specified in the configuration object: 3.3.1 initHandler `initHandler' is called at the end of the initialization triggered by `Terminal.open()'. The default action - if no `initHandler' is specified - is: // default initilization this.write(this.conf.greeting); this.newLine(); this.prompt(); Use `initHandler' to perform your own start up tasks (e.g. show a start up screen). Keep in mind that you should unlock the terminal and possibly show a cursor to give the impression of a usable terminal. 3.3.2 exitHandler `exitHandler' is called by `Terminal.close()' just before hiding the terminal. You can use this handler to implement any tasks to be performed on exit. Note that this handler is called even if the terminal is closed on outside of your inputHandlers control. See the file "multiterm_test.html" for an example. 3.4 Overview: Flags for Behaviour Control These falgs are accessible as `Terminal.' at runtime. If not stated else, the initial value may be specified in the configuration object. The configuration object and its properties are accessible at runtime via `Terminal.conf'. NAME DEFAULT VALUE MEANING blink_delay 500 delay for cursor blinking in milliseconds. crsrBlinkMode false true for blinking cursor. if false, cursor is static. crsrBlockMode true true for block-cursor else underscore. DELisBS false handle as . printTab true handle as printable (prints as space) if false is handled as a control character printEuro true handle the euro sign as valid input char. if false char 0x20AC is printed, but not accepted in the command line catchCtrlH true handle ^H as . if false, ^H must be tracked by a custom ctrlHandler. closeOnESC true close terminal on . if true, is not available for ctrHandler. historyUnique false unique history entries. if true, entries that are identical to the last entry in the user history will not be added. charMode false terminal in character mode (tracks next key-code). (runtime only) rawMode false terminal in raw mode (no echo, no editing). (runtime only) wrapping false text wrapping on/off mapANSI false filter ANSI escape sequences and apply SGR styles and color codes for write() ANSItrueBlack false force output of ANSI code 30m (black) as black (default: render color 0 as foreground color) Not exactly a flag but useful: ps '>' prompt string. 4 Output Methods Please note that any output to the terminal implies an advance of the cursor. This means, that if your output reaches the last column of your terminal, the cursor is advanced and a new line is opened automatically. This procedure may include scrolling to make room for the new line. While this is not of much interest for most purposes, please note that, if you output a string of length 80 to a 80-columns-terminal, and a new line, and another string, this will result in an empty line between the two strings. 4.1 Terminal.type( [,] ) Types the string at the current cursor position to the terminal. Long lines are broken where the last column of the terminal is reached and continued in the next line. `Terminal.write()' does not support any kind of arbitrary line breaks. (This is just a basic output routine. See `Terminal.write()' for a more powerful output method.) A bitvector may be supplied as an optional second argument to represent a style or a combination of styles. The meanings of the bits set are interpreted as follows: : 1 ... reverse (2 power 0) 2 ... underline (2 power 1) 4 ... italics (2 power 2) 8 ... strike (2 power 3) 16 ... bold (2 power 4) *displayed as italics, used internally for ANSI-mapping* So "Terminal.type( 'text', 5 )" types "text" in italics and reverse video. Note: There is no bold, for most monospaced fonts (including Courier) tend to render wider in bold. Since this would bring the terminal's layout out of balance, we just can't use bold as a style. - Sorry. The HTML-representation of this styles are defined in "TermGlobals.termStyleOpen" and "TermGlobals.termStyleClose". (Version 1.4: "TermGlobals" is now a reference to "Terminal.prototype.globals".) Version 1.2 introduces additional styles for colors. Please read also sect. 4.10 "Using Color" for the extended color values. 4.2 Terminal.write( [,] ) Writes a text with markup to the terminal. If an optional second argument evaluates to true, a UN*X-style utility like `more' is used to page the text. The text may be supplied as a single string (with newline character "\n") or as an array of lines. Any other input is transformed to a string value before output. 4.2.1 Mark-up: `Terminal.write()' employs a simple mark-up with the following syntax: : %((+|-)