github.com/fanux/shipyard@v0.0.0-20161009071005-6515ce223235/controller/static/app/term.js (about) 1 /** 2 * term.js - an xterm emulator 3 * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) 4 * https://github.com/chjj/term.js 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 * 24 * Originally forked from (with the author's permission): 25 * Fabrice Bellard's javascript vt100 for jslinux: 26 * http://bellard.org/jslinux/ 27 * Copyright (c) 2011 Fabrice Bellard 28 * The original design remains. The terminal itself 29 * has been extended to include xterm CSI codes, among 30 * other features. 31 */ 32 33 ;(function() { 34 35 /** 36 * Terminal Emulation References: 37 * http://vt100.net/ 38 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt 39 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html 40 * http://invisible-island.net/vttest/ 41 * http://www.inwap.com/pdp10/ansicode.txt 42 * http://linux.die.net/man/4/console_codes 43 * http://linux.die.net/man/7/urxvt 44 */ 45 46 'use strict'; 47 48 /** 49 * Shared 50 */ 51 52 var window = this 53 , document = this.document; 54 55 /** 56 * EventEmitter 57 */ 58 59 function EventEmitter() { 60 this._events = this._events || {}; 61 } 62 63 EventEmitter.prototype.addListener = function(type, listener) { 64 this._events[type] = this._events[type] || []; 65 this._events[type].push(listener); 66 }; 67 68 EventEmitter.prototype.on = EventEmitter.prototype.addListener; 69 70 EventEmitter.prototype.removeListener = function(type, listener) { 71 if (!this._events[type]) return; 72 73 var obj = this._events[type] 74 , i = obj.length; 75 76 while (i--) { 77 if (obj[i] === listener || obj[i].listener === listener) { 78 obj.splice(i, 1); 79 return; 80 } 81 } 82 }; 83 84 EventEmitter.prototype.off = EventEmitter.prototype.removeListener; 85 86 EventEmitter.prototype.removeAllListeners = function(type) { 87 if (this._events[type]) delete this._events[type]; 88 }; 89 90 EventEmitter.prototype.once = function(type, listener) { 91 function on() { 92 var args = Array.prototype.slice.call(arguments); 93 this.removeListener(type, on); 94 return listener.apply(this, args); 95 } 96 on.listener = listener; 97 return this.on(type, on); 98 }; 99 100 EventEmitter.prototype.emit = function(type) { 101 if (!this._events[type]) return; 102 103 var args = Array.prototype.slice.call(arguments, 1) 104 , obj = this._events[type] 105 , l = obj.length 106 , i = 0; 107 108 for (; i < l; i++) { 109 obj[i].apply(this, args); 110 } 111 }; 112 113 EventEmitter.prototype.listeners = function(type) { 114 return this._events[type] = this._events[type] || []; 115 }; 116 117 /** 118 * States 119 */ 120 121 var normal = 0 122 , escaped = 1 123 , csi = 2 124 , osc = 3 125 , charset = 4 126 , dcs = 5 127 , ignore = 6; 128 129 /** 130 * Terminal 131 */ 132 133 function Terminal(options) { 134 var self = this; 135 136 if (!(this instanceof Terminal)) { 137 return new Terminal(arguments[0], arguments[1], arguments[2]); 138 } 139 140 EventEmitter.call(this); 141 142 if (typeof options === 'number') { 143 options = { 144 cols: arguments[0], 145 rows: arguments[1], 146 handler: arguments[2] 147 }; 148 } 149 150 options = options || {}; 151 152 each(keys(Terminal.defaults), function(key) { 153 if (options[key] == null) { 154 options[key] = Terminal.options[key]; 155 // Legacy: 156 if (Terminal[key] !== Terminal.defaults[key]) { 157 options[key] = Terminal[key]; 158 } 159 } 160 self[key] = options[key]; 161 }); 162 163 if (options.colors.length === 8) { 164 options.colors = options.colors.concat(Terminal._colors.slice(8)); 165 } else if (options.colors.length === 16) { 166 options.colors = options.colors.concat(Terminal._colors.slice(16)); 167 } else if (options.colors.length === 10) { 168 options.colors = options.colors.slice(0, -2).concat( 169 Terminal._colors.slice(8, -2), options.colors.slice(-2)); 170 } else if (options.colors.length === 18) { 171 options.colors = options.colors.slice(0, -2).concat( 172 Terminal._colors.slice(16, -2), options.colors.slice(-2)); 173 } 174 this.colors = options.colors; 175 176 this.options = options; 177 178 // this.context = options.context || window; 179 // this.document = options.document || document; 180 this.parent = options.body || options.parent 181 || (document ? document.getElementsByTagName('body')[0] : null); 182 183 this.cols = options.cols || options.geometry[0]; 184 this.rows = options.rows || options.geometry[1]; 185 186 if (options.handler) { 187 this.on('data', options.handler); 188 } 189 190 this.ybase = 0; 191 this.ydisp = 0; 192 this.x = 0; 193 this.y = 0; 194 this.cursorState = 0; 195 this.cursorHidden = false; 196 this.convertEol; 197 this.state = 0; 198 this.queue = ''; 199 this.scrollTop = 0; 200 this.scrollBottom = this.rows - 1; 201 202 // modes 203 this.applicationKeypad = false; 204 this.applicationCursor = false; 205 this.originMode = false; 206 this.insertMode = false; 207 this.wraparoundMode = false; 208 this.normal = null; 209 210 // select modes 211 this.prefixMode = false; 212 this.selectMode = false; 213 this.visualMode = false; 214 this.searchMode = false; 215 this.searchDown; 216 this.entry = ''; 217 this.entryPrefix = 'Search: '; 218 this._real; 219 this._selected; 220 this._textarea; 221 222 // charset 223 this.charset = null; 224 this.gcharset = null; 225 this.glevel = 0; 226 this.charsets = [null]; 227 228 // mouse properties 229 this.decLocator; 230 this.x10Mouse; 231 this.vt200Mouse; 232 this.vt300Mouse; 233 this.normalMouse; 234 this.mouseEvents; 235 this.sendFocus; 236 this.utfMouse; 237 this.sgrMouse; 238 this.urxvtMouse; 239 240 // misc 241 this.element; 242 this.children; 243 this.refreshStart; 244 this.refreshEnd; 245 this.savedX; 246 this.savedY; 247 this.savedCols; 248 249 // stream 250 this.readable = true; 251 this.writable = true; 252 253 this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); 254 this.curAttr = this.defAttr; 255 256 this.params = []; 257 this.currentParam = 0; 258 this.prefix = ''; 259 this.postfix = ''; 260 261 this.lines = []; 262 var i = this.rows; 263 while (i--) { 264 this.lines.push(this.blankLine()); 265 } 266 267 this.tabs; 268 this.setupStops(); 269 } 270 271 inherits(Terminal, EventEmitter); 272 273 // back_color_erase feature for xterm. 274 Terminal.prototype.eraseAttr = function() { 275 // if (this.is('screen')) return this.defAttr; 276 return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); 277 }; 278 279 /** 280 * Colors 281 */ 282 283 // Colors 0-15 284 Terminal.tangoColors = [ 285 // dark: 286 '#2e3436', 287 '#cc0000', 288 '#4e9a06', 289 '#c4a000', 290 '#3465a4', 291 '#75507b', 292 '#06989a', 293 '#d3d7cf', 294 // bright: 295 '#555753', 296 '#ef2929', 297 '#8ae234', 298 '#fce94f', 299 '#729fcf', 300 '#ad7fa8', 301 '#34e2e2', 302 '#eeeeec' 303 ]; 304 305 Terminal.xtermColors = [ 306 // dark: 307 '#000000', // black 308 '#cd0000', // red3 309 '#00cd00', // green3 310 '#cdcd00', // yellow3 311 '#0000ee', // blue2 312 '#cd00cd', // magenta3 313 '#00cdcd', // cyan3 314 '#e5e5e5', // gray90 315 // bright: 316 '#7f7f7f', // gray50 317 '#ff0000', // red 318 '#00ff00', // green 319 '#ffff00', // yellow 320 '#5c5cff', // rgb:5c/5c/ff 321 '#ff00ff', // magenta 322 '#00ffff', // cyan 323 '#ffffff' // white 324 ]; 325 326 // Colors 0-15 + 16-255 327 // Much thanks to TooTallNate for writing this. 328 Terminal.colors = (function() { 329 var colors = Terminal.tangoColors.slice() 330 , r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff] 331 , i; 332 333 // 16-231 334 i = 0; 335 for (; i < 216; i++) { 336 out(r[(i / 36) % 6 | 0], r[(i / 6) % 6 | 0], r[i % 6]); 337 } 338 339 // 232-255 (grey) 340 i = 0; 341 for (; i < 24; i++) { 342 r = 8 + i * 10; 343 out(r, r, r); 344 } 345 346 function out(r, g, b) { 347 colors.push('#' + hex(r) + hex(g) + hex(b)); 348 } 349 350 function hex(c) { 351 c = c.toString(16); 352 return c.length < 2 ? '0' + c : c; 353 } 354 355 return colors; 356 })(); 357 358 // Default BG/FG 359 Terminal.colors[256] = '#000000'; 360 Terminal.colors[257] = '#f0f0f0'; 361 362 Terminal._colors = Terminal.colors.slice(); 363 364 Terminal.vcolors = (function() { 365 var out = [] 366 , colors = Terminal.colors 367 , i = 0 368 , color; 369 370 for (; i < 256; i++) { 371 color = parseInt(colors[i].substring(1), 16); 372 out.push([ 373 (color >> 16) & 0xff, 374 (color >> 8) & 0xff, 375 color & 0xff 376 ]); 377 } 378 379 return out; 380 })(); 381 382 /** 383 * Options 384 */ 385 386 Terminal.defaults = { 387 colors: Terminal.colors, 388 convertEol: false, 389 termName: 'xterm', 390 geometry: [80, 24], 391 cursorBlink: true, 392 visualBell: false, 393 popOnBell: false, 394 scrollback: 1000, 395 screenKeys: false, 396 debug: false, 397 useStyle: false 398 // programFeatures: false, 399 // focusKeys: false, 400 }; 401 402 Terminal.options = {}; 403 404 each(keys(Terminal.defaults), function(key) { 405 Terminal[key] = Terminal.defaults[key]; 406 Terminal.options[key] = Terminal.defaults[key]; 407 }); 408 409 /** 410 * Focused Terminal 411 */ 412 413 Terminal.focus = null; 414 415 Terminal.prototype.focus = function() { 416 if (Terminal.focus === this) return; 417 418 if (Terminal.focus) { 419 Terminal.focus.blur(); 420 } 421 422 if (this.sendFocus) this.send('\x1b[I'); 423 this.showCursor(); 424 425 // try { 426 // this.element.focus(); 427 // } catch (e) { 428 // ; 429 // } 430 431 // this.emit('focus'); 432 433 Terminal.focus = this; 434 }; 435 436 Terminal.prototype.blur = function() { 437 if (Terminal.focus !== this) return; 438 439 this.cursorState = 0; 440 this.refresh(this.y, this.y); 441 if (this.sendFocus) this.send('\x1b[O'); 442 443 // try { 444 // this.element.blur(); 445 // } catch (e) { 446 // ; 447 // } 448 449 // this.emit('blur'); 450 451 Terminal.focus = null; 452 }; 453 454 /** 455 * Initialize global behavior 456 */ 457 458 Terminal.prototype.initGlobal = function() { 459 var document = this.document; 460 461 Terminal._boundDocs = Terminal._boundDocs || []; 462 if (~indexOf(Terminal._boundDocs, document)) { 463 return; 464 } 465 Terminal._boundDocs.push(document); 466 467 Terminal.bindPaste(document); 468 469 Terminal.bindKeys(document); 470 471 Terminal.bindCopy(document); 472 473 if (this.isMobile) { 474 this.fixMobile(document); 475 } 476 477 if (this.useStyle) { 478 Terminal.insertStyle(document, this.colors[256], this.colors[257]); 479 } 480 }; 481 482 /** 483 * Bind to paste event 484 */ 485 486 Terminal.bindPaste = function(document) { 487 // This seems to work well for ctrl-V and middle-click, 488 // even without the contentEditable workaround. 489 var window = document.defaultView; 490 on(window, 'paste', function(ev) { 491 var term = Terminal.focus; 492 if (!term) return; 493 if (ev.clipboardData) { 494 term.send(ev.clipboardData.getData('text/plain')); 495 } else if (term.context.clipboardData) { 496 term.send(term.context.clipboardData.getData('Text')); 497 } 498 // Not necessary. Do it anyway for good measure. 499 term.element.contentEditable = 'inherit'; 500 return cancel(ev); 501 }); 502 }; 503 504 /** 505 * Global Events for key handling 506 */ 507 508 Terminal.bindKeys = function(document) { 509 // We should only need to check `target === body` below, 510 // but we can check everything for good measure. 511 on(document, 'keydown', function(ev) { 512 if (!Terminal.focus) return; 513 var target = ev.target || ev.srcElement; 514 if (!target) return; 515 if (target === Terminal.focus.element 516 || target === Terminal.focus.context 517 || target === Terminal.focus.document 518 || target === Terminal.focus.body 519 || target === Terminal._textarea 520 || target === Terminal.focus.parent) { 521 return Terminal.focus.keyDown(ev); 522 } 523 }, true); 524 525 on(document, 'keypress', function(ev) { 526 if (!Terminal.focus) return; 527 var target = ev.target || ev.srcElement; 528 if (!target) return; 529 if (target === Terminal.focus.element 530 || target === Terminal.focus.context 531 || target === Terminal.focus.document 532 || target === Terminal.focus.body 533 || target === Terminal._textarea 534 || target === Terminal.focus.parent) { 535 return Terminal.focus.keyPress(ev); 536 } 537 }, true); 538 539 // If we click somewhere other than a 540 // terminal, unfocus the terminal. 541 on(document, 'mousedown', function(ev) { 542 if (!Terminal.focus) return; 543 544 var el = ev.target || ev.srcElement; 545 if (!el) return; 546 547 do { 548 if (el === Terminal.focus.element) return; 549 } while (el = el.parentNode); 550 551 Terminal.focus.blur(); 552 }); 553 }; 554 555 /** 556 * Copy Selection w/ Ctrl-C (Select Mode) 557 */ 558 559 Terminal.bindCopy = function(document) { 560 var window = document.defaultView; 561 562 // if (!('onbeforecopy' in document)) { 563 // // Copies to *only* the clipboard. 564 // on(window, 'copy', function fn(ev) { 565 // var term = Terminal.focus; 566 // if (!term) return; 567 // if (!term._selected) return; 568 // var text = term.grabText( 569 // term._selected.x1, term._selected.x2, 570 // term._selected.y1, term._selected.y2); 571 // term.emit('copy', text); 572 // ev.clipboardData.setData('text/plain', text); 573 // }); 574 // return; 575 // } 576 577 // Copies to primary selection *and* clipboard. 578 // NOTE: This may work better on capture phase, 579 // or using the `beforecopy` event. 580 on(window, 'copy', function(ev) { 581 var term = Terminal.focus; 582 if (!term) return; 583 if (!term._selected) return; 584 var textarea = term.getCopyTextarea(); 585 var text = term.grabText( 586 term._selected.x1, term._selected.x2, 587 term._selected.y1, term._selected.y2); 588 term.emit('copy', text); 589 textarea.focus(); 590 textarea.textContent = text; 591 textarea.value = text; 592 textarea.setSelectionRange(0, text.length); 593 setTimeout(function() { 594 term.element.focus(); 595 term.focus(); 596 }, 1); 597 }); 598 }; 599 600 /** 601 * Fix Mobile 602 */ 603 604 Terminal.prototype.fixMobile = function(document) { 605 var self = this; 606 607 var textarea = document.createElement('textarea'); 608 textarea.style.position = 'absolute'; 609 textarea.style.left = '-32000px'; 610 textarea.style.top = '-32000px'; 611 textarea.style.width = '0px'; 612 textarea.style.height = '0px'; 613 textarea.style.opacity = '0'; 614 textarea.style.backgroundColor = 'transparent'; 615 textarea.style.borderStyle = 'none'; 616 textarea.style.outlineStyle = 'none'; 617 textarea.autocapitalize = 'none'; 618 textarea.autocorrect = 'off'; 619 620 document.getElementsByTagName('body')[0].appendChild(textarea); 621 622 Terminal._textarea = textarea; 623 624 setTimeout(function() { 625 textarea.focus(); 626 }, 1000); 627 628 if (this.isAndroid) { 629 on(textarea, 'change', function() { 630 var value = textarea.textContent || textarea.value; 631 textarea.value = ''; 632 textarea.textContent = ''; 633 self.send(value + '\r'); 634 }); 635 } 636 }; 637 638 /** 639 * Insert a default style 640 */ 641 642 Terminal.insertStyle = function(document, bg, fg) { 643 var style = document.getElementById('term-style'); 644 if (style) return; 645 646 var head = document.getElementsByTagName('head')[0]; 647 if (!head) return; 648 649 var style = document.createElement('style'); 650 style.id = 'term-style'; 651 652 // textContent doesn't work well with IE for <style> elements. 653 style.innerHTML = '' 654 + '.terminal {\n' 655 + ' float: left;\n' 656 + ' border: ' + bg + ' solid 5px;\n' 657 + ' font-family: "DejaVu Sans Mono", "Liberation Mono", monospace;\n' 658 + ' font-size: 11px;\n' 659 + ' color: ' + fg + ';\n' 660 + ' background: ' + bg + ';\n' 661 + '}\n' 662 + '\n' 663 + '.terminal-cursor {\n' 664 + ' color: ' + bg + ';\n' 665 + ' background: ' + fg + ';\n' 666 + '}\n'; 667 668 // var out = ''; 669 // each(Terminal.colors, function(color, i) { 670 // if (i === 256) { 671 // out += '\n.term-bg-color-default { background-color: ' + color + '; }'; 672 // } 673 // if (i === 257) { 674 // out += '\n.term-fg-color-default { color: ' + color + '; }'; 675 // } 676 // out += '\n.term-bg-color-' + i + ' { background-color: ' + color + '; }'; 677 // out += '\n.term-fg-color-' + i + ' { color: ' + color + '; }'; 678 // }); 679 // style.innerHTML += out + '\n'; 680 681 head.insertBefore(style, head.firstChild); 682 }; 683 684 /** 685 * Open Terminal 686 */ 687 688 Terminal.prototype.open = function(parent) { 689 var self = this 690 , i = 0 691 , div; 692 693 this.parent = parent || this.parent; 694 695 if (!this.parent) { 696 throw new Error('Terminal requires a parent element.'); 697 } 698 699 // Grab global elements. 700 this.context = this.parent.ownerDocument.defaultView; 701 this.document = this.parent.ownerDocument; 702 this.body = this.document.getElementsByTagName('body')[0]; 703 704 // Parse user-agent strings. 705 if (this.context.navigator && this.context.navigator.userAgent) { 706 this.isMac = !!~this.context.navigator.userAgent.indexOf('Mac'); 707 this.isIpad = !!~this.context.navigator.userAgent.indexOf('iPad'); 708 this.isIphone = !!~this.context.navigator.userAgent.indexOf('iPhone'); 709 this.isAndroid = !!~this.context.navigator.userAgent.indexOf('Android'); 710 this.isMobile = this.isIpad || this.isIphone || this.isAndroid; 711 this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE'); 712 } 713 714 // Create our main terminal element. 715 this.element = this.document.createElement('div'); 716 this.element.className = 'terminal'; 717 this.element.style.outline = 'none'; 718 this.element.setAttribute('tabindex', 0); 719 this.element.setAttribute('spellcheck', 'false'); 720 this.element.style.backgroundColor = this.colors[256]; 721 this.element.style.color = this.colors[257]; 722 723 // Create the lines for our terminal. 724 this.children = []; 725 for (; i < this.rows; i++) { 726 div = this.document.createElement('div'); 727 this.element.appendChild(div); 728 this.children.push(div); 729 } 730 this.parent.appendChild(this.element); 731 732 // Draw the screen. 733 this.refresh(0, this.rows - 1); 734 735 // Initialize global actions that 736 // need to be taken on the document. 737 this.initGlobal(); 738 739 // Ensure there is a Terminal.focus. 740 this.focus(); 741 742 // Start blinking the cursor. 743 this.startBlink(); 744 745 // Bind to DOM events related 746 // to focus and paste behavior. 747 on(this.element, 'focus', function() { 748 self.focus(); 749 if (self.isMobile) { 750 Terminal._textarea.focus(); 751 } 752 }); 753 754 // This causes slightly funky behavior. 755 // on(this.element, 'blur', function() { 756 // self.blur(); 757 // }); 758 759 on(this.element, 'mousedown', function() { 760 self.focus(); 761 }); 762 763 // Clickable paste workaround, using contentEditable. 764 // This probably shouldn't work, 765 // ... but it does. Firefox's paste 766 // event seems to only work for textareas? 767 on(this.element, 'mousedown', function(ev) { 768 var button = ev.button != null 769 ? +ev.button 770 : ev.which != null 771 ? ev.which - 1 772 : null; 773 774 // Does IE9 do this? 775 if (self.isMSIE) { 776 button = button === 1 ? 0 : button === 4 ? 1 : button; 777 } 778 779 if (button !== 2) return; 780 781 self.element.contentEditable = 'true'; 782 setTimeout(function() { 783 self.element.contentEditable = 'inherit'; // 'false'; 784 }, 1); 785 }, true); 786 787 // Listen for mouse events and translate 788 // them into terminal mouse protocols. 789 this.bindMouse(); 790 791 // Figure out whether boldness affects 792 // the character width of monospace fonts. 793 if (Terminal.brokenBold == null) { 794 Terminal.brokenBold = isBoldBroken(this.document); 795 } 796 797 // this.emit('open'); 798 799 // This can be useful for pasting, 800 // as well as the iPad fix. 801 setTimeout(function() { 802 self.element.focus(); 803 }, 100); 804 }; 805 806 // XTerm mouse events 807 // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking 808 // To better understand these 809 // the xterm code is very helpful: 810 // Relevant files: 811 // button.c, charproc.c, misc.c 812 // Relevant functions in xterm/button.c: 813 // BtnCode, EmitButtonCode, EditorButton, SendMousePosition 814 Terminal.prototype.bindMouse = function() { 815 var el = this.element 816 , self = this 817 , pressed = 32; 818 819 var wheelEvent = 'onmousewheel' in this.context 820 ? 'mousewheel' 821 : 'DOMMouseScroll'; 822 823 // mouseup, mousedown, mousewheel 824 // left click: ^[[M 3<^[[M#3< 825 // mousewheel up: ^[[M`3> 826 function sendButton(ev) { 827 var button 828 , pos; 829 830 // get the xterm-style button 831 button = getButton(ev); 832 833 // get mouse coordinates 834 pos = getCoords(ev); 835 if (!pos) return; 836 837 sendEvent(button, pos); 838 839 switch (ev.type) { 840 case 'mousedown': 841 pressed = button; 842 break; 843 case 'mouseup': 844 // keep it at the left 845 // button, just in case. 846 pressed = 32; 847 break; 848 case wheelEvent: 849 // nothing. don't 850 // interfere with 851 // `pressed`. 852 break; 853 } 854 } 855 856 // motion example of a left click: 857 // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< 858 function sendMove(ev) { 859 var button = pressed 860 , pos; 861 862 pos = getCoords(ev); 863 if (!pos) return; 864 865 // buttons marked as motions 866 // are incremented by 32 867 button += 32; 868 869 sendEvent(button, pos); 870 } 871 872 // encode button and 873 // position to characters 874 function encode(data, ch) { 875 if (!self.utfMouse) { 876 if (ch === 255) return data.push(0); 877 if (ch > 127) ch = 127; 878 data.push(ch); 879 } else { 880 if (ch === 2047) return data.push(0); 881 if (ch < 127) { 882 data.push(ch); 883 } else { 884 if (ch > 2047) ch = 2047; 885 data.push(0xC0 | (ch >> 6)); 886 data.push(0x80 | (ch & 0x3F)); 887 } 888 } 889 } 890 891 // send a mouse event: 892 // regular/utf8: ^[[M Cb Cx Cy 893 // urxvt: ^[[ Cb ; Cx ; Cy M 894 // sgr: ^[[ Cb ; Cx ; Cy M/m 895 // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r 896 // locator: CSI P e ; P b ; P r ; P c ; P p & w 897 function sendEvent(button, pos) { 898 // self.emit('mouse', { 899 // x: pos.x - 32, 900 // y: pos.x - 32, 901 // button: button 902 // }); 903 904 if (self.vt300Mouse) { 905 // NOTE: Unstable. 906 // http://www.vt100.net/docs/vt3xx-gp/chapter15.html 907 button &= 3; 908 pos.x -= 32; 909 pos.y -= 32; 910 var data = '\x1b[24'; 911 if (button === 0) data += '1'; 912 else if (button === 1) data += '3'; 913 else if (button === 2) data += '5'; 914 else if (button === 3) return; 915 else data += '0'; 916 data += '~[' + pos.x + ',' + pos.y + ']\r'; 917 self.send(data); 918 return; 919 } 920 921 if (self.decLocator) { 922 // NOTE: Unstable. 923 button &= 3; 924 pos.x -= 32; 925 pos.y -= 32; 926 if (button === 0) button = 2; 927 else if (button === 1) button = 4; 928 else if (button === 2) button = 6; 929 else if (button === 3) button = 3; 930 self.send('\x1b[' 931 + button 932 + ';' 933 + (button === 3 ? 4 : 0) 934 + ';' 935 + pos.y 936 + ';' 937 + pos.x 938 + ';' 939 + (pos.page || 0) 940 + '&w'); 941 return; 942 } 943 944 if (self.urxvtMouse) { 945 pos.x -= 32; 946 pos.y -= 32; 947 pos.x++; 948 pos.y++; 949 self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M'); 950 return; 951 } 952 953 if (self.sgrMouse) { 954 pos.x -= 32; 955 pos.y -= 32; 956 self.send('\x1b[<' 957 + ((button & 3) === 3 ? button & ~3 : button) 958 + ';' 959 + pos.x 960 + ';' 961 + pos.y 962 + ((button & 3) === 3 ? 'm' : 'M')); 963 return; 964 } 965 966 var data = []; 967 968 encode(data, button); 969 encode(data, pos.x); 970 encode(data, pos.y); 971 972 self.send('\x1b[M' + String.fromCharCode.apply(String, data)); 973 } 974 975 function getButton(ev) { 976 var button 977 , shift 978 , meta 979 , ctrl 980 , mod; 981 982 // two low bits: 983 // 0 = left 984 // 1 = middle 985 // 2 = right 986 // 3 = release 987 // wheel up/down: 988 // 1, and 2 - with 64 added 989 switch (ev.type) { 990 case 'mousedown': 991 button = ev.button != null 992 ? +ev.button 993 : ev.which != null 994 ? ev.which - 1 995 : null; 996 997 if (self.isMSIE) { 998 button = button === 1 ? 0 : button === 4 ? 1 : button; 999 } 1000 break; 1001 case 'mouseup': 1002 button = 3; 1003 break; 1004 case 'DOMMouseScroll': 1005 button = ev.detail < 0 1006 ? 64 1007 : 65; 1008 break; 1009 case 'mousewheel': 1010 button = ev.wheelDeltaY > 0 1011 ? 64 1012 : 65; 1013 break; 1014 } 1015 1016 // next three bits are the modifiers: 1017 // 4 = shift, 8 = meta, 16 = control 1018 shift = ev.shiftKey ? 4 : 0; 1019 meta = ev.metaKey ? 8 : 0; 1020 ctrl = ev.ctrlKey ? 16 : 0; 1021 mod = shift | meta | ctrl; 1022 1023 // no mods 1024 if (self.vt200Mouse) { 1025 // ctrl only 1026 mod &= ctrl; 1027 } else if (!self.normalMouse) { 1028 mod = 0; 1029 } 1030 1031 // increment to SP 1032 button = (32 + (mod << 2)) + button; 1033 1034 return button; 1035 } 1036 1037 // mouse coordinates measured in cols/rows 1038 function getCoords(ev) { 1039 var x, y, w, h, el; 1040 1041 // ignore browsers without pageX for now 1042 if (ev.pageX == null) return; 1043 1044 x = ev.pageX; 1045 y = ev.pageY; 1046 el = self.element; 1047 1048 // should probably check offsetParent 1049 // but this is more portable 1050 while (el && el !== self.document.documentElement) { 1051 x -= el.offsetLeft; 1052 y -= el.offsetTop; 1053 el = 'offsetParent' in el 1054 ? el.offsetParent 1055 : el.parentNode; 1056 } 1057 1058 // convert to cols/rows 1059 w = self.element.clientWidth; 1060 h = self.element.clientHeight; 1061 x = Math.round((x / w) * self.cols); 1062 y = Math.round((y / h) * self.rows); 1063 1064 // be sure to avoid sending 1065 // bad positions to the program 1066 if (x < 0) x = 0; 1067 if (x > self.cols) x = self.cols; 1068 if (y < 0) y = 0; 1069 if (y > self.rows) y = self.rows; 1070 1071 // xterm sends raw bytes and 1072 // starts at 32 (SP) for each. 1073 x += 32; 1074 y += 32; 1075 1076 return { 1077 x: x, 1078 y: y, 1079 type: ev.type === wheelEvent 1080 ? 'mousewheel' 1081 : ev.type 1082 }; 1083 } 1084 1085 on(el, 'mousedown', function(ev) { 1086 if (!self.mouseEvents) return; 1087 1088 // send the button 1089 sendButton(ev); 1090 1091 // ensure focus 1092 self.focus(); 1093 1094 // fix for odd bug 1095 //if (self.vt200Mouse && !self.normalMouse) { 1096 if (self.vt200Mouse) { 1097 sendButton({ __proto__: ev, type: 'mouseup' }); 1098 return cancel(ev); 1099 } 1100 1101 // bind events 1102 if (self.normalMouse) on(self.document, 'mousemove', sendMove); 1103 1104 // x10 compatibility mode can't send button releases 1105 if (!self.x10Mouse) { 1106 on(self.document, 'mouseup', function up(ev) { 1107 sendButton(ev); 1108 if (self.normalMouse) off(self.document, 'mousemove', sendMove); 1109 off(self.document, 'mouseup', up); 1110 return cancel(ev); 1111 }); 1112 } 1113 1114 return cancel(ev); 1115 }); 1116 1117 //if (self.normalMouse) { 1118 // on(self.document, 'mousemove', sendMove); 1119 //} 1120 1121 on(el, wheelEvent, function(ev) { 1122 if (!self.mouseEvents) return; 1123 if (self.x10Mouse 1124 || self.vt300Mouse 1125 || self.decLocator) return; 1126 sendButton(ev); 1127 return cancel(ev); 1128 }); 1129 1130 // allow mousewheel scrolling in 1131 // the shell for example 1132 on(el, wheelEvent, function(ev) { 1133 if (self.mouseEvents) return; 1134 if (self.applicationKeypad) return; 1135 if (ev.type === 'DOMMouseScroll') { 1136 self.scrollDisp(ev.detail < 0 ? -5 : 5); 1137 } else { 1138 self.scrollDisp(ev.wheelDeltaY > 0 ? -5 : 5); 1139 } 1140 return cancel(ev); 1141 }); 1142 }; 1143 1144 /** 1145 * Destroy Terminal 1146 */ 1147 1148 Terminal.prototype.destroy = function() { 1149 this.readable = false; 1150 this.writable = false; 1151 this._events = {}; 1152 this.handler = function() {}; 1153 this.write = function() {}; 1154 if (this.element.parentNode) { 1155 this.element.parentNode.removeChild(this.element); 1156 } 1157 //this.emit('close'); 1158 }; 1159 1160 /** 1161 * Rendering Engine 1162 */ 1163 1164 // In the screen buffer, each character 1165 // is stored as a an array with a character 1166 // and a 32-bit integer. 1167 // First value: a utf-16 character. 1168 // Second value: 1169 // Next 9 bits: background color (0-511). 1170 // Next 9 bits: foreground color (0-511). 1171 // Next 14 bits: a mask for misc. flags: 1172 // 1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible 1173 1174 Terminal.prototype.refresh = function(start, end) { 1175 var x 1176 , y 1177 , i 1178 , line 1179 , out 1180 , ch 1181 , width 1182 , data 1183 , attr 1184 , bg 1185 , fg 1186 , flags 1187 , row 1188 , parent; 1189 1190 if (end - start >= this.rows / 2) { 1191 parent = this.element.parentNode; 1192 if (parent) parent.removeChild(this.element); 1193 } 1194 1195 width = this.cols; 1196 y = start; 1197 1198 if (end >= this.lines.length) { 1199 this.log('`end` is too large. Most likely a bad CSR.'); 1200 end = this.lines.length - 1; 1201 } 1202 1203 for (; y <= end; y++) { 1204 row = y + this.ydisp; 1205 1206 line = this.lines[row]; 1207 out = ''; 1208 1209 if (y === this.y 1210 && this.cursorState 1211 && (this.ydisp === this.ybase || this.selectMode) 1212 && !this.cursorHidden) { 1213 x = this.x; 1214 } else { 1215 x = -1; 1216 } 1217 1218 attr = this.defAttr; 1219 i = 0; 1220 1221 for (; i < width; i++) { 1222 data = line[i][0]; 1223 ch = line[i][1]; 1224 1225 if (i === x) data = -1; 1226 1227 if (data !== attr) { 1228 if (attr !== this.defAttr) { 1229 out += '</span>'; 1230 } 1231 if (data !== this.defAttr) { 1232 if (data === -1) { 1233 out += '<span class="reverse-video terminal-cursor">'; 1234 } else { 1235 out += '<span style="'; 1236 1237 bg = data & 0x1ff; 1238 fg = (data >> 9) & 0x1ff; 1239 flags = data >> 18; 1240 1241 // bold 1242 if (flags & 1) { 1243 if (!Terminal.brokenBold) { 1244 out += 'font-weight:bold;'; 1245 } 1246 // See: XTerm*boldColors 1247 if (fg < 8) fg += 8; 1248 } 1249 1250 // underline 1251 if (flags & 2) { 1252 out += 'text-decoration:underline;'; 1253 } 1254 1255 // blink 1256 if (flags & 4) { 1257 if (flags & 2) { 1258 out = out.slice(0, -1); 1259 out += ' blink;'; 1260 } else { 1261 out += 'text-decoration:blink;'; 1262 } 1263 } 1264 1265 // inverse 1266 if (flags & 8) { 1267 bg = (data >> 9) & 0x1ff; 1268 fg = data & 0x1ff; 1269 // Should inverse just be before the 1270 // above boldColors effect instead? 1271 if ((flags & 1) && fg < 8) fg += 8; 1272 } 1273 1274 // invisible 1275 if (flags & 16) { 1276 out += 'visibility:hidden;'; 1277 } 1278 1279 // out += '" class="' 1280 // + 'term-bg-color-' + bg 1281 // + ' ' 1282 // + 'term-fg-color-' + fg 1283 // + '">'; 1284 1285 if (bg !== 256) { 1286 out += 'background-color:' 1287 + this.colors[bg] 1288 + ';'; 1289 } 1290 1291 if (fg !== 257) { 1292 out += 'color:' 1293 + this.colors[fg] 1294 + ';'; 1295 } 1296 1297 out += '">'; 1298 } 1299 } 1300 } 1301 1302 switch (ch) { 1303 case '&': 1304 out += '&'; 1305 break; 1306 case '<': 1307 out += '<'; 1308 break; 1309 case '>': 1310 out += '>'; 1311 break; 1312 default: 1313 if (ch <= ' ') { 1314 out += ' '; 1315 } else { 1316 if (isWide(ch)) i++; 1317 out += ch; 1318 } 1319 break; 1320 } 1321 1322 attr = data; 1323 } 1324 1325 if (attr !== this.defAttr) { 1326 out += '</span>'; 1327 } 1328 1329 this.children[y].innerHTML = out; 1330 } 1331 1332 if (parent) parent.appendChild(this.element); 1333 }; 1334 1335 Terminal.prototype._cursorBlink = function() { 1336 if (Terminal.focus !== this) return; 1337 this.cursorState ^= 1; 1338 this.refresh(this.y, this.y); 1339 }; 1340 1341 Terminal.prototype.showCursor = function() { 1342 if (!this.cursorState) { 1343 this.cursorState = 1; 1344 this.refresh(this.y, this.y); 1345 } else { 1346 // Temporarily disabled: 1347 // this.refreshBlink(); 1348 } 1349 }; 1350 1351 Terminal.prototype.startBlink = function() { 1352 if (!this.cursorBlink) return; 1353 var self = this; 1354 this._blinker = function() { 1355 self._cursorBlink(); 1356 }; 1357 this._blink = setInterval(this._blinker, 500); 1358 }; 1359 1360 Terminal.prototype.refreshBlink = function() { 1361 if (!this.cursorBlink) return; 1362 clearInterval(this._blink); 1363 this._blink = setInterval(this._blinker, 500); 1364 }; 1365 1366 Terminal.prototype.scroll = function() { 1367 var row; 1368 1369 if (++this.ybase === this.scrollback) { 1370 this.ybase = this.ybase / 2 | 0; 1371 this.lines = this.lines.slice(-(this.ybase + this.rows) + 1); 1372 } 1373 1374 this.ydisp = this.ybase; 1375 1376 // last line 1377 row = this.ybase + this.rows - 1; 1378 1379 // subtract the bottom scroll region 1380 row -= this.rows - 1 - this.scrollBottom; 1381 1382 if (row === this.lines.length) { 1383 // potential optimization: 1384 // pushing is faster than splicing 1385 // when they amount to the same 1386 // behavior. 1387 this.lines.push(this.blankLine()); 1388 } else { 1389 // add our new line 1390 this.lines.splice(row, 0, this.blankLine()); 1391 } 1392 1393 if (this.scrollTop !== 0) { 1394 if (this.ybase !== 0) { 1395 this.ybase--; 1396 this.ydisp = this.ybase; 1397 } 1398 this.lines.splice(this.ybase + this.scrollTop, 1); 1399 } 1400 1401 // this.maxRange(); 1402 this.updateRange(this.scrollTop); 1403 this.updateRange(this.scrollBottom); 1404 }; 1405 1406 Terminal.prototype.scrollDisp = function(disp) { 1407 this.ydisp += disp; 1408 1409 if (this.ydisp > this.ybase) { 1410 this.ydisp = this.ybase; 1411 } else if (this.ydisp < 0) { 1412 this.ydisp = 0; 1413 } 1414 1415 this.refresh(0, this.rows - 1); 1416 }; 1417 1418 Terminal.prototype.write = function(data) { 1419 var l = data.length 1420 , i = 0 1421 , j 1422 , cs 1423 , ch; 1424 1425 this.refreshStart = this.y; 1426 this.refreshEnd = this.y; 1427 1428 if (this.ybase !== this.ydisp) { 1429 this.ydisp = this.ybase; 1430 this.maxRange(); 1431 } 1432 1433 // this.log(JSON.stringify(data.replace(/\x1b/g, '^['))); 1434 1435 for (; i < l; i++) { 1436 ch = data[i]; 1437 switch (this.state) { 1438 case normal: 1439 switch (ch) { 1440 // '\0' 1441 // case '\0': 1442 // case '\200': 1443 // break; 1444 1445 // '\a' 1446 case '\x07': 1447 this.bell(); 1448 break; 1449 1450 // '\n', '\v', '\f' 1451 case '\n': 1452 case '\x0b': 1453 case '\x0c': 1454 if (this.convertEol) { 1455 this.x = 0; 1456 } 1457 // TODO: Implement eat_newline_glitch. 1458 // if (this.realX >= this.cols) break; 1459 // this.realX = 0; 1460 this.y++; 1461 if (this.y > this.scrollBottom) { 1462 this.y--; 1463 this.scroll(); 1464 } 1465 break; 1466 1467 // '\r' 1468 case '\r': 1469 this.x = 0; 1470 break; 1471 1472 // '\b' 1473 case '\x08': 1474 if (this.x > 0) { 1475 this.x--; 1476 } 1477 break; 1478 1479 // '\t' 1480 case '\t': 1481 this.x = this.nextStop(); 1482 break; 1483 1484 // shift out 1485 case '\x0e': 1486 this.setgLevel(1); 1487 break; 1488 1489 // shift in 1490 case '\x0f': 1491 this.setgLevel(0); 1492 break; 1493 1494 // '\e' 1495 case '\x1b': 1496 this.state = escaped; 1497 break; 1498 1499 default: 1500 // ' ' 1501 if (ch >= ' ') { 1502 if (this.charset && this.charset[ch]) { 1503 ch = this.charset[ch]; 1504 } 1505 1506 if (this.x >= this.cols) { 1507 this.x = 0; 1508 this.y++; 1509 if (this.y > this.scrollBottom) { 1510 this.y--; 1511 this.scroll(); 1512 } 1513 } 1514 1515 this.lines[this.y + this.ybase][this.x] = [this.curAttr, ch]; 1516 this.x++; 1517 this.updateRange(this.y); 1518 1519 if (isWide(ch)) { 1520 j = this.y + this.ybase; 1521 if (this.cols < 2 || this.x >= this.cols) { 1522 this.lines[j][this.x - 1] = [this.curAttr, ' ']; 1523 break; 1524 } 1525 this.lines[j][this.x] = [this.curAttr, ' ']; 1526 this.x++; 1527 } 1528 } 1529 break; 1530 } 1531 break; 1532 case escaped: 1533 switch (ch) { 1534 // ESC [ Control Sequence Introducer ( CSI is 0x9b). 1535 case '[': 1536 this.params = []; 1537 this.currentParam = 0; 1538 this.state = csi; 1539 break; 1540 1541 // ESC ] Operating System Command ( OSC is 0x9d). 1542 case ']': 1543 this.params = []; 1544 this.currentParam = 0; 1545 this.state = osc; 1546 break; 1547 1548 // ESC P Device Control String ( DCS is 0x90). 1549 case 'P': 1550 this.params = []; 1551 this.currentParam = 0; 1552 this.state = dcs; 1553 break; 1554 1555 // ESC _ Application Program Command ( APC is 0x9f). 1556 case '_': 1557 this.state = ignore; 1558 break; 1559 1560 // ESC ^ Privacy Message ( PM is 0x9e). 1561 case '^': 1562 this.state = ignore; 1563 break; 1564 1565 // ESC c Full Reset (RIS). 1566 case 'c': 1567 this.reset(); 1568 break; 1569 1570 // ESC E Next Line ( NEL is 0x85). 1571 // ESC D Index ( IND is 0x84). 1572 case 'E': 1573 this.x = 0; 1574 ; 1575 case 'D': 1576 this.index(); 1577 break; 1578 1579 // ESC M Reverse Index ( RI is 0x8d). 1580 case 'M': 1581 this.reverseIndex(); 1582 break; 1583 1584 // ESC % Select default/utf-8 character set. 1585 // @ = default, G = utf-8 1586 case '%': 1587 //this.charset = null; 1588 this.setgLevel(0); 1589 this.setgCharset(0, Terminal.charsets.US); 1590 this.state = normal; 1591 i++; 1592 break; 1593 1594 // ESC (,),*,+,-,. Designate G0-G2 Character Set. 1595 case '(': // <-- this seems to get all the attention 1596 case ')': 1597 case '*': 1598 case '+': 1599 case '-': 1600 case '.': 1601 switch (ch) { 1602 case '(': 1603 this.gcharset = 0; 1604 break; 1605 case ')': 1606 this.gcharset = 1; 1607 break; 1608 case '*': 1609 this.gcharset = 2; 1610 break; 1611 case '+': 1612 this.gcharset = 3; 1613 break; 1614 case '-': 1615 this.gcharset = 1; 1616 break; 1617 case '.': 1618 this.gcharset = 2; 1619 break; 1620 } 1621 this.state = charset; 1622 break; 1623 1624 // Designate G3 Character Set (VT300). 1625 // A = ISO Latin-1 Supplemental. 1626 // Not implemented. 1627 case '/': 1628 this.gcharset = 3; 1629 this.state = charset; 1630 i--; 1631 break; 1632 1633 // ESC N 1634 // Single Shift Select of G2 Character Set 1635 // ( SS2 is 0x8e). This affects next character only. 1636 case 'N': 1637 break; 1638 // ESC O 1639 // Single Shift Select of G3 Character Set 1640 // ( SS3 is 0x8f). This affects next character only. 1641 case 'O': 1642 break; 1643 // ESC n 1644 // Invoke the G2 Character Set as GL (LS2). 1645 case 'n': 1646 this.setgLevel(2); 1647 break; 1648 // ESC o 1649 // Invoke the G3 Character Set as GL (LS3). 1650 case 'o': 1651 this.setgLevel(3); 1652 break; 1653 // ESC | 1654 // Invoke the G3 Character Set as GR (LS3R). 1655 case '|': 1656 this.setgLevel(3); 1657 break; 1658 // ESC } 1659 // Invoke the G2 Character Set as GR (LS2R). 1660 case '}': 1661 this.setgLevel(2); 1662 break; 1663 // ESC ~ 1664 // Invoke the G1 Character Set as GR (LS1R). 1665 case '~': 1666 this.setgLevel(1); 1667 break; 1668 1669 // ESC 7 Save Cursor (DECSC). 1670 case '7': 1671 this.saveCursor(); 1672 this.state = normal; 1673 break; 1674 1675 // ESC 8 Restore Cursor (DECRC). 1676 case '8': 1677 this.restoreCursor(); 1678 this.state = normal; 1679 break; 1680 1681 // ESC # 3 DEC line height/width 1682 case '#': 1683 this.state = normal; 1684 i++; 1685 break; 1686 1687 // ESC H Tab Set (HTS is 0x88). 1688 case 'H': 1689 this.tabSet(); 1690 break; 1691 1692 // ESC = Application Keypad (DECPAM). 1693 case '=': 1694 this.log('Serial port requested application keypad.'); 1695 this.applicationKeypad = true; 1696 this.state = normal; 1697 break; 1698 1699 // ESC > Normal Keypad (DECPNM). 1700 case '>': 1701 this.log('Switching back to normal keypad.'); 1702 this.applicationKeypad = false; 1703 this.state = normal; 1704 break; 1705 1706 default: 1707 this.state = normal; 1708 this.error('Unknown ESC control: %s.', ch); 1709 break; 1710 } 1711 break; 1712 1713 case charset: 1714 switch (ch) { 1715 case '0': // DEC Special Character and Line Drawing Set. 1716 cs = Terminal.charsets.SCLD; 1717 break; 1718 case 'A': // UK 1719 cs = Terminal.charsets.UK; 1720 break; 1721 case 'B': // United States (USASCII). 1722 cs = Terminal.charsets.US; 1723 break; 1724 case '4': // Dutch 1725 cs = Terminal.charsets.Dutch; 1726 break; 1727 case 'C': // Finnish 1728 case '5': 1729 cs = Terminal.charsets.Finnish; 1730 break; 1731 case 'R': // French 1732 cs = Terminal.charsets.French; 1733 break; 1734 case 'Q': // FrenchCanadian 1735 cs = Terminal.charsets.FrenchCanadian; 1736 break; 1737 case 'K': // German 1738 cs = Terminal.charsets.German; 1739 break; 1740 case 'Y': // Italian 1741 cs = Terminal.charsets.Italian; 1742 break; 1743 case 'E': // NorwegianDanish 1744 case '6': 1745 cs = Terminal.charsets.NorwegianDanish; 1746 break; 1747 case 'Z': // Spanish 1748 cs = Terminal.charsets.Spanish; 1749 break; 1750 case 'H': // Swedish 1751 case '7': 1752 cs = Terminal.charsets.Swedish; 1753 break; 1754 case '=': // Swiss 1755 cs = Terminal.charsets.Swiss; 1756 break; 1757 case '/': // ISOLatin (actually /A) 1758 cs = Terminal.charsets.ISOLatin; 1759 i++; 1760 break; 1761 default: // Default 1762 cs = Terminal.charsets.US; 1763 break; 1764 } 1765 this.setgCharset(this.gcharset, cs); 1766 this.gcharset = null; 1767 this.state = normal; 1768 break; 1769 1770 case osc: 1771 // OSC Ps ; Pt ST 1772 // OSC Ps ; Pt BEL 1773 // Set Text Parameters. 1774 if (ch === '\x1b' || ch === '\x07') { 1775 if (ch === '\x1b') i++; 1776 1777 this.params.push(this.currentParam); 1778 1779 switch (this.params[0]) { 1780 case 0: 1781 case 1: 1782 case 2: 1783 if (this.params[1]) { 1784 this.title = this.params[1]; 1785 this.handleTitle(this.title); 1786 } 1787 break; 1788 case 3: 1789 // set X property 1790 break; 1791 case 4: 1792 case 5: 1793 // change dynamic colors 1794 break; 1795 case 10: 1796 case 11: 1797 case 12: 1798 case 13: 1799 case 14: 1800 case 15: 1801 case 16: 1802 case 17: 1803 case 18: 1804 case 19: 1805 // change dynamic ui colors 1806 break; 1807 case 46: 1808 // change log file 1809 break; 1810 case 50: 1811 // dynamic font 1812 break; 1813 case 51: 1814 // emacs shell 1815 break; 1816 case 52: 1817 // manipulate selection data 1818 break; 1819 case 104: 1820 case 105: 1821 case 110: 1822 case 111: 1823 case 112: 1824 case 113: 1825 case 114: 1826 case 115: 1827 case 116: 1828 case 117: 1829 case 118: 1830 // reset colors 1831 break; 1832 } 1833 1834 this.params = []; 1835 this.currentParam = 0; 1836 this.state = normal; 1837 } else { 1838 if (!this.params.length) { 1839 if (ch >= '0' && ch <= '9') { 1840 this.currentParam = 1841 this.currentParam * 10 + ch.charCodeAt(0) - 48; 1842 } else if (ch === ';') { 1843 this.params.push(this.currentParam); 1844 this.currentParam = ''; 1845 } 1846 } else { 1847 this.currentParam += ch; 1848 } 1849 } 1850 break; 1851 1852 case csi: 1853 // '?', '>', '!' 1854 if (ch === '?' || ch === '>' || ch === '!') { 1855 this.prefix = ch; 1856 break; 1857 } 1858 1859 // 0 - 9 1860 if (ch >= '0' && ch <= '9') { 1861 this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; 1862 break; 1863 } 1864 1865 // '$', '"', ' ', '\'' 1866 if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') { 1867 this.postfix = ch; 1868 break; 1869 } 1870 1871 this.params.push(this.currentParam); 1872 this.currentParam = 0; 1873 1874 // ';' 1875 if (ch === ';') break; 1876 1877 this.state = normal; 1878 1879 switch (ch) { 1880 // CSI Ps A 1881 // Cursor Up Ps Times (default = 1) (CUU). 1882 case 'A': 1883 this.cursorUp(this.params); 1884 break; 1885 1886 // CSI Ps B 1887 // Cursor Down Ps Times (default = 1) (CUD). 1888 case 'B': 1889 this.cursorDown(this.params); 1890 break; 1891 1892 // CSI Ps C 1893 // Cursor Forward Ps Times (default = 1) (CUF). 1894 case 'C': 1895 this.cursorForward(this.params); 1896 break; 1897 1898 // CSI Ps D 1899 // Cursor Backward Ps Times (default = 1) (CUB). 1900 case 'D': 1901 this.cursorBackward(this.params); 1902 break; 1903 1904 // CSI Ps ; Ps H 1905 // Cursor Position [row;column] (default = [1,1]) (CUP). 1906 case 'H': 1907 this.cursorPos(this.params); 1908 break; 1909 1910 // CSI Ps J Erase in Display (ED). 1911 case 'J': 1912 this.eraseInDisplay(this.params); 1913 break; 1914 1915 // CSI Ps K Erase in Line (EL). 1916 case 'K': 1917 this.eraseInLine(this.params); 1918 break; 1919 1920 // CSI Pm m Character Attributes (SGR). 1921 case 'm': 1922 if (!this.prefix) { 1923 this.charAttributes(this.params); 1924 } 1925 break; 1926 1927 // CSI Ps n Device Status Report (DSR). 1928 case 'n': 1929 if (!this.prefix) { 1930 this.deviceStatus(this.params); 1931 } 1932 break; 1933 1934 /** 1935 * Additions 1936 */ 1937 1938 // CSI Ps @ 1939 // Insert Ps (Blank) Character(s) (default = 1) (ICH). 1940 case '@': 1941 this.insertChars(this.params); 1942 break; 1943 1944 // CSI Ps E 1945 // Cursor Next Line Ps Times (default = 1) (CNL). 1946 case 'E': 1947 this.cursorNextLine(this.params); 1948 break; 1949 1950 // CSI Ps F 1951 // Cursor Preceding Line Ps Times (default = 1) (CNL). 1952 case 'F': 1953 this.cursorPrecedingLine(this.params); 1954 break; 1955 1956 // CSI Ps G 1957 // Cursor Character Absolute [column] (default = [row,1]) (CHA). 1958 case 'G': 1959 this.cursorCharAbsolute(this.params); 1960 break; 1961 1962 // CSI Ps L 1963 // Insert Ps Line(s) (default = 1) (IL). 1964 case 'L': 1965 this.insertLines(this.params); 1966 break; 1967 1968 // CSI Ps M 1969 // Delete Ps Line(s) (default = 1) (DL). 1970 case 'M': 1971 this.deleteLines(this.params); 1972 break; 1973 1974 // CSI Ps P 1975 // Delete Ps Character(s) (default = 1) (DCH). 1976 case 'P': 1977 this.deleteChars(this.params); 1978 break; 1979 1980 // CSI Ps X 1981 // Erase Ps Character(s) (default = 1) (ECH). 1982 case 'X': 1983 this.eraseChars(this.params); 1984 break; 1985 1986 // CSI Pm ` Character Position Absolute 1987 // [column] (default = [row,1]) (HPA). 1988 case '`': 1989 this.charPosAbsolute(this.params); 1990 break; 1991 1992 // 141 61 a * HPR - 1993 // Horizontal Position Relative 1994 case 'a': 1995 this.HPositionRelative(this.params); 1996 break; 1997 1998 // CSI P s c 1999 // Send Device Attributes (Primary DA). 2000 // CSI > P s c 2001 // Send Device Attributes (Secondary DA) 2002 case 'c': 2003 this.sendDeviceAttributes(this.params); 2004 break; 2005 2006 // CSI Pm d 2007 // Line Position Absolute [row] (default = [1,column]) (VPA). 2008 case 'd': 2009 this.linePosAbsolute(this.params); 2010 break; 2011 2012 // 145 65 e * VPR - Vertical Position Relative 2013 case 'e': 2014 this.VPositionRelative(this.params); 2015 break; 2016 2017 // CSI Ps ; Ps f 2018 // Horizontal and Vertical Position [row;column] (default = 2019 // [1,1]) (HVP). 2020 case 'f': 2021 this.HVPosition(this.params); 2022 break; 2023 2024 // CSI Pm h Set Mode (SM). 2025 // CSI ? Pm h - mouse escape codes, cursor escape codes 2026 case 'h': 2027 this.setMode(this.params); 2028 break; 2029 2030 // CSI Pm l Reset Mode (RM). 2031 // CSI ? Pm l 2032 case 'l': 2033 this.resetMode(this.params); 2034 break; 2035 2036 // CSI Ps ; Ps r 2037 // Set Scrolling Region [top;bottom] (default = full size of win- 2038 // dow) (DECSTBM). 2039 // CSI ? Pm r 2040 case 'r': 2041 this.setScrollRegion(this.params); 2042 break; 2043 2044 // CSI s 2045 // Save cursor (ANSI.SYS). 2046 case 's': 2047 this.saveCursor(this.params); 2048 break; 2049 2050 // CSI u 2051 // Restore cursor (ANSI.SYS). 2052 case 'u': 2053 this.restoreCursor(this.params); 2054 break; 2055 2056 /** 2057 * Lesser Used 2058 */ 2059 2060 // CSI Ps I 2061 // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). 2062 case 'I': 2063 this.cursorForwardTab(this.params); 2064 break; 2065 2066 // CSI Ps S Scroll up Ps lines (default = 1) (SU). 2067 case 'S': 2068 this.scrollUp(this.params); 2069 break; 2070 2071 // CSI Ps T Scroll down Ps lines (default = 1) (SD). 2072 // CSI Ps ; Ps ; Ps ; Ps ; Ps T 2073 // CSI > Ps; Ps T 2074 case 'T': 2075 // if (this.prefix === '>') { 2076 // this.resetTitleModes(this.params); 2077 // break; 2078 // } 2079 // if (this.params.length > 2) { 2080 // this.initMouseTracking(this.params); 2081 // break; 2082 // } 2083 if (this.params.length < 2 && !this.prefix) { 2084 this.scrollDown(this.params); 2085 } 2086 break; 2087 2088 // CSI Ps Z 2089 // Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). 2090 case 'Z': 2091 this.cursorBackwardTab(this.params); 2092 break; 2093 2094 // CSI Ps b Repeat the preceding graphic character Ps times (REP). 2095 case 'b': 2096 this.repeatPrecedingCharacter(this.params); 2097 break; 2098 2099 // CSI Ps g Tab Clear (TBC). 2100 case 'g': 2101 this.tabClear(this.params); 2102 break; 2103 2104 // CSI Pm i Media Copy (MC). 2105 // CSI ? Pm i 2106 // case 'i': 2107 // this.mediaCopy(this.params); 2108 // break; 2109 2110 // CSI Pm m Character Attributes (SGR). 2111 // CSI > Ps; Ps m 2112 // case 'm': // duplicate 2113 // if (this.prefix === '>') { 2114 // this.setResources(this.params); 2115 // } else { 2116 // this.charAttributes(this.params); 2117 // } 2118 // break; 2119 2120 // CSI Ps n Device Status Report (DSR). 2121 // CSI > Ps n 2122 // case 'n': // duplicate 2123 // if (this.prefix === '>') { 2124 // this.disableModifiers(this.params); 2125 // } else { 2126 // this.deviceStatus(this.params); 2127 // } 2128 // break; 2129 2130 // CSI > Ps p Set pointer mode. 2131 // CSI ! p Soft terminal reset (DECSTR). 2132 // CSI Ps$ p 2133 // Request ANSI mode (DECRQM). 2134 // CSI ? Ps$ p 2135 // Request DEC private mode (DECRQM). 2136 // CSI Ps ; Ps " p 2137 case 'p': 2138 switch (this.prefix) { 2139 // case '>': 2140 // this.setPointerMode(this.params); 2141 // break; 2142 case '!': 2143 this.softReset(this.params); 2144 break; 2145 // case '?': 2146 // if (this.postfix === '$') { 2147 // this.requestPrivateMode(this.params); 2148 // } 2149 // break; 2150 // default: 2151 // if (this.postfix === '"') { 2152 // this.setConformanceLevel(this.params); 2153 // } else if (this.postfix === '$') { 2154 // this.requestAnsiMode(this.params); 2155 // } 2156 // break; 2157 } 2158 break; 2159 2160 // CSI Ps q Load LEDs (DECLL). 2161 // CSI Ps SP q 2162 // CSI Ps " q 2163 // case 'q': 2164 // if (this.postfix === ' ') { 2165 // this.setCursorStyle(this.params); 2166 // break; 2167 // } 2168 // if (this.postfix === '"') { 2169 // this.setCharProtectionAttr(this.params); 2170 // break; 2171 // } 2172 // this.loadLEDs(this.params); 2173 // break; 2174 2175 // CSI Ps ; Ps r 2176 // Set Scrolling Region [top;bottom] (default = full size of win- 2177 // dow) (DECSTBM). 2178 // CSI ? Pm r 2179 // CSI Pt; Pl; Pb; Pr; Ps$ r 2180 // case 'r': // duplicate 2181 // if (this.prefix === '?') { 2182 // this.restorePrivateValues(this.params); 2183 // } else if (this.postfix === '$') { 2184 // this.setAttrInRectangle(this.params); 2185 // } else { 2186 // this.setScrollRegion(this.params); 2187 // } 2188 // break; 2189 2190 // CSI s Save cursor (ANSI.SYS). 2191 // CSI ? Pm s 2192 // case 's': // duplicate 2193 // if (this.prefix === '?') { 2194 // this.savePrivateValues(this.params); 2195 // } else { 2196 // this.saveCursor(this.params); 2197 // } 2198 // break; 2199 2200 // CSI Ps ; Ps ; Ps t 2201 // CSI Pt; Pl; Pb; Pr; Ps$ t 2202 // CSI > Ps; Ps t 2203 // CSI Ps SP t 2204 // case 't': 2205 // if (this.postfix === '$') { 2206 // this.reverseAttrInRectangle(this.params); 2207 // } else if (this.postfix === ' ') { 2208 // this.setWarningBellVolume(this.params); 2209 // } else { 2210 // if (this.prefix === '>') { 2211 // this.setTitleModeFeature(this.params); 2212 // } else { 2213 // this.manipulateWindow(this.params); 2214 // } 2215 // } 2216 // break; 2217 2218 // CSI u Restore cursor (ANSI.SYS). 2219 // CSI Ps SP u 2220 // case 'u': // duplicate 2221 // if (this.postfix === ' ') { 2222 // this.setMarginBellVolume(this.params); 2223 // } else { 2224 // this.restoreCursor(this.params); 2225 // } 2226 // break; 2227 2228 // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v 2229 // case 'v': 2230 // if (this.postfix === '$') { 2231 // this.copyRectagle(this.params); 2232 // } 2233 // break; 2234 2235 // CSI Pt ; Pl ; Pb ; Pr ' w 2236 // case 'w': 2237 // if (this.postfix === '\'') { 2238 // this.enableFilterRectangle(this.params); 2239 // } 2240 // break; 2241 2242 // CSI Ps x Request Terminal Parameters (DECREQTPARM). 2243 // CSI Ps x Select Attribute Change Extent (DECSACE). 2244 // CSI Pc; Pt; Pl; Pb; Pr$ x 2245 // case 'x': 2246 // if (this.postfix === '$') { 2247 // this.fillRectangle(this.params); 2248 // } else { 2249 // this.requestParameters(this.params); 2250 // //this.__(this.params); 2251 // } 2252 // break; 2253 2254 // CSI Ps ; Pu ' z 2255 // CSI Pt; Pl; Pb; Pr$ z 2256 // case 'z': 2257 // if (this.postfix === '\'') { 2258 // this.enableLocatorReporting(this.params); 2259 // } else if (this.postfix === '$') { 2260 // this.eraseRectangle(this.params); 2261 // } 2262 // break; 2263 2264 // CSI Pm ' { 2265 // CSI Pt; Pl; Pb; Pr$ { 2266 // case '{': 2267 // if (this.postfix === '\'') { 2268 // this.setLocatorEvents(this.params); 2269 // } else if (this.postfix === '$') { 2270 // this.selectiveEraseRectangle(this.params); 2271 // } 2272 // break; 2273 2274 // CSI Ps ' | 2275 // case '|': 2276 // if (this.postfix === '\'') { 2277 // this.requestLocatorPosition(this.params); 2278 // } 2279 // break; 2280 2281 // CSI P m SP } 2282 // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. 2283 // case '}': 2284 // if (this.postfix === ' ') { 2285 // this.insertColumns(this.params); 2286 // } 2287 // break; 2288 2289 // CSI P m SP ~ 2290 // Delete P s Column(s) (default = 1) (DECDC), VT420 and up 2291 // case '~': 2292 // if (this.postfix === ' ') { 2293 // this.deleteColumns(this.params); 2294 // } 2295 // break; 2296 2297 default: 2298 this.error('Unknown CSI code: %s.', ch); 2299 break; 2300 } 2301 2302 this.prefix = ''; 2303 this.postfix = ''; 2304 break; 2305 2306 case dcs: 2307 if (ch === '\x1b' || ch === '\x07') { 2308 if (ch === '\x1b') i++; 2309 2310 switch (this.prefix) { 2311 // User-Defined Keys (DECUDK). 2312 case '': 2313 break; 2314 2315 // Request Status String (DECRQSS). 2316 // test: echo -e '\eP$q"p\e\\' 2317 case '$q': 2318 var pt = this.currentParam 2319 , valid = false; 2320 2321 switch (pt) { 2322 // DECSCA 2323 case '"q': 2324 pt = '0"q'; 2325 break; 2326 2327 // DECSCL 2328 case '"p': 2329 pt = '61"p'; 2330 break; 2331 2332 // DECSTBM 2333 case 'r': 2334 pt = '' 2335 + (this.scrollTop + 1) 2336 + ';' 2337 + (this.scrollBottom + 1) 2338 + 'r'; 2339 break; 2340 2341 // SGR 2342 case 'm': 2343 pt = '0m'; 2344 break; 2345 2346 default: 2347 this.error('Unknown DCS Pt: %s.', pt); 2348 pt = ''; 2349 break; 2350 } 2351 2352 this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\'); 2353 break; 2354 2355 // Set Termcap/Terminfo Data (xterm, experimental). 2356 case '+p': 2357 break; 2358 2359 // Request Termcap/Terminfo String (xterm, experimental) 2360 // Regular xterm does not even respond to this sequence. 2361 // This can cause a small glitch in vim. 2362 // test: echo -ne '\eP+q6b64\e\\' 2363 case '+q': 2364 var pt = this.currentParam 2365 , valid = false; 2366 2367 this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\'); 2368 break; 2369 2370 default: 2371 this.error('Unknown DCS prefix: %s.', this.prefix); 2372 break; 2373 } 2374 2375 this.currentParam = 0; 2376 this.prefix = ''; 2377 this.state = normal; 2378 } else if (!this.currentParam) { 2379 if (!this.prefix && ch !== '$' && ch !== '+') { 2380 this.currentParam = ch; 2381 } else if (this.prefix.length === 2) { 2382 this.currentParam = ch; 2383 } else { 2384 this.prefix += ch; 2385 } 2386 } else { 2387 this.currentParam += ch; 2388 } 2389 break; 2390 2391 case ignore: 2392 // For PM and APC. 2393 if (ch === '\x1b' || ch === '\x07') { 2394 if (ch === '\x1b') i++; 2395 this.state = normal; 2396 } 2397 break; 2398 } 2399 } 2400 2401 this.updateRange(this.y); 2402 this.refresh(this.refreshStart, this.refreshEnd); 2403 }; 2404 2405 Terminal.prototype.writeln = function(data) { 2406 this.write(data + '\r\n'); 2407 }; 2408 2409 // Key Resources: 2410 // https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent 2411 Terminal.prototype.keyDown = function(ev) { 2412 var self = this 2413 , key; 2414 2415 switch (ev.keyCode) { 2416 // backspace 2417 case 8: 2418 if (ev.shiftKey) { 2419 key = '\x08'; // ^H 2420 break; 2421 } 2422 key = '\x7f'; // ^? 2423 break; 2424 // tab 2425 case 9: 2426 if (ev.shiftKey) { 2427 key = '\x1b[Z'; 2428 break; 2429 } 2430 key = '\t'; 2431 break; 2432 // return/enter 2433 case 13: 2434 key = '\r'; 2435 break; 2436 // escape 2437 case 27: 2438 key = '\x1b'; 2439 break; 2440 // left-arrow 2441 case 37: 2442 if (this.applicationCursor) { 2443 key = '\x1bOD'; // SS3 as ^[O for 7-bit 2444 //key = '\x8fD'; // SS3 as 0x8f for 8-bit 2445 break; 2446 } 2447 key = '\x1b[D'; 2448 break; 2449 // right-arrow 2450 case 39: 2451 if (this.applicationCursor) { 2452 key = '\x1bOC'; 2453 break; 2454 } 2455 key = '\x1b[C'; 2456 break; 2457 // up-arrow 2458 case 38: 2459 if (this.applicationCursor) { 2460 key = '\x1bOA'; 2461 break; 2462 } 2463 if (ev.ctrlKey) { 2464 this.scrollDisp(-1); 2465 return cancel(ev); 2466 } else { 2467 key = '\x1b[A'; 2468 } 2469 break; 2470 // down-arrow 2471 case 40: 2472 if (this.applicationCursor) { 2473 key = '\x1bOB'; 2474 break; 2475 } 2476 if (ev.ctrlKey) { 2477 this.scrollDisp(1); 2478 return cancel(ev); 2479 } else { 2480 key = '\x1b[B'; 2481 } 2482 break; 2483 // delete 2484 case 46: 2485 key = '\x1b[3~'; 2486 break; 2487 // insert 2488 case 45: 2489 key = '\x1b[2~'; 2490 break; 2491 // home 2492 case 36: 2493 if (this.applicationKeypad) { 2494 key = '\x1bOH'; 2495 break; 2496 } 2497 key = '\x1bOH'; 2498 break; 2499 // end 2500 case 35: 2501 if (this.applicationKeypad) { 2502 key = '\x1bOF'; 2503 break; 2504 } 2505 key = '\x1bOF'; 2506 break; 2507 // page up 2508 case 33: 2509 if (ev.shiftKey) { 2510 this.scrollDisp(-(this.rows - 1)); 2511 return cancel(ev); 2512 } else { 2513 key = '\x1b[5~'; 2514 } 2515 break; 2516 // page down 2517 case 34: 2518 if (ev.shiftKey) { 2519 this.scrollDisp(this.rows - 1); 2520 return cancel(ev); 2521 } else { 2522 key = '\x1b[6~'; 2523 } 2524 break; 2525 // F1 2526 case 112: 2527 key = '\x1bOP'; 2528 break; 2529 // F2 2530 case 113: 2531 key = '\x1bOQ'; 2532 break; 2533 // F3 2534 case 114: 2535 key = '\x1bOR'; 2536 break; 2537 // F4 2538 case 115: 2539 key = '\x1bOS'; 2540 break; 2541 // F5 2542 case 116: 2543 key = '\x1b[15~'; 2544 break; 2545 // F6 2546 case 117: 2547 key = '\x1b[17~'; 2548 break; 2549 // F7 2550 case 118: 2551 key = '\x1b[18~'; 2552 break; 2553 // F8 2554 case 119: 2555 key = '\x1b[19~'; 2556 break; 2557 // F9 2558 case 120: 2559 key = '\x1b[20~'; 2560 break; 2561 // F10 2562 case 121: 2563 key = '\x1b[21~'; 2564 break; 2565 // F11 2566 case 122: 2567 key = '\x1b[23~'; 2568 break; 2569 // F12 2570 case 123: 2571 key = '\x1b[24~'; 2572 break; 2573 default: 2574 // a-z and space 2575 if (ev.ctrlKey) { 2576 if (ev.keyCode >= 65 && ev.keyCode <= 90) { 2577 // Ctrl-A 2578 if (this.screenKeys) { 2579 if (!this.prefixMode && !this.selectMode && ev.keyCode === 65) { 2580 this.enterPrefix(); 2581 return cancel(ev); 2582 } 2583 } 2584 // Ctrl-V 2585 if (this.prefixMode && ev.keyCode === 86) { 2586 this.leavePrefix(); 2587 return; 2588 } 2589 // Ctrl-C 2590 if ((this.prefixMode || this.selectMode) && ev.keyCode === 67) { 2591 if (this.visualMode) { 2592 setTimeout(function() { 2593 self.leaveVisual(); 2594 }, 1); 2595 } 2596 return; 2597 } 2598 key = String.fromCharCode(ev.keyCode - 64); 2599 } else if (ev.keyCode === 32) { 2600 // NUL 2601 key = String.fromCharCode(0); 2602 } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { 2603 // escape, file sep, group sep, record sep, unit sep 2604 key = String.fromCharCode(ev.keyCode - 51 + 27); 2605 } else if (ev.keyCode === 56) { 2606 // delete 2607 key = String.fromCharCode(127); 2608 } else if (ev.keyCode === 219) { 2609 // ^[ - escape 2610 key = String.fromCharCode(27); 2611 } else if (ev.keyCode === 221) { 2612 // ^] - group sep 2613 key = String.fromCharCode(29); 2614 } 2615 } else if ((!this.isMac && ev.altKey) || (this.isMac && ev.metaKey)) { 2616 if (ev.keyCode >= 65 && ev.keyCode <= 90) { 2617 key = '\x1b' + String.fromCharCode(ev.keyCode + 32); 2618 } else if (ev.keyCode === 192) { 2619 key = '\x1b`'; 2620 } else if (ev.keyCode >= 48 && ev.keyCode <= 57) { 2621 key = '\x1b' + (ev.keyCode - 48); 2622 } 2623 } 2624 break; 2625 } 2626 2627 if (!key) return true; 2628 2629 if (this.prefixMode) { 2630 this.leavePrefix(); 2631 return cancel(ev); 2632 } 2633 2634 if (this.selectMode) { 2635 this.keySelect(ev, key); 2636 return cancel(ev); 2637 } 2638 2639 this.emit('keydown', ev); 2640 this.emit('key', key, ev); 2641 2642 this.showCursor(); 2643 this.handler(key); 2644 2645 return cancel(ev); 2646 }; 2647 2648 Terminal.prototype.setgLevel = function(g) { 2649 this.glevel = g; 2650 this.charset = this.charsets[g]; 2651 }; 2652 2653 Terminal.prototype.setgCharset = function(g, charset) { 2654 this.charsets[g] = charset; 2655 if (this.glevel === g) { 2656 this.charset = charset; 2657 } 2658 }; 2659 2660 Terminal.prototype.keyPress = function(ev) { 2661 var key; 2662 2663 cancel(ev); 2664 2665 if (ev.charCode) { 2666 key = ev.charCode; 2667 } else if (ev.which == null) { 2668 key = ev.keyCode; 2669 } else if (ev.which !== 0 && ev.charCode !== 0) { 2670 key = ev.which; 2671 } else { 2672 return false; 2673 } 2674 2675 if (!key || ev.ctrlKey || ev.altKey || ev.metaKey) return false; 2676 2677 key = String.fromCharCode(key); 2678 2679 if (this.prefixMode) { 2680 this.leavePrefix(); 2681 this.keyPrefix(ev, key); 2682 return false; 2683 } 2684 2685 if (this.selectMode) { 2686 this.keySelect(ev, key); 2687 return false; 2688 } 2689 2690 this.emit('keypress', key, ev); 2691 this.emit('key', key, ev); 2692 2693 this.showCursor(); 2694 this.handler(key); 2695 2696 return false; 2697 }; 2698 2699 Terminal.prototype.send = function(data) { 2700 var self = this; 2701 2702 if (!this.queue) { 2703 setTimeout(function() { 2704 self.handler(self.queue); 2705 self.queue = ''; 2706 }, 1); 2707 } 2708 2709 this.queue += data; 2710 }; 2711 2712 Terminal.prototype.bell = function() { 2713 this.emit('bell'); 2714 if (!this.visualBell) return; 2715 var self = this; 2716 this.element.style.borderColor = 'white'; 2717 setTimeout(function() { 2718 self.element.style.borderColor = ''; 2719 }, 10); 2720 if (this.popOnBell) this.focus(); 2721 }; 2722 2723 Terminal.prototype.log = function() { 2724 if (!this.debug) return; 2725 if (!this.context.console || !this.context.console.log) return; 2726 var args = Array.prototype.slice.call(arguments); 2727 this.context.console.log.apply(this.context.console, args); 2728 }; 2729 2730 Terminal.prototype.error = function() { 2731 if (!this.debug) return; 2732 if (!this.context.console || !this.context.console.error) return; 2733 var args = Array.prototype.slice.call(arguments); 2734 this.context.console.error.apply(this.context.console, args); 2735 }; 2736 2737 Terminal.prototype.resize = function(x, y) { 2738 var line 2739 , el 2740 , i 2741 , j 2742 , ch; 2743 2744 if (x < 1) x = 1; 2745 if (y < 1) y = 1; 2746 2747 // resize cols 2748 j = this.cols; 2749 if (j < x) { 2750 ch = [this.defAttr, ' ']; // does xterm use the default attr? 2751 i = this.lines.length; 2752 while (i--) { 2753 while (this.lines[i].length < x) { 2754 this.lines[i].push(ch); 2755 } 2756 } 2757 } else if (j > x) { 2758 i = this.lines.length; 2759 while (i--) { 2760 while (this.lines[i].length > x) { 2761 this.lines[i].pop(); 2762 } 2763 } 2764 } 2765 this.setupStops(j); 2766 this.cols = x; 2767 2768 // resize rows 2769 j = this.rows; 2770 if (j < y) { 2771 el = this.element; 2772 while (j++ < y) { 2773 if (this.lines.length < y + this.ybase) { 2774 this.lines.push(this.blankLine()); 2775 } 2776 if (this.children.length < y) { 2777 line = this.document.createElement('div'); 2778 el.appendChild(line); 2779 this.children.push(line); 2780 } 2781 } 2782 } else if (j > y) { 2783 while (j-- > y) { 2784 if (this.lines.length > y + this.ybase) { 2785 this.lines.pop(); 2786 } 2787 if (this.children.length > y) { 2788 el = this.children.pop(); 2789 if (!el) continue; 2790 el.parentNode.removeChild(el); 2791 } 2792 } 2793 } 2794 this.rows = y; 2795 2796 // make sure the cursor stays on screen 2797 if (this.y >= y) this.y = y - 1; 2798 if (this.x >= x) this.x = x - 1; 2799 2800 this.scrollTop = 0; 2801 this.scrollBottom = y - 1; 2802 2803 this.refresh(0, this.rows - 1); 2804 2805 // it's a real nightmare trying 2806 // to resize the original 2807 // screen buffer. just set it 2808 // to null for now. 2809 this.normal = null; 2810 }; 2811 2812 Terminal.prototype.updateRange = function(y) { 2813 if (y < this.refreshStart) this.refreshStart = y; 2814 if (y > this.refreshEnd) this.refreshEnd = y; 2815 // if (y > this.refreshEnd) { 2816 // this.refreshEnd = y; 2817 // if (y > this.rows - 1) { 2818 // this.refreshEnd = this.rows - 1; 2819 // } 2820 // } 2821 }; 2822 2823 Terminal.prototype.maxRange = function() { 2824 this.refreshStart = 0; 2825 this.refreshEnd = this.rows - 1; 2826 }; 2827 2828 Terminal.prototype.setupStops = function(i) { 2829 if (i != null) { 2830 if (!this.tabs[i]) { 2831 i = this.prevStop(i); 2832 } 2833 } else { 2834 this.tabs = {}; 2835 i = 0; 2836 } 2837 2838 for (; i < this.cols; i += 8) { 2839 this.tabs[i] = true; 2840 } 2841 }; 2842 2843 Terminal.prototype.prevStop = function(x) { 2844 if (x == null) x = this.x; 2845 while (!this.tabs[--x] && x > 0); 2846 return x >= this.cols 2847 ? this.cols - 1 2848 : x < 0 ? 0 : x; 2849 }; 2850 2851 Terminal.prototype.nextStop = function(x) { 2852 if (x == null) x = this.x; 2853 while (!this.tabs[++x] && x < this.cols); 2854 return x >= this.cols 2855 ? this.cols - 1 2856 : x < 0 ? 0 : x; 2857 }; 2858 2859 Terminal.prototype.eraseRight = function(x, y) { 2860 var line = this.lines[this.ybase + y] 2861 , ch = [this.eraseAttr(), ' ']; // xterm 2862 2863 2864 for (; x < this.cols; x++) { 2865 line[x] = ch; 2866 } 2867 2868 this.updateRange(y); 2869 }; 2870 2871 Terminal.prototype.eraseLeft = function(x, y) { 2872 var line = this.lines[this.ybase + y] 2873 , ch = [this.eraseAttr(), ' ']; // xterm 2874 2875 x++; 2876 while (x--) line[x] = ch; 2877 2878 this.updateRange(y); 2879 }; 2880 2881 Terminal.prototype.eraseLine = function(y) { 2882 this.eraseRight(0, y); 2883 }; 2884 2885 Terminal.prototype.blankLine = function(cur) { 2886 var attr = cur 2887 ? this.eraseAttr() 2888 : this.defAttr; 2889 2890 var ch = [attr, ' '] 2891 , line = [] 2892 , i = 0; 2893 2894 for (; i < this.cols; i++) { 2895 line[i] = ch; 2896 } 2897 2898 return line; 2899 }; 2900 2901 Terminal.prototype.ch = function(cur) { 2902 return cur 2903 ? [this.eraseAttr(), ' '] 2904 : [this.defAttr, ' ']; 2905 }; 2906 2907 Terminal.prototype.is = function(term) { 2908 var name = this.termName; 2909 return (name + '').indexOf(term) === 0; 2910 }; 2911 2912 Terminal.prototype.handler = function(data) { 2913 this.emit('data', data); 2914 }; 2915 2916 Terminal.prototype.handleTitle = function(title) { 2917 this.emit('title', title); 2918 }; 2919 2920 /** 2921 * ESC 2922 */ 2923 2924 // ESC D Index (IND is 0x84). 2925 Terminal.prototype.index = function() { 2926 this.y++; 2927 if (this.y > this.scrollBottom) { 2928 this.y--; 2929 this.scroll(); 2930 } 2931 this.state = normal; 2932 }; 2933 2934 // ESC M Reverse Index (RI is 0x8d). 2935 Terminal.prototype.reverseIndex = function() { 2936 var j; 2937 this.y--; 2938 if (this.y < this.scrollTop) { 2939 this.y++; 2940 // possibly move the code below to term.reverseScroll(); 2941 // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' 2942 // blankLine(true) is xterm/linux behavior 2943 this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); 2944 j = this.rows - 1 - this.scrollBottom; 2945 this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); 2946 // this.maxRange(); 2947 this.updateRange(this.scrollTop); 2948 this.updateRange(this.scrollBottom); 2949 } 2950 this.state = normal; 2951 }; 2952 2953 // ESC c Full Reset (RIS). 2954 Terminal.prototype.reset = function() { 2955 this.options.rows = this.rows; 2956 this.options.cols = this.cols; 2957 Terminal.call(this, this.options); 2958 this.refresh(0, this.rows - 1); 2959 }; 2960 2961 // ESC H Tab Set (HTS is 0x88). 2962 Terminal.prototype.tabSet = function() { 2963 this.tabs[this.x] = true; 2964 this.state = normal; 2965 }; 2966 2967 /** 2968 * CSI 2969 */ 2970 2971 // CSI Ps A 2972 // Cursor Up Ps Times (default = 1) (CUU). 2973 Terminal.prototype.cursorUp = function(params) { 2974 var param = params[0]; 2975 if (param < 1) param = 1; 2976 this.y -= param; 2977 if (this.y < 0) this.y = 0; 2978 }; 2979 2980 // CSI Ps B 2981 // Cursor Down Ps Times (default = 1) (CUD). 2982 Terminal.prototype.cursorDown = function(params) { 2983 var param = params[0]; 2984 if (param < 1) param = 1; 2985 this.y += param; 2986 if (this.y >= this.rows) { 2987 this.y = this.rows - 1; 2988 } 2989 }; 2990 2991 // CSI Ps C 2992 // Cursor Forward Ps Times (default = 1) (CUF). 2993 Terminal.prototype.cursorForward = function(params) { 2994 var param = params[0]; 2995 if (param < 1) param = 1; 2996 this.x += param; 2997 if (this.x >= this.cols) { 2998 this.x = this.cols - 1; 2999 } 3000 }; 3001 3002 // CSI Ps D 3003 // Cursor Backward Ps Times (default = 1) (CUB). 3004 Terminal.prototype.cursorBackward = function(params) { 3005 var param = params[0]; 3006 if (param < 1) param = 1; 3007 this.x -= param; 3008 if (this.x < 0) this.x = 0; 3009 }; 3010 3011 // CSI Ps ; Ps H 3012 // Cursor Position [row;column] (default = [1,1]) (CUP). 3013 Terminal.prototype.cursorPos = function(params) { 3014 var row, col; 3015 3016 row = params[0] - 1; 3017 3018 if (params.length >= 2) { 3019 col = params[1] - 1; 3020 } else { 3021 col = 0; 3022 } 3023 3024 if (row < 0) { 3025 row = 0; 3026 } else if (row >= this.rows) { 3027 row = this.rows - 1; 3028 } 3029 3030 if (col < 0) { 3031 col = 0; 3032 } else if (col >= this.cols) { 3033 col = this.cols - 1; 3034 } 3035 3036 this.x = col; 3037 this.y = row; 3038 }; 3039 3040 // CSI Ps J Erase in Display (ED). 3041 // Ps = 0 -> Erase Below (default). 3042 // Ps = 1 -> Erase Above. 3043 // Ps = 2 -> Erase All. 3044 // Ps = 3 -> Erase Saved Lines (xterm). 3045 // CSI ? Ps J 3046 // Erase in Display (DECSED). 3047 // Ps = 0 -> Selective Erase Below (default). 3048 // Ps = 1 -> Selective Erase Above. 3049 // Ps = 2 -> Selective Erase All. 3050 Terminal.prototype.eraseInDisplay = function(params) { 3051 var j; 3052 switch (params[0]) { 3053 case 0: 3054 this.eraseRight(this.x, this.y); 3055 j = this.y + 1; 3056 for (; j < this.rows; j++) { 3057 this.eraseLine(j); 3058 } 3059 break; 3060 case 1: 3061 this.eraseLeft(this.x, this.y); 3062 j = this.y; 3063 while (j--) { 3064 this.eraseLine(j); 3065 } 3066 break; 3067 case 2: 3068 j = this.rows; 3069 while (j--) this.eraseLine(j); 3070 break; 3071 case 3: 3072 ; // no saved lines 3073 break; 3074 } 3075 }; 3076 3077 // CSI Ps K Erase in Line (EL). 3078 // Ps = 0 -> Erase to Right (default). 3079 // Ps = 1 -> Erase to Left. 3080 // Ps = 2 -> Erase All. 3081 // CSI ? Ps K 3082 // Erase in Line (DECSEL). 3083 // Ps = 0 -> Selective Erase to Right (default). 3084 // Ps = 1 -> Selective Erase to Left. 3085 // Ps = 2 -> Selective Erase All. 3086 Terminal.prototype.eraseInLine = function(params) { 3087 switch (params[0]) { 3088 case 0: 3089 this.eraseRight(this.x, this.y); 3090 break; 3091 case 1: 3092 this.eraseLeft(this.x, this.y); 3093 break; 3094 case 2: 3095 this.eraseLine(this.y); 3096 break; 3097 } 3098 }; 3099 3100 // CSI Pm m Character Attributes (SGR). 3101 // Ps = 0 -> Normal (default). 3102 // Ps = 1 -> Bold. 3103 // Ps = 4 -> Underlined. 3104 // Ps = 5 -> Blink (appears as Bold). 3105 // Ps = 7 -> Inverse. 3106 // Ps = 8 -> Invisible, i.e., hidden (VT300). 3107 // Ps = 2 2 -> Normal (neither bold nor faint). 3108 // Ps = 2 4 -> Not underlined. 3109 // Ps = 2 5 -> Steady (not blinking). 3110 // Ps = 2 7 -> Positive (not inverse). 3111 // Ps = 2 8 -> Visible, i.e., not hidden (VT300). 3112 // Ps = 3 0 -> Set foreground color to Black. 3113 // Ps = 3 1 -> Set foreground color to Red. 3114 // Ps = 3 2 -> Set foreground color to Green. 3115 // Ps = 3 3 -> Set foreground color to Yellow. 3116 // Ps = 3 4 -> Set foreground color to Blue. 3117 // Ps = 3 5 -> Set foreground color to Magenta. 3118 // Ps = 3 6 -> Set foreground color to Cyan. 3119 // Ps = 3 7 -> Set foreground color to White. 3120 // Ps = 3 9 -> Set foreground color to default (original). 3121 // Ps = 4 0 -> Set background color to Black. 3122 // Ps = 4 1 -> Set background color to Red. 3123 // Ps = 4 2 -> Set background color to Green. 3124 // Ps = 4 3 -> Set background color to Yellow. 3125 // Ps = 4 4 -> Set background color to Blue. 3126 // Ps = 4 5 -> Set background color to Magenta. 3127 // Ps = 4 6 -> Set background color to Cyan. 3128 // Ps = 4 7 -> Set background color to White. 3129 // Ps = 4 9 -> Set background color to default (original). 3130 3131 // If 16-color support is compiled, the following apply. Assume 3132 // that xterm's resources are set so that the ISO color codes are 3133 // the first 8 of a set of 16. Then the aixterm colors are the 3134 // bright versions of the ISO colors: 3135 // Ps = 9 0 -> Set foreground color to Black. 3136 // Ps = 9 1 -> Set foreground color to Red. 3137 // Ps = 9 2 -> Set foreground color to Green. 3138 // Ps = 9 3 -> Set foreground color to Yellow. 3139 // Ps = 9 4 -> Set foreground color to Blue. 3140 // Ps = 9 5 -> Set foreground color to Magenta. 3141 // Ps = 9 6 -> Set foreground color to Cyan. 3142 // Ps = 9 7 -> Set foreground color to White. 3143 // Ps = 1 0 0 -> Set background color to Black. 3144 // Ps = 1 0 1 -> Set background color to Red. 3145 // Ps = 1 0 2 -> Set background color to Green. 3146 // Ps = 1 0 3 -> Set background color to Yellow. 3147 // Ps = 1 0 4 -> Set background color to Blue. 3148 // Ps = 1 0 5 -> Set background color to Magenta. 3149 // Ps = 1 0 6 -> Set background color to Cyan. 3150 // Ps = 1 0 7 -> Set background color to White. 3151 3152 // If xterm is compiled with the 16-color support disabled, it 3153 // supports the following, from rxvt: 3154 // Ps = 1 0 0 -> Set foreground and background color to 3155 // default. 3156 3157 // If 88- or 256-color support is compiled, the following apply. 3158 // Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second 3159 // Ps. 3160 // Ps = 4 8 ; 5 ; Ps -> Set background color to the second 3161 // Ps. 3162 Terminal.prototype.charAttributes = function(params) { 3163 // Optimize a single SGR0. 3164 if (params.length === 1 && params[0] === 0) { 3165 this.curAttr = this.defAttr; 3166 return; 3167 } 3168 3169 var l = params.length 3170 , i = 0 3171 , flags = this.curAttr >> 18 3172 , fg = (this.curAttr >> 9) & 0x1ff 3173 , bg = this.curAttr & 0x1ff 3174 , p; 3175 3176 for (; i < l; i++) { 3177 p = params[i]; 3178 if (p >= 30 && p <= 37) { 3179 // fg color 8 3180 fg = p - 30; 3181 } else if (p >= 40 && p <= 47) { 3182 // bg color 8 3183 bg = p - 40; 3184 } else if (p >= 90 && p <= 97) { 3185 // fg color 16 3186 p += 8; 3187 fg = p - 90; 3188 } else if (p >= 100 && p <= 107) { 3189 // bg color 16 3190 p += 8; 3191 bg = p - 100; 3192 } else if (p === 0) { 3193 // default 3194 flags = this.defAttr >> 18; 3195 fg = (this.defAttr >> 9) & 0x1ff; 3196 bg = this.defAttr & 0x1ff; 3197 // flags = 0; 3198 // fg = 0x1ff; 3199 // bg = 0x1ff; 3200 } else if (p === 1) { 3201 // bold text 3202 flags |= 1; 3203 } else if (p === 4) { 3204 // underlined text 3205 flags |= 2; 3206 } else if (p === 5) { 3207 // blink 3208 flags |= 4; 3209 } else if (p === 7) { 3210 // inverse and positive 3211 // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' 3212 flags |= 8; 3213 } else if (p === 8) { 3214 // invisible 3215 flags |= 16; 3216 } else if (p === 22) { 3217 // not bold 3218 flags &= ~1; 3219 } else if (p === 24) { 3220 // not underlined 3221 flags &= ~2; 3222 } else if (p === 25) { 3223 // not blink 3224 flags &= ~4; 3225 } else if (p === 27) { 3226 // not inverse 3227 flags &= ~8; 3228 } else if (p === 28) { 3229 // not invisible 3230 flags &= ~16; 3231 } else if (p === 39) { 3232 // reset fg 3233 fg = (this.defAttr >> 9) & 0x1ff; 3234 } else if (p === 49) { 3235 // reset bg 3236 bg = this.defAttr & 0x1ff; 3237 } else if (p === 38) { 3238 // fg color 256 3239 if (params[i + 1] === 2) { 3240 i += 2; 3241 fg = matchColor( 3242 params[i] & 0xff, 3243 params[i + 1] & 0xff, 3244 params[i + 2] & 0xff); 3245 if (fg === -1) fg = 0x1ff; 3246 i += 2; 3247 } else if (params[i + 1] === 5) { 3248 i += 2; 3249 p = params[i] & 0xff; 3250 fg = p; 3251 } 3252 } else if (p === 48) { 3253 // bg color 256 3254 if (params[i + 1] === 2) { 3255 i += 2; 3256 bg = matchColor( 3257 params[i] & 0xff, 3258 params[i + 1] & 0xff, 3259 params[i + 2] & 0xff); 3260 if (bg === -1) bg = 0x1ff; 3261 i += 2; 3262 } else if (params[i + 1] === 5) { 3263 i += 2; 3264 p = params[i] & 0xff; 3265 bg = p; 3266 } 3267 } else if (p === 100) { 3268 // reset fg/bg 3269 fg = (this.defAttr >> 9) & 0x1ff; 3270 bg = this.defAttr & 0x1ff; 3271 } else { 3272 this.error('Unknown SGR attribute: %d.', p); 3273 } 3274 } 3275 3276 this.curAttr = (flags << 18) | (fg << 9) | bg; 3277 }; 3278 3279 // CSI Ps n Device Status Report (DSR). 3280 // Ps = 5 -> Status Report. Result (``OK'') is 3281 // CSI 0 n 3282 // Ps = 6 -> Report Cursor Position (CPR) [row;column]. 3283 // Result is 3284 // CSI r ; c R 3285 // CSI ? Ps n 3286 // Device Status Report (DSR, DEC-specific). 3287 // Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI 3288 // ? r ; c R (assumes page is zero). 3289 // Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready). 3290 // or CSI ? 1 1 n (not ready). 3291 // Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked) 3292 // or CSI ? 2 1 n (locked). 3293 // Ps = 2 6 -> Report Keyboard status as 3294 // CSI ? 2 7 ; 1 ; 0 ; 0 n (North American). 3295 // The last two parameters apply to VT400 & up, and denote key- 3296 // board ready and LK01 respectively. 3297 // Ps = 5 3 -> Report Locator status as 3298 // CSI ? 5 3 n Locator available, if compiled-in, or 3299 // CSI ? 5 0 n No Locator, if not. 3300 Terminal.prototype.deviceStatus = function(params) { 3301 if (!this.prefix) { 3302 switch (params[0]) { 3303 case 5: 3304 // status report 3305 this.send('\x1b[0n'); 3306 break; 3307 case 6: 3308 // cursor position 3309 this.send('\x1b[' 3310 + (this.y + 1) 3311 + ';' 3312 + (this.x + 1) 3313 + 'R'); 3314 break; 3315 } 3316 } else if (this.prefix === '?') { 3317 // modern xterm doesnt seem to 3318 // respond to any of these except ?6, 6, and 5 3319 switch (params[0]) { 3320 case 6: 3321 // cursor position 3322 this.send('\x1b[?' 3323 + (this.y + 1) 3324 + ';' 3325 + (this.x + 1) 3326 + 'R'); 3327 break; 3328 case 15: 3329 // no printer 3330 // this.send('\x1b[?11n'); 3331 break; 3332 case 25: 3333 // dont support user defined keys 3334 // this.send('\x1b[?21n'); 3335 break; 3336 case 26: 3337 // north american keyboard 3338 // this.send('\x1b[?27;1;0;0n'); 3339 break; 3340 case 53: 3341 // no dec locator/mouse 3342 // this.send('\x1b[?50n'); 3343 break; 3344 } 3345 } 3346 }; 3347 3348 /** 3349 * Additions 3350 */ 3351 3352 // CSI Ps @ 3353 // Insert Ps (Blank) Character(s) (default = 1) (ICH). 3354 Terminal.prototype.insertChars = function(params) { 3355 var param, row, j, ch; 3356 3357 param = params[0]; 3358 if (param < 1) param = 1; 3359 3360 row = this.y + this.ybase; 3361 j = this.x; 3362 ch = [this.eraseAttr(), ' ']; // xterm 3363 3364 while (param-- && j < this.cols) { 3365 this.lines[row].splice(j++, 0, ch); 3366 this.lines[row].pop(); 3367 } 3368 }; 3369 3370 // CSI Ps E 3371 // Cursor Next Line Ps Times (default = 1) (CNL). 3372 // same as CSI Ps B ? 3373 Terminal.prototype.cursorNextLine = function(params) { 3374 var param = params[0]; 3375 if (param < 1) param = 1; 3376 this.y += param; 3377 if (this.y >= this.rows) { 3378 this.y = this.rows - 1; 3379 } 3380 this.x = 0; 3381 }; 3382 3383 // CSI Ps F 3384 // Cursor Preceding Line Ps Times (default = 1) (CNL). 3385 // reuse CSI Ps A ? 3386 Terminal.prototype.cursorPrecedingLine = function(params) { 3387 var param = params[0]; 3388 if (param < 1) param = 1; 3389 this.y -= param; 3390 if (this.y < 0) this.y = 0; 3391 this.x = 0; 3392 }; 3393 3394 // CSI Ps G 3395 // Cursor Character Absolute [column] (default = [row,1]) (CHA). 3396 Terminal.prototype.cursorCharAbsolute = function(params) { 3397 var param = params[0]; 3398 if (param < 1) param = 1; 3399 this.x = param - 1; 3400 }; 3401 3402 // CSI Ps L 3403 // Insert Ps Line(s) (default = 1) (IL). 3404 Terminal.prototype.insertLines = function(params) { 3405 var param, row, j; 3406 3407 param = params[0]; 3408 if (param < 1) param = 1; 3409 row = this.y + this.ybase; 3410 3411 j = this.rows - 1 - this.scrollBottom; 3412 j = this.rows - 1 + this.ybase - j + 1; 3413 3414 while (param--) { 3415 // test: echo -e '\e[44m\e[1L\e[0m' 3416 // blankLine(true) - xterm/linux behavior 3417 this.lines.splice(row, 0, this.blankLine(true)); 3418 this.lines.splice(j, 1); 3419 } 3420 3421 // this.maxRange(); 3422 this.updateRange(this.y); 3423 this.updateRange(this.scrollBottom); 3424 }; 3425 3426 // CSI Ps M 3427 // Delete Ps Line(s) (default = 1) (DL). 3428 Terminal.prototype.deleteLines = function(params) { 3429 var param, row, j; 3430 3431 param = params[0]; 3432 if (param < 1) param = 1; 3433 row = this.y + this.ybase; 3434 3435 j = this.rows - 1 - this.scrollBottom; 3436 j = this.rows - 1 + this.ybase - j; 3437 3438 while (param--) { 3439 // test: echo -e '\e[44m\e[1M\e[0m' 3440 // blankLine(true) - xterm/linux behavior 3441 this.lines.splice(j + 1, 0, this.blankLine(true)); 3442 this.lines.splice(row, 1); 3443 } 3444 3445 // this.maxRange(); 3446 this.updateRange(this.y); 3447 this.updateRange(this.scrollBottom); 3448 }; 3449 3450 // CSI Ps P 3451 // Delete Ps Character(s) (default = 1) (DCH). 3452 Terminal.prototype.deleteChars = function(params) { 3453 var param, row, ch; 3454 3455 param = params[0]; 3456 if (param < 1) param = 1; 3457 3458 row = this.y + this.ybase; 3459 ch = [this.eraseAttr(), ' ']; // xterm 3460 3461 while (param--) { 3462 this.lines[row].splice(this.x, 1); 3463 this.lines[row].push(ch); 3464 } 3465 }; 3466 3467 // CSI Ps X 3468 // Erase Ps Character(s) (default = 1) (ECH). 3469 Terminal.prototype.eraseChars = function(params) { 3470 var param, row, j, ch; 3471 3472 param = params[0]; 3473 if (param < 1) param = 1; 3474 3475 row = this.y + this.ybase; 3476 j = this.x; 3477 ch = [this.eraseAttr(), ' ']; // xterm 3478 3479 while (param-- && j < this.cols) { 3480 this.lines[row][j++] = ch; 3481 } 3482 }; 3483 3484 // CSI Pm ` Character Position Absolute 3485 // [column] (default = [row,1]) (HPA). 3486 Terminal.prototype.charPosAbsolute = function(params) { 3487 var param = params[0]; 3488 if (param < 1) param = 1; 3489 this.x = param - 1; 3490 if (this.x >= this.cols) { 3491 this.x = this.cols - 1; 3492 } 3493 }; 3494 3495 // 141 61 a * HPR - 3496 // Horizontal Position Relative 3497 // reuse CSI Ps C ? 3498 Terminal.prototype.HPositionRelative = function(params) { 3499 var param = params[0]; 3500 if (param < 1) param = 1; 3501 this.x += param; 3502 if (this.x >= this.cols) { 3503 this.x = this.cols - 1; 3504 } 3505 }; 3506 3507 // CSI Ps c Send Device Attributes (Primary DA). 3508 // Ps = 0 or omitted -> request attributes from terminal. The 3509 // response depends on the decTerminalID resource setting. 3510 // -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'') 3511 // -> CSI ? 1 ; 0 c (``VT101 with No Options'') 3512 // -> CSI ? 6 c (``VT102'') 3513 // -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'') 3514 // The VT100-style response parameters do not mean anything by 3515 // themselves. VT220 parameters do, telling the host what fea- 3516 // tures the terminal supports: 3517 // Ps = 1 -> 132-columns. 3518 // Ps = 2 -> Printer. 3519 // Ps = 6 -> Selective erase. 3520 // Ps = 8 -> User-defined keys. 3521 // Ps = 9 -> National replacement character sets. 3522 // Ps = 1 5 -> Technical characters. 3523 // Ps = 2 2 -> ANSI color, e.g., VT525. 3524 // Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode). 3525 // CSI > Ps c 3526 // Send Device Attributes (Secondary DA). 3527 // Ps = 0 or omitted -> request the terminal's identification 3528 // code. The response depends on the decTerminalID resource set- 3529 // ting. It should apply only to VT220 and up, but xterm extends 3530 // this to VT100. 3531 // -> CSI > Pp ; Pv ; Pc c 3532 // where Pp denotes the terminal type 3533 // Pp = 0 -> ``VT100''. 3534 // Pp = 1 -> ``VT220''. 3535 // and Pv is the firmware version (for xterm, this was originally 3536 // the XFree86 patch number, starting with 95). In a DEC termi- 3537 // nal, Pc indicates the ROM cartridge registration number and is 3538 // always zero. 3539 // More information: 3540 // xterm/charproc.c - line 2012, for more information. 3541 // vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) 3542 Terminal.prototype.sendDeviceAttributes = function(params) { 3543 if (params[0] > 0) return; 3544 3545 if (!this.prefix) { 3546 if (this.is('xterm') 3547 || this.is('rxvt-unicode') 3548 || this.is('screen')) { 3549 this.send('\x1b[?1;2c'); 3550 } else if (this.is('linux')) { 3551 this.send('\x1b[?6c'); 3552 } 3553 } else if (this.prefix === '>') { 3554 // xterm and urxvt 3555 // seem to spit this 3556 // out around ~370 times (?). 3557 if (this.is('xterm')) { 3558 this.send('\x1b[>0;276;0c'); 3559 } else if (this.is('rxvt-unicode')) { 3560 this.send('\x1b[>85;95;0c'); 3561 } else if (this.is('linux')) { 3562 // not supported by linux console. 3563 // linux console echoes parameters. 3564 this.send(params[0] + 'c'); 3565 } else if (this.is('screen')) { 3566 this.send('\x1b[>83;40003;0c'); 3567 } 3568 } 3569 }; 3570 3571 // CSI Pm d 3572 // Line Position Absolute [row] (default = [1,column]) (VPA). 3573 Terminal.prototype.linePosAbsolute = function(params) { 3574 var param = params[0]; 3575 if (param < 1) param = 1; 3576 this.y = param - 1; 3577 if (this.y >= this.rows) { 3578 this.y = this.rows - 1; 3579 } 3580 }; 3581 3582 // 145 65 e * VPR - Vertical Position Relative 3583 // reuse CSI Ps B ? 3584 Terminal.prototype.VPositionRelative = function(params) { 3585 var param = params[0]; 3586 if (param < 1) param = 1; 3587 this.y += param; 3588 if (this.y >= this.rows) { 3589 this.y = this.rows - 1; 3590 } 3591 }; 3592 3593 // CSI Ps ; Ps f 3594 // Horizontal and Vertical Position [row;column] (default = 3595 // [1,1]) (HVP). 3596 Terminal.prototype.HVPosition = function(params) { 3597 if (params[0] < 1) params[0] = 1; 3598 if (params[1] < 1) params[1] = 1; 3599 3600 this.y = params[0] - 1; 3601 if (this.y >= this.rows) { 3602 this.y = this.rows - 1; 3603 } 3604 3605 this.x = params[1] - 1; 3606 if (this.x >= this.cols) { 3607 this.x = this.cols - 1; 3608 } 3609 }; 3610 3611 // CSI Pm h Set Mode (SM). 3612 // Ps = 2 -> Keyboard Action Mode (AM). 3613 // Ps = 4 -> Insert Mode (IRM). 3614 // Ps = 1 2 -> Send/receive (SRM). 3615 // Ps = 2 0 -> Automatic Newline (LNM). 3616 // CSI ? Pm h 3617 // DEC Private Mode Set (DECSET). 3618 // Ps = 1 -> Application Cursor Keys (DECCKM). 3619 // Ps = 2 -> Designate USASCII for character sets G0-G3 3620 // (DECANM), and set VT100 mode. 3621 // Ps = 3 -> 132 Column Mode (DECCOLM). 3622 // Ps = 4 -> Smooth (Slow) Scroll (DECSCLM). 3623 // Ps = 5 -> Reverse Video (DECSCNM). 3624 // Ps = 6 -> Origin Mode (DECOM). 3625 // Ps = 7 -> Wraparound Mode (DECAWM). 3626 // Ps = 8 -> Auto-repeat Keys (DECARM). 3627 // Ps = 9 -> Send Mouse X & Y on button press. See the sec- 3628 // tion Mouse Tracking. 3629 // Ps = 1 0 -> Show toolbar (rxvt). 3630 // Ps = 1 2 -> Start Blinking Cursor (att610). 3631 // Ps = 1 8 -> Print form feed (DECPFF). 3632 // Ps = 1 9 -> Set print extent to full screen (DECPEX). 3633 // Ps = 2 5 -> Show Cursor (DECTCEM). 3634 // Ps = 3 0 -> Show scrollbar (rxvt). 3635 // Ps = 3 5 -> Enable font-shifting functions (rxvt). 3636 // Ps = 3 8 -> Enter Tektronix Mode (DECTEK). 3637 // Ps = 4 0 -> Allow 80 -> 132 Mode. 3638 // Ps = 4 1 -> more(1) fix (see curses resource). 3639 // Ps = 4 2 -> Enable Nation Replacement Character sets (DECN- 3640 // RCM). 3641 // Ps = 4 4 -> Turn On Margin Bell. 3642 // Ps = 4 5 -> Reverse-wraparound Mode. 3643 // Ps = 4 6 -> Start Logging. This is normally disabled by a 3644 // compile-time option. 3645 // Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis- 3646 // abled by the titeInhibit resource). 3647 // Ps = 6 6 -> Application keypad (DECNKM). 3648 // Ps = 6 7 -> Backarrow key sends backspace (DECBKM). 3649 // Ps = 1 0 0 0 -> Send Mouse X & Y on button press and 3650 // release. See the section Mouse Tracking. 3651 // Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. 3652 // Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking. 3653 // Ps = 1 0 0 3 -> Use All Motion Mouse Tracking. 3654 // Ps = 1 0 0 4 -> Send FocusIn/FocusOut events. 3655 // Ps = 1 0 0 5 -> Enable Extended Mouse Mode. 3656 // Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt). 3657 // Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt). 3658 // Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit. 3659 // (enables the eightBitInput resource). 3660 // Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num- 3661 // Lock keys. (This enables the numLock resource). 3662 // Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This 3663 // enables the metaSendsEscape resource). 3664 // Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete 3665 // key. 3666 // Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This 3667 // enables the altSendsEscape resource). 3668 // Ps = 1 0 4 0 -> Keep selection even if not highlighted. 3669 // (This enables the keepSelection resource). 3670 // Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables 3671 // the selectToClipboard resource). 3672 // Ps = 1 0 4 2 -> Enable Urgency window manager hint when 3673 // Control-G is received. (This enables the bellIsUrgent 3674 // resource). 3675 // Ps = 1 0 4 3 -> Enable raising of the window when Control-G 3676 // is received. (enables the popOnBell resource). 3677 // Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be 3678 // disabled by the titeInhibit resource). 3679 // Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis- 3680 // abled by the titeInhibit resource). 3681 // Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate 3682 // Screen Buffer, clearing it first. (This may be disabled by 3683 // the titeInhibit resource). This combines the effects of the 1 3684 // 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based 3685 // applications rather than the 4 7 mode. 3686 // Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. 3687 // Ps = 1 0 5 1 -> Set Sun function-key mode. 3688 // Ps = 1 0 5 2 -> Set HP function-key mode. 3689 // Ps = 1 0 5 3 -> Set SCO function-key mode. 3690 // Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6). 3691 // Ps = 1 0 6 1 -> Set VT220 keyboard emulation. 3692 // Ps = 2 0 0 4 -> Set bracketed paste mode. 3693 // Modes: 3694 // http://vt100.net/docs/vt220-rm/chapter4.html 3695 Terminal.prototype.setMode = function(params) { 3696 if (typeof params === 'object') { 3697 var l = params.length 3698 , i = 0; 3699 3700 for (; i < l; i++) { 3701 this.setMode(params[i]); 3702 } 3703 3704 return; 3705 } 3706 3707 if (!this.prefix) { 3708 switch (params) { 3709 case 4: 3710 this.insertMode = true; 3711 break; 3712 case 20: 3713 //this.convertEol = true; 3714 break; 3715 } 3716 } else if (this.prefix === '?') { 3717 switch (params) { 3718 case 1: 3719 this.applicationCursor = true; 3720 break; 3721 case 2: 3722 this.setgCharset(0, Terminal.charsets.US); 3723 this.setgCharset(1, Terminal.charsets.US); 3724 this.setgCharset(2, Terminal.charsets.US); 3725 this.setgCharset(3, Terminal.charsets.US); 3726 // set VT100 mode here 3727 break; 3728 case 3: // 132 col mode 3729 this.savedCols = this.cols; 3730 this.resize(132, this.rows); 3731 break; 3732 case 6: 3733 this.originMode = true; 3734 break; 3735 case 7: 3736 this.wraparoundMode = true; 3737 break; 3738 case 12: 3739 // this.cursorBlink = true; 3740 break; 3741 case 66: 3742 this.log('Serial port requested application keypad.'); 3743 this.applicationKeypad = true; 3744 break; 3745 case 9: // X10 Mouse 3746 // no release, no motion, no wheel, no modifiers. 3747 case 1000: // vt200 mouse 3748 // no motion. 3749 // no modifiers, except control on the wheel. 3750 case 1002: // button event mouse 3751 case 1003: // any event mouse 3752 // any event - sends motion events, 3753 // even if there is no button held down. 3754 this.x10Mouse = params === 9; 3755 this.vt200Mouse = params === 1000; 3756 this.normalMouse = params > 1000; 3757 this.mouseEvents = true; 3758 this.element.style.cursor = 'default'; 3759 this.log('Binding to mouse events.'); 3760 break; 3761 case 1004: // send focusin/focusout events 3762 // focusin: ^[[I 3763 // focusout: ^[[O 3764 this.sendFocus = true; 3765 break; 3766 case 1005: // utf8 ext mode mouse 3767 this.utfMouse = true; 3768 // for wide terminals 3769 // simply encodes large values as utf8 characters 3770 break; 3771 case 1006: // sgr ext mode mouse 3772 this.sgrMouse = true; 3773 // for wide terminals 3774 // does not add 32 to fields 3775 // press: ^[[<b;x;yM 3776 // release: ^[[<b;x;ym 3777 break; 3778 case 1015: // urxvt ext mode mouse 3779 this.urxvtMouse = true; 3780 // for wide terminals 3781 // numbers for fields 3782 // press: ^[[b;x;yM 3783 // motion: ^[[b;x;yT 3784 break; 3785 case 25: // show cursor 3786 this.cursorHidden = false; 3787 break; 3788 case 1049: // alt screen buffer cursor 3789 //this.saveCursor(); 3790 ; // FALL-THROUGH 3791 case 47: // alt screen buffer 3792 case 1047: // alt screen buffer 3793 if (!this.normal) { 3794 var normal = { 3795 lines: this.lines, 3796 ybase: this.ybase, 3797 ydisp: this.ydisp, 3798 x: this.x, 3799 y: this.y, 3800 scrollTop: this.scrollTop, 3801 scrollBottom: this.scrollBottom, 3802 tabs: this.tabs 3803 // XXX save charset(s) here? 3804 // charset: this.charset, 3805 // glevel: this.glevel, 3806 // charsets: this.charsets 3807 }; 3808 this.reset(); 3809 this.normal = normal; 3810 this.showCursor(); 3811 } 3812 break; 3813 } 3814 } 3815 }; 3816 3817 // CSI Pm l Reset Mode (RM). 3818 // Ps = 2 -> Keyboard Action Mode (AM). 3819 // Ps = 4 -> Replace Mode (IRM). 3820 // Ps = 1 2 -> Send/receive (SRM). 3821 // Ps = 2 0 -> Normal Linefeed (LNM). 3822 // CSI ? Pm l 3823 // DEC Private Mode Reset (DECRST). 3824 // Ps = 1 -> Normal Cursor Keys (DECCKM). 3825 // Ps = 2 -> Designate VT52 mode (DECANM). 3826 // Ps = 3 -> 80 Column Mode (DECCOLM). 3827 // Ps = 4 -> Jump (Fast) Scroll (DECSCLM). 3828 // Ps = 5 -> Normal Video (DECSCNM). 3829 // Ps = 6 -> Normal Cursor Mode (DECOM). 3830 // Ps = 7 -> No Wraparound Mode (DECAWM). 3831 // Ps = 8 -> No Auto-repeat Keys (DECARM). 3832 // Ps = 9 -> Don't send Mouse X & Y on button press. 3833 // Ps = 1 0 -> Hide toolbar (rxvt). 3834 // Ps = 1 2 -> Stop Blinking Cursor (att610). 3835 // Ps = 1 8 -> Don't print form feed (DECPFF). 3836 // Ps = 1 9 -> Limit print to scrolling region (DECPEX). 3837 // Ps = 2 5 -> Hide Cursor (DECTCEM). 3838 // Ps = 3 0 -> Don't show scrollbar (rxvt). 3839 // Ps = 3 5 -> Disable font-shifting functions (rxvt). 3840 // Ps = 4 0 -> Disallow 80 -> 132 Mode. 3841 // Ps = 4 1 -> No more(1) fix (see curses resource). 3842 // Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- 3843 // NRCM). 3844 // Ps = 4 4 -> Turn Off Margin Bell. 3845 // Ps = 4 5 -> No Reverse-wraparound Mode. 3846 // Ps = 4 6 -> Stop Logging. (This is normally disabled by a 3847 // compile-time option). 3848 // Ps = 4 7 -> Use Normal Screen Buffer. 3849 // Ps = 6 6 -> Numeric keypad (DECNKM). 3850 // Ps = 6 7 -> Backarrow key sends delete (DECBKM). 3851 // Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and 3852 // release. See the section Mouse Tracking. 3853 // Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. 3854 // Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. 3855 // Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. 3856 // Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. 3857 // Ps = 1 0 0 5 -> Disable Extended Mouse Mode. 3858 // Ps = 1 0 1 0 -> Don't scroll to bottom on tty output 3859 // (rxvt). 3860 // Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). 3861 // Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables 3862 // the eightBitInput resource). 3863 // Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- 3864 // Lock keys. (This disables the numLock resource). 3865 // Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. 3866 // (This disables the metaSendsEscape resource). 3867 // Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad 3868 // Delete key. 3869 // Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. 3870 // (This disables the altSendsEscape resource). 3871 // Ps = 1 0 4 0 -> Do not keep selection when not highlighted. 3872 // (This disables the keepSelection resource). 3873 // Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables 3874 // the selectToClipboard resource). 3875 // Ps = 1 0 4 2 -> Disable Urgency window manager hint when 3876 // Control-G is received. (This disables the bellIsUrgent 3877 // resource). 3878 // Ps = 1 0 4 3 -> Disable raising of the window when Control- 3879 // G is received. (This disables the popOnBell resource). 3880 // Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen 3881 // first if in the Alternate Screen. (This may be disabled by 3882 // the titeInhibit resource). 3883 // Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be 3884 // disabled by the titeInhibit resource). 3885 // Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor 3886 // as in DECRC. (This may be disabled by the titeInhibit 3887 // resource). This combines the effects of the 1 0 4 7 and 1 0 3888 // 4 8 modes. Use this with terminfo-based applications rather 3889 // than the 4 7 mode. 3890 // Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. 3891 // Ps = 1 0 5 1 -> Reset Sun function-key mode. 3892 // Ps = 1 0 5 2 -> Reset HP function-key mode. 3893 // Ps = 1 0 5 3 -> Reset SCO function-key mode. 3894 // Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). 3895 // Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. 3896 // Ps = 2 0 0 4 -> Reset bracketed paste mode. 3897 Terminal.prototype.resetMode = function(params) { 3898 if (typeof params === 'object') { 3899 var l = params.length 3900 , i = 0; 3901 3902 for (; i < l; i++) { 3903 this.resetMode(params[i]); 3904 } 3905 3906 return; 3907 } 3908 3909 if (!this.prefix) { 3910 switch (params) { 3911 case 4: 3912 this.insertMode = false; 3913 break; 3914 case 20: 3915 //this.convertEol = false; 3916 break; 3917 } 3918 } else if (this.prefix === '?') { 3919 switch (params) { 3920 case 1: 3921 this.applicationCursor = false; 3922 break; 3923 case 3: 3924 if (this.cols === 132 && this.savedCols) { 3925 this.resize(this.savedCols, this.rows); 3926 } 3927 delete this.savedCols; 3928 break; 3929 case 6: 3930 this.originMode = false; 3931 break; 3932 case 7: 3933 this.wraparoundMode = false; 3934 break; 3935 case 12: 3936 // this.cursorBlink = false; 3937 break; 3938 case 66: 3939 this.log('Switching back to normal keypad.'); 3940 this.applicationKeypad = false; 3941 break; 3942 case 9: // X10 Mouse 3943 case 1000: // vt200 mouse 3944 case 1002: // button event mouse 3945 case 1003: // any event mouse 3946 this.x10Mouse = false; 3947 this.vt200Mouse = false; 3948 this.normalMouse = false; 3949 this.mouseEvents = false; 3950 this.element.style.cursor = ''; 3951 break; 3952 case 1004: // send focusin/focusout events 3953 this.sendFocus = false; 3954 break; 3955 case 1005: // utf8 ext mode mouse 3956 this.utfMouse = false; 3957 break; 3958 case 1006: // sgr ext mode mouse 3959 this.sgrMouse = false; 3960 break; 3961 case 1015: // urxvt ext mode mouse 3962 this.urxvtMouse = false; 3963 break; 3964 case 25: // hide cursor 3965 this.cursorHidden = true; 3966 break; 3967 case 1049: // alt screen buffer cursor 3968 ; // FALL-THROUGH 3969 case 47: // normal screen buffer 3970 case 1047: // normal screen buffer - clearing it first 3971 if (this.normal) { 3972 this.lines = this.normal.lines; 3973 this.ybase = this.normal.ybase; 3974 this.ydisp = this.normal.ydisp; 3975 this.x = this.normal.x; 3976 this.y = this.normal.y; 3977 this.scrollTop = this.normal.scrollTop; 3978 this.scrollBottom = this.normal.scrollBottom; 3979 this.tabs = this.normal.tabs; 3980 this.normal = null; 3981 // if (params === 1049) { 3982 // this.x = this.savedX; 3983 // this.y = this.savedY; 3984 // } 3985 this.refresh(0, this.rows - 1); 3986 this.showCursor(); 3987 } 3988 break; 3989 } 3990 } 3991 }; 3992 3993 // CSI Ps ; Ps r 3994 // Set Scrolling Region [top;bottom] (default = full size of win- 3995 // dow) (DECSTBM). 3996 // CSI ? Pm r 3997 Terminal.prototype.setScrollRegion = function(params) { 3998 if (this.prefix) return; 3999 this.scrollTop = (params[0] || 1) - 1; 4000 this.scrollBottom = (params[1] || this.rows) - 1; 4001 this.x = 0; 4002 this.y = 0; 4003 }; 4004 4005 // CSI s 4006 // Save cursor (ANSI.SYS). 4007 Terminal.prototype.saveCursor = function(params) { 4008 this.savedX = this.x; 4009 this.savedY = this.y; 4010 }; 4011 4012 // CSI u 4013 // Restore cursor (ANSI.SYS). 4014 Terminal.prototype.restoreCursor = function(params) { 4015 this.x = this.savedX || 0; 4016 this.y = this.savedY || 0; 4017 }; 4018 4019 /** 4020 * Lesser Used 4021 */ 4022 4023 // CSI Ps I 4024 // Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). 4025 Terminal.prototype.cursorForwardTab = function(params) { 4026 var param = params[0] || 1; 4027 while (param--) { 4028 this.x = this.nextStop(); 4029 } 4030 }; 4031 4032 // CSI Ps S Scroll up Ps lines (default = 1) (SU). 4033 Terminal.prototype.scrollUp = function(params) { 4034 var param = params[0] || 1; 4035 while (param--) { 4036 this.lines.splice(this.ybase + this.scrollTop, 1); 4037 this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine()); 4038 } 4039 // this.maxRange(); 4040 this.updateRange(this.scrollTop); 4041 this.updateRange(this.scrollBottom); 4042 }; 4043 4044 // CSI Ps T Scroll down Ps lines (default = 1) (SD). 4045 Terminal.prototype.scrollDown = function(params) { 4046 var param = params[0] || 1; 4047 while (param--) { 4048 this.lines.splice(this.ybase + this.scrollBottom, 1); 4049 this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine()); 4050 } 4051 // this.maxRange(); 4052 this.updateRange(this.scrollTop); 4053 this.updateRange(this.scrollBottom); 4054 }; 4055 4056 // CSI Ps ; Ps ; Ps ; Ps ; Ps T 4057 // Initiate highlight mouse tracking. Parameters are 4058 // [func;startx;starty;firstrow;lastrow]. See the section Mouse 4059 // Tracking. 4060 Terminal.prototype.initMouseTracking = function(params) { 4061 // Relevant: DECSET 1001 4062 }; 4063 4064 // CSI > Ps; Ps T 4065 // Reset one or more features of the title modes to the default 4066 // value. Normally, "reset" disables the feature. It is possi- 4067 // ble to disable the ability to reset features by compiling a 4068 // different default for the title modes into xterm. 4069 // Ps = 0 -> Do not set window/icon labels using hexadecimal. 4070 // Ps = 1 -> Do not query window/icon labels using hexadeci- 4071 // mal. 4072 // Ps = 2 -> Do not set window/icon labels using UTF-8. 4073 // Ps = 3 -> Do not query window/icon labels using UTF-8. 4074 // (See discussion of "Title Modes"). 4075 Terminal.prototype.resetTitleModes = function(params) { 4076 ; 4077 }; 4078 4079 // CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). 4080 Terminal.prototype.cursorBackwardTab = function(params) { 4081 var param = params[0] || 1; 4082 while (param--) { 4083 this.x = this.prevStop(); 4084 } 4085 }; 4086 4087 // CSI Ps b Repeat the preceding graphic character Ps times (REP). 4088 Terminal.prototype.repeatPrecedingCharacter = function(params) { 4089 var param = params[0] || 1 4090 , line = this.lines[this.ybase + this.y] 4091 , ch = line[this.x - 1] || [this.defAttr, ' ']; 4092 4093 while (param--) line[this.x++] = ch; 4094 }; 4095 4096 // CSI Ps g Tab Clear (TBC). 4097 // Ps = 0 -> Clear Current Column (default). 4098 // Ps = 3 -> Clear All. 4099 // Potentially: 4100 // Ps = 2 -> Clear Stops on Line. 4101 // http://vt100.net/annarbor/aaa-ug/section6.html 4102 Terminal.prototype.tabClear = function(params) { 4103 var param = params[0]; 4104 if (param <= 0) { 4105 delete this.tabs[this.x]; 4106 } else if (param === 3) { 4107 this.tabs = {}; 4108 } 4109 }; 4110 4111 // CSI Pm i Media Copy (MC). 4112 // Ps = 0 -> Print screen (default). 4113 // Ps = 4 -> Turn off printer controller mode. 4114 // Ps = 5 -> Turn on printer controller mode. 4115 // CSI ? Pm i 4116 // Media Copy (MC, DEC-specific). 4117 // Ps = 1 -> Print line containing cursor. 4118 // Ps = 4 -> Turn off autoprint mode. 4119 // Ps = 5 -> Turn on autoprint mode. 4120 // Ps = 1 0 -> Print composed display, ignores DECPEX. 4121 // Ps = 1 1 -> Print all pages. 4122 Terminal.prototype.mediaCopy = function(params) { 4123 ; 4124 }; 4125 4126 // CSI > Ps; Ps m 4127 // Set or reset resource-values used by xterm to decide whether 4128 // to construct escape sequences holding information about the 4129 // modifiers pressed with a given key. The first parameter iden- 4130 // tifies the resource to set/reset. The second parameter is the 4131 // value to assign to the resource. If the second parameter is 4132 // omitted, the resource is reset to its initial value. 4133 // Ps = 1 -> modifyCursorKeys. 4134 // Ps = 2 -> modifyFunctionKeys. 4135 // Ps = 4 -> modifyOtherKeys. 4136 // If no parameters are given, all resources are reset to their 4137 // initial values. 4138 Terminal.prototype.setResources = function(params) { 4139 ; 4140 }; 4141 4142 // CSI > Ps n 4143 // Disable modifiers which may be enabled via the CSI > Ps; Ps m 4144 // sequence. This corresponds to a resource value of "-1", which 4145 // cannot be set with the other sequence. The parameter identi- 4146 // fies the resource to be disabled: 4147 // Ps = 1 -> modifyCursorKeys. 4148 // Ps = 2 -> modifyFunctionKeys. 4149 // Ps = 4 -> modifyOtherKeys. 4150 // If the parameter is omitted, modifyFunctionKeys is disabled. 4151 // When modifyFunctionKeys is disabled, xterm uses the modifier 4152 // keys to make an extended sequence of functions rather than 4153 // adding a parameter to each function key to denote the modi- 4154 // fiers. 4155 Terminal.prototype.disableModifiers = function(params) { 4156 ; 4157 }; 4158 4159 // CSI > Ps p 4160 // Set resource value pointerMode. This is used by xterm to 4161 // decide whether to hide the pointer cursor as the user types. 4162 // Valid values for the parameter: 4163 // Ps = 0 -> never hide the pointer. 4164 // Ps = 1 -> hide if the mouse tracking mode is not enabled. 4165 // Ps = 2 -> always hide the pointer. If no parameter is 4166 // given, xterm uses the default, which is 1 . 4167 Terminal.prototype.setPointerMode = function(params) { 4168 ; 4169 }; 4170 4171 // CSI ! p Soft terminal reset (DECSTR). 4172 // http://vt100.net/docs/vt220-rm/table4-10.html 4173 Terminal.prototype.softReset = function(params) { 4174 this.cursorHidden = false; 4175 this.insertMode = false; 4176 this.originMode = false; 4177 this.wraparoundMode = false; // autowrap 4178 this.applicationKeypad = false; // ? 4179 this.applicationCursor = false; 4180 this.scrollTop = 0; 4181 this.scrollBottom = this.rows - 1; 4182 this.curAttr = this.defAttr; 4183 this.x = this.y = 0; // ? 4184 this.charset = null; 4185 this.glevel = 0; // ?? 4186 this.charsets = [null]; // ?? 4187 }; 4188 4189 // CSI Ps$ p 4190 // Request ANSI mode (DECRQM). For VT300 and up, reply is 4191 // CSI Ps; Pm$ y 4192 // where Ps is the mode number as in RM, and Pm is the mode 4193 // value: 4194 // 0 - not recognized 4195 // 1 - set 4196 // 2 - reset 4197 // 3 - permanently set 4198 // 4 - permanently reset 4199 Terminal.prototype.requestAnsiMode = function(params) { 4200 ; 4201 }; 4202 4203 // CSI ? Ps$ p 4204 // Request DEC private mode (DECRQM). For VT300 and up, reply is 4205 // CSI ? Ps; Pm$ p 4206 // where Ps is the mode number as in DECSET, Pm is the mode value 4207 // as in the ANSI DECRQM. 4208 Terminal.prototype.requestPrivateMode = function(params) { 4209 ; 4210 }; 4211 4212 // CSI Ps ; Ps " p 4213 // Set conformance level (DECSCL). Valid values for the first 4214 // parameter: 4215 // Ps = 6 1 -> VT100. 4216 // Ps = 6 2 -> VT200. 4217 // Ps = 6 3 -> VT300. 4218 // Valid values for the second parameter: 4219 // Ps = 0 -> 8-bit controls. 4220 // Ps = 1 -> 7-bit controls (always set for VT100). 4221 // Ps = 2 -> 8-bit controls. 4222 Terminal.prototype.setConformanceLevel = function(params) { 4223 ; 4224 }; 4225 4226 // CSI Ps q Load LEDs (DECLL). 4227 // Ps = 0 -> Clear all LEDS (default). 4228 // Ps = 1 -> Light Num Lock. 4229 // Ps = 2 -> Light Caps Lock. 4230 // Ps = 3 -> Light Scroll Lock. 4231 // Ps = 2 1 -> Extinguish Num Lock. 4232 // Ps = 2 2 -> Extinguish Caps Lock. 4233 // Ps = 2 3 -> Extinguish Scroll Lock. 4234 Terminal.prototype.loadLEDs = function(params) { 4235 ; 4236 }; 4237 4238 // CSI Ps SP q 4239 // Set cursor style (DECSCUSR, VT520). 4240 // Ps = 0 -> blinking block. 4241 // Ps = 1 -> blinking block (default). 4242 // Ps = 2 -> steady block. 4243 // Ps = 3 -> blinking underline. 4244 // Ps = 4 -> steady underline. 4245 Terminal.prototype.setCursorStyle = function(params) { 4246 ; 4247 }; 4248 4249 // CSI Ps " q 4250 // Select character protection attribute (DECSCA). Valid values 4251 // for the parameter: 4252 // Ps = 0 -> DECSED and DECSEL can erase (default). 4253 // Ps = 1 -> DECSED and DECSEL cannot erase. 4254 // Ps = 2 -> DECSED and DECSEL can erase. 4255 Terminal.prototype.setCharProtectionAttr = function(params) { 4256 ; 4257 }; 4258 4259 // CSI ? Pm r 4260 // Restore DEC Private Mode Values. The value of Ps previously 4261 // saved is restored. Ps values are the same as for DECSET. 4262 Terminal.prototype.restorePrivateValues = function(params) { 4263 ; 4264 }; 4265 4266 // CSI Pt; Pl; Pb; Pr; Ps$ r 4267 // Change Attributes in Rectangular Area (DECCARA), VT400 and up. 4268 // Pt; Pl; Pb; Pr denotes the rectangle. 4269 // Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7. 4270 // NOTE: xterm doesn't enable this code by default. 4271 Terminal.prototype.setAttrInRectangle = function(params) { 4272 var t = params[0] 4273 , l = params[1] 4274 , b = params[2] 4275 , r = params[3] 4276 , attr = params[4]; 4277 4278 var line 4279 , i; 4280 4281 for (; t < b + 1; t++) { 4282 line = this.lines[this.ybase + t]; 4283 for (i = l; i < r; i++) { 4284 line[i] = [attr, line[i][1]]; 4285 } 4286 } 4287 4288 // this.maxRange(); 4289 this.updateRange(params[0]); 4290 this.updateRange(params[2]); 4291 }; 4292 4293 // CSI ? Pm s 4294 // Save DEC Private Mode Values. Ps values are the same as for 4295 // DECSET. 4296 Terminal.prototype.savePrivateValues = function(params) { 4297 ; 4298 }; 4299 4300 // CSI Ps ; Ps ; Ps t 4301 // Window manipulation (from dtterm, as well as extensions). 4302 // These controls may be disabled using the allowWindowOps 4303 // resource. Valid values for the first (and any additional 4304 // parameters) are: 4305 // Ps = 1 -> De-iconify window. 4306 // Ps = 2 -> Iconify window. 4307 // Ps = 3 ; x ; y -> Move window to [x, y]. 4308 // Ps = 4 ; height ; width -> Resize the xterm window to 4309 // height and width in pixels. 4310 // Ps = 5 -> Raise the xterm window to the front of the stack- 4311 // ing order. 4312 // Ps = 6 -> Lower the xterm window to the bottom of the 4313 // stacking order. 4314 // Ps = 7 -> Refresh the xterm window. 4315 // Ps = 8 ; height ; width -> Resize the text area to 4316 // [height;width] in characters. 4317 // Ps = 9 ; 0 -> Restore maximized window. 4318 // Ps = 9 ; 1 -> Maximize window (i.e., resize to screen 4319 // size). 4320 // Ps = 1 0 ; 0 -> Undo full-screen mode. 4321 // Ps = 1 0 ; 1 -> Change to full-screen. 4322 // Ps = 1 1 -> Report xterm window state. If the xterm window 4323 // is open (non-iconified), it returns CSI 1 t . If the xterm 4324 // window is iconified, it returns CSI 2 t . 4325 // Ps = 1 3 -> Report xterm window position. Result is CSI 3 4326 // ; x ; y t 4327 // Ps = 1 4 -> Report xterm window in pixels. Result is CSI 4328 // 4 ; height ; width t 4329 // Ps = 1 8 -> Report the size of the text area in characters. 4330 // Result is CSI 8 ; height ; width t 4331 // Ps = 1 9 -> Report the size of the screen in characters. 4332 // Result is CSI 9 ; height ; width t 4333 // Ps = 2 0 -> Report xterm window's icon label. Result is 4334 // OSC L label ST 4335 // Ps = 2 1 -> Report xterm window's title. Result is OSC l 4336 // label ST 4337 // Ps = 2 2 ; 0 -> Save xterm icon and window title on 4338 // stack. 4339 // Ps = 2 2 ; 1 -> Save xterm icon title on stack. 4340 // Ps = 2 2 ; 2 -> Save xterm window title on stack. 4341 // Ps = 2 3 ; 0 -> Restore xterm icon and window title from 4342 // stack. 4343 // Ps = 2 3 ; 1 -> Restore xterm icon title from stack. 4344 // Ps = 2 3 ; 2 -> Restore xterm window title from stack. 4345 // Ps >= 2 4 -> Resize to Ps lines (DECSLPP). 4346 Terminal.prototype.manipulateWindow = function(params) { 4347 ; 4348 }; 4349 4350 // CSI Pt; Pl; Pb; Pr; Ps$ t 4351 // Reverse Attributes in Rectangular Area (DECRARA), VT400 and 4352 // up. 4353 // Pt; Pl; Pb; Pr denotes the rectangle. 4354 // Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7. 4355 // NOTE: xterm doesn't enable this code by default. 4356 Terminal.prototype.reverseAttrInRectangle = function(params) { 4357 ; 4358 }; 4359 4360 // CSI > Ps; Ps t 4361 // Set one or more features of the title modes. Each parameter 4362 // enables a single feature. 4363 // Ps = 0 -> Set window/icon labels using hexadecimal. 4364 // Ps = 1 -> Query window/icon labels using hexadecimal. 4365 // Ps = 2 -> Set window/icon labels using UTF-8. 4366 // Ps = 3 -> Query window/icon labels using UTF-8. (See dis- 4367 // cussion of "Title Modes") 4368 Terminal.prototype.setTitleModeFeature = function(params) { 4369 ; 4370 }; 4371 4372 // CSI Ps SP t 4373 // Set warning-bell volume (DECSWBV, VT520). 4374 // Ps = 0 or 1 -> off. 4375 // Ps = 2 , 3 or 4 -> low. 4376 // Ps = 5 , 6 , 7 , or 8 -> high. 4377 Terminal.prototype.setWarningBellVolume = function(params) { 4378 ; 4379 }; 4380 4381 // CSI Ps SP u 4382 // Set margin-bell volume (DECSMBV, VT520). 4383 // Ps = 1 -> off. 4384 // Ps = 2 , 3 or 4 -> low. 4385 // Ps = 0 , 5 , 6 , 7 , or 8 -> high. 4386 Terminal.prototype.setMarginBellVolume = function(params) { 4387 ; 4388 }; 4389 4390 // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v 4391 // Copy Rectangular Area (DECCRA, VT400 and up). 4392 // Pt; Pl; Pb; Pr denotes the rectangle. 4393 // Pp denotes the source page. 4394 // Pt; Pl denotes the target location. 4395 // Pp denotes the target page. 4396 // NOTE: xterm doesn't enable this code by default. 4397 Terminal.prototype.copyRectangle = function(params) { 4398 ; 4399 }; 4400 4401 // CSI Pt ; Pl ; Pb ; Pr ' w 4402 // Enable Filter Rectangle (DECEFR), VT420 and up. 4403 // Parameters are [top;left;bottom;right]. 4404 // Defines the coordinates of a filter rectangle and activates 4405 // it. Anytime the locator is detected outside of the filter 4406 // rectangle, an outside rectangle event is generated and the 4407 // rectangle is disabled. Filter rectangles are always treated 4408 // as "one-shot" events. Any parameters that are omitted default 4409 // to the current locator position. If all parameters are omit- 4410 // ted, any locator motion will be reported. DECELR always can- 4411 // cels any prevous rectangle definition. 4412 Terminal.prototype.enableFilterRectangle = function(params) { 4413 ; 4414 }; 4415 4416 // CSI Ps x Request Terminal Parameters (DECREQTPARM). 4417 // if Ps is a "0" (default) or "1", and xterm is emulating VT100, 4418 // the control sequence elicits a response of the same form whose 4419 // parameters describe the terminal: 4420 // Ps -> the given Ps incremented by 2. 4421 // Pn = 1 <- no parity. 4422 // Pn = 1 <- eight bits. 4423 // Pn = 1 <- 2 8 transmit 38.4k baud. 4424 // Pn = 1 <- 2 8 receive 38.4k baud. 4425 // Pn = 1 <- clock multiplier. 4426 // Pn = 0 <- STP flags. 4427 Terminal.prototype.requestParameters = function(params) { 4428 ; 4429 }; 4430 4431 // CSI Ps x Select Attribute Change Extent (DECSACE). 4432 // Ps = 0 -> from start to end position, wrapped. 4433 // Ps = 1 -> from start to end position, wrapped. 4434 // Ps = 2 -> rectangle (exact). 4435 Terminal.prototype.selectChangeExtent = function(params) { 4436 ; 4437 }; 4438 4439 // CSI Pc; Pt; Pl; Pb; Pr$ x 4440 // Fill Rectangular Area (DECFRA), VT420 and up. 4441 // Pc is the character to use. 4442 // Pt; Pl; Pb; Pr denotes the rectangle. 4443 // NOTE: xterm doesn't enable this code by default. 4444 Terminal.prototype.fillRectangle = function(params) { 4445 var ch = params[0] 4446 , t = params[1] 4447 , l = params[2] 4448 , b = params[3] 4449 , r = params[4]; 4450 4451 var line 4452 , i; 4453 4454 for (; t < b + 1; t++) { 4455 line = this.lines[this.ybase + t]; 4456 for (i = l; i < r; i++) { 4457 line[i] = [line[i][0], String.fromCharCode(ch)]; 4458 } 4459 } 4460 4461 // this.maxRange(); 4462 this.updateRange(params[1]); 4463 this.updateRange(params[3]); 4464 }; 4465 4466 // CSI Ps ; Pu ' z 4467 // Enable Locator Reporting (DECELR). 4468 // Valid values for the first parameter: 4469 // Ps = 0 -> Locator disabled (default). 4470 // Ps = 1 -> Locator enabled. 4471 // Ps = 2 -> Locator enabled for one report, then disabled. 4472 // The second parameter specifies the coordinate unit for locator 4473 // reports. 4474 // Valid values for the second parameter: 4475 // Pu = 0 <- or omitted -> default to character cells. 4476 // Pu = 1 <- device physical pixels. 4477 // Pu = 2 <- character cells. 4478 Terminal.prototype.enableLocatorReporting = function(params) { 4479 var val = params[0] > 0; 4480 //this.mouseEvents = val; 4481 //this.decLocator = val; 4482 }; 4483 4484 // CSI Pt; Pl; Pb; Pr$ z 4485 // Erase Rectangular Area (DECERA), VT400 and up. 4486 // Pt; Pl; Pb; Pr denotes the rectangle. 4487 // NOTE: xterm doesn't enable this code by default. 4488 Terminal.prototype.eraseRectangle = function(params) { 4489 var t = params[0] 4490 , l = params[1] 4491 , b = params[2] 4492 , r = params[3]; 4493 4494 var line 4495 , i 4496 , ch; 4497 4498 ch = [this.eraseAttr(), ' ']; // xterm? 4499 4500 for (; t < b + 1; t++) { 4501 line = this.lines[this.ybase + t]; 4502 for (i = l; i < r; i++) { 4503 line[i] = ch; 4504 } 4505 } 4506 4507 // this.maxRange(); 4508 this.updateRange(params[0]); 4509 this.updateRange(params[2]); 4510 }; 4511 4512 // CSI Pm ' { 4513 // Select Locator Events (DECSLE). 4514 // Valid values for the first (and any additional parameters) 4515 // are: 4516 // Ps = 0 -> only respond to explicit host requests (DECRQLP). 4517 // (This is default). It also cancels any filter 4518 // rectangle. 4519 // Ps = 1 -> report button down transitions. 4520 // Ps = 2 -> do not report button down transitions. 4521 // Ps = 3 -> report button up transitions. 4522 // Ps = 4 -> do not report button up transitions. 4523 Terminal.prototype.setLocatorEvents = function(params) { 4524 ; 4525 }; 4526 4527 // CSI Pt; Pl; Pb; Pr$ { 4528 // Selective Erase Rectangular Area (DECSERA), VT400 and up. 4529 // Pt; Pl; Pb; Pr denotes the rectangle. 4530 Terminal.prototype.selectiveEraseRectangle = function(params) { 4531 ; 4532 }; 4533 4534 // CSI Ps ' | 4535 // Request Locator Position (DECRQLP). 4536 // Valid values for the parameter are: 4537 // Ps = 0 , 1 or omitted -> transmit a single DECLRP locator 4538 // report. 4539 4540 // If Locator Reporting has been enabled by a DECELR, xterm will 4541 // respond with a DECLRP Locator Report. This report is also 4542 // generated on button up and down events if they have been 4543 // enabled with a DECSLE, or when the locator is detected outside 4544 // of a filter rectangle, if filter rectangles have been enabled 4545 // with a DECEFR. 4546 4547 // -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w 4548 4549 // Parameters are [event;button;row;column;page]. 4550 // Valid values for the event: 4551 // Pe = 0 -> locator unavailable - no other parameters sent. 4552 // Pe = 1 -> request - xterm received a DECRQLP. 4553 // Pe = 2 -> left button down. 4554 // Pe = 3 -> left button up. 4555 // Pe = 4 -> middle button down. 4556 // Pe = 5 -> middle button up. 4557 // Pe = 6 -> right button down. 4558 // Pe = 7 -> right button up. 4559 // Pe = 8 -> M4 button down. 4560 // Pe = 9 -> M4 button up. 4561 // Pe = 1 0 -> locator outside filter rectangle. 4562 // ``button'' parameter is a bitmask indicating which buttons are 4563 // pressed: 4564 // Pb = 0 <- no buttons down. 4565 // Pb & 1 <- right button down. 4566 // Pb & 2 <- middle button down. 4567 // Pb & 4 <- left button down. 4568 // Pb & 8 <- M4 button down. 4569 // ``row'' and ``column'' parameters are the coordinates of the 4570 // locator position in the xterm window, encoded as ASCII deci- 4571 // mal. 4572 // The ``page'' parameter is not used by xterm, and will be omit- 4573 // ted. 4574 Terminal.prototype.requestLocatorPosition = function(params) { 4575 ; 4576 }; 4577 4578 // CSI P m SP } 4579 // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. 4580 // NOTE: xterm doesn't enable this code by default. 4581 Terminal.prototype.insertColumns = function() { 4582 var param = params[0] 4583 , l = this.ybase + this.rows 4584 , ch = [this.eraseAttr(), ' '] // xterm? 4585 , i; 4586 4587 while (param--) { 4588 for (i = this.ybase; i < l; i++) { 4589 this.lines[i].splice(this.x + 1, 0, ch); 4590 this.lines[i].pop(); 4591 } 4592 } 4593 4594 this.maxRange(); 4595 }; 4596 4597 // CSI P m SP ~ 4598 // Delete P s Column(s) (default = 1) (DECDC), VT420 and up 4599 // NOTE: xterm doesn't enable this code by default. 4600 Terminal.prototype.deleteColumns = function() { 4601 var param = params[0] 4602 , l = this.ybase + this.rows 4603 , ch = [this.eraseAttr(), ' '] // xterm? 4604 , i; 4605 4606 while (param--) { 4607 for (i = this.ybase; i < l; i++) { 4608 this.lines[i].splice(this.x, 1); 4609 this.lines[i].push(ch); 4610 } 4611 } 4612 4613 this.maxRange(); 4614 }; 4615 4616 /** 4617 * Prefix/Select/Visual/Search Modes 4618 */ 4619 4620 Terminal.prototype.enterPrefix = function() { 4621 this.prefixMode = true; 4622 }; 4623 4624 Terminal.prototype.leavePrefix = function() { 4625 this.prefixMode = false; 4626 }; 4627 4628 Terminal.prototype.enterSelect = function() { 4629 this._real = { 4630 x: this.x, 4631 y: this.y, 4632 ydisp: this.ydisp, 4633 ybase: this.ybase, 4634 cursorHidden: this.cursorHidden, 4635 lines: this.copyBuffer(this.lines), 4636 write: this.write 4637 }; 4638 this.write = function() {}; 4639 this.selectMode = true; 4640 this.visualMode = false; 4641 this.cursorHidden = false; 4642 this.refresh(this.y, this.y); 4643 }; 4644 4645 Terminal.prototype.leaveSelect = function() { 4646 this.x = this._real.x; 4647 this.y = this._real.y; 4648 this.ydisp = this._real.ydisp; 4649 this.ybase = this._real.ybase; 4650 this.cursorHidden = this._real.cursorHidden; 4651 this.lines = this._real.lines; 4652 this.write = this._real.write; 4653 delete this._real; 4654 this.selectMode = false; 4655 this.visualMode = false; 4656 this.refresh(0, this.rows - 1); 4657 }; 4658 4659 Terminal.prototype.enterVisual = function() { 4660 this._real.preVisual = this.copyBuffer(this.lines); 4661 this.selectText(this.x, this.x, this.ydisp + this.y, this.ydisp + this.y); 4662 this.visualMode = true; 4663 }; 4664 4665 Terminal.prototype.leaveVisual = function() { 4666 this.lines = this._real.preVisual; 4667 delete this._real.preVisual; 4668 delete this._selected; 4669 this.visualMode = false; 4670 this.refresh(0, this.rows - 1); 4671 }; 4672 4673 Terminal.prototype.enterSearch = function(down) { 4674 this.entry = ''; 4675 this.searchMode = true; 4676 this.searchDown = down; 4677 this._real.preSearch = this.copyBuffer(this.lines); 4678 this._real.preSearchX = this.x; 4679 this._real.preSearchY = this.y; 4680 4681 var bottom = this.ydisp + this.rows - 1; 4682 for (var i = 0; i < this.entryPrefix.length; i++) { 4683 //this.lines[bottom][i][0] = (this.defAttr & ~0x1ff) | 4; 4684 //this.lines[bottom][i][1] = this.entryPrefix[i]; 4685 this.lines[bottom][i] = [ 4686 (this.defAttr & ~0x1ff) | 4, 4687 this.entryPrefix[i] 4688 ]; 4689 } 4690 4691 this.y = this.rows - 1; 4692 this.x = this.entryPrefix.length; 4693 4694 this.refresh(this.rows - 1, this.rows - 1); 4695 }; 4696 4697 Terminal.prototype.leaveSearch = function() { 4698 this.searchMode = false; 4699 4700 if (this._real.preSearch) { 4701 this.lines = this._real.preSearch; 4702 this.x = this._real.preSearchX; 4703 this.y = this._real.preSearchY; 4704 delete this._real.preSearch; 4705 delete this._real.preSearchX; 4706 delete this._real.preSearchY; 4707 } 4708 4709 this.refresh(this.rows - 1, this.rows - 1); 4710 }; 4711 4712 Terminal.prototype.copyBuffer = function(lines) { 4713 var lines = lines || this.lines 4714 , out = []; 4715 4716 for (var y = 0; y < lines.length; y++) { 4717 out[y] = []; 4718 for (var x = 0; x < lines[y].length; x++) { 4719 out[y][x] = [lines[y][x][0], lines[y][x][1]]; 4720 } 4721 } 4722 4723 return out; 4724 }; 4725 4726 Terminal.prototype.getCopyTextarea = function(text) { 4727 var textarea = this._copyTextarea 4728 , document = this.document; 4729 4730 if (!textarea) { 4731 textarea = document.createElement('textarea'); 4732 textarea.style.position = 'absolute'; 4733 textarea.style.left = '-32000px'; 4734 textarea.style.top = '-32000px'; 4735 textarea.style.width = '0px'; 4736 textarea.style.height = '0px'; 4737 textarea.style.opacity = '0'; 4738 textarea.style.backgroundColor = 'transparent'; 4739 textarea.style.borderStyle = 'none'; 4740 textarea.style.outlineStyle = 'none'; 4741 4742 document.getElementsByTagName('body')[0].appendChild(textarea); 4743 4744 this._copyTextarea = textarea; 4745 } 4746 4747 return textarea; 4748 }; 4749 4750 // NOTE: Only works for primary selection on X11. 4751 // Non-X11 users should use Ctrl-C instead. 4752 Terminal.prototype.copyText = function(text) { 4753 var self = this 4754 , textarea = this.getCopyTextarea(); 4755 4756 this.emit('copy', text); 4757 4758 textarea.focus(); 4759 textarea.textContent = text; 4760 textarea.value = text; 4761 textarea.setSelectionRange(0, text.length); 4762 4763 setTimeout(function() { 4764 self.element.focus(); 4765 self.focus(); 4766 }, 1); 4767 }; 4768 4769 Terminal.prototype.selectText = function(x1, x2, y1, y2) { 4770 var ox1 4771 , ox2 4772 , oy1 4773 , oy2 4774 , tmp 4775 , x 4776 , y 4777 , xl 4778 , attr; 4779 4780 if (this._selected) { 4781 ox1 = this._selected.x1; 4782 ox2 = this._selected.x2; 4783 oy1 = this._selected.y1; 4784 oy2 = this._selected.y2; 4785 4786 if (oy2 < oy1) { 4787 tmp = ox2; 4788 ox2 = ox1; 4789 ox1 = tmp; 4790 tmp = oy2; 4791 oy2 = oy1; 4792 oy1 = tmp; 4793 } 4794 4795 if (ox2 < ox1 && oy1 === oy2) { 4796 tmp = ox2; 4797 ox2 = ox1; 4798 ox1 = tmp; 4799 } 4800 4801 for (y = oy1; y <= oy2; y++) { 4802 x = 0; 4803 xl = this.cols - 1; 4804 if (y === oy1) { 4805 x = ox1; 4806 } 4807 if (y === oy2) { 4808 xl = ox2; 4809 } 4810 for (; x <= xl; x++) { 4811 if (this.lines[y][x].old != null) { 4812 //this.lines[y][x][0] = this.lines[y][x].old; 4813 //delete this.lines[y][x].old; 4814 attr = this.lines[y][x].old; 4815 delete this.lines[y][x].old; 4816 this.lines[y][x] = [attr, this.lines[y][x][1]]; 4817 } 4818 } 4819 } 4820 4821 y1 = this._selected.y1; 4822 x1 = this._selected.x1; 4823 } 4824 4825 y1 = Math.max(y1, 0); 4826 y1 = Math.min(y1, this.ydisp + this.rows - 1); 4827 4828 y2 = Math.max(y2, 0); 4829 y2 = Math.min(y2, this.ydisp + this.rows - 1); 4830 4831 this._selected = { x1: x1, x2: x2, y1: y1, y2: y2 }; 4832 4833 if (y2 < y1) { 4834 tmp = x2; 4835 x2 = x1; 4836 x1 = tmp; 4837 tmp = y2; 4838 y2 = y1; 4839 y1 = tmp; 4840 } 4841 4842 if (x2 < x1 && y1 === y2) { 4843 tmp = x2; 4844 x2 = x1; 4845 x1 = tmp; 4846 } 4847 4848 for (y = y1; y <= y2; y++) { 4849 x = 0; 4850 xl = this.cols - 1; 4851 if (y === y1) { 4852 x = x1; 4853 } 4854 if (y === y2) { 4855 xl = x2; 4856 } 4857 for (; x <= xl; x++) { 4858 //this.lines[y][x].old = this.lines[y][x][0]; 4859 //this.lines[y][x][0] &= ~0x1ff; 4860 //this.lines[y][x][0] |= (0x1ff << 9) | 4; 4861 attr = this.lines[y][x][0]; 4862 this.lines[y][x] = [ 4863 (attr & ~0x1ff) | ((0x1ff << 9) | 4), 4864 this.lines[y][x][1] 4865 ]; 4866 this.lines[y][x].old = attr; 4867 } 4868 } 4869 4870 y1 = y1 - this.ydisp; 4871 y2 = y2 - this.ydisp; 4872 4873 y1 = Math.max(y1, 0); 4874 y1 = Math.min(y1, this.rows - 1); 4875 4876 y2 = Math.max(y2, 0); 4877 y2 = Math.min(y2, this.rows - 1); 4878 4879 //this.refresh(y1, y2); 4880 this.refresh(0, this.rows - 1); 4881 }; 4882 4883 Terminal.prototype.grabText = function(x1, x2, y1, y2) { 4884 var out = '' 4885 , buf = '' 4886 , ch 4887 , x 4888 , y 4889 , xl 4890 , tmp; 4891 4892 if (y2 < y1) { 4893 tmp = x2; 4894 x2 = x1; 4895 x1 = tmp; 4896 tmp = y2; 4897 y2 = y1; 4898 y1 = tmp; 4899 } 4900 4901 if (x2 < x1 && y1 === y2) { 4902 tmp = x2; 4903 x2 = x1; 4904 x1 = tmp; 4905 } 4906 4907 for (y = y1; y <= y2; y++) { 4908 x = 0; 4909 xl = this.cols - 1; 4910 if (y === y1) { 4911 x = x1; 4912 } 4913 if (y === y2) { 4914 xl = x2; 4915 } 4916 for (; x <= xl; x++) { 4917 ch = this.lines[y][x][1]; 4918 if (ch === ' ') { 4919 buf += ch; 4920 continue; 4921 } 4922 if (buf) { 4923 out += buf; 4924 buf = ''; 4925 } 4926 out += ch; 4927 if (isWide(ch)) x++; 4928 } 4929 buf = ''; 4930 out += '\n'; 4931 } 4932 4933 // If we're not at the end of the 4934 // line, don't add a newline. 4935 for (x = x2, y = y2; x < this.cols; x++) { 4936 if (this.lines[y][x][1] !== ' ') { 4937 out = out.slice(0, -1); 4938 break; 4939 } 4940 } 4941 4942 return out; 4943 }; 4944 4945 Terminal.prototype.keyPrefix = function(ev, key) { 4946 if (key === 'k' || key === '&') { 4947 this.destroy(); 4948 } else if (key === 'p' || key === ']') { 4949 this.emit('request paste'); 4950 } else if (key === 'c') { 4951 this.emit('request create'); 4952 } else if (key >= '0' && key <= '9') { 4953 key = +key - 1; 4954 if (!~key) key = 9; 4955 this.emit('request term', key); 4956 } else if (key === 'n') { 4957 this.emit('request term next'); 4958 } else if (key === 'P') { 4959 this.emit('request term previous'); 4960 } else if (key === ':') { 4961 this.emit('request command mode'); 4962 } else if (key === '[') { 4963 this.enterSelect(); 4964 } 4965 }; 4966 4967 Terminal.prototype.keySelect = function(ev, key) { 4968 this.showCursor(); 4969 4970 if (this.searchMode || key === 'n' || key === 'N') { 4971 return this.keySearch(ev, key); 4972 } 4973 4974 if (key === '\x04') { // ctrl-d 4975 var y = this.ydisp + this.y; 4976 if (this.ydisp === this.ybase) { 4977 // Mimic vim behavior 4978 this.y = Math.min(this.y + (this.rows - 1) / 2 | 0, this.rows - 1); 4979 this.refresh(0, this.rows - 1); 4980 } else { 4981 this.scrollDisp((this.rows - 1) / 2 | 0); 4982 } 4983 if (this.visualMode) { 4984 this.selectText(this.x, this.x, y, this.ydisp + this.y); 4985 } 4986 return; 4987 } 4988 4989 if (key === '\x15') { // ctrl-u 4990 var y = this.ydisp + this.y; 4991 if (this.ydisp === 0) { 4992 // Mimic vim behavior 4993 this.y = Math.max(this.y - (this.rows - 1) / 2 | 0, 0); 4994 this.refresh(0, this.rows - 1); 4995 } else { 4996 this.scrollDisp(-(this.rows - 1) / 2 | 0); 4997 } 4998 if (this.visualMode) { 4999 this.selectText(this.x, this.x, y, this.ydisp + this.y); 5000 } 5001 return; 5002 } 5003 5004 if (key === '\x06') { // ctrl-f 5005 var y = this.ydisp + this.y; 5006 this.scrollDisp(this.rows - 1); 5007 if (this.visualMode) { 5008 this.selectText(this.x, this.x, y, this.ydisp + this.y); 5009 } 5010 return; 5011 } 5012 5013 if (key === '\x02') { // ctrl-b 5014 var y = this.ydisp + this.y; 5015 this.scrollDisp(-(this.rows - 1)); 5016 if (this.visualMode) { 5017 this.selectText(this.x, this.x, y, this.ydisp + this.y); 5018 } 5019 return; 5020 } 5021 5022 if (key === 'k' || key === '\x1b[A') { 5023 var y = this.ydisp + this.y; 5024 this.y--; 5025 if (this.y < 0) { 5026 this.y = 0; 5027 this.scrollDisp(-1); 5028 } 5029 if (this.visualMode) { 5030 this.selectText(this.x, this.x, y, this.ydisp + this.y); 5031 } else { 5032 this.refresh(this.y, this.y + 1); 5033 } 5034 return; 5035 } 5036 5037 if (key === 'j' || key === '\x1b[B') { 5038 var y = this.ydisp + this.y; 5039 this.y++; 5040 if (this.y >= this.rows) { 5041 this.y = this.rows - 1; 5042 this.scrollDisp(1); 5043 } 5044 if (this.visualMode) { 5045 this.selectText(this.x, this.x, y, this.ydisp + this.y); 5046 } else { 5047 this.refresh(this.y - 1, this.y); 5048 } 5049 return; 5050 } 5051 5052 if (key === 'h' || key === '\x1b[D') { 5053 var x = this.x; 5054 this.x--; 5055 if (this.x < 0) { 5056 this.x = 0; 5057 } 5058 if (this.visualMode) { 5059 this.selectText(x, this.x, this.ydisp + this.y, this.ydisp + this.y); 5060 } else { 5061 this.refresh(this.y, this.y); 5062 } 5063 return; 5064 } 5065 5066 if (key === 'l' || key === '\x1b[C') { 5067 var x = this.x; 5068 this.x++; 5069 if (this.x >= this.cols) { 5070 this.x = this.cols - 1; 5071 } 5072 if (this.visualMode) { 5073 this.selectText(x, this.x, this.ydisp + this.y, this.ydisp + this.y); 5074 } else { 5075 this.refresh(this.y, this.y); 5076 } 5077 return; 5078 } 5079 5080 if (key === 'v' || key === ' ') { 5081 if (!this.visualMode) { 5082 this.enterVisual(); 5083 } else { 5084 this.leaveVisual(); 5085 } 5086 return; 5087 } 5088 5089 if (key === 'y') { 5090 if (this.visualMode) { 5091 var text = this.grabText( 5092 this._selected.x1, this._selected.x2, 5093 this._selected.y1, this._selected.y2); 5094 this.copyText(text); 5095 this.leaveVisual(); 5096 // this.leaveSelect(); 5097 } 5098 return; 5099 } 5100 5101 if (key === 'q' || key === '\x1b') { 5102 if (this.visualMode) { 5103 this.leaveVisual(); 5104 } else { 5105 this.leaveSelect(); 5106 } 5107 return; 5108 } 5109 5110 if (key === 'w' || key === 'W') { 5111 var ox = this.x; 5112 var oy = this.y; 5113 var oyd = this.ydisp; 5114 5115 var x = this.x; 5116 var y = this.y; 5117 var yb = this.ydisp; 5118 var saw_space = false; 5119 5120 for (;;) { 5121 var line = this.lines[yb + y]; 5122 while (x < this.cols) { 5123 if (line[x][1] <= ' ') { 5124 saw_space = true; 5125 } else if (saw_space) { 5126 break; 5127 } 5128 x++; 5129 } 5130 if (x >= this.cols) x = this.cols - 1; 5131 if (x === this.cols - 1 && line[x][1] <= ' ') { 5132 x = 0; 5133 if (++y >= this.rows) { 5134 y--; 5135 if (++yb > this.ybase) { 5136 yb = this.ybase; 5137 x = this.x; 5138 break; 5139 } 5140 } 5141 continue; 5142 } 5143 break; 5144 } 5145 5146 this.x = x, this.y = y; 5147 this.scrollDisp(-this.ydisp + yb); 5148 5149 if (this.visualMode) { 5150 this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); 5151 } 5152 return; 5153 } 5154 5155 if (key === 'b' || key === 'B') { 5156 var ox = this.x; 5157 var oy = this.y; 5158 var oyd = this.ydisp; 5159 5160 var x = this.x; 5161 var y = this.y; 5162 var yb = this.ydisp; 5163 5164 for (;;) { 5165 var line = this.lines[yb + y]; 5166 var saw_space = x > 0 && line[x][1] > ' ' && line[x - 1][1] > ' '; 5167 while (x >= 0) { 5168 if (line[x][1] <= ' ') { 5169 if (saw_space && (x + 1 < this.cols && line[x + 1][1] > ' ')) { 5170 x++; 5171 break; 5172 } else { 5173 saw_space = true; 5174 } 5175 } 5176 x--; 5177 } 5178 if (x < 0) x = 0; 5179 if (x === 0 && (line[x][1] <= ' ' || !saw_space)) { 5180 x = this.cols - 1; 5181 if (--y < 0) { 5182 y++; 5183 if (--yb < 0) { 5184 yb++; 5185 x = 0; 5186 break; 5187 } 5188 } 5189 continue; 5190 } 5191 break; 5192 } 5193 5194 this.x = x, this.y = y; 5195 this.scrollDisp(-this.ydisp + yb); 5196 5197 if (this.visualMode) { 5198 this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); 5199 } 5200 return; 5201 } 5202 5203 if (key === 'e' || key === 'E') { 5204 var x = this.x + 1; 5205 var y = this.y; 5206 var yb = this.ydisp; 5207 if (x >= this.cols) x--; 5208 5209 for (;;) { 5210 var line = this.lines[yb + y]; 5211 while (x < this.cols) { 5212 if (line[x][1] <= ' ') { 5213 x++; 5214 } else { 5215 break; 5216 } 5217 } 5218 while (x < this.cols) { 5219 if (line[x][1] <= ' ') { 5220 if (x - 1 >= 0 && line[x - 1][1] > ' ') { 5221 x--; 5222 break; 5223 } 5224 } 5225 x++; 5226 } 5227 if (x >= this.cols) x = this.cols - 1; 5228 if (x === this.cols - 1 && line[x][1] <= ' ') { 5229 x = 0; 5230 if (++y >= this.rows) { 5231 y--; 5232 if (++yb > this.ybase) { 5233 yb = this.ybase; 5234 break; 5235 } 5236 } 5237 continue; 5238 } 5239 break; 5240 } 5241 5242 this.x = x, this.y = y; 5243 this.scrollDisp(-this.ydisp + yb); 5244 5245 if (this.visualMode) { 5246 this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); 5247 } 5248 return; 5249 } 5250 5251 if (key === '^' || key === '0') { 5252 var ox = this.x; 5253 5254 if (key === '0') { 5255 this.x = 0; 5256 } else if (key === '^') { 5257 var line = this.lines[this.ydisp + this.y]; 5258 var x = 0; 5259 while (x < this.cols) { 5260 if (line[x][1] > ' ') { 5261 break; 5262 } 5263 x++; 5264 } 5265 if (x >= this.cols) x = this.cols - 1; 5266 this.x = x; 5267 } 5268 5269 if (this.visualMode) { 5270 this.selectText(ox, this.x, this.ydisp + this.y, this.ydisp + this.y); 5271 } else { 5272 this.refresh(this.y, this.y); 5273 } 5274 return; 5275 } 5276 5277 if (key === '$') { 5278 var ox = this.x; 5279 var line = this.lines[this.ydisp + this.y]; 5280 var x = this.cols - 1; 5281 while (x >= 0) { 5282 if (line[x][1] > ' ') { 5283 if (this.visualMode && x < this.cols - 1) x++; 5284 break; 5285 } 5286 x--; 5287 } 5288 if (x < 0) x = 0; 5289 this.x = x; 5290 if (this.visualMode) { 5291 this.selectText(ox, this.x, this.ydisp + this.y, this.ydisp + this.y); 5292 } else { 5293 this.refresh(this.y, this.y); 5294 } 5295 return; 5296 } 5297 5298 if (key === 'g' || key === 'G') { 5299 var ox = this.x; 5300 var oy = this.y; 5301 var oyd = this.ydisp; 5302 if (key === 'g') { 5303 this.x = 0, this.y = 0; 5304 this.scrollDisp(-this.ydisp); 5305 } else if (key === 'G') { 5306 this.x = 0, this.y = this.rows - 1; 5307 this.scrollDisp(this.ybase); 5308 } 5309 if (this.visualMode) { 5310 this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); 5311 } 5312 return; 5313 } 5314 5315 if (key === 'H' || key === 'M' || key === 'L') { 5316 var ox = this.x; 5317 var oy = this.y; 5318 if (key === 'H') { 5319 this.x = 0, this.y = 0; 5320 } else if (key === 'M') { 5321 this.x = 0, this.y = this.rows / 2 | 0; 5322 } else if (key === 'L') { 5323 this.x = 0, this.y = this.rows - 1; 5324 } 5325 if (this.visualMode) { 5326 this.selectText(ox, this.x, this.ydisp + oy, this.ydisp + this.y); 5327 } else { 5328 this.refresh(oy, oy); 5329 this.refresh(this.y, this.y); 5330 } 5331 return; 5332 } 5333 5334 if (key === '{' || key === '}') { 5335 var ox = this.x; 5336 var oy = this.y; 5337 var oyd = this.ydisp; 5338 5339 var line; 5340 var saw_full = false; 5341 var found = false; 5342 var first_is_space = -1; 5343 var y = this.y + (key === '{' ? -1 : 1); 5344 var yb = this.ydisp; 5345 var i; 5346 5347 if (key === '{') { 5348 if (y < 0) { 5349 y++; 5350 if (yb > 0) yb--; 5351 } 5352 } else if (key === '}') { 5353 if (y >= this.rows) { 5354 y--; 5355 if (yb < this.ybase) yb++; 5356 } 5357 } 5358 5359 for (;;) { 5360 line = this.lines[yb + y]; 5361 5362 for (i = 0; i < this.cols; i++) { 5363 if (line[i][1] > ' ') { 5364 if (first_is_space === -1) { 5365 first_is_space = 0; 5366 } 5367 saw_full = true; 5368 break; 5369 } else if (i === this.cols - 1) { 5370 if (first_is_space === -1) { 5371 first_is_space = 1; 5372 } else if (first_is_space === 0) { 5373 found = true; 5374 } else if (first_is_space === 1) { 5375 if (saw_full) found = true; 5376 } 5377 break; 5378 } 5379 } 5380 5381 if (found) break; 5382 5383 if (key === '{') { 5384 y--; 5385 if (y < 0) { 5386 y++; 5387 if (yb > 0) yb--; 5388 else break; 5389 } 5390 } else if (key === '}') { 5391 y++; 5392 if (y >= this.rows) { 5393 y--; 5394 if (yb < this.ybase) yb++; 5395 else break; 5396 } 5397 } 5398 } 5399 5400 if (!found) { 5401 if (key === '{') { 5402 y = 0; 5403 yb = 0; 5404 } else if (key === '}') { 5405 y = this.rows - 1; 5406 yb = this.ybase; 5407 } 5408 } 5409 5410 this.x = 0, this.y = y; 5411 this.scrollDisp(-this.ydisp + yb); 5412 5413 if (this.visualMode) { 5414 this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); 5415 } 5416 return; 5417 } 5418 5419 if (key === '/' || key === '?') { 5420 if (!this.visualMode) { 5421 this.enterSearch(key === '/'); 5422 } 5423 return; 5424 } 5425 5426 return false; 5427 }; 5428 5429 Terminal.prototype.keySearch = function(ev, key) { 5430 if (key === '\x1b') { 5431 this.leaveSearch(); 5432 return; 5433 } 5434 5435 if (key === '\r' || (!this.searchMode && (key === 'n' || key === 'N'))) { 5436 this.leaveSearch(); 5437 5438 var entry = this.entry; 5439 5440 if (!entry) { 5441 this.refresh(0, this.rows - 1); 5442 return; 5443 } 5444 5445 var ox = this.x; 5446 var oy = this.y; 5447 var oyd = this.ydisp; 5448 5449 var line; 5450 var found = false; 5451 var wrapped = false; 5452 var x = this.x + 1; 5453 var y = this.ydisp + this.y; 5454 var yb, i; 5455 var up = key === 'N' 5456 ? this.searchDown 5457 : !this.searchDown; 5458 5459 for (;;) { 5460 line = this.lines[y]; 5461 5462 while (x < this.cols) { 5463 for (i = 0; i < entry.length; i++) { 5464 if (x + i >= this.cols) break; 5465 if (line[x + i][1] !== entry[i]) { 5466 break; 5467 } else if (line[x + i][1] === entry[i] && i === entry.length - 1) { 5468 found = true; 5469 break; 5470 } 5471 } 5472 if (found) break; 5473 x += i + 1; 5474 } 5475 if (found) break; 5476 5477 x = 0; 5478 5479 if (!up) { 5480 y++; 5481 if (y > this.ybase + this.rows - 1) { 5482 if (wrapped) break; 5483 // this.setMessage('Search wrapped. Continuing at TOP.'); 5484 wrapped = true; 5485 y = 0; 5486 } 5487 } else { 5488 y--; 5489 if (y < 0) { 5490 if (wrapped) break; 5491 // this.setMessage('Search wrapped. Continuing at BOTTOM.'); 5492 wrapped = true; 5493 y = this.ybase + this.rows - 1; 5494 } 5495 } 5496 } 5497 5498 if (found) { 5499 if (y - this.ybase < 0) { 5500 yb = y; 5501 y = 0; 5502 if (yb > this.ybase) { 5503 y = yb - this.ybase; 5504 yb = this.ybase; 5505 } 5506 } else { 5507 yb = this.ybase; 5508 y -= this.ybase; 5509 } 5510 5511 this.x = x, this.y = y; 5512 this.scrollDisp(-this.ydisp + yb); 5513 5514 if (this.visualMode) { 5515 this.selectText(ox, this.x, oy + oyd, this.ydisp + this.y); 5516 } 5517 return; 5518 } 5519 5520 // this.setMessage("No matches found."); 5521 this.refresh(0, this.rows - 1); 5522 5523 return; 5524 } 5525 5526 if (key === '\b' || key === '\x7f') { 5527 if (this.entry.length === 0) return; 5528 var bottom = this.ydisp + this.rows - 1; 5529 this.entry = this.entry.slice(0, -1); 5530 var i = this.entryPrefix.length + this.entry.length; 5531 //this.lines[bottom][i][1] = ' '; 5532 this.lines[bottom][i] = [ 5533 this.lines[bottom][i][0], 5534 ' ' 5535 ]; 5536 this.x--; 5537 this.refresh(this.rows - 1, this.rows - 1); 5538 this.refresh(this.y, this.y); 5539 return; 5540 } 5541 5542 if (key.length === 1 && key >= ' ' && key <= '~') { 5543 var bottom = this.ydisp + this.rows - 1; 5544 this.entry += key; 5545 var i = this.entryPrefix.length + this.entry.length - 1; 5546 //this.lines[bottom][i][0] = (this.defAttr & ~0x1ff) | 4; 5547 //this.lines[bottom][i][1] = key; 5548 this.lines[bottom][i] = [ 5549 (this.defAttr & ~0x1ff) | 4, 5550 key 5551 ]; 5552 this.x++; 5553 this.refresh(this.rows - 1, this.rows - 1); 5554 this.refresh(this.y, this.y); 5555 return; 5556 } 5557 5558 return false; 5559 }; 5560 5561 /** 5562 * Character Sets 5563 */ 5564 5565 Terminal.charsets = {}; 5566 5567 // DEC Special Character and Line Drawing Set. 5568 // http://vt100.net/docs/vt102-ug/table5-13.html 5569 // A lot of curses apps use this if they see TERM=xterm. 5570 // testing: echo -e '\e(0a\e(B' 5571 // The xterm output sometimes seems to conflict with the 5572 // reference above. xterm seems in line with the reference 5573 // when running vttest however. 5574 // The table below now uses xterm's output from vttest. 5575 Terminal.charsets.SCLD = { // (0 5576 '`': '\u25c6', // '◆' 5577 'a': '\u2592', // '▒' 5578 'b': '\u0009', // '\t' 5579 'c': '\u000c', // '\f' 5580 'd': '\u000d', // '\r' 5581 'e': '\u000a', // '\n' 5582 'f': '\u00b0', // '°' 5583 'g': '\u00b1', // '±' 5584 'h': '\u2424', // '\u2424' (NL) 5585 'i': '\u000b', // '\v' 5586 'j': '\u2518', // '┘' 5587 'k': '\u2510', // '┐' 5588 'l': '\u250c', // '┌' 5589 'm': '\u2514', // '└' 5590 'n': '\u253c', // '┼' 5591 'o': '\u23ba', // '⎺' 5592 'p': '\u23bb', // '⎻' 5593 'q': '\u2500', // '─' 5594 'r': '\u23bc', // '⎼' 5595 's': '\u23bd', // '⎽' 5596 't': '\u251c', // '├' 5597 'u': '\u2524', // '┤' 5598 'v': '\u2534', // '┴' 5599 'w': '\u252c', // '┬' 5600 'x': '\u2502', // '│' 5601 'y': '\u2264', // '≤' 5602 'z': '\u2265', // '≥' 5603 '{': '\u03c0', // 'π' 5604 '|': '\u2260', // '≠' 5605 '}': '\u00a3', // '£' 5606 '~': '\u00b7' // '·' 5607 }; 5608 5609 Terminal.charsets.UK = null; // (A 5610 Terminal.charsets.US = null; // (B (USASCII) 5611 Terminal.charsets.Dutch = null; // (4 5612 Terminal.charsets.Finnish = null; // (C or (5 5613 Terminal.charsets.French = null; // (R 5614 Terminal.charsets.FrenchCanadian = null; // (Q 5615 Terminal.charsets.German = null; // (K 5616 Terminal.charsets.Italian = null; // (Y 5617 Terminal.charsets.NorwegianDanish = null; // (E or (6 5618 Terminal.charsets.Spanish = null; // (Z 5619 Terminal.charsets.Swedish = null; // (H or (7 5620 Terminal.charsets.Swiss = null; // (= 5621 Terminal.charsets.ISOLatin = null; // /A 5622 5623 /** 5624 * Helpers 5625 */ 5626 5627 function on(el, type, handler, capture) { 5628 el.addEventListener(type, handler, capture || false); 5629 } 5630 5631 function off(el, type, handler, capture) { 5632 el.removeEventListener(type, handler, capture || false); 5633 } 5634 5635 function cancel(ev) { 5636 if (ev.preventDefault) ev.preventDefault(); 5637 ev.returnValue = false; 5638 if (ev.stopPropagation) ev.stopPropagation(); 5639 ev.cancelBubble = true; 5640 return false; 5641 } 5642 5643 function inherits(child, parent) { 5644 function f() { 5645 this.constructor = child; 5646 } 5647 f.prototype = parent.prototype; 5648 child.prototype = new f; 5649 } 5650 5651 // if bold is broken, we can't 5652 // use it in the terminal. 5653 function isBoldBroken(document) { 5654 var body = document.getElementsByTagName('body')[0]; 5655 var el = document.createElement('span'); 5656 el.innerHTML = 'hello world'; 5657 body.appendChild(el); 5658 var w1 = el.scrollWidth; 5659 el.style.fontWeight = 'bold'; 5660 var w2 = el.scrollWidth; 5661 body.removeChild(el); 5662 return w1 !== w2; 5663 } 5664 5665 var String = this.String; 5666 var setTimeout = this.setTimeout; 5667 var setInterval = this.setInterval; 5668 5669 function indexOf(obj, el) { 5670 var i = obj.length; 5671 while (i--) { 5672 if (obj[i] === el) return i; 5673 } 5674 return -1; 5675 } 5676 5677 function isWide(ch) { 5678 if (ch <= '\uff00') return false; 5679 return (ch >= '\uff01' && ch <= '\uffbe') 5680 || (ch >= '\uffc2' && ch <= '\uffc7') 5681 || (ch >= '\uffca' && ch <= '\uffcf') 5682 || (ch >= '\uffd2' && ch <= '\uffd7') 5683 || (ch >= '\uffda' && ch <= '\uffdc') 5684 || (ch >= '\uffe0' && ch <= '\uffe6') 5685 || (ch >= '\uffe8' && ch <= '\uffee'); 5686 } 5687 5688 function matchColor(r1, g1, b1) { 5689 var hash = (r1 << 16) | (g1 << 8) | b1; 5690 5691 if (matchColor._cache[hash] != null) { 5692 return matchColor._cache[hash]; 5693 } 5694 5695 var ldiff = Infinity 5696 , li = -1 5697 , i = 0 5698 , c 5699 , r2 5700 , g2 5701 , b2 5702 , diff; 5703 5704 for (; i < Terminal.vcolors.length; i++) { 5705 c = Terminal.vcolors[i]; 5706 r2 = c[0]; 5707 g2 = c[1]; 5708 b2 = c[2]; 5709 5710 diff = matchColor.distance(r1, g1, b1, r2, g2, b2); 5711 5712 if (diff === 0) { 5713 li = i; 5714 break; 5715 } 5716 5717 if (diff < ldiff) { 5718 ldiff = diff; 5719 li = i; 5720 } 5721 } 5722 5723 return matchColor._cache[hash] = li; 5724 } 5725 5726 matchColor._cache = {}; 5727 5728 // http://stackoverflow.com/questions/1633828 5729 matchColor.distance = function(r1, g1, b1, r2, g2, b2) { 5730 return Math.pow(30 * (r1 - r2), 2) 5731 + Math.pow(59 * (g1 - g2), 2) 5732 + Math.pow(11 * (b1 - b2), 2); 5733 }; 5734 5735 function each(obj, iter, con) { 5736 if (obj.forEach) return obj.forEach(iter, con); 5737 for (var i = 0; i < obj.length; i++) { 5738 iter.call(con, obj[i], i, obj); 5739 } 5740 } 5741 5742 function keys(obj) { 5743 if (Object.keys) return Object.keys(obj); 5744 var key, keys = []; 5745 for (key in obj) { 5746 if (Object.prototype.hasOwnProperty.call(obj, key)) { 5747 keys.push(key); 5748 } 5749 } 5750 return keys; 5751 } 5752 5753 /** 5754 * Expose 5755 */ 5756 5757 Terminal.EventEmitter = EventEmitter; 5758 Terminal.inherits = inherits; 5759 Terminal.on = on; 5760 Terminal.off = off; 5761 Terminal.cancel = cancel; 5762 5763 if (typeof module !== 'undefined') { 5764 module.exports = Terminal; 5765 } else { 5766 this.Terminal = Terminal; 5767 } 5768 5769 }).call(function() { 5770 return this || (typeof window !== 'undefined' ? window : global); 5771 }());