├── .gitignore ├── LICENSE ├── MANIFEST ├── MANIFEST.in ├── README ├── README.md ├── django-console ├── __init__.py ├── admin.py ├── static │ ├── images │ │ ├── console-128x128.png │ │ ├── console.png │ │ └── screenshot.png │ ├── js │ │ └── termlib.js │ └── readme.txt ├── templates │ └── django-console │ │ └── admin │ │ └── index.html └── tests.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea/* 3 | dist/* 4 | build/* 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | * Copyright (c) 2015 Anoop Thomas Mathew (@atmb4u) 2 | * 3 | * All rights reserved. 4 | * 5 | * 6 | * Redistribution and use in source and binary forms, with or without modification, 7 | * are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * 3. Neither the name of autojs nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | LICENSE 3 | README 4 | README.md 5 | setup.py 6 | django-console/__init__.py 7 | django-console/admin.py 8 | django-console/tests.py 9 | django-console/static/images/console-128x128.png 10 | django-console/static/images/console.png 11 | django-console/static/images/screenshot.png 12 | django-console/static/js/termlib.js 13 | django-console/templates/django-console/admin/index.html 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README README.md LICENSE 2 | recursive-include * *.py *.js *.png *.txt *.html 3 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Django-Console](https://raw.githubusercontent.com/atmb4u/django-console/master/django-console/static/images/console-128x128.png) 2 | #Django-Console 3 | 4 | bash console in the browser for django devops! 5 | 6 | ##IMPORTANT 7 | 8 | Service needs to be running on ```https``` to securely POST commands to the server. 9 | 10 | ![Django-Console](https://raw.githubusercontent.com/atmb4u/django-console/master/django-console/static/images/screenshot.png) 11 | 12 | Did a quick update on the code; need to pull the code and restart the server, and waiting for server admin to do that? 13 | Django-Console is for you! 14 | 15 | ## Installation 16 | 17 | **Step 1** 18 | > pip install django-console 19 | 20 | **Step 2** 21 | 22 | include __django-console__ into INSTALLED_APPS ```settings.py``` 23 | 24 | ```python 25 | INSTALLED_APPS = ( 26 | # add to the existing apps 27 | 'django-console' 28 | ) 29 | ``` 30 | 31 | **Step 3** 32 | 33 | include two more variables to ```settings.py``` 34 | 35 | > Even without these settings, it will work. 36 | 37 | > allows requests from all ips, and works even when not in https **(NOT GOOD).** 38 | 39 | ```python 40 | SECURE_CONSOLE = True # False to allow http 41 | CONSOLE_WHITELIST = [ 42 | "127.0.0.1" 43 | ] # List of IPs to be allowed - NB: All allowed by default 44 | ``` 45 | **Step 4** 46 | 47 | run 48 | > python manage.py collectstatic 49 | 50 | Done! 51 | 52 | in your browser, goto http://127.0.0.1:8000/admin/console/ to access the web console. 53 | 54 | NB: make sure you got superuser privileges. 55 | 56 | 57 | ##Tip 58 | To run sudo tasks, you can use 59 | 60 | ```bash 61 | echo mypassword | sudo -S command 62 | ``` 63 | 64 | Example commands 65 | ```bash 66 | $ echo pa$$w0rD | sudo -S service nginx restart 67 | 68 | $ git pull origin master 69 | 70 | $ ls -al 71 | ``` 72 | 73 | ##Caveats 74 | 75 | > all the **django superusers** can access this portal, so make sure only the right guys have got access before deploying django-console to live. 76 | 77 | > long running tasks and interactive commands won't probably work. 78 | 79 | 80 | ## License 81 | 82 | BSD License - checkout LICENSE file for the complete license document 83 | 84 | 85 | ## Author 86 | [Anoop Thomas Mathew](https://twitter.com/atmb4u "atmb4u") 87 | -------------------------------------------------------------------------------- /django-console/__init__.py: -------------------------------------------------------------------------------- 1 | VERSION = "0.4.7" 2 | -------------------------------------------------------------------------------- /django-console/admin.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns 2 | from django.contrib import admin 3 | from django.core.context_processors import csrf 4 | from django.http import HttpResponse 5 | from django.shortcuts import render_to_response 6 | from django.conf import settings 7 | import subprocess 8 | 9 | 10 | def get_client_ip(request): 11 | x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') 12 | if x_forwarded_for: 13 | ip = x_forwarded_for.split(',')[0] 14 | else: 15 | ip = request.META.get('REMOTE_ADDR') 16 | return ip 17 | 18 | 19 | def console(request): 20 | """ 21 | Serves the console at /admin/console 22 | SECURE_CONSOLE 23 | values: True/False 24 | Defined in settings to denote whether to allow access from http or https 25 | default: False - ALLOW access to ALL. 26 | CONSOLE_WHITELIST 27 | values: list of ip strings 28 | defines list of ips to be allowed 29 | default: ALLOW ALL ips unless defined. 30 | 31 | """ 32 | try: 33 | v1 = request.is_secure() == settings.SECURE_CONSOLE 34 | except AttributeError: 35 | v1 = True 36 | try: 37 | v2 = get_client_ip(request) in settings.CONSOLE_WHITELIST 38 | except AttributeError: 39 | v2 = True 40 | except: 41 | print("CONSOLE_WHITELIST needs to be a list of ip addresses to be allowed access") 42 | v2 = True 43 | settings_variables = v1 and v2 44 | if request.user.is_superuser and settings_variables: 45 | context = { 46 | 'STATIC_URL': settings.STATIC_URL 47 | } 48 | context.update(csrf(request)) 49 | return render_to_response("django-console/admin/index.html", context) 50 | else: 51 | return HttpResponse("Unauthorized.", status=403) 52 | 53 | 54 | def console_post(request): 55 | """ 56 | Accepts POST requests from the web console, processes it and returns the result. 57 | """ 58 | if request.user.is_superuser and request.POST: 59 | command = request.POST.get("command") 60 | if command: 61 | try: 62 | data = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT) 63 | except subprocess.CalledProcessError as e: 64 | data = e.output 65 | data = data.decode('utf-8') 66 | output = "%c(@olive)%" + data + "%c()" 67 | else: 68 | output = "%c(@orange)%Try `ls` to start with.%c()" 69 | return HttpResponse(output) 70 | return HttpResponse("Unauthorized.", status=403) 71 | 72 | 73 | def get_admin_urls(urls): 74 | """ 75 | Appends the console and post urls to the url patterns 76 | """ 77 | def get_urls(): 78 | my_urls = patterns('', 79 | (r'^console/$', admin.site.admin_view(console)), 80 | (r'^console/post/$', admin.site.admin_view(console_post))) 81 | return my_urls + urls 82 | 83 | return get_urls 84 | 85 | 86 | admin_urls = get_admin_urls(admin.site.get_urls()) 87 | admin.site.get_urls = admin_urls 88 | -------------------------------------------------------------------------------- /django-console/static/images/console-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atmb4u/django-console/135c911127b6d311549c5a075e40aad0ca6ef8f6/django-console/static/images/console-128x128.png -------------------------------------------------------------------------------- /django-console/static/images/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atmb4u/django-console/135c911127b6d311549c5a075e40aad0ca6ef8f6/django-console/static/images/console.png -------------------------------------------------------------------------------- /django-console/static/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atmb4u/django-console/135c911127b6d311549c5a075e40aad0ca6ef8f6/django-console/static/images/screenshot.png -------------------------------------------------------------------------------- /django-console/static/js/termlib.js: -------------------------------------------------------------------------------- 1 | /* 2 | termlib.js - JS-WebTerminal Object v1.65 3 | 4 | (c) Norbert Landsteiner 2003-2013 5 | mass:werk - media environments 6 | 7 | 8 | Creates [multiple] Terminal instances. 9 | 10 | Synopsis: 11 | 12 | myTerminal = new Terminal(); 13 | myTerminal.open(); 14 | 15 | overrides any values of object `TerminalDefaults'. 16 | individual values of `id' must be supplied for multiple terminals. 17 | `handler' specifies a function to be called for input handling. 18 | (see `Terminal.prototype.defaultHandler()' and documentation.) 19 | 20 | globals defined in this library: 21 | Terminal (Terminal object) 22 | TerminalDefaults (default configuration) 23 | termDefaultHandler (default command line handler) 24 | TermGlobals (common vars and code for all instances) 25 | termKey (named mappings for special keys) 26 | termDomKeyRef (special key mapping for DOM constants) 27 | 28 | (please see the v. 1.4 history entry on these elements) 29 | 30 | required CSS classes for font definitions: ".term", ".termReverse". 31 | 32 | Compatibilty: 33 | Standard web browsers with a JavaScript implementation compliant to 34 | ECMA-262 2nd edition and support for the anonymous array and object 35 | constructs and the anonymous function construct in the form of 36 | "myfunc=function(x) {}" (c.f. ECMA-262 3rd edion for details). 37 | This comprises almost all current browsers but Konquerer (khtml) and 38 | versions of Apple Safari for Mac OS 10.0-10.28 (Safari 1.0) which 39 | lack support for keyboard events. 40 | v1.5: Dropped support of Netscape 4 (layers) 41 | 42 | License: 43 | This JavaScript-library is free. 44 | Include a visible backlink to in the 45 | embedding web page or application. 46 | The library should always be accompanied by the 'readme.txt' and the 47 | sample HTML-documents. 48 | 49 | Any changes should be commented and must be reflected in `Terminal.version' 50 | in the format: "Version.Subversion (compatibility)". 51 | 52 | Donations: 53 | Donations are welcome: You may support and/or honor the development of 54 | "termlib.js" via PayPal at: 55 | 56 | Disclaimer: 57 | This software is distributed AS IS and in the hope that it will be useful, 58 | but WITHOUT ANY WARRANTY; without even the implied warranty of 59 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The entire risk as to 60 | the quality and performance of the product is borne by the user. No use of 61 | the product is authorized hereunder except under this disclaimer. 62 | 63 | ### The sections above must not be removed. ### 64 | 65 | version 1.01: added Terminal.prototype.resizeTo(x,y) 66 | added Terminal.conf.fontClass (=> configureable class name) 67 | Terminal.prototype.open() now checks for element conf.termDiv 68 | in advance and returns success. 69 | 70 | version 1.02: added support for and Euro sign 71 | (Terminal.conf.printTab, Terminal.conf.printEuro) 72 | and a method to evaluate printable chars: 73 | Terminal.prototype.isPrintable(keycode) 74 | 75 | version 1.03: added global keyboard locking (TermGlobals.keylock) 76 | modified Terminal.prototype.redraw for speed (use of locals) 77 | 78 | version 1.04: modified the key handler to fix a bug with MSIE5/Mac 79 | fixed a bug in TermGlobals.setVisible with older MSIE-alike 80 | browsers without DOM support. 81 | 82 | version 1.05: added config flag historyUnique. 83 | 84 | version 1.06: fixed CTRl+ALT (Windows alt gr) isn't CTRL any more 85 | fixed double backspace bug for Safari; 86 | added TermGlobals.setDisplay for setting style.display props 87 | termlib.js now outputs lower case html (xhtml compatibility) 88 | 89 | version 1.07: added method rebuild() to rebuild with new color settings. 90 | 91 | version 1.1: fixed a bug in 'more' output mode (cursor could be hidden after 92 | quit) 93 | added socket-extension for server-client talk in a separate file 94 | -> "temlib_socket.js" (to be loaded after termlib.js) 95 | (this is a separate file because we break our compatibility 96 | guide lines with this IO/AJAX library.) 97 | 98 | version 1.2 added color support ("%[+-]c()" markup) 99 | moved paste support from sample file to lib 100 | * TermGlobals.insertText( ) 101 | * TermGlobals.importEachLine( ) 102 | * TermGlobals.importMultiLine( ) 103 | 104 | version 1.3 added word wrapping to write() 105 | * activate with myTerm.wrapOn() 106 | * deactivate with myTerm.wrapOff() 107 | use conf.wrapping (boolean) for a global setting 108 | 109 | version 1.4 Terminal is now an entirely self-contained object 110 | Global references to inner objects for backward compatipility: 111 | * TerminalDefaults => Terminal.prototype.Defaults 112 | * termDefaultHandler => Terminal.prototype.defaultHandler 113 | * termKey => Terminal.prototype.globals.termKey 114 | see also: Terminal.prototype.termKey 115 | * TermGlobals => Terminal.prototype.globals 116 | * termDomKeyRef => Terminal.prototype.globals.termDomKeyRef 117 | 118 | So in effect to outside scripts everything remains the same; 119 | no need to rewrite any existing scripts. 120 | You may now use "this.globals" inside any handlers 121 | to refer to the static global object (TermGlobals). 122 | You may also refer to key definitions as "this.termKey.*". 123 | (Please mind that "this.termKey" is a reference to the static object 124 | and not specific to the instance. A change to "this.termKey" will be 125 | by any other instances of Terminal too.) 126 | 127 | Added method TermGlobals.assignStyle() for custom styles & mark up. 128 | 129 | Unified the color mark up: You may now use color codes (decimal or hex) 130 | inside brackets. e.g.: %c(10)DARKRED%c() or %c(a)DARKRED%c() 131 | 132 | Added key repeat for remapped keys (cursor movements etc). 133 | 134 | version 1.41 fixed a bug in the word wrapping regarding write() output, when 135 | the cursor was set with cursorSet() before. 136 | 137 | version 1.42 fixed a bug which caused Opera to delete 2 chars at once. 138 | introduced property Terminal.isOpera (Boolean) 139 | 140 | version 1.43 enhanced the control handler so it also catches ESC if flag closeOnESC 141 | is set to false. fixed a bug with Safari which fired repeated events 142 | for the control handler for TAB if flag printTab was set to false. 143 | 144 | version 1.5 Changed the license. 145 | Dropped support for Netscape 4 (layers). 146 | HTML-elements are now created by document.createElement, if applicable. 147 | Included the formerly separate socket extension in the main library. 148 | Added methods 'backupScreen()' and 'restoreScreen()' to save a screen 149 | and restore it's content from backup. (see the globbing sample). 150 | 151 | version 1.51 Added basic support of ANSI-SGR-sequences. 152 | 153 | version 1.52 Added method swapBackup(), reorganized some of the accompanying files. 154 | 155 | version 1.54 Fixed BACK_SPACE for Chrome, DELETE for Safari/WebKit. 156 | 157 | version 1.55 Fixed dead keys issue for Mac OS (Leapard & later), vowels only. 158 | version 1.56 Fixed new ESC issue for Safari. 159 | version 1.57 Fixed dead keys fix: now only for Safari/Mac, German (de-de). 160 | version 1.59 Dropped dead keys fix, fixed backspace for Safari. 161 | version 1.6 Saved some bytes by discarding traces of ancient condition syntax 162 | Added input mode "fieldMode" 163 | version 1.61 Changes to defaults implementation of the constructor. 164 | version 1.62 Fixed a bug related to AltGr-sequences with IE8+. 165 | version 1.65 Added options for textColor and textBlur. 166 | 167 | */ 168 | 169 | var Terminal = function(conf) { 170 | if (typeof conf != 'object') conf=new Object(); 171 | for (var i in this.Defaults) { 172 | if (typeof conf[i] == 'undefined') conf[i]=this.Defaults[i]; 173 | } 174 | if (typeof conf.handler != 'function') conf.handler=Terminal.prototype.defaultHandler; 175 | this.conf=conf; 176 | this.setInitValues(); 177 | } 178 | 179 | 180 | Terminal.prototype = { 181 | // prototype definitions (save some 2k on indentation) 182 | 183 | version: '1.62 (original)', 184 | 185 | Defaults: { 186 | // dimensions 187 | cols:80, 188 | rows:24, 189 | // appearance 190 | x:100, 191 | y:100, 192 | termDiv:'termDiv', 193 | bgColor:'#181818', 194 | frameColor:'#555555', 195 | frameWidth:1, 196 | rowHeight:15, 197 | blinkDelay:500, 198 | // css class 199 | fontClass:'term', 200 | // initial cursor mode 201 | crsrBlinkMode:false, 202 | crsrBlockMode:true, 203 | // key mapping 204 | DELisBS:false, 205 | printTab:true, 206 | printEuro:true, 207 | catchCtrlH:true, 208 | closeOnESC:true, 209 | // prevent consecutive history doublets 210 | historyUnique:false, 211 | // optional id 212 | id:0, 213 | // strings 214 | ps:'>', 215 | greeting:'%+r Terminal ready. %-r', 216 | // handlers 217 | handler:null, 218 | ctrlHandler:null, 219 | initHandler:null, 220 | exitHandler:null, 221 | wrapping:false, 222 | mapANSI:false, 223 | ANSItrueBlack:false, 224 | textBlur: 0, 225 | textColor: '' 226 | }, 227 | 228 | setInitValues: function() { 229 | this.isSafari= (navigator.userAgent.indexOf('Safari')>=0 || navigator.userAgent.indexOf('WebKit')>=0)? true:false; 230 | this.isOpera= (window.opera && navigator.userAgent.indexOf('Opera')>=0)? true:false; 231 | this.isChrome= (navigator.userAgent.indexOf('Chrome/')>=0 && navigator.userAgent.indexOf('WebKit')>=0)? true:false; 232 | this.domAPI= (document && document.createElement)? true:false; 233 | this.isMac= (navigator.userAgent.indexOf('Mac')>=0)? true:false; 234 | this.id=this.conf.id; 235 | this.maxLines=this.conf.rows; 236 | this.maxCols=this.conf.cols; 237 | this.termDiv=this.conf.termDiv; 238 | this.crsrBlinkMode=this.conf.crsrBlinkMode; 239 | this.crsrBlockMode=this.conf.crsrBlockMode; 240 | this.blinkDelay=this.conf.blinkDelay; 241 | this.DELisBS=this.conf.DELisBS; 242 | this.printTab=this.conf.printTab; 243 | this.printEuro=this.conf.printEuro; 244 | this.catchCtrlH=this.conf.catchCtrlH; 245 | this.closeOnESC=this.conf.closeOnESC; 246 | this.historyUnique=this.conf.historyUnique; 247 | this.ps=this.conf.ps; 248 | this.closed=false; 249 | this.r; 250 | this.c; 251 | this.charBuf=new Array(); 252 | this.styleBuf=new Array(); 253 | this.scrollBuf=null; 254 | this.blinkBuffer=0; 255 | this.blinkTimer; 256 | this.cursoractive=false; 257 | this.lock=true; 258 | this.insert=false; 259 | this.charMode=false; 260 | this.rawMode=false; 261 | this.lineBuffer=''; 262 | this.inputChar=0; 263 | this.lastLine=''; 264 | this.guiCounter=0; 265 | this.history=new Array(); 266 | this.histPtr=0; 267 | this.env=new Object(); 268 | this.buckupBuffer=null; 269 | this.handler=this.conf.handler; 270 | this.wrapping=this.conf.wrapping; 271 | this.mapANSI=this.conf.mapANSI; 272 | this.ANSItrueBlack=this.conf.ANSItrueBlack; 273 | this.ctrlHandler=this.conf.ctrlHandler; 274 | this.initHandler=this.conf.initHandler; 275 | this.exitHandler=this.conf.exitHandler; 276 | this.fieldMode=false; 277 | this.fieldStart=this.fieldEnd=this.fieldC=0; 278 | this.textBlur=Number(this.conf.textBlur); 279 | if (isNaN(this.textBlur) || this.textBlur<0 || this.textBlur>10) this.textBlur=0; 280 | this.textColor=this.conf.textColor || ''; 281 | }, 282 | 283 | defaultHandler: function() { 284 | this.newLine(); 285 | if (this.lineBuffer != '') { 286 | this.type('You typed: '+this.lineBuffer); 287 | this.newLine(); 288 | } 289 | this.prompt(); 290 | }, 291 | 292 | open: function() { 293 | if (this.termDivReady()) { 294 | if (!this.closed) this._makeTerm(); 295 | this.init(); 296 | return true; 297 | } 298 | else { 299 | return false; 300 | } 301 | }, 302 | 303 | close: function() { 304 | this.lock=true; 305 | this.cursorOff(); 306 | if (this.exitHandler) this.exitHandler(); 307 | this.globals.setVisible(this.termDiv,0); 308 | this.closed=true; 309 | }, 310 | 311 | init: function() { 312 | // wait for gui 313 | if (this.guiReady()) { 314 | this.guiCounter=0; 315 | // clean up at re-entry 316 | if (this.closed) { 317 | this.setInitValues(); 318 | } 319 | this.clear(); 320 | this.globals.setVisible(this.termDiv,1); 321 | this.globals.enableKeyboard(this); 322 | if (this.initHandler) { 323 | this.initHandler(); 324 | } 325 | else { 326 | this.write(this.conf.greeting); 327 | this.newLine(); 328 | this.prompt(); 329 | } 330 | } 331 | else { 332 | this.guiCounter++; 333 | if (this.guiCounter>18000) { 334 | if (confirm('Terminal:\nYour browser hasn\'t responded for more than 2 minutes.\nRetry?')) { 335 | this.guiCounter=0; 336 | } 337 | else { 338 | return; 339 | } 340 | }; 341 | this.globals.termToInitialze=this; 342 | window.setTimeout('Terminal.prototype.globals.termToInitialze.init()',200); 343 | } 344 | }, 345 | 346 | getRowArray: function(l,v) { 347 | // returns a fresh array of l length initialized with value v 348 | var a=new Array(); 349 | for (var i=0; i10) v=0; 365 | if (v!=this.textBlur) { 366 | this.textBlur=v; 367 | for (var r=0, l=this.conf.rows; r=0) { 404 | var ta=text.split('\n'); 405 | text=ta.join('%n'); 406 | } 407 | } 408 | else { 409 | if (text.join) { 410 | text=text.join('%n'); 411 | } 412 | else { 413 | text=''+text; 414 | } 415 | if (text.indexOf('\n')>=0) { 416 | var ta=text.split('\n'); 417 | text=ta.join('%n'); 418 | } 419 | } 420 | if (this.mapANSI) text=this.globals.ANSI_map(text, this.ANSItrueBlack); 421 | this._sbInit(usemore); 422 | var chunks=text.split('%'); 423 | var esc=(text.charAt(0)!='%'); 424 | var style=0; 425 | var styleMarkUp=this.globals.termStyleMarkup; 426 | for (var i=0; i0) { 429 | this._sbType(chunks[i],style); 430 | } 431 | else if (i>0) { 432 | this._sbType('%', style); 433 | } 434 | esc=false; 435 | } 436 | else { 437 | var func=chunks[i].charAt(0); 438 | if (chunks[i].length==0 && i>0) { 439 | this._sbType("%",style); 440 | esc=true; 441 | } 442 | else if (func=='n') { 443 | this._sbNewLine(true); 444 | if (chunks[i].length>1) this._sbType(chunks[i].substring(1),style); 445 | } 446 | else if (func=='+') { 447 | var opt=chunks[i].charAt(1); 448 | opt=opt.toLowerCase(); 449 | if (opt=='p') { 450 | style=0; 451 | } 452 | else if (styleMarkUp[opt]) { 453 | style|=styleMarkUp[opt]; 454 | } 455 | if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style); 456 | } 457 | else if (func=='-') { 458 | var opt=chunks[i].charAt(1); 459 | opt=opt.toLowerCase(); 460 | if (opt=='p') { 461 | style=0; 462 | } 463 | else if (styleMarkUp[opt]) { 464 | style&=~styleMarkUp[opt]; 465 | } 466 | if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style); 467 | } 468 | else if (chunks[i].length>1 && func=='c') { 469 | var cinfo=this._parseColor(chunks[i].substring(1)); 470 | style=(style&(~0xfffff0))|cinfo.style; 471 | if (cinfo.rest) this._sbType(cinfo.rest,style); 472 | } 473 | else if (chunks[i].length>1 && chunks[i].charAt(0)=='C' && chunks[i].charAt(1)=='S') { 474 | this.clear(); 475 | this._sbInit(); 476 | if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style); 477 | } 478 | else { 479 | if (chunks[i].length>0) this._sbType(chunks[i],style); 480 | } 481 | } 482 | } 483 | this._sbOut(); 484 | }, 485 | 486 | // parse a color markup 487 | _parseColor: function(chunk) { 488 | var rest=''; 489 | var style=0; 490 | if (chunk.length) { 491 | if (chunk.charAt(0)=='(') { 492 | var clabel=''; 493 | for (var i=1; ii) rest=chunk.substring(i+1); 497 | break; 498 | } 499 | clabel+=c; 500 | } 501 | if (clabel) { 502 | if (clabel.charAt(0) == '@') { 503 | var sc=this.globals.nsColors[clabel.substring(1).toLowerCase()]; 504 | if (sc) style=(16+sc)*0x100; 505 | } 506 | else if (clabel.charAt(0) == '#') { 507 | var cl=clabel.substring(1).toLowerCase(); 508 | var sc=this.globals.webColors[cl]; 509 | if (sc) { 510 | style=sc*0x10000; 511 | } 512 | else { 513 | cl=this.globals.webifyColor(cl); 514 | if (cl) style=this.globals.webColors[cl]*0x10000; 515 | } 516 | } 517 | else if (clabel.length && clabel.length<=2) { 518 | var isHex=false; 519 | for (var i=0; i=this.maxCols) this._sbNewLine(); 577 | } 578 | }, 579 | 580 | _sbNewLine: function(forced) { 581 | var sb=this.scrollBuf; 582 | if (this.wrapping && forced) { 583 | sb.lines[sb.r][sb.c]=10; 584 | sb.lines[sb.r].length=sb.c+1; 585 | } 586 | sb.r++; 587 | sb.c=0; 588 | sb.lines[sb.r]=this.getRowArray(this.conf.cols,0); 589 | sb.styles[sb.r]=this.getRowArray(this.conf.cols,0); 590 | }, 591 | 592 | _sbWrap: function() { 593 | // create a temp wrap buffer wb and scan for words/wrap-chars 594 | // then re-asign lines & styles to scrollBuf 595 | var wb=new Object(); 596 | wb.lines=new Array(); 597 | wb.styles=new Array(); 598 | wb.lines[0]=this.getRowArray(this.conf.cols,0); 599 | wb.styles[0]=this.getRowArray(this.conf.cols,0); 600 | wb.r=0; 601 | wb.c=0; 602 | var sb=this.scrollBuf; 603 | var sbl=sb.lines; 604 | var sbs=sb.styles; 605 | var ch, st, wrap, lc, ls; 606 | var l=this.c; 607 | var lastR=0; 608 | var lastC=0; 609 | wb.cBreak=false; 610 | for (var r=0; r=this.maxCols) this._wbIncLine(wb); 635 | } 636 | if (wrap==3) { 637 | lastR=r; 638 | lastC=c; 639 | l=1; 640 | } 641 | else { 642 | l=0; 643 | lastR=r; 644 | lastC=c+1; 645 | if (lastC==lc.length) { 646 | lastR++; 647 | lastC=0; 648 | } 649 | if (wrap==4) wb.cBreak=true; 650 | } 651 | } 652 | else { 653 | l++; 654 | } 655 | } 656 | else { 657 | continue; 658 | } 659 | } 660 | } 661 | if (l) { 662 | if (wb.cBreak && wb.c!=0) wb.c--; 663 | this._wbOut(wb, lastR, lastC, l); 664 | } 665 | sb.lines=wb.lines; 666 | sb.styles=wb.styles; 667 | sb.r=wb.r; 668 | sb.c=wb.c; 669 | }, 670 | 671 | _wbOut: function(wb, br, bc, l) { 672 | // copy a word (of l length from br/bc) to wrap buffer wb 673 | var sb=this.scrollBuf; 674 | var sbl=sb.lines; 675 | var sbs=sb.styles; 676 | var ofs=0; 677 | var lc, ls; 678 | if (l+wb.c>this.maxCols) { 679 | if (l=ml-1) this.clear(); 764 | } 765 | } 766 | if (this.r+buflen-sb.line<=ml) { 767 | for (var i=sb.line; i=ml) { 819 | var ofs=buflen-ml; 820 | for (var i=0; i0) { 876 | r=this.conf.rows-offset; 877 | } 878 | else { 879 | r=this.conf.rows-1; 880 | } 881 | for (var i=0; i=0 && r=this.maxCols) { 938 | this.c=0; 939 | this._incRow(); 940 | } 941 | }, 942 | 943 | _incRow: function() { 944 | this.r++; 945 | if (this.r>=this.maxLines) { 946 | this._scrollLines(0,this.maxLines); 947 | this.r=this.maxLines-1; 948 | } 949 | }, 950 | 951 | _scrollLines: function(start, end) { 952 | window.status='Scrolling lines ...'; 953 | start++; 954 | for (var ri=start; ri=start; r--) this.redraw(r-1); 965 | window.status=''; 966 | }, 967 | 968 | // control methods 969 | 970 | clear: function() { 971 | window.status='Clearing display ...'; 972 | this.cursorOff(); 973 | this.insert=false; 974 | for (var ri=0; ri0) this.newLine(); 1000 | this.type(this.ps); 1001 | this._charOut(1); 1002 | this.lock=false; 1003 | this.cursorOn(); 1004 | }, 1005 | 1006 | isPrintable: function(ch, unicodePage1only) { 1007 | if (this.wrapping && this.globals.wrapChars[ch]==4) return true; 1008 | if (unicodePage1only && ch>255) { 1009 | return (ch==this.termKey.EURO && this.printEuro)? true:false; 1010 | } 1011 | return ( 1012 | (ch>=32 && ch!=this.termKey.DEL) || 1013 | (this.printTab && ch==this.termKey.TAB) 1014 | ); 1015 | }, 1016 | 1017 | // cursor methods 1018 | 1019 | cursorSet: function(r,c) { 1020 | var crsron=this.cursoractive; 1021 | if (crsron) this.cursorOff(); 1022 | this.r=r%this.maxLines; 1023 | this.c=c%this.maxCols; 1024 | this._cursorReset(crsron); 1025 | }, 1026 | 1027 | cursorOn: function() { 1028 | if (this.blinkTimer) clearTimeout(this.blinkTimer); 1029 | this.blinkBuffer=this.styleBuf[this.r][this.c]; 1030 | this._cursorBlink(); 1031 | this.cursoractive=true; 1032 | }, 1033 | 1034 | cursorOff: function() { 1035 | if (this.blinkTimer) clearTimeout(this.blinkTimer); 1036 | if (this.cursoractive) { 1037 | this.styleBuf[this.r][this.c]=this.blinkBuffer; 1038 | this.redraw(this.r); 1039 | this.cursoractive=false; 1040 | } 1041 | }, 1042 | 1043 | cursorLeft: function() { 1044 | var crsron=this.cursoractive; 1045 | if (crsron) this.cursorOff(); 1046 | var r=this.r; 1047 | var c=this.c; 1048 | if (c>0) { 1049 | c--; 1050 | } 1051 | else if (r>0) { 1052 | c=this.maxCols-1; 1053 | r--; 1054 | } 1055 | if (this.isPrintable(this.charBuf[r][c])) { 1056 | this.r=r; 1057 | this.c=c; 1058 | } 1059 | this.insert=true; 1060 | this._cursorReset(crsron); 1061 | }, 1062 | 1063 | cursorRight: function() { 1064 | var crsron=this.cursoractive; 1065 | if (crsron) this.cursorOff(); 1066 | var r=this.r; 1067 | var c=this.c; 1068 | if (c0) c-- 1091 | else if (r>0) { 1092 | c=this.maxCols-1; 1093 | r--; 1094 | }; 1095 | if (this.isPrintable(this.charBuf[r][c])) { 1096 | this._scrollLeft(r, c); 1097 | this.r=r; 1098 | this.c=c; 1099 | }; 1100 | this._cursorReset(crsron); 1101 | }, 1102 | 1103 | fwdDelete: function() { 1104 | var crsron=this.cursoractive; 1105 | if (crsron) this.cursorOff(); 1106 | if (this.isPrintable(this.charBuf[this.r][this.c])) { 1107 | this._scrollLeft(this.r,this.c); 1108 | if (!this.isPrintable(this.charBuf[this.r][this.c])) this.insert=false; 1109 | } 1110 | this._cursorReset(crsron); 1111 | }, 1112 | 1113 | _cursorReset: function(crsron) { 1114 | if (crsron) { 1115 | this.cursorOn(); 1116 | } 1117 | else { 1118 | this.blinkBuffer=this.styleBuf[this.r][this.c]; 1119 | } 1120 | }, 1121 | 1122 | _cursorBlink: function() { 1123 | if (this.blinkTimer) clearTimeout(this.blinkTimer); 1124 | if (this == this.globals.activeTerm) { 1125 | if (this.crsrBlockMode) { 1126 | this.styleBuf[this.r][this.c]=(this.styleBuf[this.r][this.c]&1)? 1127 | this.styleBuf[this.r][this.c]&0xfffffe:this.styleBuf[this.r][this.c]|1; 1128 | } 1129 | else { 1130 | this.styleBuf[this.r][this.c]=(this.styleBuf[this.r][this.c]&2)? 1131 | this.styleBuf[this.r][this.c]&0xffffd:this.styleBuf[this.r][this.c]|2; 1132 | } 1133 | this.redraw(this.r); 1134 | } 1135 | if (this.crsrBlinkMode) this.blinkTimer=setTimeout('Terminal.prototype.globals.activeTerm._cursorBlink()', this.blinkDelay); 1136 | }, 1137 | 1138 | _scrollLeft: function(r,c) { 1139 | var rows=new Array(); 1140 | rows[0]=r; 1141 | while (this.isPrintable(this.charBuf[r][c])) { 1142 | var ri=r; 1143 | var ci=c+1; 1144 | if (ci==this.maxCols) { 1145 | if (ri0) { 1204 | r--; 1205 | c=this.maxCols-1; 1206 | } 1207 | else { 1208 | c=0; 1209 | } 1210 | } 1211 | } 1212 | if (this.isPrintable(this.charBuf[r][c])) { 1213 | while (true) { 1214 | var ri=r; 1215 | var ci=c+1; 1216 | if (ci==this.maxCols) { 1217 | if (ri=this.maxCols) this.c=this.maxCols-1; 1275 | } 1276 | var line=new Array(); 1277 | while (this.isPrintable(this.charBuf[r][c])) { 1278 | line[line.length]=String.fromCharCode(this.charBuf[r][c]); 1279 | if (c>0) { 1280 | c--; 1281 | } 1282 | else if (r>0) { 1283 | c=this.maxCols-1; 1284 | r--; 1285 | } 1286 | else { 1287 | break; 1288 | } 1289 | } 1290 | line.reverse(); 1291 | return line.join(''); 1292 | }, 1293 | 1294 | _clearLine: function() { 1295 | var end=this._getLineEnd(this.r,this.c); 1296 | var r=end[0]; 1297 | var c=end[1]; 1298 | var line=''; 1299 | while (this.isPrintable(this.charBuf[r][c])) { 1300 | this.charBuf[r][c]=0; 1301 | if (c>0) { 1302 | c--; 1303 | } 1304 | else if (r>0) { 1305 | this.redraw(r); 1306 | c=this.maxCols-1; 1307 | r--; 1308 | } 1309 | else { 1310 | break; 1311 | } 1312 | } 1313 | if (r!=end[0]) this.redraw(r); 1314 | c++; 1315 | this.cursorSet(r,c); 1316 | this.insert=false; 1317 | }, 1318 | 1319 | // backup/restore screen & state 1320 | 1321 | backupScreen: function() { 1322 | var backup=this.backupBuffer=new Object(); 1323 | var rl=this.conf.rows; 1324 | var cl=this.conf.cols; 1325 | backup.cbuf=new Array(rl); 1326 | backup.sbuf=new Array(rl); 1327 | backup.maxCols=this.maxCols; 1328 | backup.maxLines=this.maxLines; 1329 | backup.r=this.r; 1330 | backup.c=this.c; 1331 | backup.charMode=this.charMode; 1332 | backup.rawMode=this.rawMode; 1333 | backup.handler=this.handler; 1334 | backup.ctrlHandler=this.ctrlHandler; 1335 | backup.cursoractive=this.cursoractive; 1336 | 1337 | backup.crsrBlinkMode=this.crsrBlinkMode; 1338 | backup.crsrBlockMode=this.crsrBlockMode; 1339 | backup.blinkDelay=this.blinkDelay; 1340 | backup.DELisBS=this.DELisBS; 1341 | backup.printTab=this.printTab; 1342 | backup.printEuro=this.printEuro; 1343 | backup.catchCtrlH=this.catchCtrlH; 1344 | backup.closeOnESC=this.closeOnESC; 1345 | backup.historyUnique=this.historyUnique; 1346 | backup.ps=this.ps; 1347 | backup.lineBuffer=this.lineBuffer; 1348 | backup.inputChar=this.inputChar; 1349 | backup.lastLine=this.lastLine; 1350 | backup.historyLength=this.history.length; 1351 | backup.histPtr=this.histPtr; 1352 | backup.wrapping=this.wrapping; 1353 | backup.mapANSI=this.mapANSI; 1354 | backup.ANSItrueBlack=this.ANSItrueBlack; 1355 | if (this.cursoractive) this.cursorOff(); 1356 | for (var r=0; rbackup.historyLength) { 1400 | this.history.length=backup.historyLength; 1401 | this.histPtr=backup.histPtr; 1402 | } 1403 | this.wrapping=backup.wrapping; 1404 | this.mapANSI=backup.mapANSI; 1405 | this.ANSItrueBlack=backup.ANSItrueBlack; 1406 | if (this.cursoractive) this.cursorOn(); 1407 | this.backupBuffer=null; 1408 | }, 1409 | 1410 | swapBackup: function() { 1411 | // swap current state and backup buffer (e.g.: toggle do/undo) 1412 | var backup=this.backupBuffer; 1413 | this.backupScreen; 1414 | if (backup) { 1415 | var backup2=this.backupBuffer; 1416 | this.backupBuffer=backup; 1417 | this.restoreScreen(); 1418 | this.backupBuffer=backup2; 1419 | } 1420 | }, 1421 | 1422 | // simple markup escaping 1423 | 1424 | escapeMarkup: function(t) { 1425 | return t.replace(/%/g, '%%'); 1426 | }, 1427 | 1428 | // field mode 1429 | 1430 | enterFieldMode: function(start, end, style) { 1431 | this.cursorOff(); 1432 | if (start===undefined || start<0) start=this.c; 1433 | if (end=== undefined || endthis.maxCols) end=this.maxCols; 1434 | if (!style) style=0; 1435 | this.fieldStart=start; 1436 | this.fieldEnd=end; 1437 | this.fieldStyle=style; 1438 | this.fieldC=0; 1439 | this.lastLine=''; 1440 | this.fieldMode=true; 1441 | this.rawMode=this.charMode=false; 1442 | if (style&1) { 1443 | this._crsrWasBlockMode=this.crsrBlockMode; 1444 | this._crsrWasBlinkMode=this.crsrBlinkMode; 1445 | this.crsrBlockMode=false; 1446 | this.crsrBlinkMode=true; 1447 | } 1448 | this.drawField(); 1449 | this.lock=false; 1450 | }, 1451 | 1452 | exitFieldMode: function() { 1453 | this.drawField(true); 1454 | this.fieldMode=false; 1455 | this.c=this.fieldEnd; 1456 | if (this.c==this.maxLine) this.newLine(); 1457 | this.lock=true; 1458 | }, 1459 | 1460 | drawField: function(isfinal) { 1461 | this.cursorOff(); 1462 | if (isfinal) this.fieldC=0; 1463 | var fl=this.fieldEnd-this.fieldStart; 1464 | if (this.fieldC==this.lastLine.length) fl--; 1465 | var ofs=this.fieldC-fl; 1466 | if (ofs<0) ofs=0; 1467 | var line = (ofs)? this.lastLine.substring(ofs):this.lastLine; 1468 | var sb=this.styleBuf[this.r]; 1469 | var cb=this.charBuf[this.r]; 1470 | var max=line.length; 1471 | for (var i=this.fieldStart, k=0; i\n'; 1564 | s+='
\n'; 1565 | var rstr=''; 1566 | for (var c=0; c'+rstr+'<\/td><\/tr>\n'; 1570 | } 1571 | s+='<\/table><\/td><\/tr>\n'; 1572 | s+='<\/table><\/td><\/tr>\n'; 1573 | s+='<\/table>\n'; 1574 | var termOffset=2+this.conf.frameWidth; 1575 | if (this.globals.hasSubDivs) { 1576 | for (var r=0; r<\/div>\n'; 1578 | } 1579 | this.globals.termStringStart='
'; 1580 | this.globals.termStringEnd='<\/td><\/tr><\/table>'; 1581 | } 1582 | this.globals.writeElement(this.termDiv,s); 1583 | } 1584 | if (!rebuild) { 1585 | this.globals.setElementXY(this.termDiv,this.conf.x,this.conf.y); 1586 | this.globals.setVisible(this.termDiv,1); 1587 | } 1588 | window.status=''; 1589 | }, 1590 | 1591 | rebuild: function() { 1592 | // check for bounds and array lengths 1593 | var rl=this.conf.rows; 1594 | var cl=this.conf.cols; 1595 | for (var r=0; r=rl) { 1610 | r=rl-1; 1611 | resetcrsr=true; 1612 | } 1613 | if (this.c>=cl) { 1614 | c=cl-1; 1615 | resetcrsr=true; 1616 | } 1617 | if (resetcrsr && this.cursoractive) this.cursorOn(); 1618 | // and actually rebuild 1619 | this._makeTerm(true); 1620 | for (var r=0; r=0; k--) { 1669 | var st=tstls[k]; 1670 | if (curStyle & st) s+=tscls[st]; 1671 | } 1672 | } 1673 | curStyle=cs; 1674 | for (var k=0; k>>8; 1681 | clr= (cc<16)? tclrs[cc] : '#'+tnclrs[cc-16]; 1682 | } 1683 | else if (curStyle & 0xff0000) { 1684 | clr='#'+twclrs[(curStyle & 0xff0000)>>>16]; 1685 | } 1686 | if (clr) { 1687 | if (curStyle&1) { 1688 | s+=''; 1689 | } 1690 | else if (blur) { 1691 | s+=''; 1692 | } 1693 | else { 1694 | s+=''; 1695 | } 1696 | } 1697 | } 1698 | s+= (tspcl[c])? tspcl[c] : String.fromCharCode(c); 1699 | } 1700 | if (curStyle>0) { 1701 | if (curStyle & 0xffff00) s+=''; 1702 | for (var k=tstls.length-1; k>=0; k--) { 1703 | var st=tstls[k]; 1704 | if (curStyle&st) s+=tscls[st]; 1705 | } 1706 | } 1707 | s+=this.globals.termStringEnd; 1708 | this.globals.writeElement(this.termDiv+'_r'+r,s); 1709 | }, 1710 | 1711 | guiReady: function() { 1712 | var ready=true; 1713 | if (this.globals.guiElementsReady(this.termDiv)) { 1714 | for (var r=0; r='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))? true:false; 1803 | }, 1804 | 1805 | isHexOnlyChar: function(c) { 1806 | return ((c>='a' && c<='f') || (c>='A' && c<='F'))? true:false; 1807 | }, 1808 | 1809 | hexToNum: { 1810 | '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, 1811 | '8': 8, '9': 9, 'a': 10, 'b': 11, 'c': 12, 'd': 13, 'e': 14, 'f': 15, 1812 | 'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15 1813 | }, 1814 | 1815 | // data for color support 1816 | 1817 | webColors: [], 1818 | webColorCodes: [''], 1819 | 1820 | colors: { 1821 | // ANSI bright (bold) color set 1822 | black: 1, 1823 | red: 2, 1824 | green: 3, 1825 | yellow: 4, 1826 | blue: 5, 1827 | magenta: 6, 1828 | cyan: 7, 1829 | white: 8, 1830 | // dark color set 1831 | grey: 9, 1832 | red2: 10, 1833 | green2: 11, 1834 | yellow2: 12, 1835 | blue2: 13, 1836 | magenta2: 14, 1837 | cyan2: 15, 1838 | // synonyms 1839 | red1: 2, 1840 | green1: 3, 1841 | yellow1: 4, 1842 | blue1: 5, 1843 | magenta1: 6, 1844 | cyan1: 7, 1845 | gray: 9, 1846 | darkred: 10, 1847 | darkgreen: 11, 1848 | darkyellow: 12, 1849 | darkblue: 13, 1850 | darkmagenta: 14, 1851 | darkcyan: 15, 1852 | // default color 1853 | 'default': 0, 1854 | clear: 0 1855 | }, 1856 | 1857 | colorCodes: [ 1858 | '', '#000000', '#ff0000', '#00ff00', '#ffff00', '#0066ff', '#ff00ff', '#00ffff', '#ffffff', 1859 | '#808080', '#990000', '#009900', '#999900', '#003399', '#990099', '#009999' 1860 | ], 1861 | 1862 | nsColors: { 1863 | 'aliceblue': 1, 'antiquewhite': 2, 'aqua': 3, 'aquamarine': 4, 1864 | 'azure': 5, 'beige': 6, 'black': 7, 'blue': 8, 1865 | 'blueviolet': 9, 'brown': 10, 'burlywood': 11, 'cadetblue': 12, 1866 | 'chartreuse': 13, 'chocolate': 14, 'coral': 15, 'cornflowerblue': 16, 1867 | 'cornsilk': 17, 'crimson': 18, 'darkblue': 19, 'darkcyan': 20, 1868 | 'darkgoldenrod': 21, 'darkgray': 22, 'darkgreen': 23, 'darkkhaki': 24, 1869 | 'darkmagenta': 25, 'darkolivegreen': 26, 'darkorange': 27, 'darkorchid': 28, 1870 | 'darkred': 29, 'darksalmon': 30, 'darkseagreen': 31, 'darkslateblue': 32, 1871 | 'darkslategray': 33, 'darkturquoise': 34, 'darkviolet': 35, 'deeppink': 36, 1872 | 'deepskyblue': 37, 'dimgray': 38, 'dodgerblue': 39, 'firebrick': 40, 1873 | 'floralwhite': 41, 'forestgreen': 42, 'fuchsia': 43, 'gainsboro': 44, 1874 | 'ghostwhite': 45, 'gold': 46, 'goldenrod': 47, 'gray': 48, 1875 | 'green': 49, 'greenyellow': 50, 'honeydew': 51, 'hotpink': 52, 1876 | 'indianred': 53, 'indigo': 54, 'ivory': 55, 'khaki': 56, 1877 | 'lavender': 57, 'lavenderblush': 58, 'lawngreen': 59, 'lemonchiffon': 60, 1878 | 'lightblue': 61, 'lightcoral': 62, 'lightcyan': 63, 'lightgoldenrodyellow': 64, 1879 | 'lightgreen': 65, 'lightgrey': 66, 'lightpink': 67, 'lightsalmon': 68, 1880 | 'lightseagreen': 69, 'lightskyblue': 70, 'lightslategray': 71, 'lightsteelblue': 72, 1881 | 'lightyellow': 73, 'lime': 74, 'limegreen': 75, 'linen': 76, 1882 | 'maroon': 77, 'mediumaquamarine': 78, 'mediumblue': 79, 'mediumorchid': 80, 1883 | 'mediumpurple': 81, 'mediumseagreen': 82, 'mediumslateblue': 83, 'mediumspringgreen': 84, 1884 | 'mediumturquoise': 85, 'mediumvioletred': 86, 'midnightblue': 87, 'mintcream': 88, 1885 | 'mistyrose': 89, 'moccasin': 90, 'navajowhite': 91, 'navy': 92, 1886 | 'oldlace': 93, 'olive': 94, 'olivedrab': 95, 'orange': 96, 1887 | 'orangered': 97, 'orchid': 98, 'palegoldenrod': 99, 'palegreen': 100, 1888 | 'paleturquoise': 101, 'palevioletred': 102, 'papayawhip': 103, 'peachpuff': 104, 1889 | 'peru': 105, 'pink': 106, 'plum': 107, 'powderblue': 108, 1890 | 'purple': 109, 'red': 110, 'rosybrown': 111, 'royalblue': 112, 1891 | 'saddlebrown': 113, 'salmon': 114, 'sandybrown': 115, 'seagreen': 116, 1892 | 'seashell': 117, 'sienna': 118, 'silver': 119, 'skyblue': 120, 1893 | 'slateblue': 121, 'slategray': 122, 'snow': 123, 'springgreen': 124, 1894 | 'steelblue': 125, 'tan': 126, 'teal': 127, 'thistle': 128, 1895 | 'tomato': 129, 'turquoise': 130, 'violet': 131, 'wheat': 132, 1896 | 'white': 133, 'whitesmoke': 134, 'yellow': 135, 'yellowgreen': 136 1897 | }, 1898 | 1899 | nsColorCodes: [ 1900 | '', 1901 | 'f0f8ff', 'faebd7', '00ffff', '7fffd4', 1902 | 'f0ffff', 'f5f5dc', '000000', '0000ff', 1903 | '8a2be2', 'a52a2a', 'deb887', '5f9ea0', 1904 | '7fff00', 'd2691e', 'ff7f50', '6495ed', 1905 | 'fff8dc', 'dc143c', '00008b', '008b8b', 1906 | 'b8860b', 'a9a9a9', '006400', 'bdb76b', 1907 | '8b008b', '556b2f', 'ff8c00', '9932cc', 1908 | '8b0000', 'e9967a', '8fbc8f', '483d8b', 1909 | '2f4f4f', '00ced1', '9400d3', 'ff1493', 1910 | '00bfff', '696969', '1e90ff', 'b22222', 1911 | 'fffaf0', '228b22', 'ff00ff', 'dcdcdc', 1912 | 'f8f8ff', 'ffd700', 'daa520', '808080', 1913 | '008000', 'adff2f', 'f0fff0', 'ff69b4', 1914 | 'cd5c5c', '4b0082', 'fffff0', 'f0e68c', 1915 | 'e6e6fa', 'fff0f5', '7cfc00', 'fffacd', 1916 | 'add8e6', 'f08080', 'e0ffff', 'fafad2', 1917 | '90ee90', 'd3d3d3', 'ffb6c1', 'ffa07a', 1918 | '20b2aa', '87cefa', '778899', 'b0c4de', 1919 | 'ffffe0', '00ff00', '32cd32', 'faf0e6', 1920 | '800000', '66cdaa', '0000cd', 'ba55d3', 1921 | '9370db', '3cb371', '7b68ee', '00fa9a', 1922 | '48d1cc', 'c71585', '191970', 'f5fffa', 1923 | 'ffe4e1', 'ffe4b5', 'ffdead', '000080', 1924 | 'fdf5e6', '808000', '6b8e23', 'ffa500', 1925 | 'ff4500', 'da70d6', 'eee8aa', '98fb98', 1926 | 'afeeee', 'db7093', 'ffefd5', 'ffdab9', 1927 | 'cd853f', 'ffc0cb', 'dda0dd', 'b0e0e6', 1928 | '800080', 'ff0000', 'bc8f8f', '4169e1', 1929 | '8b4513', 'fa8072', 'f4a460', '2e8b57', 1930 | 'fff5ee', 'a0522d', 'c0c0c0', '87ceeb', 1931 | '6a5acd', '708090', 'fffafa', '00ff7f', 1932 | '4682b4', 'd2b48c', '008080', 'd8bfd8', 1933 | 'ff6347', '40e0d0', 'ee82ee', 'f5deb3', 1934 | 'ffffff', 'f5f5f5', 'ffff00', '9acd32' 1935 | ], 1936 | _webSwatchChars: ['0','3','6','9','c','f'], 1937 | _initWebColors: function() { 1938 | // generate long and short web color ref 1939 | var tg=Terminal.prototype.globals; 1940 | var ws=tg._webColorSwatch; 1941 | var wn=tg.webColors; 1942 | var cc=tg.webColorCodes; 1943 | var n=1; 1944 | var a, b, c, al, bl, bs, cl; 1945 | for (var i=0; i<6; i++) { 1946 | a=tg._webSwatchChars[i]; 1947 | al=a+a; 1948 | for (var j=0; j<6; j++) { 1949 | b=tg._webSwatchChars[j]; 1950 | bl=al+b+b; 1951 | bs=a+b; 1952 | for (var k=0; k<6; k++) { 1953 | c=tg._webSwatchChars[k]; 1954 | cl=bl+c+c; 1955 | wn[bs+c]=wn[cl]=n; 1956 | cc[n]=cl; 1957 | n++; 1958 | } 1959 | } 1960 | } 1961 | }, 1962 | 1963 | webifyColor: function(s) { 1964 | // return nearest web color in 3 digit format 1965 | // (do without RegExp for compatibility) 1966 | var tg=Terminal.prototype.globals; 1967 | if (s.length==6) { 1968 | var c=''; 1969 | for (var i=0; i<6; i+=2) { 1970 | var a=s.charAt(i); 1971 | var b=s.charAt(i+1); 1972 | if (tg.isHexChar(a) && tg.isHexChar(b)) { 1973 | c+=tg._webSwatchChars[Math.round(parseInt(a+b,16)/255*5)]; 1974 | } 1975 | else { 1976 | return ''; 1977 | } 1978 | } 1979 | return c; 1980 | } 1981 | else if (s.length==3) { 1982 | var c=''; 1983 | for (var i=0; i<3; i++) { 1984 | var a=s.charAt(i); 1985 | if (tg.isHexChar(a)) { 1986 | c+=tg._webSwatchChars[Math.round(parseInt(a,16)/15*5)]; 1987 | } 1988 | else { 1989 | return ''; 1990 | } 1991 | } 1992 | return c; 1993 | } 1994 | else { 1995 | return ''; 1996 | } 1997 | }, 1998 | 1999 | // public methods for color support 2000 | 2001 | setColor: function(label, value) { 2002 | var tg=Terminal.prototype.globals; 2003 | if (typeof label == 'number' && label>=1 && label<=15) { 2004 | tg.colorCodes[label]=value; 2005 | } 2006 | else if (typeof label == 'string') { 2007 | label=label.toLowerCase(); 2008 | if (label.length==1 && tg.isHexChar(label)) { 2009 | var n=tg.hexToNum[label]; 2010 | if (n) tg.colorCodes[n]=value; 2011 | } 2012 | else if (typeof tg.colors[label] != 'undefined') { 2013 | var n=tg.colors[label]; 2014 | if (n) tg.colorCodes[n]=value; 2015 | } 2016 | } 2017 | }, 2018 | 2019 | getColorString: function(label) { 2020 | var tg=Terminal.prototype.globals; 2021 | if (typeof label == 'number' && label>=0 && label<=15) { 2022 | return tg.colorCodes[label]; 2023 | } 2024 | else if (typeof label == 'string') { 2025 | label=label.toLowerCase(); 2026 | if (label.length==1 && tg.isHexChar(label)) { 2027 | return tg.colorCodes[tg.hexToNum[label]]; 2028 | } 2029 | else if (typeof tg.colors[label] != 'undefined') { 2030 | return tg.colorCodes[tg.colors[label]]; 2031 | } 2032 | } 2033 | return ''; 2034 | }, 2035 | 2036 | getColorCode: function(label) { 2037 | var tg=Terminal.prototype.globals; 2038 | if (typeof label == 'number' && label>=0 && label<=15) { 2039 | return label; 2040 | } 2041 | else if (typeof label == 'string') { 2042 | label=label.toLowerCase(); 2043 | if (label.length==1 && tg.isHexChar(label)) { 2044 | return parseInt(label,16); 2045 | } 2046 | else if (typeof tg.colors[label] != 'undefined') { 2047 | return tg.colors[label]; 2048 | } 2049 | } 2050 | return 0; 2051 | }, 2052 | 2053 | // import/paste methods (methods return success) 2054 | 2055 | insertText: function(text) { 2056 | // auto-types a given string to the active terminal 2057 | // returns success (false indicates a lock or no active terminal) 2058 | var tg=Terminal.prototype.globals; 2059 | var termRef = tg.activeTerm; 2060 | if (!termRef || termRef.closed || tg.keylock || termRef.lock || termRef.charMode || termRef.fieldMode) return false; 2061 | // terminal open and unlocked, so type the text 2062 | for (var i=0; i; 2107 | // (no history entry for this) 2108 | termRef.lineBuffer = text; 2109 | termRef.lastLine = ''; 2110 | termRef.inputChar = 0; 2111 | termRef.handler(); 2112 | return true; 2113 | }, 2114 | 2115 | // text related service functions 2116 | 2117 | normalize: function(n,m) { 2118 | var s=''+n; 2119 | while (s.length=0) { 2141 | t=t.substring(0,ofs)+s2+t.substring(ofs+l1); 2142 | ofs=t.indexOf(s1,ofs+l2); 2143 | } 2144 | return t; 2145 | }, 2146 | 2147 | 2148 | // config data for text wrap 2149 | 2150 | wrapChars: { 2151 | // keys: charCode 2152 | // values: 1 = white space, 2 = wrap after, 3 = wrap before, 4 = conditional word break 2153 | 9: 1, // tab 2154 | 10: 1, // new line - don't change this (used internally)!!! 2155 | 12: 4, // form feed (use this for conditional word breaks) 2156 | 13: 1, // cr 2157 | 32: 1, // blank 2158 | 40: 3, // ( 2159 | 45: 2, // dash/hyphen 2160 | 61: 2, // = 2161 | 91: 3, // [ 2162 | 94: 3, // caret (non-printing chars) 2163 | 123: 3 // { 2164 | }, 2165 | 2166 | 2167 | // keyboard methods & controls 2168 | 2169 | setFocus: function(termref) { 2170 | Terminal.prototype.globals.activeTerm=termref; 2171 | Terminal.prototype.globals.clearRepeatTimer(); 2172 | }, 2173 | 2174 | termKey: { 2175 | // codes of special keys 2176 | 'NUL': 0x00, 2177 | 'SOH': 0x01, 2178 | 'STX': 0x02, 2179 | 'ETX': 0x03, 2180 | 'EOT': 0x04, 2181 | 'ENQ': 0x05, 2182 | 'ACK': 0x06, 2183 | 'BEL': 0x07, 2184 | 'BS': 0x08, 2185 | 'BACKSPACE': 0x08, 2186 | 'HT': 0x09, 2187 | 'TAB': 0x09, 2188 | 'LF': 0x0A, 2189 | 'VT': 0x0B, 2190 | 'FF': 0x0C, 2191 | 'CR': 0x0D, 2192 | 'SO': 0x0E, 2193 | 'SI': 0x0F, 2194 | 'DLE': 0x10, 2195 | 'DC1': 0x11, 2196 | 'DC2': 0x12, 2197 | 'DC3': 0x13, 2198 | 'DC4': 0x14, 2199 | 'NAK': 0x15, 2200 | 'SYN': 0x16, 2201 | 'ETB': 0x17, 2202 | 'CAN': 0x18, 2203 | 'EM': 0x19, 2204 | 'SUB': 0x1A, 2205 | 'ESC': 0x1B, 2206 | 'IS4': 0x1C, 2207 | 'IS3': 0x1D, 2208 | 'IS2': 0x1E, 2209 | 'IS1': 0x1F, 2210 | 'DEL': 0x7F, 2211 | // other specials 2212 | 'EURO': 0x20AC, 2213 | // cursor mapping 2214 | 'LEFT': 0x1C, 2215 | 'RIGHT': 0x1D, 2216 | 'UP': 0x1E, 2217 | 'DOWN': 0x1F 2218 | }, 2219 | 2220 | // map some DOM_VK_* properties to values defined in termKey 2221 | termDomKeyRef: {}, 2222 | _domKeyMappingData: { 2223 | 'LEFT': 'LEFT', 2224 | 'RIGHT': 'RIGHT', 2225 | 'UP': 'UP', 2226 | 'DOWN': 'DOWN', 2227 | 'BACK_SPACE': 'BS', 2228 | 'RETURN': 'CR', 2229 | 'ENTER': 'CR', 2230 | 'ESCAPE': 'ESC', 2231 | 'DELETE': 'DEL', 2232 | 'TAB': 'TAB' 2233 | }, 2234 | _initDomKeyRef: function() { 2235 | var tg=Terminal.prototype.globals; 2236 | var m=tg._domKeyMappingData; 2237 | var r=tg.termDomKeyRef; 2238 | var k=tg.termKey; 2239 | for (var i in m) r['DOM_VK_'+i]=k[m[i]]; 2240 | }, 2241 | 2242 | registerEvent: function(obj, eventType, handler, capture) { 2243 | if (obj.addEventListener) { 2244 | obj.addEventListener(eventType.toLowerCase(), handler, capture); 2245 | } 2246 | /* 2247 | else if (obj.attachEvent) { 2248 | obj.attachEvent('on'+eventType.toLowerCase(), handler); 2249 | } 2250 | */ 2251 | else { 2252 | var et=eventType.toUpperCase(); 2253 | if (window.Event && window.Event[et] && obj.captureEvents) obj.captureEvents(Event[et]); 2254 | obj['on'+eventType.toLowerCase()]=handler; 2255 | } 2256 | }, 2257 | releaseEvent: function(obj, eventType, handler, capture) { 2258 | if (obj.removeEventListener) { 2259 | obj.removeEventListener(eventType.toLowerCase(), handler, capture); 2260 | } 2261 | /* 2262 | else if (obj.detachEvent) { 2263 | obj.detachEvent('on'+eventType.toLowerCase(), handler); 2264 | } 2265 | */ 2266 | else { 2267 | var et=eventType.toUpperCase(); 2268 | if (window.Event && window.Event[et] && obj.releaseEvents) obj.releaseEvents(Event[et]); 2269 | et='on'+eventType.toLowerCase(); 2270 | if (obj[et] && obj[et]==handler) obj.et=null; 2271 | } 2272 | }, 2273 | 2274 | enableKeyboard: function(term) { 2275 | var tg=Terminal.prototype.globals; 2276 | if (!tg.kbdEnabled) { 2277 | tg.registerEvent(document, 'keypress', tg.keyHandler, true); 2278 | tg.registerEvent(document, 'keydown', tg.keyFix, true); 2279 | tg.registerEvent(document, 'keyup', tg.clearRepeatTimer, true); 2280 | tg.kbdEnabled=true; 2281 | } 2282 | tg.activeTerm=term; 2283 | }, 2284 | 2285 | disableKeyboard: function(term) { 2286 | var tg=Terminal.prototype.globals; 2287 | if (tg.kbdEnabled) { 2288 | tg.releaseEvent(document, 'keypress', tg.keyHandler, true); 2289 | tg.releaseEvent(document, 'keydown', tg.keyFix, true); 2290 | tg.releaseEvent(document, 'keyup', tg.clearRepeatTimer, true); 2291 | tg.kbdEnabled=false; 2292 | } 2293 | tg.activeTerm=null; 2294 | }, 2295 | 2296 | // remap some special key mappings on keydown 2297 | 2298 | keyFix: function(e) { 2299 | var tg=Terminal.prototype.globals; 2300 | var term=tg.activeTerm; 2301 | var ch; 2302 | if (tg.keylock || term.lock) return true; 2303 | if (window.event) { 2304 | if (!e) e=window.event; 2305 | ch=e.keyCode; 2306 | if (e.DOM_VK_UP) { 2307 | for (var i in tg.termDomKeyRef) { 2308 | if (e[i] && ch == e[i]) { 2309 | tg.keyHandler({which:tg.termDomKeyRef[i],_remapped:true,_repeat:(ch==0x1B)? true:false}); 2310 | if (e.preventDefault) e.preventDefault(); 2311 | if (e.stopPropagation) e.stopPropagation(); 2312 | e.cancelBubble=true; 2313 | return false; 2314 | } 2315 | } 2316 | e.cancelBubble=false; 2317 | return true; 2318 | } 2319 | else { 2320 | // no DOM support 2321 | var termKey=term.termKey; 2322 | var keyHandler=tg.keyHandler; 2323 | if (ch==8 && !term.isOpera) { keyHandler({which:termKey.BS,_remapped:true,_repeat:true}); } 2324 | else if (ch==9) { keyHandler({which:termKey.TAB,_remapped:true,_repeat: (term.printTab)? false:true}); } 2325 | else if (ch==27) { keyHandler({which:termKey.ESC,_remapped:true,_repeat: (term.printTab)? false:true}); } 2326 | else if (ch==37) { keyHandler({which:termKey.LEFT,_remapped:true,_repeat:true}); } 2327 | else if (ch==39) { keyHandler({which:termKey.RIGHT,_remapped:true,_repeat:true}); } 2328 | else if (ch==38) { keyHandler({which:termKey.UP,_remapped:true,_repeat:true}); } 2329 | else if (ch==40) { keyHandler({which:termKey.DOWN,_remapped:true,_repeat:true}); } 2330 | else if (ch==127 || ch==46) { keyHandler({which:termKey.DEL,_remapped:true,_repeat:true}); } 2331 | else if (ch>=57373 && ch<=57376) { 2332 | if (ch==57373) { keyHandler({which:termKey.UP,_remapped:true,_repeat:true}); } 2333 | else if (ch==57374) { keyHandler({which:termKey.DOWN,_remapped:true,_repeat:true}); } 2334 | else if (ch==57375) { keyHandler({which:termKey.LEFT,_remapped:true,_repeat:true}); } 2335 | else if (ch==57376) { keyHandler({which:termKey.RIGHT,_remapped:true,_repeat:true}); } 2336 | } 2337 | else { 2338 | e.cancelBubble=false; 2339 | return true; 2340 | } 2341 | if (e.preventDefault) e.preventDefault(); 2342 | if (e.stopPropagation) e.stopPropagation(); 2343 | e.cancelBubble=true; 2344 | return false; 2345 | } 2346 | } 2347 | }, 2348 | 2349 | clearRepeatTimer: function(e) { 2350 | var tg=Terminal.prototype.globals; 2351 | if (tg.keyRepeatTimer) { 2352 | clearTimeout(tg.keyRepeatTimer); 2353 | tg.keyRepeatTimer=null; 2354 | } 2355 | }, 2356 | 2357 | doKeyRepeat: function(ch) { 2358 | Terminal.prototype.globals.keyHandler({which:ch,_remapped:true,_repeated:true}) 2359 | }, 2360 | 2361 | keyHandler: function(e) { 2362 | var tg=Terminal.prototype.globals; 2363 | var term=tg.activeTerm; 2364 | if (tg.keylock || term.lock || term.isMac && e && e.metaKey) return true; 2365 | if (window.event) { 2366 | if (window.event.preventDefault) window.event.preventDefault(); 2367 | if (window.event.stopPropagation) window.event.stopPropagation(); 2368 | } 2369 | else if (e) { 2370 | if (e.preventDefault) e.preventDefault(); 2371 | if (e.stopPropagation) e.stopPropagation(); 2372 | } 2373 | var ch; 2374 | var ctrl=false; 2375 | var shft=false; 2376 | var remapped=false; 2377 | var termKey=term.termKey; 2378 | var keyRepeat=0; 2379 | if (e) { 2380 | ch=e.which; 2381 | ctrl=((e.ctrlKey && !e.altKey) || e.modifiers==2); 2382 | shft=(e.shiftKey || e.modifiers==4); 2383 | if (e._remapped) { 2384 | remapped=true; 2385 | if (window.event) { 2386 | //ctrl=(ctrl || window.event.ctrlKey); 2387 | ctrl=(ctrl || (window.event.ctrlKey && !window.event.altKey)); 2388 | shft=(shft || window.event.shiftKey); 2389 | } 2390 | } 2391 | if (e._repeated) { 2392 | keyRepeat=2; 2393 | } 2394 | else if (e._repeat) { 2395 | keyRepeat=1; 2396 | } 2397 | } 2398 | else if (window.event) { 2399 | ch=window.event.keyCode; 2400 | //ctrl=(window.event.ctrlKey); 2401 | ctrl=(window.event.ctrlKey && !window.event.altKey); // allow alt gr == ctrl alt 2402 | shft=(window.event.shiftKey); 2403 | if (window.event._repeated) { 2404 | keyRepeat=2; 2405 | } 2406 | else if (window.event._repeat) { 2407 | keyRepeat=1; 2408 | } 2409 | } 2410 | else { 2411 | return true; 2412 | } 2413 | if (ch=='' && remapped==false) { 2414 | // map specials 2415 | if (e==null) e=window.event; 2416 | if (e.charCode==0 && e.keyCode) { 2417 | if (e.DOM_VK_UP) { 2418 | var dkr=tg.termDomKeyRef; 2419 | for (var i in dkr) { 2420 | if (e[i] && e.keyCode == e[i]) { 2421 | ch=dkr[i]; 2422 | break; 2423 | } 2424 | } 2425 | } 2426 | else { 2427 | // NS4 2428 | if (e.keyCode==28) { ch=termKey.LEFT; } 2429 | else if (e.keyCode==29) { ch=termKey.RIGHT; } 2430 | else if (e.keyCode==30) { ch=termKey.UP; } 2431 | else if (e.keyCode==31) { ch=termKey.DOWN; } 2432 | // Mozilla alike but no DOM support 2433 | else if (e.keyCode==37) { ch=termKey.LEFT; } 2434 | else if (e.keyCode==39) { ch=termKey.RIGHT; } 2435 | else if (e.keyCode==38) { ch=termKey.UP; } 2436 | else if (e.keyCode==40) { ch=termKey.DOWN; } 2437 | // just to have the TAB mapping here too 2438 | else if (e.keyCode==9) { ch=termKey.TAB; } 2439 | } 2440 | } 2441 | } 2442 | // leave on unicode private use area (might be function key etc) 2443 | if ((ch>=0xE000) && (ch<= 0xF8FF)) return; 2444 | if (keyRepeat) { 2445 | tg.clearRepeatTimer(); 2446 | tg.keyRepeatTimer = window.setTimeout( 2447 | 'Terminal.prototype.globals.doKeyRepeat('+ch+')', 2448 | (keyRepeat==1)? tg.keyRepeatDelay1:tg.keyRepeatDelay2 2449 | ); 2450 | } 2451 | // key actions 2452 | if (term.charMode) { 2453 | term.insert=false; 2454 | term.inputChar=ch; 2455 | term.lineBuffer=''; 2456 | term.handler(); 2457 | if (ch<=32 && window.event) window.event.cancelBubble=true; 2458 | return false; 2459 | } 2460 | if (!ctrl) { 2461 | // special keys 2462 | if (ch==termKey.CR) { 2463 | term.lock=true; 2464 | term.cursorOff(); 2465 | term.insert=false; 2466 | if (term.rawMode) { 2467 | term.lineBuffer=term.lastLine; 2468 | } 2469 | else if (term.fieldMode) { 2470 | term.lineBuffer=term.lastLine; 2471 | term.exitFieldMode(); 2472 | } 2473 | else { 2474 | term.lineBuffer=term._getLine(true); 2475 | if ( 2476 | term.lineBuffer!='' && 2477 | (!term.historyUnique || term.history.length==0 || 2478 | term.lineBuffer!=term.history[term.history.length-1]) 2479 | ) { 2480 | term.history[term.history.length]=term.lineBuffer; 2481 | } 2482 | term.histPtr=term.history.length; 2483 | } 2484 | term.lastLine=''; 2485 | term.inputChar=0; 2486 | term.handler(); 2487 | if (window.event) window.event.cancelBubble=true; 2488 | return false; 2489 | } 2490 | else if (term.fieldMode) { 2491 | if (ch==termKey.ESC) { 2492 | term.lineBuffer=term.lastLine=''; 2493 | term.exitFieldMode(); 2494 | term.lastLine=''; 2495 | term.inputChar=0; 2496 | term.handler(); 2497 | if (window.event) window.event.cancelBubble=true; 2498 | return false; 2499 | } 2500 | else if (ch==termKey.LEFT) { 2501 | if (term.fieldC>0) term.fieldC--; 2502 | } 2503 | else if (ch==termKey.RIGHT) { 2504 | if (term.fieldC0) { 2508 | term.lastLine=term.lastLine.substring(0,term.fieldC-1)+term.lastLine.substring(term.fieldC); 2509 | term.fieldC--; 2510 | } 2511 | } 2512 | else if (ch==termKey.DEL) { 2513 | if (term.fieldC=32) { 2518 | term.lastLine=term.lastLine.substring(0,term.fieldC)+String.fromCharCode(ch)+term.lastLine.substring(term.fieldC); 2519 | term.fieldC++; 2520 | } 2521 | term.drawField(); 2522 | return false; 2523 | } 2524 | else if (ch==termKey.ESC && term.conf.closeOnESC) { 2525 | term.close(); 2526 | if (window.event) window.event.cancelBubble=true; 2527 | return false; 2528 | } 2529 | if (ch<32 && term.rawMode) { 2530 | if (window.event) window.event.cancelBubble=true; 2531 | return false; 2532 | } 2533 | else { 2534 | if (ch==termKey.LEFT) { 2535 | term.cursorLeft(); 2536 | if (window.event) window.event.cancelBubble=true; 2537 | return false; 2538 | } 2539 | else if (ch==termKey.RIGHT) { 2540 | term.cursorRight(); 2541 | if (window.event) window.event.cancelBubble=true; 2542 | return false; 2543 | } 2544 | else if (ch==termKey.UP) { 2545 | term.cursorOff(); 2546 | if (term.histPtr==term.history.length) term.lastLine=term._getLine(); 2547 | term._clearLine(); 2548 | if (term.history.length && term.histPtr>=0) { 2549 | if (term.histPtr>0) term.histPtr--; 2550 | term.type(term.history[term.histPtr]); 2551 | } 2552 | else if (term.lastLine) { 2553 | term.type(term.lastLine); 2554 | } 2555 | term.cursorOn(); 2556 | if (window.event) window.event.cancelBubble=true; 2557 | return false; 2558 | } 2559 | else if (ch==termKey.DOWN) { 2560 | term.cursorOff(); 2561 | if (term.histPtr==term.history.length) term.lastLine=term._getLine(); 2562 | term._clearLine(); 2563 | if (term.history.length && term.histPtr<=term.history.length) { 2564 | if (term.histPtr=65 && ch<=96) || ch==63) { 2617 | // remap canonical 2618 | if (ch==63) { 2619 | ch=31; 2620 | } 2621 | else { 2622 | ch-=64; 2623 | } 2624 | } 2625 | term.inputChar=ch; 2626 | term.ctrlHandler(); 2627 | if (window.event) window.event.cancelBubble=true; 2628 | return false; 2629 | } 2630 | else if (ctrl || !term.isPrintable(ch,true)) { 2631 | if (window.event) window.event.cancelBubble=true; 2632 | return false; 2633 | } 2634 | else if (term.isPrintable(ch,true)) { 2635 | if (term.blinkTimer) clearTimeout(term.blinkTimer); 2636 | if (term.insert) { 2637 | term.cursorOff(); 2638 | term._scrollRight(term.r,term.c); 2639 | } 2640 | term._charOut(ch); 2641 | term.cursorOn(); 2642 | if (ch==32 && window.event) { 2643 | window.event.cancelBubble=true; 2644 | } 2645 | else if (window.opera && window.event) { 2646 | window.event.cancelBubble=true; 2647 | } 2648 | return false; 2649 | } 2650 | } 2651 | return true; 2652 | }, 2653 | 2654 | 2655 | // gui mappings 2656 | 2657 | hasSubDivs: false, 2658 | termStringStart: '', 2659 | termStringEnd: '', 2660 | 2661 | termSpecials: { 2662 | // special HTML escapes 2663 | 0: ' ', 2664 | 1: ' ', 2665 | 9: ' ', 2666 | 32: ' ', 2667 | 34: '"', 2668 | 38: '&', 2669 | 60: '<', 2670 | 62: '>', 2671 | 127: '◊', 2672 | 0x20AC: '€' 2673 | }, 2674 | 2675 | // extensive list of max 8 styles (2^n, n<16) 2676 | termStyles: [1,2,4,8, 16], 2677 | // style markup: one letter keys, reserved keys: "p" (plain), "c" (color) 2678 | termStyleMarkup: { 2679 | 'r': 1, 2680 | 'u': 2, 2681 | 'i': 4, 2682 | 's': 8, 2683 | 'b': 16 // map "b" to 16 (italics) for ANSI mapping 2684 | }, 2685 | // mappings for styles (heading HTML) 2686 | termStyleOpen: { 2687 | 1: '', 2688 | 2: '', 2689 | 4: '', 2690 | 8: '', 2691 | 16: '' 2692 | }, 2693 | // mapping for styles (trailing HTML) 2694 | termStyleClose: { 2695 | 1: '<\/span>', 2696 | 2: '<\/u>', 2697 | 4: '<\/i>', 2698 | 8: '<\/strike>', 2699 | 16: '' 2700 | }, 2701 | 2702 | // method to install custom styles 2703 | assignStyle: function(styleCode, markup, htmlOpen, htmlClose) { 2704 | var tg=Terminal.prototype.globals; 2705 | // check params 2706 | if (!styleCode || isNaN(styleCode)) { 2707 | if (styleCode>=256) { 2708 | alert('termlib.js:\nCould not assign style.\n'+s+' is not a valid power of 2 between 0 and 256.'); 2709 | return; 2710 | } 2711 | } 2712 | var s=styleCode&0xff; 2713 | var matched=false; 2714 | for (var i=0; i<8; i++) { 2715 | if ((s>>>i)&1) { 2716 | if (matched) { 2717 | alert('termlib.js:\nCould not assign style code.\n'+s+' is not a power of 2!'); 2718 | return; 2719 | } 2720 | matched=true; 2721 | } 2722 | } 2723 | if (!matched) { 2724 | alert('termlib.js:\nCould not assign style code.\n'+s+' is not a valid power of 2 between 0 and 256.'); 2725 | return; 2726 | } 2727 | markup=String(markup).toLowerCase(); 2728 | if (markup=='c' || markup=='p') { 2729 | alert('termlib.js:\nCould not assign mark up.\n"'+markup+'" is a reserved code.'); 2730 | return; 2731 | } 2732 | if (markup.length>1) { 2733 | alert('termlib.js:\nCould not assign mark up.\n"'+markup+'" is not a single letter code.'); 2734 | return; 2735 | } 2736 | var exists=false; 2737 | for (var i=0; i=32) { 2899 | var cs=unescape("%"+high+low); 2900 | termString_keyref[cc]=cs; 2901 | termString_keycoderef[cs]=cc; 2902 | } 2903 | } 2904 | } 2905 | }, 2906 | 2907 | _extendMissingStringMethods: function() { 2908 | if (!String.fromCharCode || !String.prototype.charCodeAt) { 2909 | Terminal.prototype.globals._termString_makeKeyref(); 2910 | } 2911 | if (!String.fromCharCode) { 2912 | String.fromCharCode=function(cc) { 2913 | return (cc!=null)? Terminal.prototype.globals.termString_keyref[cc] : ''; 2914 | }; 2915 | } 2916 | if (!String.prototype.charCodeAt) { 2917 | String.prototype.charCodeAt=function(n) { 2918 | cs=this.charAt(n); 2919 | return (Terminal.prototype.globals.termString_keycoderef[cs])? 2920 | Terminal.prototype.globals.termString_keycoderef[cs] : 0; 2921 | }; 2922 | } 2923 | } 2924 | 2925 | // end of Terminal.prototype.globals 2926 | } 2927 | 2928 | // end of Terminal.prototype 2929 | } 2930 | 2931 | // initialize global data 2932 | Terminal.prototype.globals._initGlobals(); 2933 | 2934 | // global entities for backward compatibility with termlib 1.x applications 2935 | var TerminalDefaults = Terminal.prototype.Defaults; 2936 | var termDefaultHandler = Terminal.prototype.defaultHandler; 2937 | var TermGlobals = Terminal.prototype.globals; 2938 | var termKey = Terminal.prototype.globals.termKey; 2939 | var termDomKeyRef = Terminal.prototype.globals.termDomKeyRef; 2940 | 2941 | 2942 | /* 2943 | === termlib.js Socket Extension v.1.02 === 2944 | 2945 | (c) Norbert Landsteiner 2003-2007 2946 | mass:werk - media environments 2947 | 2948 | 2949 | # Synopsis: 2950 | Integrates async XMLHttpRequests (AJAX/JSON) tightly into termlib.js 2951 | 2952 | # Example: 2953 | 2954 | myTerm = new Terminal( { handler: myTermHandler } ); 2955 | myTerm.open(); 2956 | 2957 | function myTermHandler() { 2958 | this.newLine(); 2959 | if (this.lineBuffer == 'get file') { 2960 | myTerm.send( 2961 | { 2962 | url: 'myservice', 2963 | data: { 2964 | book: 'theBook', 2965 | chapter: 7, 2966 | page: 45 2967 | }, 2968 | callback: myCallback 2969 | } 2970 | ); 2971 | return; 2972 | } 2973 | else { 2974 | // ... 2975 | } 2976 | this.prompt(); 2977 | } 2978 | 2979 | function myCallback() { 2980 | if (this.socket.success) { 2981 | this.write(this.socket.responseText); 2982 | } 2983 | else { 2984 | this.write('OOPS: ' + this.socket.status + ' ' + this.socket.statusText); 2985 | if (this.socket.errno) { 2986 | this.newLine(); 2987 | this.write('Error: ' + this.socket.errstring); 2988 | } 2989 | } 2990 | this.prompt(); 2991 | } 2992 | 2993 | 2994 | # Documentation: 2995 | 2996 | for usage and description see readme.txt chapter 13: 2997 | 2998 | 2999 | or refer to the sample page: 3000 | 3001 | 3002 | */ 3003 | 3004 | Terminal.prototype._HttpSocket = function() { 3005 | var req=null; 3006 | if (window.XMLHttpRequest) { 3007 | try { 3008 | req=new XMLHttpRequest(); 3009 | } 3010 | catch(e) {} 3011 | } 3012 | else if (window.ActiveXObject) { 3013 | var prtcls=this._msXMLHttpObjects; 3014 | for (var i=0; i1) this.prototype._msXMLHttpObjects= [ prtcls[i] ]; 3020 | break; 3021 | } 3022 | } 3023 | catch(e) {} 3024 | } 3025 | } 3026 | this.request=req; 3027 | this.url; 3028 | this.data=null; 3029 | this.query=''; 3030 | this.timeoutTimer=null; 3031 | this.localMode=Boolean(window.location.href.search(/^file:/i)==0); 3032 | this.error=0; 3033 | } 3034 | 3035 | Terminal.prototype._HttpSocket.prototype = { 3036 | version: '1.02', 3037 | // config 3038 | useXMLEncoding: false, // use ";" as separator if true, "&" else 3039 | defaulTimeout: 10000, // request timeout in ticks (milliseconds) 3040 | defaultMethod: 'GET', 3041 | forceNewline: true, // translate line-breaks in responseText to newlines 3042 | 3043 | // static const 3044 | errno: { 3045 | OK: 0, 3046 | NOTIMPLEMENTED: 1, 3047 | FATALERROR: 2, 3048 | TIMEOUT: 3, 3049 | NETWORKERROR: 4, 3050 | LOCALFILEERROR: 5 3051 | }, 3052 | errstring: [ 3053 | '', 3054 | 'XMLHttpRequest not implemented.', 3055 | 'Could not open XMLHttpRequest.', 3056 | 'The connection timed out.', 3057 | 'Network error.', 3058 | 'The requested local document was not found.' 3059 | ], 3060 | 3061 | // private static data 3062 | _msXMLHttpObjects: [ 3063 | 'Msxml2.XMLHTTP', 3064 | 'Microsoft.XMLHTTP', 3065 | 'Msxml2.XMLHTTP.5.0', 3066 | 'Msxml2.XMLHTTP.4.0', 3067 | 'Msxml2.XMLHTTP.3.0' 3068 | ], 3069 | 3070 | // internal methods 3071 | serializeData: function() { 3072 | this.query=this.serialize(this.data); 3073 | }, 3074 | serialize: function(data) { 3075 | var v=''; 3076 | if( data != null ) { 3077 | switch (typeof data) { 3078 | case 'object': 3079 | var d=[]; 3080 | if (data instanceof Array) { 3081 | // array 3082 | for (var i=0; i= 200 && r.status < 300) { 3167 | success=true; 3168 | } 3169 | else if (r.status >= 12000) { 3170 | // MSIE network error 3171 | failed=true; 3172 | this.error=this.errno.NETWORKERROR; 3173 | } 3174 | } 3175 | } 3176 | } 3177 | catch(e) {} 3178 | if (!failed) { 3179 | response.status=r.status; 3180 | response.statusText= (r.status==404)? 'Not Found':r.statusText; // force correct header 3181 | response.responseText=r.responseText; 3182 | response.responseXML=r.responseXML; 3183 | if (this.getHeaders) { 3184 | if (this.getHeaders instanceof Array) { 3185 | for (var i=0; i 6 | 7 | 8 | 9 | 10 | ### COMPATIBILITY WARNING ### 11 | 12 | Dropped support of Netscape 4 (layers) with version 1.5! 13 | Netscape 4 is now outdated for more than 10 years. Any further support of this browser 14 | would be of academic nature. As a benefit this step allows us to include the socket 15 | extension in the main library, so there are no additional files to load anymore. 16 | 17 | 18 | For the first time there is a backward compatibility issue from version 1.3 to version 1.4: 19 | The following applies to the style vector for the `type()' method while using colors: 20 | 21 | while with version 1.3 a color was encoded using the color code times 16 (0xf), e.g.: 22 | 23 | myTerm.type( 'This is red.', 2*16 ); 24 | 25 | this changed with version 1.4 to the color code times 256 (0xff), e.g.: 26 | 27 | myTerm.type( 'This is red.', 2*256 ); 28 | 29 | 30 | All other style encodings or color API remain unchanged. 31 | Since this feature was only introduced in version 1.3 and there are no known applications 32 | that would use a statement like the above (since you would usually use the `write()' method 33 | for complex output), this seems to be good bargain for some codes for custom styles. 34 | C.f.: sect 7.5 "TermGlobals.assignStyle()" 35 | 36 | 37 | ### Mac OS X Dead-Keys ### 38 | 39 | (Dead-keys: combinations of accents and characters that are built by two consecutively pressed keys.) 40 | Mac OS X 10.5 and later doesn't fire a keyboard event for dead keys anymore. 41 | It's possible to fix this for Safari by a custom dead keys emulation, but not for Chrome or Firefox. 42 | "termlib.js" provides automatic translations of common dead-keys for Safari in German (de-de). 43 | In case you would need dead-keys for another language, please contact me via http://www.masswerk.at/. 44 | 45 | 46 | 47 | Contents: 48 | 49 | 1 About 50 | 2 Creating a new Terminal Instance 51 | 2.1 Configuration Values 52 | 3 Using the Terminal 53 | 3.1 The Default Handler 54 | 3.2 Input Modes 55 | 3.2.1 Normal Line Input (Command Line Mode) 56 | 3.2.1.2 Special Keys (ctrlHandler) 57 | 3.2.2 Raw Mode 58 | 3.2.3 Character Mode 59 | 3.3 Other Handlers 60 | 3.3.1 initHandler 61 | 3.3.2 exitHandler 62 | 3.4 Flags for Behaviour Control 63 | 4 Output Methods 64 | 4.1 Terminal.type() 65 | 4.2 Terminal.write() 66 | 4.3 Terminal.typeAt() 67 | 4.4 Terminal.setChar() 68 | 4.5 Terminal.newLine() 69 | 4.6 Terminal.clear() 70 | 4.7 Terminal.statusLine() 71 | 4.8 Terminal.printRowFromString() 72 | 4.9 Terminal.redraw() 73 | 4.10 Using Color 74 | 4.11 Text Wrap - Terminal.wrapOn(), Terminal.wrapOff() 75 | 4.12 ANSI Support 76 | 5 Cursor Methods and Editing 77 | 5.1 Terminal.cursorOn() 78 | 5.2 Terminal.cursorOff() 79 | 5.3 Terminal.cursorSet() 80 | 5.4 Terminal.cursorLeft() 81 | 5.5 Terminal.cursorRight() 82 | 5.6 Terminal.backspace() 83 | 5.7 Terminal.fwdDelete() 84 | 5.8 Terminal.isPrintable() 85 | 6 Other Methods of the Terminal Object 86 | 6.1 Terminal.prompt() 87 | 6.2 Terminal.reset() 88 | 6.3 Terminal.open() 89 | 6.4 Terminal.close() 90 | 6.5 Terminal.focus() 91 | 6.6 Terminal.moveTo() 92 | 6.7 Terminal.resizeTo() 93 | 6.8 Terminal.getDimensions() 94 | 6.9 Terminal.rebuild() 95 | 6.10 Terminal.backupScreen() 96 | 6.11 Terminal.restoreScreen() 97 | 6.12 Terminal.swapBackup() 98 | 6.13 Terminal.setTextColor() 99 | 6.14 Terminal.setTextBlur() 100 | 7 Global Static Methods (TermGlobals) 101 | 7.1 TermGlobals.setFocus() 102 | 7.2 TermGlobals.keylock (Global Locking Flag) 103 | 7.3 TermGlobals Text Methods 104 | 7.3.1 TermGlobals.normalize() 105 | 7.3.2 TermGlobals.fillLeft() 106 | 7.3.3 TermGlobals.center() 107 | 7.3.4 TermGlobals.stringReplace() 108 | 7.4 TermGlobals Import Methods 109 | 7.4.1 TermGlobals.insertText() 110 | 7.4.2 TermGlobals.importEachLine() 111 | 7.4.3 TermGlobals.importMultiLine() 112 | 7.5 TermGlobals.assignStyle() 113 | 8 Localization 114 | 9 The Socket Extension (Remote Communication) 115 | 9.1 A First Example 116 | 9.2 The send() API 117 | 9.3 Global Config Settings 118 | 9.4 The Callback (Response Handling) 119 | 9.5 Error Codes 120 | 9.6 Note on Compatibly / Browser Requirements 121 | 9.7 termlib_socket.js Version History 122 | 10 Cross Browser Functions 123 | 11 Architecture, Internals 124 | 11.1 Global Entities 125 | 11.2 I/O Architecture 126 | 11.3 Compatibility 127 | 12 History 128 | 13 Example for a Command Line Parser 129 | 14 License 130 | 15 Disclaimer 131 | 16 Donations 132 | 17 References 133 | 134 | 135 | 136 | 137 | 1 About 138 | 139 | The Terminal library "termlib.js" provides an object oriented constructor and control 140 | methods for a terminal-like DHTML interface. 141 | 142 | "termlib.js" features direct keyboard input and powerful output methods for multiple 143 | instances of the `Terminal' object (including focus control). 144 | "termlib.js" also comprises methods for a transparent handling of client-server com- 145 | munications via XMLHttpRequests (see sect. 9 "The Socket Extension"). 146 | 147 | The library was written with the aim of simple usage and a maximum of compatibility with 148 | minimal foot print in the global namespace. 149 | 150 | 151 | A simple example: 152 | 153 | // creating a terminal and using it 154 | 155 | var term = new Terminal( {handler: termHandler} ); 156 | term.open(); 157 | 158 | function termHandler() { 159 | var line = this.lineBuffer; 160 | this.newLine(); 161 | if (line == "help") { 162 | this.write(helpPage) 163 | } 164 | else if (line == "exit") { 165 | this.close(); 166 | return; 167 | } 168 | else if (line != "") { 169 | this.write("You typed: "+line); 170 | } 171 | this.prompt(); 172 | } 173 | 174 | var helpPage = [ 175 | "This is the monstrous help page for my groovy terminal.", 176 | "Commands available:", 177 | " help ... print this monstrous help page", 178 | " exit ... leave this groovy terminal", 179 | " ", 180 | "Have fun!" 181 | ]; 182 | 183 | 184 | You should provide CSS font definitions for the classes ".term" (normal video) and 185 | ".termReverse" (reverse video) in a monospaced font. 186 | A sample stylesheet "term_styles.css" comes with this library. 187 | 188 | See the sample application "multiterm_test.html" for a demo of multiple terminals. 189 | 190 | v.1.01: If you configure to use another font class (see 2.1 Configuration Values), 191 | you must provide a subclass ".termReverse" for reversed video. 192 | 193 | p.e.: .myFontClass .termReverse { 194 | /* your definitions for reverse video here */ 195 | } 196 | 197 | With the addition of `conf.fontClass' you can now create multiple 198 | instances with independend appearences. 199 | 200 | 201 | 202 | 203 | 2 Creating a new Terminal Instance 204 | 205 | Use the `new' constructor to create a new instance of the Terminal object. You will want 206 | to supply a configuration object as an argument to the constructor. If the `new' 207 | constructor is called without an object as its first argument, default values are used. 208 | 209 | p.e.: 210 | 211 | // creating a new instance of Terminal 212 | 213 | var conf= { 214 | x: 100, 215 | y: 100, 216 | cols: 80, 217 | rows: 24 218 | } 219 | 220 | var term = new Term(conf); 221 | term.open(); 222 | 223 | `Terminal.open()' initializes the terminal and makes it visible to the user. 224 | This is handled in by separate method to allow the re-initilization of instances 225 | previously closed. 226 | 227 | NOTE: 228 | The division or HTML-element that holds the terminal must be present when calling 229 | `Terminal.open()'. So you must not call this method from the header of a HTML-document at 230 | compile time. 231 | 232 | 233 | 234 | 2.1 Configuration Values 235 | 236 | Set any of these values in your configuration object to override: 237 | 238 | 239 | LABEL DEFAULT VALUE COMMENT 240 | 241 | x 100 terminal's position x in px 242 | y 100 terminal's position y in px 243 | divDiv 'termDiv' id of terminals CSS division 244 | bgColor '#181818' background color (HTML hex value) 245 | frameColor '#555555' frame color (HTML hex value) 246 | frameWidth 1 frame border width in px 247 | fontClass 'term' class name of CSS font definition to use 248 | cols 80 number of cols per row 249 | rows 24 number of rows 250 | rowHeight 15 a row's line-height in px 251 | blinkDelay 500 delay for cursor blinking in milliseconds 252 | crsrBlinkMode false true for blinking cursor 253 | crsrBlockMode true true for block-cursor else underscore 254 | DELisBS false handle as 255 | printTab true handle as printable (prints as space) 256 | printEuro true handle unicode 0x20AC (Euro sign) as printable 257 | catchCtrlH true handle ^H as 258 | closeOnESC true close terminal on 259 | historyUnique false prevent consecutive and identical entries in history 260 | id 0 terminal id 261 | ps '>' prompt string 262 | greeting '%+r Terminal ready. %-r' string for greeting if no initHandler is used 263 | handler defaultHandler reference to handler for command interpretation 264 | ctrlHandler null reference to handler called on uncatched special keys 265 | initHandler null reference to handler called at end of init() 266 | exitHandler null reference to handler called on close() 267 | wrapping false text wrapping for `write()' on/off 268 | mapANSI false enable mapping of ANSI escape sequences (SGR only) 269 | ANSItrueBlack false force ANSI 30m to be rendered as black (default: fg color) 270 | textColor '' String, default text color (color 0), overrides any CSS rules 271 | textBlur 0 Number, if set, adds a CSS text-shadow with the given number of px 272 | ("text-shadow: 0 0 px "), use this with textColor 273 | 274 | 275 | At least you will want to specify `handler' to implement your own command parser. 276 | 277 | Note: While `id' is not used by the Termninal object, it provides an easy way to identify 278 | multiple terminals by the use of "this.id". (e.g.: "if (this.id == 1) startupterm = true;") 279 | 280 | p.e.: 281 | 282 | // creating two individual Terminal instances 283 | 284 | var term1 = new Terminal( 285 | { 286 | id: 1, 287 | x: 200, 288 | y: 10, 289 | cols: 80, 290 | rows: 12, 291 | greeting: "*** This is Terminal 1 ***", 292 | handler: myTerminalHandler 293 | } 294 | ); 295 | term1.open(); 296 | 297 | var term2 = new Terminal( 298 | { 299 | id: 2, 300 | x, 200, 301 | y: 220, 302 | cols: 80 303 | rows: 12, 304 | greeting: "*** This is Terminal 2 ***", 305 | handler: myTerminalHandler 306 | } 307 | ); 308 | term2.open(); 309 | 310 | 311 | 312 | 313 | 3 Using the Terminal 314 | 315 | There are 4 different handlers that are called by a Terminal instance to process input and 316 | some flags to control the input mode and behaviour. 317 | 318 | 319 | 320 | 3.1 The Default Handler (a simlple example for input handling) 321 | 322 | If no handlers are defined in the configuration object, a default handler is called to 323 | handle a line of user input. The default command line handler `defaultHandler' just 324 | closes the command line with a new line and echos the input back to the user: 325 | 326 | function termDefaultHandler() { 327 | this.newLine(); 328 | if (this.lineBuffer != '') { 329 | this.type('You typed: '+this.lineBuffer); 330 | this.newLine(); 331 | } 332 | this.prompt(); 333 | } 334 | 335 | // Note: This used to be top level function. With version 1.4 `termDefaultHandler' became 336 | // a reference to the method `Terminal.prototype.defaultHandler'. 337 | 338 | First you may note that the instance is refered to as `this'. So you need not worry about 339 | which Terminal instance is calling your handler. As the handler is entered, the terminal 340 | is locked for user input and the cursor is off. The current input is available as a string 341 | value in `this.lineBuffer'. 342 | 343 | The method `type()' just does what it says and types a string at the current cursor 344 | position to the terminal screen. 345 | 346 | `newLine()' moves the cursor to a new line. 347 | 348 | The method `prompt()' adds a new line if the cursor isn't at the start of a line, outputs 349 | the prompt string (as specified in the configuration), activates the cursor, and unlocks 350 | the terminal for further input. While you're doing normal command line processing, always 351 | call `prompt()' when leaving your handler. 352 | 353 | In fact this is all you need to create your own terminal application. Please see at least 354 | the method `write()' for a more powerful output method. 355 | 356 | Below we will refer to all methods of the Terminal object as `Terminal.()'. 357 | You can call them as `this.()' in a handler or as methods of your named instance 358 | in other context (e.g.: "myTerminal.close()"). 359 | 360 | [In technical terms these methods are methods of the Terminal's prototype object, while 361 | the properties are properties of a Termninal instance. Since this doesn't make any 362 | difference to your script, we'll refer to both as `Terminal.'.] 363 | 364 | 365 | 366 | 3.2 Input Modes 367 | 368 | 3.2.1 Normal Line Input (Command Line Mode) 369 | 370 | By default the terminal is in normal input mode. Any printable characters in the range of 371 | ASCII 0x20 - 0xff are echoed to the terminal and may be edited with the use of the cursor 372 | keys and the key. 373 | The cursor keys UP and DOWN let the user browse in the command line history (the list of 374 | all commands issued previously in this Terminal instance). 375 | 376 | If the user presses or , the line is read from the terminal buffer, converted 377 | to a string, and placed in `Terminal.lineBuffer' (-> `this.lineBuffer') for further use. 378 | The terminal is then locked for further input and the specified handler 379 | (`Terminal.handler') is called. 380 | 381 | 382 | 3.2.1.2 Special Keys (ctrlHandler) 383 | 384 | If a special character (ASCII<0x20) or an according combination of and a key is 385 | pressed, which is not caught for editing or "enter", and a handler for `ctrlHandler' is 386 | specified, this handler is called. 387 | The ASCII value of the special character is available in `Terminal.inputChar'. Please note 388 | that the terminal is neither locked, nor is the cursor off - all further actions have to 389 | be controlled by `ctrlHandler'. (The tracking of - combinations as "^C" usually 390 | works but cannot be taken for granted.) 391 | 392 | A named reference of the special control values in POSIX form (as well as the values of 393 | the cursor keys [LEFT, RIGHT, UP, DOWN]) is available in the `termKey' object. 394 | 395 | Note: 396 | With version 1.4 `termKey' is a reference to `Terminal.prototype.globals.termKey'. 397 | This object is also mapped to `Terminal.prototype.termKey', so you may also access it as 398 | "this.termKey" inside handlers. 399 | 400 | p.e.: 401 | 402 | // a simple ctrlHandler 403 | 404 | function myCtrlHandler() { 405 | if (this.inputChar == termKey.ETX) { 406 | // exit on ^C (^C == ASCII 0x03 == ) 407 | this.close(); 408 | } 409 | } 410 | 411 | If no `ctrlHandler' is specified, control keys are ignored (default). 412 | 413 | 414 | 3.2.2 Raw Mode 415 | 416 | If the flag `Terminal.rawMode' is set to a value evaluating to `true', no special keys are 417 | tracked but and (and , if the flag `Terminal.closeOnESC' is set). 418 | The input is NOT echoed to the terminal. All printable key values [0x20-0xff] are 419 | transformed to characters and added to `Terminal.lineBuffer' sequentially. The command 420 | line input is NOT added to the history. 421 | 422 | This mode is especially suitable for password input. 423 | 424 | p.e.: 425 | 426 | // using raw mode for password input 427 | 428 | function myTermHandler() { 429 | this.newLine(); 430 | // we stored a flag in Terminal.env to track the status 431 | if (this.env.getpassword) { 432 | // leave raw mode 433 | this.rawMode = false; 434 | if (passwords[this.env.user] == this.lineBuffer) { 435 | // matched 436 | this.type('Welcome '+this.env.user); 437 | this.env.loggedin = true; 438 | } 439 | else { 440 | this.type('Sorry.'); 441 | } 442 | this.env.getpassword = false; 443 | } 444 | else { 445 | // simple parsing 446 | var args = this.lineBuffer.split(' '); 447 | var cmd = args[0]; 448 | if (cmd == 'login') { 449 | var user = args[1]; 450 | if (!user) { 451 | this.type('usage: login '); 452 | } 453 | else { 454 | this.env.user = user; 455 | this.env.getpassword = true; 456 | this.type('password? '); 457 | // enter raw mode 458 | this.rawMode = true; 459 | // leave without prompt so we must unlock first 460 | this.lock = false; 461 | return; 462 | } 463 | } 464 | /* 465 | other actions ... 466 | */ 467 | } 468 | this.prompt(); 469 | } 470 | 471 | In this example a handler is set up to process the command "login " and ask for 472 | a password for the given user name in raw mode. Note the use of the object `Terminal.env' 473 | which is just an empty object set up at the creation of the Terminal instance. Its only 474 | purpose is to provide an individual namespace for private data to be stored by a Terminal 475 | instance. 476 | 477 | NOTE: The flag `Terminal.lock' is used to control the keyboard locking. If we would not 478 | set this to `false' before leaving in raw mode, we would be caught in dead-lock, since no 479 | input could be entered and our handler wouldn't be called again. - A dreadful end of our 480 | terminal session. 481 | 482 | NOTE: Raw mode utilizes the property `Terminal.lastLine' to collect the input string. 483 | This is normally emty, when a handler is called. This is not the case if your script left 484 | the input process on a call of ctrlHandler. You should clear `Terminal.lastLine' in such 485 | a case, if you're going to enter raw mode immediatly after this. 486 | 487 | 488 | 3.2.3 Character Mode 489 | 490 | If the flag `Terminal.charMode' is set to a value evaluating to `true', the terminal is in 491 | character mode. In this mode the numeric ASCII value of the next key typed is stored in 492 | `Terminal.inputChar'. The input is NOT echoed to the terminal. NO locking or cursor 493 | control is performed and left to the handler. 494 | You can use this mode to implement your editor or a console game. 495 | `Terminal.charMode' takes precedence over `Terminal.rawMode'. 496 | 497 | p.e.: 498 | 499 | // using char mode 500 | 501 | function myTermHandler() { 502 | // this is the normal handler 503 | this.newLine(); 504 | // simple parsing 505 | var args = this.lineBuffer.split(' '); 506 | var cmd = args[0]; 507 | if (cmd == 'edit') { 508 | // init the editor 509 | myEditor(this); 510 | // redirect the handler to editor 511 | this.handler = myEditor; 512 | // leave in char mode 513 | this.charMode = true; 514 | // show cursor 515 | this.cursorOn(); 516 | // don't forget unlocking 517 | this.lock = false; 518 | return; 519 | } 520 | /* 521 | other actions ... 522 | */ 523 | this.prompt(); 524 | } 525 | 526 | function myEditor(initterm) { 527 | // our dummy editor (featuring modal behaviour) 528 | if (initterm) { 529 | // perform initialization tasks 530 | initterm.clear(); 531 | initterm.write('this is a simple test editor; leave with then "q"%n%n'); 532 | initterm.env.mode = ''; 533 | // store a reference of the calling handler 534 | initterm.env.handler = initterm.handler; 535 | return; 536 | } 537 | // called as handler -> lock first 538 | this.lock=true; 539 | // hide cursor 540 | this.cursorOff(); 541 | var key = this.inputChar; 542 | if (this.env.mode == 'ctrl') { 543 | // control mode 544 | if (key == 113) { 545 | // "q" => quit 546 | // leave charMode and reset the handler to normal 547 | this.charMode = false; 548 | this.handler = this.env.handler; 549 | // clear the screen 550 | this.clear(); 551 | // prompt and return 552 | this.prompt(); 553 | return; 554 | } 555 | else { 556 | // leave control mode 557 | this.env.mode = ''; 558 | } 559 | } 560 | else { 561 | // edit mode 562 | if (key == termKey.ESC) { 563 | // enter control mode 564 | // we'd better indicate this in a status line ... 565 | this.env.mode = 'ctrl'; 566 | } 567 | else if (key == termKey.LEFT) { 568 | // cursor left 569 | } 570 | else if (key == termKey.RIGHT) { 571 | // cursor right 572 | } 573 | if (key == termKey.UP) { 574 | // cursor up 575 | } 576 | else if (key == termKey.DOWN) { 577 | // cursor down 578 | } 579 | else if (key == termKey.CR) { 580 | // cr or enter 581 | } 582 | else if (key == termKey.BS) { 583 | // backspace 584 | } 585 | else if (key == termKey.DEL) { 586 | // fwd delete 587 | // conf.DELisBS is not evaluated in charMode! 588 | } 589 | else if (this.isPrintable(key)) { 590 | // printable char - just type it 591 | var ch = String.fromCharCode(key); 592 | this.type(ch); 593 | } 594 | } 595 | // leave unlocked with cursor 596 | this.lock = false; 597 | this.cursorOn(); 598 | } 599 | 600 | 601 | Note the redirecting of the input handler to replace the command line handler by the 602 | editor. The method `Terminal.clear()' clears the terminal. 603 | `Terminal.cursorOn()' and `Terminal.cursorOff()' are used to show and hide the cursor. 604 | 605 | 606 | 607 | 3.3 Other Handlers 608 | 609 | There are two more handlers that can be specified in the configuration object: 610 | 611 | 612 | 3.3.1 initHandler 613 | 614 | `initHandler' is called at the end of the initialization triggered by `Terminal.open()'. 615 | The default action - if no `initHandler' is specified - is: 616 | 617 | // default initilization 618 | 619 | this.write(this.conf.greeting); 620 | this.newLine(); 621 | this.prompt(); 622 | 623 | Use `initHandler' to perform your own start up tasks (e.g. show a start up screen). Keep 624 | in mind that you should unlock the terminal and possibly show a cursor to give the 625 | impression of a usable terminal. 626 | 627 | 628 | 3.3.2 exitHandler 629 | 630 | `exitHandler' is called by `Terminal.close()' just before hiding the terminal. You can use 631 | this handler to implement any tasks to be performed on exit. Note that this handler is 632 | called even if the terminal is closed on outside of your inputHandlers control. 633 | 634 | See the file "multiterm_test.html" for an example. 635 | 636 | 637 | 638 | 3.4 Overview: Flags for Behaviour Control 639 | 640 | These falgs are accessible as `Terminal.' at runtime. If not stated else, the 641 | initial value may be specified in the configuration object. 642 | The configuration object and its properties are accessible at runtime via `Terminal.conf'. 643 | 644 | 645 | NAME DEFAULT VALUE MEANING 646 | 647 | blink_delay 500 delay for cursor blinking in milliseconds. 648 | 649 | crsrBlinkMode false true for blinking cursor. 650 | if false, cursor is static. 651 | 652 | crsrBlockMode true true for block-cursor else underscore. 653 | 654 | DELisBS false handle as . 655 | 656 | printTab true handle as printable (prints as space) 657 | if false is handled as a control character 658 | 659 | printEuro true handle the euro sign as valid input char. 660 | if false char 0x20AC is printed, but not accepted 661 | in the command line 662 | 663 | catchCtrlH true handle ^H as . 664 | if false, ^H must be tracked by a custom 665 | ctrlHandler. 666 | 667 | closeOnESC true close terminal on . 668 | if true, is not available for ctrHandler. 669 | 670 | 671 | historyUnique false unique history entries. 672 | if true, entries that are identical to the last 673 | entry in the user history will not be added. 674 | 675 | charMode false terminal in character mode (tracks next key-code). 676 | (runtime only) 677 | 678 | rawMode false terminal in raw mode (no echo, no editing). 679 | (runtime only) 680 | 681 | wrapping false text wrapping on/off 682 | 683 | mapANSI false filter ANSI escape sequences and apply SGR styles 684 | and color codes for write() 685 | 686 | ANSItrueBlack false force output of ANSI code 30m (black) as black 687 | (default: render color 0 as foreground color) 688 | 689 | 690 | Not exactly a flag but useful: 691 | 692 | ps '>' prompt string. 693 | 694 | 695 | 696 | 697 | 4 Output Methods 698 | 699 | Please note that any output to the terminal implies an advance of the cursor. This means, 700 | that if your output reaches the last column of your terminal, the cursor is advanced and 701 | a new line is opened automatically. This procedure may include scrolling to make room for 702 | the new line. While this is not of much interest for most purposes, please note that, if 703 | you output a string of length 80 to a 80-columns-terminal, and a new line, and another 704 | string, this will result in an empty line between the two strings. 705 | 706 | 707 | 4.1 Terminal.type( [,] ) 708 | 709 | Types the string at the current cursor position to the terminal. Long lines are 710 | broken where the last column of the terminal is reached and continued in the next line. 711 | `Terminal.write()' does not support any kind of arbitrary line breaks. (This is just a 712 | basic output routine. See `Terminal.write()' for a more powerful output method.) 713 | 714 | A bitvector may be supplied as an optional second argument to represent a style or a 715 | combination of styles. The meanings of the bits set are interpreted as follows: 716 | 717 | : 718 | 719 | 1 ... reverse (2 power 0) 720 | 2 ... underline (2 power 1) 721 | 4 ... italics (2 power 2) 722 | 8 ... strike (2 power 3) 723 | 16 ... bold (2 power 4) *displayed as italics, used internally for ANSI-mapping* 724 | 725 | So "Terminal.type( 'text', 5 )" types "text" in italics and reverse video. 726 | 727 | Note: 728 | There is no bold, for most monospaced fonts (including Courier) tend to render wider in 729 | bold. Since this would bring the terminal's layout out of balance, we just can't use bold 730 | as a style. - Sorry. 731 | 732 | The HTML-representation of this styles are defined in "TermGlobals.termStyleOpen" and 733 | "TermGlobals.termStyleClose". 734 | (Version 1.4: "TermGlobals" is now a reference to "Terminal.prototype.globals".) 735 | 736 | Version 1.2 introduces additional styles for colors. 737 | Please read also sect. 4.10 "Using Color" for the extended color values. 738 | 739 | 740 | 4.2 Terminal.write( [,] ) 741 | 742 | Writes a text with markup to the terminal. If an optional second argument evaluates to 743 | true, a UN*X-style utility like `more' is used to page the text. The text may be supplied 744 | as a single string (with newline character "\n") or as an array of lines. Any other input 745 | is transformed to a string value before output. 746 | 747 | 4.2.1 Mark-up: 748 | 749 | `Terminal.write()' employs a simple mark-up with the following syntax: 750 | 751 | : %((+|-) 22 | 23 | 24 |
25 |
{% csrf_token %}
26 | 27 | 62 | 63 | -------------------------------------------------------------------------------- /django-console/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from setuptools import find_packages 3 | 4 | setup( 5 | name='django-console', 6 | description='bash console in the browser for django admins', 7 | keywords='django, bash', 8 | packages=find_packages(), 9 | include_package_data=True, 10 | version="0.4.8", 11 | author="Anoop Thomas Mathew", 12 | author_email="atmb4u@gmail.com", 13 | url='http://github.com/atmb4u/django-console', 14 | classifiers=['Development Status :: 4 - Beta', 15 | 'Environment :: Web Environment', 16 | 'Framework :: Django', 17 | 'Intended Audience :: Developers', 18 | 'License :: OSI Approved :: GNU General Public License (GPL)', 19 | 'Operating System :: OS Independent', 20 | 'Programming Language :: Python', 21 | 'Topic :: Internet :: WWW/HTTP', 22 | 'Topic :: Internet :: WWW/HTTP :: Web Console', 23 | 'Topic :: Internet :: WWW/HTTP :: WSGI', 24 | 'Topic :: Software Development :: Libraries :: Application Frameworks', 25 | 'Topic :: Software Development :: Libraries :: Python Modules', 26 | ], 27 | license="BSD License", 28 | platforms=["all"], 29 | zip_safe=False 30 | ) 31 | --------------------------------------------------------------------------------