OLD | NEW |
(Empty) | |
| 1 /** |
| 2 * xterm.js: xterm, in the browser |
| 3 * Copyright (c) 2014, SourceLair Limited <www.sourcelair.com> (MIT License) |
| 4 * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) |
| 5 * https://github.com/chjj/term.js |
| 6 * |
| 7 * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 * of this software and associated documentation files (the "Software"), to deal |
| 9 * in the Software without restriction, including without limitation the rights |
| 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 11 * copies of the Software, and to permit persons to whom the Software is |
| 12 * furnished to do so, subject to the following conditions: |
| 13 * |
| 14 * The above copyright notice and this permission notice shall be included in |
| 15 * all copies or substantial portions of the Software. |
| 16 * |
| 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 23 * THE SOFTWARE. |
| 24 * |
| 25 * Originally forked from (with the author's permission): |
| 26 * Fabrice Bellard's javascript vt100 for jslinux: |
| 27 * http://bellard.org/jslinux/ |
| 28 * Copyright (c) 2011 Fabrice Bellard |
| 29 * The original design remains. The terminal itself |
| 30 * has been extended to include xterm CSI codes, among |
| 31 * other features. |
| 32 */ |
| 33 |
| 34 import { CompositionHelper } from './CompositionHelper.js'; |
| 35 import { EventEmitter } from './EventEmitter.js'; |
| 36 import { Viewport } from './Viewport.js'; |
| 37 |
| 38 /** |
| 39 * Terminal Emulation References: |
| 40 * http://vt100.net/ |
| 41 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt |
| 42 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html |
| 43 * http://invisible-island.net/vttest/ |
| 44 * http://www.inwap.com/pdp10/ansicode.txt |
| 45 * http://linux.die.net/man/4/console_codes |
| 46 * http://linux.die.net/man/7/urxvt |
| 47 */ |
| 48 |
| 49 // Let it work inside Node.js for automated testing purposes. |
| 50 var document = (typeof window != 'undefined') ? window.document : null; |
| 51 |
| 52 /** |
| 53 * States |
| 54 */ |
| 55 var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6; |
| 56 |
| 57 /** |
| 58 * Terminal |
| 59 */ |
| 60 |
| 61 /** |
| 62 * Creates a new `Terminal` object. |
| 63 * |
| 64 * @param {object} options An object containing a set of options, the available
options are: |
| 65 * - cursorBlink (boolean): Whether the terminal cursor blinks |
| 66 * |
| 67 * @public |
| 68 * @class Xterm Xterm |
| 69 * @alias module:xterm/src/xterm |
| 70 */ |
| 71 function Terminal(options) { |
| 72 var self = this; |
| 73 |
| 74 if (!(this instanceof Terminal)) { |
| 75 return new Terminal(arguments[0], arguments[1], arguments[2]); |
| 76 } |
| 77 |
| 78 self.cancel = Terminal.cancel; |
| 79 |
| 80 EventEmitter.call(this); |
| 81 |
| 82 if (typeof options === 'number') { |
| 83 options = { |
| 84 cols: arguments[0], |
| 85 rows: arguments[1], |
| 86 handler: arguments[2] |
| 87 }; |
| 88 } |
| 89 |
| 90 options = options || {}; |
| 91 |
| 92 |
| 93 Object.keys(Terminal.defaults).forEach(function(key) { |
| 94 if (options[key] == null) { |
| 95 options[key] = Terminal.options[key]; |
| 96 |
| 97 if (Terminal[key] !== Terminal.defaults[key]) { |
| 98 options[key] = Terminal[key]; |
| 99 } |
| 100 } |
| 101 self[key] = options[key]; |
| 102 }); |
| 103 |
| 104 if (options.colors.length === 8) { |
| 105 options.colors = options.colors.concat(Terminal._colors.slice(8)); |
| 106 } else if (options.colors.length === 16) { |
| 107 options.colors = options.colors.concat(Terminal._colors.slice(16)); |
| 108 } else if (options.colors.length === 10) { |
| 109 options.colors = options.colors.slice(0, -2).concat( |
| 110 Terminal._colors.slice(8, -2), options.colors.slice(-2)); |
| 111 } else if (options.colors.length === 18) { |
| 112 options.colors = options.colors.concat( |
| 113 Terminal._colors.slice(16, -2), options.colors.slice(-2)); |
| 114 } |
| 115 this.colors = options.colors; |
| 116 |
| 117 this.options = options; |
| 118 |
| 119 // this.context = options.context || window; |
| 120 // this.document = options.document || document; |
| 121 this.parent = options.body || options.parent || ( |
| 122 document ? document.getElementsByTagName('body')[0] : null |
| 123 ); |
| 124 |
| 125 this.cols = options.cols || options.geometry[0]; |
| 126 this.rows = options.rows || options.geometry[1]; |
| 127 |
| 128 if (options.handler) { |
| 129 this.on('data', options.handler); |
| 130 } |
| 131 |
| 132 /** |
| 133 * The scroll position of the y cursor, ie. ybase + y = the y position within
the entire |
| 134 * buffer |
| 135 */ |
| 136 this.ybase = 0; |
| 137 |
| 138 /** |
| 139 * The scroll position of the viewport |
| 140 */ |
| 141 this.ydisp = 0; |
| 142 |
| 143 /** |
| 144 * The cursor's x position after ybase |
| 145 */ |
| 146 this.x = 0; |
| 147 |
| 148 /** |
| 149 * The cursor's y position after ybase |
| 150 */ |
| 151 this.y = 0; |
| 152 |
| 153 /** |
| 154 * Used to debounce the refresh function |
| 155 */ |
| 156 this.isRefreshing = false; |
| 157 |
| 158 /** |
| 159 * Whether there is a full terminal refresh queued |
| 160 */ |
| 161 |
| 162 this.cursorState = 0; |
| 163 this.cursorHidden = false; |
| 164 this.convertEol; |
| 165 this.state = 0; |
| 166 this.queue = ''; |
| 167 this.scrollTop = 0; |
| 168 this.scrollBottom = this.rows - 1; |
| 169 this.customKeydownHandler = null; |
| 170 |
| 171 // modes |
| 172 this.applicationKeypad = false; |
| 173 this.applicationCursor = false; |
| 174 this.originMode = false; |
| 175 this.insertMode = false; |
| 176 this.wraparoundMode = true; // defaults: xterm - true, vt100 - false |
| 177 this.normal = null; |
| 178 |
| 179 // charset |
| 180 this.charset = null; |
| 181 this.gcharset = null; |
| 182 this.glevel = 0; |
| 183 this.charsets = [null]; |
| 184 |
| 185 // mouse properties |
| 186 this.decLocator; |
| 187 this.x10Mouse; |
| 188 this.vt200Mouse; |
| 189 this.vt300Mouse; |
| 190 this.normalMouse; |
| 191 this.mouseEvents; |
| 192 this.sendFocus; |
| 193 this.utfMouse; |
| 194 this.sgrMouse; |
| 195 this.urxvtMouse; |
| 196 |
| 197 // misc |
| 198 this.element; |
| 199 this.children; |
| 200 this.refreshStart; |
| 201 this.refreshEnd; |
| 202 this.savedX; |
| 203 this.savedY; |
| 204 this.savedCols; |
| 205 |
| 206 // stream |
| 207 this.readable = true; |
| 208 this.writable = true; |
| 209 |
| 210 this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); |
| 211 this.curAttr = this.defAttr; |
| 212 |
| 213 this.params = []; |
| 214 this.currentParam = 0; |
| 215 this.prefix = ''; |
| 216 this.postfix = ''; |
| 217 |
| 218 // leftover surrogate high from previous write invocation |
| 219 this.surrogate_high = ''; |
| 220 |
| 221 /** |
| 222 * An array of all lines in the entire buffer, including the prompt. The lines
are array of |
| 223 * characters which are 2-length arrays where [0] is an attribute and [1] is t
he character. |
| 224 */ |
| 225 this.lines = []; |
| 226 var i = this.rows; |
| 227 while (i--) { |
| 228 this.lines.push(this.blankLine()); |
| 229 } |
| 230 |
| 231 this.tabs; |
| 232 this.setupStops(); |
| 233 } |
| 234 |
| 235 inherits(Terminal, EventEmitter); |
| 236 |
| 237 /** |
| 238 * back_color_erase feature for xterm. |
| 239 */ |
| 240 Terminal.prototype.eraseAttr = function() { |
| 241 // if (this.is('screen')) return this.defAttr; |
| 242 return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff); |
| 243 }; |
| 244 |
| 245 /** |
| 246 * Colors |
| 247 */ |
| 248 |
| 249 // Colors 0-15 |
| 250 Terminal.tangoColors = [ |
| 251 // dark: |
| 252 '#2e3436', |
| 253 '#cc0000', |
| 254 '#4e9a06', |
| 255 '#c4a000', |
| 256 '#3465a4', |
| 257 '#75507b', |
| 258 '#06989a', |
| 259 '#d3d7cf', |
| 260 // bright: |
| 261 '#555753', |
| 262 '#ef2929', |
| 263 '#8ae234', |
| 264 '#fce94f', |
| 265 '#729fcf', |
| 266 '#ad7fa8', |
| 267 '#34e2e2', |
| 268 '#eeeeec' |
| 269 ]; |
| 270 |
| 271 // Colors 0-15 + 16-255 |
| 272 // Much thanks to TooTallNate for writing this. |
| 273 Terminal.colors = (function() { |
| 274 var colors = Terminal.tangoColors.slice() |
| 275 , r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff] |
| 276 , i; |
| 277 |
| 278 // 16-231 |
| 279 i = 0; |
| 280 for (; i < 216; i++) { |
| 281 out(r[(i / 36) % 6 | 0], r[(i / 6) % 6 | 0], r[i % 6]); |
| 282 } |
| 283 |
| 284 // 232-255 (grey) |
| 285 i = 0; |
| 286 for (; i < 24; i++) { |
| 287 r = 8 + i * 10; |
| 288 out(r, r, r); |
| 289 } |
| 290 |
| 291 function out(r, g, b) { |
| 292 colors.push('#' + hex(r) + hex(g) + hex(b)); |
| 293 } |
| 294 |
| 295 function hex(c) { |
| 296 c = c.toString(16); |
| 297 return c.length < 2 ? '0' + c : c; |
| 298 } |
| 299 |
| 300 return colors; |
| 301 })(); |
| 302 |
| 303 Terminal._colors = Terminal.colors.slice(); |
| 304 |
| 305 Terminal.vcolors = (function() { |
| 306 var out = [] |
| 307 , colors = Terminal.colors |
| 308 , i = 0 |
| 309 , color; |
| 310 |
| 311 for (; i < 256; i++) { |
| 312 color = parseInt(colors[i].substring(1), 16); |
| 313 out.push([ |
| 314 (color >> 16) & 0xff, |
| 315 (color >> 8) & 0xff, |
| 316 color & 0xff |
| 317 ]); |
| 318 } |
| 319 |
| 320 return out; |
| 321 })(); |
| 322 |
| 323 /** |
| 324 * Options |
| 325 */ |
| 326 |
| 327 Terminal.defaults = { |
| 328 colors: Terminal.colors, |
| 329 theme: 'default', |
| 330 convertEol: false, |
| 331 termName: 'xterm', |
| 332 geometry: [80, 24], |
| 333 cursorBlink: false, |
| 334 visualBell: false, |
| 335 popOnBell: false, |
| 336 scrollback: 1000, |
| 337 screenKeys: false, |
| 338 debug: false, |
| 339 cancelEvents: false |
| 340 // programFeatures: false, |
| 341 // focusKeys: false, |
| 342 }; |
| 343 |
| 344 Terminal.options = {}; |
| 345 |
| 346 Terminal.focus = null; |
| 347 |
| 348 each(keys(Terminal.defaults), function(key) { |
| 349 Terminal[key] = Terminal.defaults[key]; |
| 350 Terminal.options[key] = Terminal.defaults[key]; |
| 351 }); |
| 352 |
| 353 /** |
| 354 * Focus the terminal. Delegates focus handling to the terminal's DOM element. |
| 355 */ |
| 356 Terminal.prototype.focus = function() { |
| 357 return this.textarea.focus(); |
| 358 }; |
| 359 |
| 360 /** |
| 361 * Retrieves an option's value from the terminal. |
| 362 * @param {string} key The option key. |
| 363 */ |
| 364 Terminal.prototype.getOption = function(key, value) { |
| 365 if (!(key in Terminal.defaults)) { |
| 366 throw new Error('No option with key "' + key + '"'); |
| 367 } |
| 368 |
| 369 if (typeof this.options[key] !== 'undefined') { |
| 370 return this.options[key]; |
| 371 } |
| 372 |
| 373 return this[key]; |
| 374 }; |
| 375 |
| 376 /** |
| 377 * Sets an option on the terminal. |
| 378 * @param {string} key The option key. |
| 379 * @param {string} value The option value. |
| 380 */ |
| 381 Terminal.prototype.setOption = function(key, value) { |
| 382 if (!(key in Terminal.defaults)) { |
| 383 throw new Error('No option with key "' + key + '"'); |
| 384 } |
| 385 this[key] = value; |
| 386 this.options[key] = value; |
| 387 }; |
| 388 |
| 389 /** |
| 390 * Binds the desired focus behavior on a given terminal object. |
| 391 * |
| 392 * @static |
| 393 */ |
| 394 Terminal.bindFocus = function (term) { |
| 395 on(term.textarea, 'focus', function (ev) { |
| 396 if (term.sendFocus) { |
| 397 term.send('\x1b[I'); |
| 398 } |
| 399 term.element.classList.add('focus'); |
| 400 term.showCursor(); |
| 401 Terminal.focus = term; |
| 402 term.emit('focus', {terminal: term}); |
| 403 }); |
| 404 }; |
| 405 |
| 406 /** |
| 407 * Blur the terminal. Delegates blur handling to the terminal's DOM element. |
| 408 */ |
| 409 Terminal.prototype.blur = function() { |
| 410 return this.textarea.blur(); |
| 411 }; |
| 412 |
| 413 /** |
| 414 * Binds the desired blur behavior on a given terminal object. |
| 415 * |
| 416 * @static |
| 417 */ |
| 418 Terminal.bindBlur = function (term) { |
| 419 on(term.textarea, 'blur', function (ev) { |
| 420 term.refresh(term.y, term.y); |
| 421 if (term.sendFocus) { |
| 422 term.send('\x1b[O'); |
| 423 } |
| 424 term.element.classList.remove('focus'); |
| 425 Terminal.focus = null; |
| 426 term.emit('blur', {terminal: term}); |
| 427 }); |
| 428 }; |
| 429 |
| 430 /** |
| 431 * Initialize default behavior |
| 432 */ |
| 433 Terminal.prototype.initGlobal = function() { |
| 434 Terminal.bindPaste(this); |
| 435 Terminal.bindKeys(this); |
| 436 Terminal.bindCopy(this); |
| 437 Terminal.bindFocus(this); |
| 438 Terminal.bindBlur(this); |
| 439 }; |
| 440 |
| 441 /** |
| 442 * Bind to paste event and allow both keyboard and right-click pasting, without
having the |
| 443 * contentEditable value set to true. |
| 444 */ |
| 445 Terminal.bindPaste = function(term) { |
| 446 on([term.textarea, term.element], 'paste', function(ev) { |
| 447 ev.stopPropagation(); |
| 448 if (ev.clipboardData) { |
| 449 var text = ev.clipboardData.getData('text/plain'); |
| 450 term.handler(text); |
| 451 term.textarea.value = ''; |
| 452 return term.cancel(ev); |
| 453 } |
| 454 }); |
| 455 }; |
| 456 |
| 457 /** |
| 458 * Prepares text copied from terminal selection, to be saved in the clipboard by
: |
| 459 * 1. stripping all trailing white spaces |
| 460 * 2. converting all non-breaking spaces to regular spaces |
| 461 * @param {string} text The copied text that needs processing for storing in cli
pboard |
| 462 * @returns {string} |
| 463 * @static |
| 464 */ |
| 465 Terminal.prepareCopiedTextForClipboard = function (text) { |
| 466 var space = String.fromCharCode(32), |
| 467 nonBreakingSpace = String.fromCharCode(160), |
| 468 allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'), |
| 469 processedText = text.split('\n').map(function (line) { |
| 470 /** |
| 471 * Strip all trailing white spaces and convert all non-breaking spaces t
o regular |
| 472 * spaces. |
| 473 */ |
| 474 var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpac
es, space); |
| 475 |
| 476 return processedLine; |
| 477 }).join('\n'); |
| 478 |
| 479 return processedText; |
| 480 }; |
| 481 |
| 482 /** |
| 483 * Apply key handling to the terminal |
| 484 */ |
| 485 Terminal.bindKeys = function(term) { |
| 486 on(term.element, 'keydown', function(ev) { |
| 487 if (document.activeElement != this) { |
| 488 return; |
| 489 } |
| 490 term.keyDown(ev); |
| 491 }, true); |
| 492 |
| 493 on(term.element, 'keypress', function(ev) { |
| 494 if (document.activeElement != this) { |
| 495 return; |
| 496 } |
| 497 term.keyPress(ev); |
| 498 }, true); |
| 499 |
| 500 on(term.element, 'keyup', term.focus.bind(term)); |
| 501 |
| 502 on(term.textarea, 'keydown', function(ev) { |
| 503 term.keyDown(ev); |
| 504 }, true); |
| 505 |
| 506 on(term.textarea, 'keypress', function(ev) { |
| 507 term.keyPress(ev); |
| 508 // Truncate the textarea's value, since it is not needed |
| 509 this.value = ''; |
| 510 }, true); |
| 511 |
| 512 on(term.textarea, 'compositionstart', term.compositionHelper.compositionstart.
bind(term.compositionHelper)); |
| 513 on(term.textarea, 'compositionupdate', term.compositionHelper.compositionupdat
e.bind(term.compositionHelper)); |
| 514 on(term.textarea, 'compositionend', term.compositionHelper.compositionend.bind
(term.compositionHelper)); |
| 515 term.on('refresh', term.compositionHelper.updateCompositionElements.bind(term.
compositionHelper)); |
| 516 }; |
| 517 |
| 518 /** |
| 519 * Binds copy functionality to the given terminal. |
| 520 * @static |
| 521 */ |
| 522 Terminal.bindCopy = function(term) { |
| 523 on(term.element, 'copy', function(ev) { |
| 524 return; // temporary |
| 525 }); |
| 526 }; |
| 527 |
| 528 |
| 529 /** |
| 530 * Insert the given row to the terminal or produce a new one |
| 531 * if no row argument is passed. Return the inserted row. |
| 532 * @param {HTMLElement} row (optional) The row to append to the terminal. |
| 533 */ |
| 534 Terminal.prototype.insertRow = function (row) { |
| 535 if (typeof row != 'object') { |
| 536 row = document.createElement('div'); |
| 537 } |
| 538 |
| 539 this.rowContainer.appendChild(row); |
| 540 this.children.push(row); |
| 541 |
| 542 return row; |
| 543 }; |
| 544 |
| 545 /** |
| 546 * Opens the terminal within an element. |
| 547 * |
| 548 * @param {HTMLElement} parent The element to create the terminal within. |
| 549 */ |
| 550 Terminal.prototype.open = function(parent) { |
| 551 var self=this, i=0, div; |
| 552 |
| 553 this.parent = parent || this.parent; |
| 554 |
| 555 if (!this.parent) { |
| 556 throw new Error('Terminal requires a parent element.'); |
| 557 } |
| 558 |
| 559 // Grab global elements |
| 560 this.context = this.parent.ownerDocument.defaultView; |
| 561 this.document = this.parent.ownerDocument; |
| 562 this.body = this.document.getElementsByTagName('body')[0]; |
| 563 |
| 564 // Parse User-Agent |
| 565 if (this.context.navigator && this.context.navigator.userAgent) { |
| 566 this.isMSIE = !!~this.context.navigator.userAgent.indexOf('MSIE'); |
| 567 } |
| 568 |
| 569 // Find the users platform. We use this to interpret the meta key |
| 570 // and ISO third level shifts. |
| 571 // http://stackoverflow.com/q/19877924/577598 |
| 572 if (this.context.navigator && this.context.navigator.platform) { |
| 573 this.isMac = contains( |
| 574 this.context.navigator.platform, |
| 575 ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'] |
| 576 ); |
| 577 this.isIpad = this.context.navigator.platform === 'iPad'; |
| 578 this.isIphone = this.context.navigator.platform === 'iPhone'; |
| 579 this.isMSWindows = contains( |
| 580 this.context.navigator.platform, |
| 581 ['Windows', 'Win16', 'Win32', 'WinCE'] |
| 582 ); |
| 583 } |
| 584 |
| 585 //Create main element container |
| 586 this.element = this.document.createElement('div'); |
| 587 this.element.classList.add('terminal'); |
| 588 this.element.classList.add('xterm'); |
| 589 this.element.classList.add('xterm-theme-' + this.theme); |
| 590 |
| 591 this.element.style.height |
| 592 this.element.setAttribute('tabindex', 0); |
| 593 |
| 594 this.viewportElement = document.createElement('div'); |
| 595 this.viewportElement.classList.add('xterm-viewport'); |
| 596 this.element.appendChild(this.viewportElement); |
| 597 this.viewportScrollArea = document.createElement('div'); |
| 598 this.viewportScrollArea.classList.add('xterm-scroll-area'); |
| 599 this.viewportElement.appendChild(this.viewportScrollArea); |
| 600 |
| 601 // Create the container that will hold the lines of the terminal and then |
| 602 // produce the lines the lines. |
| 603 this.rowContainer = document.createElement('div'); |
| 604 this.rowContainer.classList.add('xterm-rows'); |
| 605 this.element.appendChild(this.rowContainer); |
| 606 this.children = []; |
| 607 |
| 608 // Create the container that will hold helpers like the textarea for |
| 609 // capturing DOM Events. Then produce the helpers. |
| 610 this.helperContainer = document.createElement('div'); |
| 611 this.helperContainer.classList.add('xterm-helpers'); |
| 612 // TODO: This should probably be inserted once it's filled to prevent an addit
ional layout |
| 613 this.element.appendChild(this.helperContainer); |
| 614 this.textarea = document.createElement('textarea'); |
| 615 this.textarea.classList.add('xterm-helper-textarea'); |
| 616 this.textarea.setAttribute('autocorrect', 'off'); |
| 617 this.textarea.setAttribute('autocapitalize', 'off'); |
| 618 this.textarea.setAttribute('spellcheck', 'false'); |
| 619 this.textarea.tabIndex = 0; |
| 620 this.textarea.addEventListener('focus', function() { |
| 621 self.emit('focus', {terminal: self}); |
| 622 }); |
| 623 this.textarea.addEventListener('blur', function() { |
| 624 self.emit('blur', {terminal: self}); |
| 625 }); |
| 626 this.helperContainer.appendChild(this.textarea); |
| 627 |
| 628 this.compositionView = document.createElement('div'); |
| 629 this.compositionView.classList.add('composition-view'); |
| 630 this.compositionHelper = new CompositionHelper(this.textarea, this.composition
View, this); |
| 631 this.helperContainer.appendChild(this.compositionView); |
| 632 |
| 633 this.charMeasureElement = document.createElement('div'); |
| 634 this.charMeasureElement.classList.add('xterm-char-measure-element'); |
| 635 this.charMeasureElement.innerHTML = 'W'; |
| 636 this.helperContainer.appendChild(this.charMeasureElement); |
| 637 |
| 638 for (; i < this.rows; i++) { |
| 639 this.insertRow(); |
| 640 } |
| 641 this.parent.appendChild(this.element); |
| 642 |
| 643 this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollAr
ea, this.charMeasureElement); |
| 644 |
| 645 // Draw the screen. |
| 646 this.refresh(0, this.rows - 1); |
| 647 |
| 648 // Initialize global actions that |
| 649 // need to be taken on the document. |
| 650 this.initGlobal(); |
| 651 |
| 652 // Ensure there is a Terminal.focus. |
| 653 this.focus(); |
| 654 |
| 655 on(this.element, 'click', function() { |
| 656 var selection = document.getSelection(), |
| 657 collapsed = selection.isCollapsed, |
| 658 isRange = typeof collapsed == 'boolean' ? !collapsed : selection.type ==
'Range'; |
| 659 if (!isRange) { |
| 660 self.focus(); |
| 661 } |
| 662 }); |
| 663 |
| 664 // Listen for mouse events and translate |
| 665 // them into terminal mouse protocols. |
| 666 this.bindMouse(); |
| 667 |
| 668 // Figure out whether boldness affects |
| 669 // the character width of monospace fonts. |
| 670 if (Terminal.brokenBold == null) { |
| 671 Terminal.brokenBold = isBoldBroken(this.document); |
| 672 } |
| 673 |
| 674 this.emit('open'); |
| 675 }; |
| 676 |
| 677 |
| 678 /** |
| 679 * Attempts to load an add-on using CommonJS or RequireJS (whichever is availabl
e). |
| 680 * @param {string} addon The name of the addon to load |
| 681 * @static |
| 682 */ |
| 683 Terminal.loadAddon = function(addon, callback) { |
| 684 if (typeof exports === 'object' && typeof module === 'object') { |
| 685 // CommonJS |
| 686 return require(__dirname + '/../addons/' + addon); |
| 687 } else if (typeof define == 'function') { |
| 688 // RequireJS |
| 689 return require(['../addons/' + addon + '/' + addon], callback); |
| 690 } else { |
| 691 console.error('Cannot load a module without a CommonJS or RequireJS environm
ent.'); |
| 692 return false; |
| 693 } |
| 694 }; |
| 695 |
| 696 |
| 697 /** |
| 698 * XTerm mouse events |
| 699 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking |
| 700 * To better understand these |
| 701 * the xterm code is very helpful: |
| 702 * Relevant files: |
| 703 * button.c, charproc.c, misc.c |
| 704 * Relevant functions in xterm/button.c: |
| 705 * BtnCode, EmitButtonCode, EditorButton, SendMousePosition |
| 706 */ |
| 707 Terminal.prototype.bindMouse = function() { |
| 708 var el = this.element, self = this, pressed = 32; |
| 709 |
| 710 // mouseup, mousedown, wheel |
| 711 // left click: ^[[M 3<^[[M#3< |
| 712 // wheel up: ^[[M`3> |
| 713 function sendButton(ev) { |
| 714 var button |
| 715 , pos; |
| 716 |
| 717 // get the xterm-style button |
| 718 button = getButton(ev); |
| 719 |
| 720 // get mouse coordinates |
| 721 pos = getCoords(ev); |
| 722 if (!pos) return; |
| 723 |
| 724 sendEvent(button, pos); |
| 725 |
| 726 switch (ev.overrideType || ev.type) { |
| 727 case 'mousedown': |
| 728 pressed = button; |
| 729 break; |
| 730 case 'mouseup': |
| 731 // keep it at the left |
| 732 // button, just in case. |
| 733 pressed = 32; |
| 734 break; |
| 735 case 'wheel': |
| 736 // nothing. don't |
| 737 // interfere with |
| 738 // `pressed`. |
| 739 break; |
| 740 } |
| 741 } |
| 742 |
| 743 // motion example of a left click: |
| 744 // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< |
| 745 function sendMove(ev) { |
| 746 var button = pressed |
| 747 , pos; |
| 748 |
| 749 pos = getCoords(ev); |
| 750 if (!pos) return; |
| 751 |
| 752 // buttons marked as motions |
| 753 // are incremented by 32 |
| 754 button += 32; |
| 755 |
| 756 sendEvent(button, pos); |
| 757 } |
| 758 |
| 759 // encode button and |
| 760 // position to characters |
| 761 function encode(data, ch) { |
| 762 if (!self.utfMouse) { |
| 763 if (ch === 255) return data.push(0); |
| 764 if (ch > 127) ch = 127; |
| 765 data.push(ch); |
| 766 } else { |
| 767 if (ch === 2047) return data.push(0); |
| 768 if (ch < 127) { |
| 769 data.push(ch); |
| 770 } else { |
| 771 if (ch > 2047) ch = 2047; |
| 772 data.push(0xC0 | (ch >> 6)); |
| 773 data.push(0x80 | (ch & 0x3F)); |
| 774 } |
| 775 } |
| 776 } |
| 777 |
| 778 // send a mouse event: |
| 779 // regular/utf8: ^[[M Cb Cx Cy |
| 780 // urxvt: ^[[ Cb ; Cx ; Cy M |
| 781 // sgr: ^[[ Cb ; Cx ; Cy M/m |
| 782 // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r |
| 783 // locator: CSI P e ; P b ; P r ; P c ; P p & w |
| 784 function sendEvent(button, pos) { |
| 785 // self.emit('mouse', { |
| 786 // x: pos.x - 32, |
| 787 // y: pos.x - 32, |
| 788 // button: button |
| 789 // }); |
| 790 |
| 791 if (self.vt300Mouse) { |
| 792 // NOTE: Unstable. |
| 793 // http://www.vt100.net/docs/vt3xx-gp/chapter15.html |
| 794 button &= 3; |
| 795 pos.x -= 32; |
| 796 pos.y -= 32; |
| 797 var data = '\x1b[24'; |
| 798 if (button === 0) data += '1'; |
| 799 else if (button === 1) data += '3'; |
| 800 else if (button === 2) data += '5'; |
| 801 else if (button === 3) return; |
| 802 else data += '0'; |
| 803 data += '~[' + pos.x + ',' + pos.y + ']\r'; |
| 804 self.send(data); |
| 805 return; |
| 806 } |
| 807 |
| 808 if (self.decLocator) { |
| 809 // NOTE: Unstable. |
| 810 button &= 3; |
| 811 pos.x -= 32; |
| 812 pos.y -= 32; |
| 813 if (button === 0) button = 2; |
| 814 else if (button === 1) button = 4; |
| 815 else if (button === 2) button = 6; |
| 816 else if (button === 3) button = 3; |
| 817 self.send('\x1b[' |
| 818 + button |
| 819 + ';' |
| 820 + (button === 3 ? 4 : 0) |
| 821 + ';' |
| 822 + pos.y |
| 823 + ';' |
| 824 + pos.x |
| 825 + ';' |
| 826 + (pos.page || 0) |
| 827 + '&w'); |
| 828 return; |
| 829 } |
| 830 |
| 831 if (self.urxvtMouse) { |
| 832 pos.x -= 32; |
| 833 pos.y -= 32; |
| 834 pos.x++; |
| 835 pos.y++; |
| 836 self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M'); |
| 837 return; |
| 838 } |
| 839 |
| 840 if (self.sgrMouse) { |
| 841 pos.x -= 32; |
| 842 pos.y -= 32; |
| 843 self.send('\x1b[<' |
| 844 + ((button & 3) === 3 ? button & ~3 : button) |
| 845 + ';' |
| 846 + pos.x |
| 847 + ';' |
| 848 + pos.y |
| 849 + ((button & 3) === 3 ? 'm' : 'M')); |
| 850 return; |
| 851 } |
| 852 |
| 853 var data = []; |
| 854 |
| 855 encode(data, button); |
| 856 encode(data, pos.x); |
| 857 encode(data, pos.y); |
| 858 |
| 859 self.send('\x1b[M' + String.fromCharCode.apply(String, data)); |
| 860 } |
| 861 |
| 862 function getButton(ev) { |
| 863 var button |
| 864 , shift |
| 865 , meta |
| 866 , ctrl |
| 867 , mod; |
| 868 |
| 869 // two low bits: |
| 870 // 0 = left |
| 871 // 1 = middle |
| 872 // 2 = right |
| 873 // 3 = release |
| 874 // wheel up/down: |
| 875 // 1, and 2 - with 64 added |
| 876 switch (ev.overrideType || ev.type) { |
| 877 case 'mousedown': |
| 878 button = ev.button != null |
| 879 ? +ev.button |
| 880 : ev.which != null |
| 881 ? ev.which - 1 |
| 882 : null; |
| 883 |
| 884 if (self.isMSIE) { |
| 885 button = button === 1 ? 0 : button === 4 ? 1 : button; |
| 886 } |
| 887 break; |
| 888 case 'mouseup': |
| 889 button = 3; |
| 890 break; |
| 891 case 'DOMMouseScroll': |
| 892 button = ev.detail < 0 |
| 893 ? 64 |
| 894 : 65; |
| 895 break; |
| 896 case 'wheel': |
| 897 button = ev.wheelDeltaY > 0 |
| 898 ? 64 |
| 899 : 65; |
| 900 break; |
| 901 } |
| 902 |
| 903 // next three bits are the modifiers: |
| 904 // 4 = shift, 8 = meta, 16 = control |
| 905 shift = ev.shiftKey ? 4 : 0; |
| 906 meta = ev.metaKey ? 8 : 0; |
| 907 ctrl = ev.ctrlKey ? 16 : 0; |
| 908 mod = shift | meta | ctrl; |
| 909 |
| 910 // no mods |
| 911 if (self.vt200Mouse) { |
| 912 // ctrl only |
| 913 mod &= ctrl; |
| 914 } else if (!self.normalMouse) { |
| 915 mod = 0; |
| 916 } |
| 917 |
| 918 // increment to SP |
| 919 button = (32 + (mod << 2)) + button; |
| 920 |
| 921 return button; |
| 922 } |
| 923 |
| 924 // mouse coordinates measured in cols/rows |
| 925 function getCoords(ev) { |
| 926 var x, y, w, h, el; |
| 927 |
| 928 // ignore browsers without pageX for now |
| 929 if (ev.pageX == null) return; |
| 930 |
| 931 x = ev.pageX; |
| 932 y = ev.pageY; |
| 933 el = self.element; |
| 934 |
| 935 // should probably check offsetParent |
| 936 // but this is more portable |
| 937 while (el && el !== self.document.documentElement) { |
| 938 x -= el.offsetLeft; |
| 939 y -= el.offsetTop; |
| 940 el = 'offsetParent' in el |
| 941 ? el.offsetParent |
| 942 : el.parentNode; |
| 943 } |
| 944 |
| 945 // convert to cols/rows |
| 946 w = self.element.clientWidth; |
| 947 h = self.element.clientHeight; |
| 948 x = Math.ceil((x / w) * self.cols); |
| 949 y = Math.ceil((y / h) * self.rows); |
| 950 |
| 951 // be sure to avoid sending |
| 952 // bad positions to the program |
| 953 if (x < 0) x = 0; |
| 954 if (x > self.cols) x = self.cols; |
| 955 if (y < 0) y = 0; |
| 956 if (y > self.rows) y = self.rows; |
| 957 |
| 958 // xterm sends raw bytes and |
| 959 // starts at 32 (SP) for each. |
| 960 x += 32; |
| 961 y += 32; |
| 962 |
| 963 return { |
| 964 x: x, |
| 965 y: y, |
| 966 type: 'wheel' |
| 967 }; |
| 968 } |
| 969 |
| 970 on(el, 'mousedown', function(ev) { |
| 971 if (!self.mouseEvents) return; |
| 972 |
| 973 // send the button |
| 974 sendButton(ev); |
| 975 |
| 976 // ensure focus |
| 977 self.focus(); |
| 978 |
| 979 // fix for odd bug |
| 980 //if (self.vt200Mouse && !self.normalMouse) { |
| 981 if (self.vt200Mouse) { |
| 982 ev.overrideType = 'mouseup'; |
| 983 sendButton(ev); |
| 984 return self.cancel(ev); |
| 985 } |
| 986 |
| 987 // bind events |
| 988 if (self.normalMouse) on(self.document, 'mousemove', sendMove); |
| 989 |
| 990 // x10 compatibility mode can't send button releases |
| 991 if (!self.x10Mouse) { |
| 992 on(self.document, 'mouseup', function up(ev) { |
| 993 sendButton(ev); |
| 994 if (self.normalMouse) off(self.document, 'mousemove', sendMove); |
| 995 off(self.document, 'mouseup', up); |
| 996 return self.cancel(ev); |
| 997 }); |
| 998 } |
| 999 |
| 1000 return self.cancel(ev); |
| 1001 }); |
| 1002 |
| 1003 //if (self.normalMouse) { |
| 1004 // on(self.document, 'mousemove', sendMove); |
| 1005 //} |
| 1006 |
| 1007 on(el, 'wheel', function(ev) { |
| 1008 if (!self.mouseEvents) return; |
| 1009 if (self.x10Mouse |
| 1010 || self.vt300Mouse |
| 1011 || self.decLocator) return; |
| 1012 sendButton(ev); |
| 1013 return self.cancel(ev); |
| 1014 }); |
| 1015 |
| 1016 // allow wheel scrolling in |
| 1017 // the shell for example |
| 1018 on(el, 'wheel', function(ev) { |
| 1019 if (self.mouseEvents) return; |
| 1020 self.viewport.onWheel(ev); |
| 1021 return self.cancel(ev); |
| 1022 }); |
| 1023 }; |
| 1024 |
| 1025 /** |
| 1026 * Destroys the terminal. |
| 1027 */ |
| 1028 Terminal.prototype.destroy = function() { |
| 1029 this.readable = false; |
| 1030 this.writable = false; |
| 1031 this._events = {}; |
| 1032 this.handler = function() {}; |
| 1033 this.write = function() {}; |
| 1034 if (this.element.parentNode) { |
| 1035 this.element.parentNode.removeChild(this.element); |
| 1036 } |
| 1037 //this.emit('close'); |
| 1038 }; |
| 1039 |
| 1040 |
| 1041 /** |
| 1042 * Flags used to render terminal text properly |
| 1043 */ |
| 1044 Terminal.flags = { |
| 1045 BOLD: 1, |
| 1046 UNDERLINE: 2, |
| 1047 BLINK: 4, |
| 1048 INVERSE: 8, |
| 1049 INVISIBLE: 16 |
| 1050 } |
| 1051 |
| 1052 /** |
| 1053 * Refreshes (re-renders) terminal content within two rows (inclusive) |
| 1054 * |
| 1055 * Rendering Engine: |
| 1056 * |
| 1057 * In the screen buffer, each character is stored as a an array with a character |
| 1058 * and a 32-bit integer: |
| 1059 * - First value: a utf-16 character. |
| 1060 * - Second value: |
| 1061 * - Next 9 bits: background color (0-511). |
| 1062 * - Next 9 bits: foreground color (0-511). |
| 1063 * - Next 14 bits: a mask for misc. flags: |
| 1064 * - 1=bold |
| 1065 * - 2=underline |
| 1066 * - 4=blink |
| 1067 * - 8=inverse |
| 1068 * - 16=invisible |
| 1069 * |
| 1070 * @param {number} start The row to start from (between 0 and terminal's height
terminal - 1) |
| 1071 * @param {number} end The row to end at (between fromRow and terminal's height
terminal - 1) |
| 1072 * @param {boolean} queue Whether the refresh should ran right now or be queued |
| 1073 */ |
| 1074 Terminal.prototype.refresh = function(start, end, queue) { |
| 1075 var self = this; |
| 1076 |
| 1077 // queue defaults to true |
| 1078 queue = (typeof queue == 'undefined') ? true : queue; |
| 1079 |
| 1080 /** |
| 1081 * The refresh queue allows refresh to execute only approximately 30 times a s
econd. For |
| 1082 * commands that pass a significant amount of output to the write function, th
is prevents the |
| 1083 * terminal from maxing out the CPU and making the UI unresponsive. While comm
ands can still |
| 1084 * run beyond what they do on the terminal, it is far better with a debounce i
n place as |
| 1085 * every single terminal manipulation does not need to be constructed in the D
OM. |
| 1086 * |
| 1087 * A side-effect of this is that it makes ^C to interrupt a process seem more
responsive. |
| 1088 */ |
| 1089 if (queue) { |
| 1090 // If refresh should be queued, order the refresh and return. |
| 1091 if (this._refreshIsQueued) { |
| 1092 // If a refresh has already been queued, just order a full refresh next |
| 1093 this._fullRefreshNext = true; |
| 1094 } else { |
| 1095 setTimeout(function () { |
| 1096 self.refresh(start, end, false); |
| 1097 }, 34) |
| 1098 this._refreshIsQueued = true; |
| 1099 } |
| 1100 return; |
| 1101 } |
| 1102 |
| 1103 // If refresh should be run right now (not be queued), release the lock |
| 1104 this._refreshIsQueued = false; |
| 1105 |
| 1106 // If multiple refreshes were requested, make a full refresh. |
| 1107 if (this._fullRefreshNext) { |
| 1108 start = 0; |
| 1109 end = this.rows - 1; |
| 1110 this._fullRefreshNext = false // reset lock |
| 1111 } |
| 1112 |
| 1113 var x, y, i, line, out, ch, ch_width, width, data, attr, bg, fg, flags, row, p
arent, focused = document.activeElement; |
| 1114 |
| 1115 // If this is a big refresh, remove the terminal rows from the DOM for faster
calculations |
| 1116 if (end - start >= this.rows / 2) { |
| 1117 parent = this.element.parentNode; |
| 1118 if (parent) { |
| 1119 this.element.removeChild(this.rowContainer); |
| 1120 } |
| 1121 } |
| 1122 |
| 1123 width = this.cols; |
| 1124 y = start; |
| 1125 |
| 1126 if (end >= this.rows.length) { |
| 1127 this.log('`end` is too large. Most likely a bad CSR.'); |
| 1128 end = this.rows.length - 1; |
| 1129 } |
| 1130 |
| 1131 for (; y <= end; y++) { |
| 1132 row = y + this.ydisp; |
| 1133 |
| 1134 line = this.lines[row]; |
| 1135 out = ''; |
| 1136 |
| 1137 if (this.y === y - (this.ybase - this.ydisp) |
| 1138 && this.cursorState |
| 1139 && !this.cursorHidden) { |
| 1140 x = this.x; |
| 1141 } else { |
| 1142 x = -1; |
| 1143 } |
| 1144 |
| 1145 attr = this.defAttr; |
| 1146 i = 0; |
| 1147 |
| 1148 for (; i < width; i++) { |
| 1149 data = line[i][0]; |
| 1150 ch = line[i][1]; |
| 1151 ch_width = line[i][2]; |
| 1152 if (!ch_width) |
| 1153 continue; |
| 1154 |
| 1155 if (i === x) data = -1; |
| 1156 |
| 1157 if (data !== attr) { |
| 1158 if (attr !== this.defAttr) { |
| 1159 out += '</span>'; |
| 1160 } |
| 1161 if (data !== this.defAttr) { |
| 1162 if (data === -1) { |
| 1163 out += '<span class="reverse-video terminal-cursor'; |
| 1164 if (this.cursorBlink) { |
| 1165 out += ' blinking'; |
| 1166 } |
| 1167 out += '">'; |
| 1168 } else { |
| 1169 var classNames = []; |
| 1170 |
| 1171 bg = data & 0x1ff; |
| 1172 fg = (data >> 9) & 0x1ff; |
| 1173 flags = data >> 18; |
| 1174 |
| 1175 if (flags & Terminal.flags.BOLD) { |
| 1176 if (!Terminal.brokenBold) { |
| 1177 classNames.push('xterm-bold'); |
| 1178 } |
| 1179 // See: XTerm*boldColors |
| 1180 if (fg < 8) fg += 8; |
| 1181 } |
| 1182 |
| 1183 if (flags & Terminal.flags.UNDERLINE) { |
| 1184 classNames.push('xterm-underline'); |
| 1185 } |
| 1186 |
| 1187 if (flags & Terminal.flags.BLINK) { |
| 1188 classNames.push('xterm-blink'); |
| 1189 } |
| 1190 |
| 1191 // If inverse flag is on, then swap the foreground and background va
riables. |
| 1192 if (flags & Terminal.flags.INVERSE) { |
| 1193 /* One-line variable swap in JavaScript: http://stackoverflow.com/
a/16201730 */ |
| 1194 bg = [fg, fg = bg][0]; |
| 1195 // Should inverse just be before the |
| 1196 // above boldColors effect instead? |
| 1197 if ((flags & 1) && fg < 8) fg += 8; |
| 1198 } |
| 1199 |
| 1200 if (flags & Terminal.flags.INVISIBLE) { |
| 1201 classNames.push('xterm-hidden'); |
| 1202 } |
| 1203 |
| 1204 /** |
| 1205 * Weird situation: Invert flag used black foreground and white back
ground results |
| 1206 * in invalid background color, positioned at the 256 index of the 2
56 terminal |
| 1207 * color map. Pin the colors manually in such a case. |
| 1208 * |
| 1209 * Source: https://github.com/sourcelair/xterm.js/issues/57 |
| 1210 */ |
| 1211 if (flags & Terminal.flags.INVERSE) { |
| 1212 if (bg == 257) { |
| 1213 bg = 15; |
| 1214 } |
| 1215 if (fg == 256) { |
| 1216 fg = 0; |
| 1217 } |
| 1218 } |
| 1219 |
| 1220 if (bg < 256) { |
| 1221 classNames.push('xterm-bg-color-' + bg); |
| 1222 } |
| 1223 |
| 1224 if (fg < 256) { |
| 1225 classNames.push('xterm-color-' + fg); |
| 1226 } |
| 1227 |
| 1228 out += '<span'; |
| 1229 if (classNames.length) { |
| 1230 out += ' class="' + classNames.join(' ') + '"'; |
| 1231 } |
| 1232 out += '>'; |
| 1233 } |
| 1234 } |
| 1235 } |
| 1236 |
| 1237 switch (ch) { |
| 1238 case '&': |
| 1239 out += '&'; |
| 1240 break; |
| 1241 case '<': |
| 1242 out += '<'; |
| 1243 break; |
| 1244 case '>': |
| 1245 out += '>'; |
| 1246 break; |
| 1247 default: |
| 1248 if (ch <= ' ') { |
| 1249 out += ' '; |
| 1250 } else { |
| 1251 out += ch; |
| 1252 } |
| 1253 break; |
| 1254 } |
| 1255 |
| 1256 attr = data; |
| 1257 } |
| 1258 |
| 1259 if (attr !== this.defAttr) { |
| 1260 out += '</span>'; |
| 1261 } |
| 1262 |
| 1263 this.children[y].innerHTML = out; |
| 1264 } |
| 1265 |
| 1266 if (parent) { |
| 1267 this.element.appendChild(this.rowContainer); |
| 1268 } |
| 1269 |
| 1270 this.emit('refresh', {element: this.element, start: start, end: end}); |
| 1271 }; |
| 1272 |
| 1273 /** |
| 1274 * Display the cursor element |
| 1275 */ |
| 1276 Terminal.prototype.showCursor = function() { |
| 1277 if (!this.cursorState) { |
| 1278 this.cursorState = 1; |
| 1279 this.refresh(this.y, this.y); |
| 1280 } |
| 1281 }; |
| 1282 |
| 1283 /** |
| 1284 * Scroll the terminal |
| 1285 */ |
| 1286 Terminal.prototype.scroll = function() { |
| 1287 var row; |
| 1288 |
| 1289 if (++this.ybase === this.scrollback) { |
| 1290 this.ybase = this.ybase / 2 | 0; |
| 1291 this.lines = this.lines.slice(-(this.ybase + this.rows) + 1); |
| 1292 } |
| 1293 |
| 1294 this.ydisp = this.ybase; |
| 1295 |
| 1296 // last line |
| 1297 row = this.ybase + this.rows - 1; |
| 1298 |
| 1299 // subtract the bottom scroll region |
| 1300 row -= this.rows - 1 - this.scrollBottom; |
| 1301 |
| 1302 if (row === this.lines.length) { |
| 1303 // potential optimization: |
| 1304 // pushing is faster than splicing |
| 1305 // when they amount to the same |
| 1306 // behavior. |
| 1307 this.lines.push(this.blankLine()); |
| 1308 } else { |
| 1309 // add our new line |
| 1310 this.lines.splice(row, 0, this.blankLine()); |
| 1311 } |
| 1312 |
| 1313 if (this.scrollTop !== 0) { |
| 1314 if (this.ybase !== 0) { |
| 1315 this.ybase--; |
| 1316 this.ydisp = this.ybase; |
| 1317 } |
| 1318 this.lines.splice(this.ybase + this.scrollTop, 1); |
| 1319 } |
| 1320 |
| 1321 // this.maxRange(); |
| 1322 this.updateRange(this.scrollTop); |
| 1323 this.updateRange(this.scrollBottom); |
| 1324 |
| 1325 this.emit('scroll', this.ydisp); |
| 1326 }; |
| 1327 |
| 1328 /** |
| 1329 * Scroll the display of the terminal |
| 1330 * @param {number} disp The number of lines to scroll down (negatives scroll up)
. |
| 1331 * @param {boolean} suppressScrollEvent Don't emit the scroll event as scrollDis
p. This is used |
| 1332 * to avoid unwanted events being handled by the veiwport when the event was tri
ggered from the |
| 1333 * viewport originally. |
| 1334 */ |
| 1335 Terminal.prototype.scrollDisp = function(disp, suppressScrollEvent) { |
| 1336 this.ydisp += disp; |
| 1337 |
| 1338 if (this.ydisp > this.ybase) { |
| 1339 this.ydisp = this.ybase; |
| 1340 } else if (this.ydisp < 0) { |
| 1341 this.ydisp = 0; |
| 1342 } |
| 1343 |
| 1344 if (!suppressScrollEvent) { |
| 1345 this.emit('scroll', this.ydisp); |
| 1346 } |
| 1347 |
| 1348 this.refresh(0, this.rows - 1); |
| 1349 }; |
| 1350 |
| 1351 /** |
| 1352 * Writes text to the terminal. |
| 1353 * @param {string} text The text to write to the terminal. |
| 1354 */ |
| 1355 Terminal.prototype.write = function(data) { |
| 1356 var l = data.length, i = 0, j, cs, ch, code, low, ch_width, row; |
| 1357 |
| 1358 this.refreshStart = this.y; |
| 1359 this.refreshEnd = this.y; |
| 1360 |
| 1361 if (this.ybase !== this.ydisp) { |
| 1362 this.ydisp = this.ybase; |
| 1363 this.emit('scroll', this.ydisp); |
| 1364 this.maxRange(); |
| 1365 } |
| 1366 |
| 1367 // apply leftover surrogate high from last write |
| 1368 if (this.surrogate_high) { |
| 1369 data = this.surrogate_high + data; |
| 1370 this.surrogate_high = ''; |
| 1371 } |
| 1372 |
| 1373 for (; i < l; i++) { |
| 1374 ch = data[i]; |
| 1375 |
| 1376 // FIXME: higher chars than 0xa0 are not allowed in escape sequences |
| 1377 // --> maybe move to default |
| 1378 code = data.charCodeAt(i); |
| 1379 if (0xD800 <= code && code <= 0xDBFF) { |
| 1380 // we got a surrogate high |
| 1381 // get surrogate low (next 2 bytes) |
| 1382 low = data.charCodeAt(i+1); |
| 1383 if (isNaN(low)) { |
| 1384 // end of data stream, save surrogate high |
| 1385 this.surrogate_high = ch; |
| 1386 continue; |
| 1387 } |
| 1388 code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; |
| 1389 ch += data.charAt(i+1); |
| 1390 } |
| 1391 // surrogate low - already handled above |
| 1392 if (0xDC00 <= code && code <= 0xDFFF) |
| 1393 continue; |
| 1394 |
| 1395 switch (this.state) { |
| 1396 case normal: |
| 1397 switch (ch) { |
| 1398 case '\x07': |
| 1399 this.bell(); |
| 1400 break; |
| 1401 |
| 1402 // '\n', '\v', '\f' |
| 1403 case '\n': |
| 1404 case '\x0b': |
| 1405 case '\x0c': |
| 1406 if (this.convertEol) { |
| 1407 this.x = 0; |
| 1408 } |
| 1409 this.y++; |
| 1410 if (this.y > this.scrollBottom) { |
| 1411 this.y--; |
| 1412 this.scroll(); |
| 1413 } |
| 1414 break; |
| 1415 |
| 1416 // '\r' |
| 1417 case '\r': |
| 1418 this.x = 0; |
| 1419 break; |
| 1420 |
| 1421 // '\b' |
| 1422 case '\x08': |
| 1423 if (this.x > 0) { |
| 1424 this.x--; |
| 1425 } |
| 1426 break; |
| 1427 |
| 1428 // '\t' |
| 1429 case '\t': |
| 1430 this.x = this.nextStop(); |
| 1431 break; |
| 1432 |
| 1433 // shift out |
| 1434 case '\x0e': |
| 1435 this.setgLevel(1); |
| 1436 break; |
| 1437 |
| 1438 // shift in |
| 1439 case '\x0f': |
| 1440 this.setgLevel(0); |
| 1441 break; |
| 1442 |
| 1443 // '\e' |
| 1444 case '\x1b': |
| 1445 this.state = escaped; |
| 1446 break; |
| 1447 |
| 1448 default: |
| 1449 // ' ' |
| 1450 // calculate print space |
| 1451 // expensive call, therefore we save width in line buffer |
| 1452 ch_width = wcwidth(code); |
| 1453 |
| 1454 if (ch >= ' ') { |
| 1455 if (this.charset && this.charset[ch]) { |
| 1456 ch = this.charset[ch]; |
| 1457 } |
| 1458 |
| 1459 row = this.y + this.ybase; |
| 1460 |
| 1461 // insert combining char in last cell |
| 1462 // FIXME: needs handling after cursor jumps |
| 1463 if (!ch_width && this.x) { |
| 1464 |
| 1465 // dont overflow left |
| 1466 if (this.lines[row][this.x-1]) { |
| 1467 if (!this.lines[row][this.x-1][2]) { |
| 1468 |
| 1469 // found empty cell after fullwidth, need to go 2 cells back |
| 1470 if (this.lines[row][this.x-2]) |
| 1471 this.lines[row][this.x-2][1] += ch; |
| 1472 |
| 1473 } else { |
| 1474 this.lines[row][this.x-1][1] += ch; |
| 1475 } |
| 1476 this.updateRange(this.y); |
| 1477 } |
| 1478 break; |
| 1479 } |
| 1480 |
| 1481 // goto next line if ch would overflow |
| 1482 // TODO: needs a global min terminal width of 2 |
| 1483 if (this.x+ch_width-1 >= this.cols) { |
| 1484 // autowrap - DECAWM |
| 1485 if (this.wraparoundMode) { |
| 1486 this.x = 0; |
| 1487 this.y++; |
| 1488 if (this.y > this.scrollBottom) { |
| 1489 this.y--; |
| 1490 this.scroll(); |
| 1491 } |
| 1492 } else { |
| 1493 this.x = this.cols-1; |
| 1494 if(ch_width===2) // FIXME: check for xterm behavior |
| 1495 continue; |
| 1496 } |
| 1497 } |
| 1498 row = this.y + this.ybase; |
| 1499 |
| 1500 // insert mode: move characters to right |
| 1501 if (this.insertMode) { |
| 1502 // do this twice for a fullwidth char |
| 1503 for (var moves=0; moves<ch_width; ++moves) { |
| 1504 // remove last cell, if it's width is 0 |
| 1505 // we have to adjust the second last cell as well |
| 1506 var removed = this.lines[this.y + this.ybase].pop(); |
| 1507 if (removed[2]===0 |
| 1508 && this.lines[row][this.cols-2] |
| 1509 && this.lines[row][this.cols-2][2]===2) |
| 1510 this.lines[row][this.cols-2] = [this.curAttr, ' ', 1]; |
| 1511 |
| 1512 // insert empty cell at cursor |
| 1513 this.lines[row].splice(this.x, 0, [this.curAttr, ' ', 1]); |
| 1514 } |
| 1515 } |
| 1516 |
| 1517 this.lines[row][this.x] = [this.curAttr, ch, ch_width]; |
| 1518 this.x++; |
| 1519 this.updateRange(this.y); |
| 1520 |
| 1521 // fullwidth char - set next cell width to zero and advance cursor |
| 1522 if (ch_width===2) { |
| 1523 this.lines[row][this.x] = [this.curAttr, '', 0]; |
| 1524 this.x++; |
| 1525 } |
| 1526 } |
| 1527 break; |
| 1528 } |
| 1529 break; |
| 1530 case escaped: |
| 1531 switch (ch) { |
| 1532 // ESC [ Control Sequence Introducer ( CSI is 0x9b). |
| 1533 case '[': |
| 1534 this.params = []; |
| 1535 this.currentParam = 0; |
| 1536 this.state = csi; |
| 1537 break; |
| 1538 |
| 1539 // ESC ] Operating System Command ( OSC is 0x9d). |
| 1540 case ']': |
| 1541 this.params = []; |
| 1542 this.currentParam = 0; |
| 1543 this.state = osc; |
| 1544 break; |
| 1545 |
| 1546 // ESC P Device Control String ( DCS is 0x90). |
| 1547 case 'P': |
| 1548 this.params = []; |
| 1549 this.currentParam = 0; |
| 1550 this.state = dcs; |
| 1551 break; |
| 1552 |
| 1553 // ESC _ Application Program Command ( APC is 0x9f). |
| 1554 case '_': |
| 1555 this.state = ignore; |
| 1556 break; |
| 1557 |
| 1558 // ESC ^ Privacy Message ( PM is 0x9e). |
| 1559 case '^': |
| 1560 this.state = ignore; |
| 1561 break; |
| 1562 |
| 1563 // ESC c Full Reset (RIS). |
| 1564 case 'c': |
| 1565 this.reset(); |
| 1566 break; |
| 1567 |
| 1568 // ESC E Next Line ( NEL is 0x85). |
| 1569 // ESC D Index ( IND is 0x84). |
| 1570 case 'E': |
| 1571 this.x = 0; |
| 1572 ; |
| 1573 case 'D': |
| 1574 this.index(); |
| 1575 break; |
| 1576 |
| 1577 // ESC M Reverse Index ( RI is 0x8d). |
| 1578 case 'M': |
| 1579 this.reverseIndex(); |
| 1580 break; |
| 1581 |
| 1582 // ESC % Select default/utf-8 character set. |
| 1583 // @ = default, G = utf-8 |
| 1584 case '%': |
| 1585 //this.charset = null; |
| 1586 this.setgLevel(0); |
| 1587 this.setgCharset(0, Terminal.charsets.US); |
| 1588 this.state = normal; |
| 1589 i++; |
| 1590 break; |
| 1591 |
| 1592 // ESC (,),*,+,-,. Designate G0-G2 Character Set. |
| 1593 case '(': // <-- this seems to get all the attention |
| 1594 case ')': |
| 1595 case '*': |
| 1596 case '+': |
| 1597 case '-': |
| 1598 case '.': |
| 1599 switch (ch) { |
| 1600 case '(': |
| 1601 this.gcharset = 0; |
| 1602 break; |
| 1603 case ')': |
| 1604 this.gcharset = 1; |
| 1605 break; |
| 1606 case '*': |
| 1607 this.gcharset = 2; |
| 1608 break; |
| 1609 case '+': |
| 1610 this.gcharset = 3; |
| 1611 break; |
| 1612 case '-': |
| 1613 this.gcharset = 1; |
| 1614 break; |
| 1615 case '.': |
| 1616 this.gcharset = 2; |
| 1617 break; |
| 1618 } |
| 1619 this.state = charset; |
| 1620 break; |
| 1621 |
| 1622 // Designate G3 Character Set (VT300). |
| 1623 // A = ISO Latin-1 Supplemental. |
| 1624 // Not implemented. |
| 1625 case '/': |
| 1626 this.gcharset = 3; |
| 1627 this.state = charset; |
| 1628 i--; |
| 1629 break; |
| 1630 |
| 1631 // ESC N |
| 1632 // Single Shift Select of G2 Character Set |
| 1633 // ( SS2 is 0x8e). This affects next character only. |
| 1634 case 'N': |
| 1635 break; |
| 1636 // ESC O |
| 1637 // Single Shift Select of G3 Character Set |
| 1638 // ( SS3 is 0x8f). This affects next character only. |
| 1639 case 'O': |
| 1640 break; |
| 1641 // ESC n |
| 1642 // Invoke the G2 Character Set as GL (LS2). |
| 1643 case 'n': |
| 1644 this.setgLevel(2); |
| 1645 break; |
| 1646 // ESC o |
| 1647 // Invoke the G3 Character Set as GL (LS3). |
| 1648 case 'o': |
| 1649 this.setgLevel(3); |
| 1650 break; |
| 1651 // ESC | |
| 1652 // Invoke the G3 Character Set as GR (LS3R). |
| 1653 case '|': |
| 1654 this.setgLevel(3); |
| 1655 break; |
| 1656 // ESC } |
| 1657 // Invoke the G2 Character Set as GR (LS2R). |
| 1658 case '}': |
| 1659 this.setgLevel(2); |
| 1660 break; |
| 1661 // ESC ~ |
| 1662 // Invoke the G1 Character Set as GR (LS1R). |
| 1663 case '~': |
| 1664 this.setgLevel(1); |
| 1665 break; |
| 1666 |
| 1667 // ESC 7 Save Cursor (DECSC). |
| 1668 case '7': |
| 1669 this.saveCursor(); |
| 1670 this.state = normal; |
| 1671 break; |
| 1672 |
| 1673 // ESC 8 Restore Cursor (DECRC). |
| 1674 case '8': |
| 1675 this.restoreCursor(); |
| 1676 this.state = normal; |
| 1677 break; |
| 1678 |
| 1679 // ESC # 3 DEC line height/width |
| 1680 case '#': |
| 1681 this.state = normal; |
| 1682 i++; |
| 1683 break; |
| 1684 |
| 1685 // ESC H Tab Set (HTS is 0x88). |
| 1686 case 'H': |
| 1687 this.tabSet(); |
| 1688 break; |
| 1689 |
| 1690 // ESC = Application Keypad (DECKPAM). |
| 1691 case '=': |
| 1692 this.log('Serial port requested application keypad.'); |
| 1693 this.applicationKeypad = true; |
| 1694 this.viewport.syncScrollArea(); |
| 1695 this.state = normal; |
| 1696 break; |
| 1697 |
| 1698 // ESC > Normal Keypad (DECKPNM). |
| 1699 case '>': |
| 1700 this.log('Switching back to normal keypad.'); |
| 1701 this.applicationKeypad = false; |
| 1702 this.viewport.syncScrollArea(); |
| 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 /** |
| 2406 * Writes text to the terminal, followed by a break line character (\n). |
| 2407 * @param {string} text The text to write to the terminal. |
| 2408 */ |
| 2409 Terminal.prototype.writeln = function(data) { |
| 2410 this.write(data + '\r\n'); |
| 2411 }; |
| 2412 |
| 2413 /** |
| 2414 * Attaches a custom keydown handler which is run before keys are processed, giv
ing consumers of |
| 2415 * xterm.js ultimate control as to what keys should be processed by the terminal
and what keys |
| 2416 * should not. |
| 2417 * @param {function} customKeydownHandler The custom KeyboardEvent handler to at
tach. This is a |
| 2418 * function that takes a KeyboardEvent, allowing consumers to stop propogation
and/or prevent |
| 2419 * the default action. The function returns whether the event should be proces
sed by xterm.js. |
| 2420 */ |
| 2421 Terminal.prototype.attachCustomKeydownHandler = function(customKeydownHandler) { |
| 2422 this.customKeydownHandler = customKeydownHandler; |
| 2423 } |
| 2424 |
| 2425 /** |
| 2426 * Handle a keydown event |
| 2427 * Key Resources: |
| 2428 * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent |
| 2429 * @param {KeyboardEvent} ev The keydown event to be handled. |
| 2430 */ |
| 2431 Terminal.prototype.keyDown = function(ev) { |
| 2432 if (this.customKeydownHandler && this.customKeydownHandler(ev) === false) { |
| 2433 return false; |
| 2434 } |
| 2435 |
| 2436 if (!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)) { |
| 2437 return false; |
| 2438 } |
| 2439 |
| 2440 var self = this; |
| 2441 var result = this.evaluateKeyEscapeSequence(ev); |
| 2442 |
| 2443 if (result.scrollDisp) { |
| 2444 this.scrollDisp(result.scrollDisp); |
| 2445 return this.cancel(ev, true); |
| 2446 } |
| 2447 |
| 2448 if (isThirdLevelShift(this, ev)) { |
| 2449 return true; |
| 2450 } |
| 2451 |
| 2452 if (result.cancel) { |
| 2453 // The event is canceled at the end already, is this necessary? |
| 2454 this.cancel(ev, true); |
| 2455 } |
| 2456 |
| 2457 if (!result.key) { |
| 2458 return true; |
| 2459 } |
| 2460 |
| 2461 this.emit('keydown', ev); |
| 2462 this.emit('key', result.key, ev); |
| 2463 this.showCursor(); |
| 2464 this.handler(result.key); |
| 2465 |
| 2466 return this.cancel(ev, true); |
| 2467 }; |
| 2468 |
| 2469 /** |
| 2470 * Returns an object that determines how a KeyboardEvent should be handled. The
key of the |
| 2471 * returned value is the new key code to pass to the PTY. |
| 2472 * |
| 2473 * Reference: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html |
| 2474 * @param {KeyboardEvent} ev The keyboard event to be translated to key escape s
equence. |
| 2475 */ |
| 2476 Terminal.prototype.evaluateKeyEscapeSequence = function(ev) { |
| 2477 var result = { |
| 2478 // Whether to cancel event propogation (NOTE: this may not be needed since t
he event is |
| 2479 // canceled at the end of keyDown |
| 2480 cancel: false, |
| 2481 // The new key even to emit |
| 2482 key: undefined, |
| 2483 // The number of characters to scroll, if this is defined it will cancel the
event |
| 2484 scrollDisp: undefined |
| 2485 }; |
| 2486 var modifiers = ev.shiftKey << 0 | ev.altKey << 1 | ev.ctrlKey << 2 | ev.metaK
ey << 3; |
| 2487 switch (ev.keyCode) { |
| 2488 case 8: |
| 2489 // backspace |
| 2490 if (ev.shiftKey) { |
| 2491 result.key = '\x08'; // ^H |
| 2492 break; |
| 2493 } |
| 2494 result.key = '\x7f'; // ^? |
| 2495 break; |
| 2496 case 9: |
| 2497 // tab |
| 2498 if (ev.shiftKey) { |
| 2499 result.key = '\x1b[Z'; |
| 2500 break; |
| 2501 } |
| 2502 result.key = '\t'; |
| 2503 result.cancel = true; |
| 2504 break; |
| 2505 case 13: |
| 2506 // return/enter |
| 2507 result.key = '\r'; |
| 2508 result.cancel = true; |
| 2509 break; |
| 2510 case 27: |
| 2511 // escape |
| 2512 result.key = '\x1b'; |
| 2513 result.cancel = true; |
| 2514 break; |
| 2515 case 37: |
| 2516 // left-arrow |
| 2517 if (modifiers) { |
| 2518 result.key = '\x1b[1;' + (modifiers + 1) + 'D'; |
| 2519 // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one w
ord backwards |
| 2520 // http://unix.stackexchange.com/a/108106 |
| 2521 if (result.key == '\x1b[1;3D') { |
| 2522 result.key = '\x1b[1;5D'; |
| 2523 } |
| 2524 } else if (this.applicationCursor) { |
| 2525 result.key = '\x1bOD'; |
| 2526 } else { |
| 2527 result.key = '\x1b[D'; |
| 2528 } |
| 2529 break; |
| 2530 case 39: |
| 2531 // right-arrow |
| 2532 if (modifiers) { |
| 2533 result.key = '\x1b[1;' + (modifiers + 1) + 'C'; |
| 2534 // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one
word forward |
| 2535 // http://unix.stackexchange.com/a/108106 |
| 2536 if (result.key == '\x1b[1;3C') { |
| 2537 result.key = '\x1b[1;5C'; |
| 2538 } |
| 2539 } else if (this.applicationCursor) { |
| 2540 result.key = '\x1bOC'; |
| 2541 } else { |
| 2542 result.key = '\x1b[C'; |
| 2543 } |
| 2544 break; |
| 2545 case 38: |
| 2546 // up-arrow |
| 2547 if (modifiers) { |
| 2548 result.key = '\x1b[1;' + (modifiers + 1) + 'A'; |
| 2549 // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow |
| 2550 // http://unix.stackexchange.com/a/108106 |
| 2551 if (result.key == '\x1b[1;3A') { |
| 2552 result.key = '\x1b[1;5A'; |
| 2553 } |
| 2554 } else if (this.applicationCursor) { |
| 2555 result.key = '\x1bOA'; |
| 2556 } else { |
| 2557 result.key = '\x1b[A'; |
| 2558 } |
| 2559 break; |
| 2560 case 40: |
| 2561 // down-arrow |
| 2562 if (modifiers) { |
| 2563 result.key = '\x1b[1;' + (modifiers + 1) + 'B'; |
| 2564 // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow |
| 2565 // http://unix.stackexchange.com/a/108106 |
| 2566 if (result.key == '\x1b[1;3B') { |
| 2567 result.key = '\x1b[1;5B'; |
| 2568 } |
| 2569 } else if (this.applicationCursor) { |
| 2570 result.key = '\x1bOB'; |
| 2571 } else { |
| 2572 result.key = '\x1b[B'; |
| 2573 } |
| 2574 break; |
| 2575 case 45: |
| 2576 // insert |
| 2577 if (!ev.shiftKey && !ev.ctrlKey) { |
| 2578 // <Ctrl> or <Shift> + <Insert> are used to |
| 2579 // copy-paste on some systems. |
| 2580 result.key = '\x1b[2~'; |
| 2581 } |
| 2582 break; |
| 2583 case 46: |
| 2584 // delete |
| 2585 if (modifiers) { |
| 2586 result.key = '\x1b[3;' + (modifiers + 1) + '~'; |
| 2587 } else { |
| 2588 result.key = '\x1b[3~'; |
| 2589 } |
| 2590 break; |
| 2591 case 36: |
| 2592 // home |
| 2593 if (modifiers) |
| 2594 result.key = '\x1b[1;' + (modifiers + 1) + 'H'; |
| 2595 else if (this.applicationCursor) |
| 2596 result.key = '\x1bOH'; |
| 2597 else |
| 2598 result.key = '\x1b[H'; |
| 2599 break; |
| 2600 case 35: |
| 2601 // end |
| 2602 if (modifiers) |
| 2603 result.key = '\x1b[1;' + (modifiers + 1) + 'F'; |
| 2604 else if (this.applicationCursor) |
| 2605 result.key = '\x1bOF'; |
| 2606 else |
| 2607 result.key = '\x1b[F'; |
| 2608 break; |
| 2609 case 33: |
| 2610 // page up |
| 2611 if (ev.shiftKey) { |
| 2612 result.scrollDisp = -(this.rows - 1); |
| 2613 } else { |
| 2614 result.key = '\x1b[5~'; |
| 2615 } |
| 2616 break; |
| 2617 case 34: |
| 2618 // page down |
| 2619 if (ev.shiftKey) { |
| 2620 result.scrollDisp = this.rows - 1; |
| 2621 } else { |
| 2622 result.key = '\x1b[6~'; |
| 2623 } |
| 2624 break; |
| 2625 case 112: |
| 2626 // F1-F12 |
| 2627 if (modifiers) { |
| 2628 result.key = '\x1b[1;' + (modifiers + 1) + 'P'; |
| 2629 } else { |
| 2630 result.key = '\x1bOP'; |
| 2631 } |
| 2632 break; |
| 2633 case 113: |
| 2634 if (modifiers) { |
| 2635 result.key = '\x1b[1;' + (modifiers + 1) + 'Q'; |
| 2636 } else { |
| 2637 result.key = '\x1bOQ'; |
| 2638 } |
| 2639 break; |
| 2640 case 114: |
| 2641 if (modifiers) { |
| 2642 result.key = '\x1b[1;' + (modifiers + 1) + 'R'; |
| 2643 } else { |
| 2644 result.key = '\x1bOR'; |
| 2645 } |
| 2646 break; |
| 2647 case 115: |
| 2648 if (modifiers) { |
| 2649 result.key = '\x1b[1;' + (modifiers + 1) + 'S'; |
| 2650 } else { |
| 2651 result.key = '\x1bOS'; |
| 2652 } |
| 2653 break; |
| 2654 case 116: |
| 2655 if (modifiers) { |
| 2656 result.key = '\x1b[15;' + (modifiers + 1) + '~'; |
| 2657 } else { |
| 2658 result.key = '\x1b[15~'; |
| 2659 } |
| 2660 break; |
| 2661 case 117: |
| 2662 if (modifiers) { |
| 2663 result.key = '\x1b[17;' + (modifiers + 1) + '~'; |
| 2664 } else { |
| 2665 result.key = '\x1b[17~'; |
| 2666 } |
| 2667 break; |
| 2668 case 118: |
| 2669 if (modifiers) { |
| 2670 result.key = '\x1b[18;' + (modifiers + 1) + '~'; |
| 2671 } else { |
| 2672 result.key = '\x1b[18~'; |
| 2673 } |
| 2674 break; |
| 2675 case 119: |
| 2676 if (modifiers) { |
| 2677 result.key = '\x1b[19;' + (modifiers + 1) + '~'; |
| 2678 } else { |
| 2679 result.key = '\x1b[19~'; |
| 2680 } |
| 2681 break; |
| 2682 case 120: |
| 2683 if (modifiers) { |
| 2684 result.key = '\x1b[20;' + (modifiers + 1) + '~'; |
| 2685 } else { |
| 2686 result.key = '\x1b[20~'; |
| 2687 } |
| 2688 break; |
| 2689 case 121: |
| 2690 if (modifiers) { |
| 2691 result.key = '\x1b[21;' + (modifiers + 1) + '~'; |
| 2692 } else { |
| 2693 result.key = '\x1b[21~'; |
| 2694 } |
| 2695 break; |
| 2696 case 122: |
| 2697 if (modifiers) { |
| 2698 result.key = '\x1b[23;' + (modifiers + 1) + '~'; |
| 2699 } else { |
| 2700 result.key = '\x1b[23~'; |
| 2701 } |
| 2702 break; |
| 2703 case 123: |
| 2704 if (modifiers) { |
| 2705 result.key = '\x1b[24;' + (modifiers + 1) + '~'; |
| 2706 } else { |
| 2707 result.key = '\x1b[24~'; |
| 2708 } |
| 2709 break; |
| 2710 default: |
| 2711 // a-z and space |
| 2712 if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { |
| 2713 if (ev.keyCode >= 65 && ev.keyCode <= 90) { |
| 2714 result.key = String.fromCharCode(ev.keyCode - 64); |
| 2715 } else if (ev.keyCode === 32) { |
| 2716 // NUL |
| 2717 result.key = String.fromCharCode(0); |
| 2718 } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { |
| 2719 // escape, file sep, group sep, record sep, unit sep |
| 2720 result.key = String.fromCharCode(ev.keyCode - 51 + 27); |
| 2721 } else if (ev.keyCode === 56) { |
| 2722 // delete |
| 2723 result.key = String.fromCharCode(127); |
| 2724 } else if (ev.keyCode === 219) { |
| 2725 // ^[ - escape |
| 2726 result.key = String.fromCharCode(27); |
| 2727 } else if (ev.keyCode === 221) { |
| 2728 // ^] - group sep |
| 2729 result.key = String.fromCharCode(29); |
| 2730 } |
| 2731 } else if (!this.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) { |
| 2732 // On Mac this is a third level shift. Use <Esc> instead. |
| 2733 if (ev.keyCode >= 65 && ev.keyCode <= 90) { |
| 2734 result.key = '\x1b' + String.fromCharCode(ev.keyCode + 32); |
| 2735 } else if (ev.keyCode === 192) { |
| 2736 result.key = '\x1b`'; |
| 2737 } else if (ev.keyCode >= 48 && ev.keyCode <= 57) { |
| 2738 result.key = '\x1b' + (ev.keyCode - 48); |
| 2739 } |
| 2740 } |
| 2741 break; |
| 2742 } |
| 2743 return result; |
| 2744 }; |
| 2745 |
| 2746 /** |
| 2747 * Set the G level of the terminal |
| 2748 * @param g |
| 2749 */ |
| 2750 Terminal.prototype.setgLevel = function(g) { |
| 2751 this.glevel = g; |
| 2752 this.charset = this.charsets[g]; |
| 2753 }; |
| 2754 |
| 2755 /** |
| 2756 * Set the charset for the given G level of the terminal |
| 2757 * @param g |
| 2758 * @param charset |
| 2759 */ |
| 2760 Terminal.prototype.setgCharset = function(g, charset) { |
| 2761 this.charsets[g] = charset; |
| 2762 if (this.glevel === g) { |
| 2763 this.charset = charset; |
| 2764 } |
| 2765 }; |
| 2766 |
| 2767 /** |
| 2768 * Handle a keypress event. |
| 2769 * Key Resources: |
| 2770 * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent |
| 2771 * @param {KeyboardEvent} ev The keypress event to be handled. |
| 2772 */ |
| 2773 Terminal.prototype.keyPress = function(ev) { |
| 2774 var key; |
| 2775 |
| 2776 this.cancel(ev); |
| 2777 |
| 2778 if (ev.charCode) { |
| 2779 key = ev.charCode; |
| 2780 } else if (ev.which == null) { |
| 2781 key = ev.keyCode; |
| 2782 } else if (ev.which !== 0 && ev.charCode !== 0) { |
| 2783 key = ev.which; |
| 2784 } else { |
| 2785 return false; |
| 2786 } |
| 2787 |
| 2788 if (!key || ( |
| 2789 (ev.altKey || ev.ctrlKey || ev.metaKey) && !isThirdLevelShift(this, ev) |
| 2790 )) { |
| 2791 return false; |
| 2792 } |
| 2793 |
| 2794 key = String.fromCharCode(key); |
| 2795 |
| 2796 this.emit('keypress', key, ev); |
| 2797 this.emit('key', key, ev); |
| 2798 this.showCursor(); |
| 2799 this.handler(key); |
| 2800 |
| 2801 return false; |
| 2802 }; |
| 2803 |
| 2804 /** |
| 2805 * Send data for handling to the terminal |
| 2806 * @param {string} data |
| 2807 */ |
| 2808 Terminal.prototype.send = function(data) { |
| 2809 var self = this; |
| 2810 |
| 2811 if (!this.queue) { |
| 2812 setTimeout(function() { |
| 2813 self.handler(self.queue); |
| 2814 self.queue = ''; |
| 2815 }, 1); |
| 2816 } |
| 2817 |
| 2818 this.queue += data; |
| 2819 }; |
| 2820 |
| 2821 /** |
| 2822 * Ring the bell. |
| 2823 * Note: We could do sweet things with webaudio here |
| 2824 */ |
| 2825 Terminal.prototype.bell = function() { |
| 2826 if (!this.visualBell) return; |
| 2827 var self = this; |
| 2828 this.element.style.borderColor = 'white'; |
| 2829 setTimeout(function() { |
| 2830 self.element.style.borderColor = ''; |
| 2831 }, 10); |
| 2832 if (this.popOnBell) this.focus(); |
| 2833 }; |
| 2834 |
| 2835 /** |
| 2836 * Log the current state to the console. |
| 2837 */ |
| 2838 Terminal.prototype.log = function() { |
| 2839 if (!this.debug) return; |
| 2840 if (!this.context.console || !this.context.console.log) return; |
| 2841 var args = Array.prototype.slice.call(arguments); |
| 2842 this.context.console.log.apply(this.context.console, args); |
| 2843 }; |
| 2844 |
| 2845 /** |
| 2846 * Log the current state as error to the console. |
| 2847 */ |
| 2848 Terminal.prototype.error = function() { |
| 2849 if (!this.debug) return; |
| 2850 if (!this.context.console || !this.context.console.error) return; |
| 2851 var args = Array.prototype.slice.call(arguments); |
| 2852 this.context.console.error.apply(this.context.console, args); |
| 2853 }; |
| 2854 |
| 2855 /** |
| 2856 * Resizes the terminal. |
| 2857 * |
| 2858 * @param {number} x The number of columns to resize to. |
| 2859 * @param {number} y The number of rows to resize to. |
| 2860 */ |
| 2861 Terminal.prototype.resize = function(x, y) { |
| 2862 var line |
| 2863 , el |
| 2864 , i |
| 2865 , j |
| 2866 , ch |
| 2867 , addToY; |
| 2868 |
| 2869 if (x === this.cols && y === this.rows) { |
| 2870 return; |
| 2871 } |
| 2872 |
| 2873 if (x < 1) x = 1; |
| 2874 if (y < 1) y = 1; |
| 2875 |
| 2876 // resize cols |
| 2877 j = this.cols; |
| 2878 if (j < x) { |
| 2879 ch = [this.defAttr, ' ', 1]; // does xterm use the default attr? |
| 2880 i = this.lines.length; |
| 2881 while (i--) { |
| 2882 while (this.lines[i].length < x) { |
| 2883 this.lines[i].push(ch); |
| 2884 } |
| 2885 } |
| 2886 } else { // (j > x) |
| 2887 i = this.lines.length; |
| 2888 while (i--) { |
| 2889 while (this.lines[i].length > x) { |
| 2890 this.lines[i].pop(); |
| 2891 } |
| 2892 } |
| 2893 } |
| 2894 this.setupStops(j); |
| 2895 this.cols = x; |
| 2896 |
| 2897 // resize rows |
| 2898 j = this.rows; |
| 2899 addToY = 0; |
| 2900 if (j < y) { |
| 2901 el = this.element; |
| 2902 while (j++ < y) { |
| 2903 // y is rows, not this.y |
| 2904 if (this.lines.length < y + this.ybase) { |
| 2905 if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY
+ 1) { |
| 2906 // There is room above the buffer and there are no empty elements belo
w the line, |
| 2907 // scroll up |
| 2908 this.ybase--; |
| 2909 addToY++ |
| 2910 if (this.ydisp > 0) { |
| 2911 // Viewport is at the top of the buffer, must increase downwards |
| 2912 this.ydisp--; |
| 2913 } |
| 2914 } else { |
| 2915 // Add a blank line if there is no buffer left at the top to scroll to
, or if there |
| 2916 // are blank lines after the cursor |
| 2917 this.lines.push(this.blankLine()); |
| 2918 } |
| 2919 } |
| 2920 if (this.children.length < y) { |
| 2921 this.insertRow(); |
| 2922 } |
| 2923 } |
| 2924 } else { // (j > y) |
| 2925 while (j-- > y) { |
| 2926 if (this.lines.length > y + this.ybase) { |
| 2927 if (this.lines.length > this.ybase + this.y + 1) { |
| 2928 // The line is a blank line below the cursor, remove it |
| 2929 this.lines.pop(); |
| 2930 } else { |
| 2931 // The line is the cursor, scroll down |
| 2932 this.ybase++; |
| 2933 this.ydisp++; |
| 2934 } |
| 2935 } |
| 2936 if (this.children.length > y) { |
| 2937 el = this.children.shift(); |
| 2938 if (!el) continue; |
| 2939 el.parentNode.removeChild(el); |
| 2940 } |
| 2941 } |
| 2942 } |
| 2943 this.rows = y; |
| 2944 |
| 2945 // Make sure that the cursor stays on screen |
| 2946 if (this.y >= y) { |
| 2947 this.y = y - 1; |
| 2948 } |
| 2949 if (addToY) { |
| 2950 this.y += addToY; |
| 2951 } |
| 2952 |
| 2953 if (this.x >= x) { |
| 2954 this.x = x - 1; |
| 2955 } |
| 2956 |
| 2957 this.scrollTop = 0; |
| 2958 this.scrollBottom = y - 1; |
| 2959 |
| 2960 this.refresh(0, this.rows - 1); |
| 2961 |
| 2962 this.normal = null; |
| 2963 |
| 2964 this.emit('resize', {terminal: this, cols: x, rows: y}); |
| 2965 }; |
| 2966 |
| 2967 /** |
| 2968 * Updates the range of rows to refresh |
| 2969 * @param {number} y The number of rows to refresh next. |
| 2970 */ |
| 2971 Terminal.prototype.updateRange = function(y) { |
| 2972 if (y < this.refreshStart) this.refreshStart = y; |
| 2973 if (y > this.refreshEnd) this.refreshEnd = y; |
| 2974 // if (y > this.refreshEnd) { |
| 2975 // this.refreshEnd = y; |
| 2976 // if (y > this.rows - 1) { |
| 2977 // this.refreshEnd = this.rows - 1; |
| 2978 // } |
| 2979 // } |
| 2980 }; |
| 2981 |
| 2982 /** |
| 2983 * Set the range of refreshing to the maximyum value |
| 2984 */ |
| 2985 Terminal.prototype.maxRange = function() { |
| 2986 this.refreshStart = 0; |
| 2987 this.refreshEnd = this.rows - 1; |
| 2988 }; |
| 2989 |
| 2990 |
| 2991 |
| 2992 /** |
| 2993 * Setup the tab stops. |
| 2994 * @param {number} i |
| 2995 */ |
| 2996 Terminal.prototype.setupStops = function(i) { |
| 2997 if (i != null) { |
| 2998 if (!this.tabs[i]) { |
| 2999 i = this.prevStop(i); |
| 3000 } |
| 3001 } else { |
| 3002 this.tabs = {}; |
| 3003 i = 0; |
| 3004 } |
| 3005 |
| 3006 for (; i < this.cols; i += 8) { |
| 3007 this.tabs[i] = true; |
| 3008 } |
| 3009 }; |
| 3010 |
| 3011 |
| 3012 /** |
| 3013 * Move the cursor to the previous tab stop from the given position (default is
current). |
| 3014 * @param {number} x The position to move the cursor to the previous tab stop. |
| 3015 */ |
| 3016 Terminal.prototype.prevStop = function(x) { |
| 3017 if (x == null) x = this.x; |
| 3018 while (!this.tabs[--x] && x > 0); |
| 3019 return x >= this.cols |
| 3020 ? this.cols - 1 |
| 3021 : x < 0 ? 0 : x; |
| 3022 }; |
| 3023 |
| 3024 |
| 3025 /** |
| 3026 * Move the cursor one tab stop forward from the given position (default is curr
ent). |
| 3027 * @param {number} x The position to move the cursor one tab stop forward. |
| 3028 */ |
| 3029 Terminal.prototype.nextStop = function(x) { |
| 3030 if (x == null) x = this.x; |
| 3031 while (!this.tabs[++x] && x < this.cols); |
| 3032 return x >= this.cols |
| 3033 ? this.cols - 1 |
| 3034 : x < 0 ? 0 : x; |
| 3035 }; |
| 3036 |
| 3037 |
| 3038 /** |
| 3039 * Erase in the identified line everything from "x" to the end of the line (righ
t). |
| 3040 * @param {number} x The column from which to start erasing to the end of the li
ne. |
| 3041 * @param {number} y The line in which to operate. |
| 3042 */ |
| 3043 Terminal.prototype.eraseRight = function(x, y) { |
| 3044 var line = this.lines[this.ybase + y] |
| 3045 , ch = [this.eraseAttr(), ' ', 1]; // xterm |
| 3046 |
| 3047 |
| 3048 for (; x < this.cols; x++) { |
| 3049 line[x] = ch; |
| 3050 } |
| 3051 |
| 3052 this.updateRange(y); |
| 3053 }; |
| 3054 |
| 3055 |
| 3056 |
| 3057 /** |
| 3058 * Erase in the identified line everything from "x" to the start of the line (le
ft). |
| 3059 * @param {number} x The column from which to start erasing to the start of the
line. |
| 3060 * @param {number} y The line in which to operate. |
| 3061 */ |
| 3062 Terminal.prototype.eraseLeft = function(x, y) { |
| 3063 var line = this.lines[this.ybase + y] |
| 3064 , ch = [this.eraseAttr(), ' ', 1]; // xterm |
| 3065 |
| 3066 x++; |
| 3067 while (x--) line[x] = ch; |
| 3068 |
| 3069 this.updateRange(y); |
| 3070 }; |
| 3071 |
| 3072 /** |
| 3073 * Clears the entire buffer, making the prompt line the new first line. |
| 3074 */ |
| 3075 Terminal.prototype.clear = function() { |
| 3076 if (this.ybase === 0 && this.y === 0) { |
| 3077 // Don't clear if it's already clear |
| 3078 return; |
| 3079 } |
| 3080 this.lines = [this.lines[this.ybase + this.y]]; |
| 3081 this.ydisp = 0; |
| 3082 this.ybase = 0; |
| 3083 this.y = 0; |
| 3084 for (var i = 1; i < this.rows; i++) { |
| 3085 this.lines.push(this.blankLine()); |
| 3086 } |
| 3087 this.refresh(0, this.rows - 1); |
| 3088 this.emit('scroll', this.ydisp); |
| 3089 }; |
| 3090 |
| 3091 /** |
| 3092 * Erase all content in the given line |
| 3093 * @param {number} y The line to erase all of its contents. |
| 3094 */ |
| 3095 Terminal.prototype.eraseLine = function(y) { |
| 3096 this.eraseRight(0, y); |
| 3097 }; |
| 3098 |
| 3099 |
| 3100 /** |
| 3101 * Return the data array of a blank line/ |
| 3102 * @param {number} cur First bunch of data for each "blank" character. |
| 3103 */ |
| 3104 Terminal.prototype.blankLine = function(cur) { |
| 3105 var attr = cur |
| 3106 ? this.eraseAttr() |
| 3107 : this.defAttr; |
| 3108 |
| 3109 var ch = [attr, ' ', 1] // width defaults to 1 halfwidth character |
| 3110 , line = [] |
| 3111 , i = 0; |
| 3112 |
| 3113 for (; i < this.cols; i++) { |
| 3114 line[i] = ch; |
| 3115 } |
| 3116 |
| 3117 return line; |
| 3118 }; |
| 3119 |
| 3120 |
| 3121 /** |
| 3122 * If cur return the back color xterm feature attribute. Else return defAttr. |
| 3123 * @param {object} cur |
| 3124 */ |
| 3125 Terminal.prototype.ch = function(cur) { |
| 3126 return cur |
| 3127 ? [this.eraseAttr(), ' ', 1] |
| 3128 : [this.defAttr, ' ', 1]; |
| 3129 }; |
| 3130 |
| 3131 |
| 3132 /** |
| 3133 * Evaluate if the current erminal is the given argument. |
| 3134 * @param {object} term The terminal to evaluate |
| 3135 */ |
| 3136 Terminal.prototype.is = function(term) { |
| 3137 var name = this.termName; |
| 3138 return (name + '').indexOf(term) === 0; |
| 3139 }; |
| 3140 |
| 3141 |
| 3142 /** |
| 3143 * Emit the 'data' event and populate the given data. |
| 3144 * @param {string} data The data to populate in the event. |
| 3145 */ |
| 3146 Terminal.prototype.handler = function(data) { |
| 3147 this.emit('data', data); |
| 3148 }; |
| 3149 |
| 3150 |
| 3151 /** |
| 3152 * Emit the 'title' event and populate the given title. |
| 3153 * @param {string} title The title to populate in the event. |
| 3154 */ |
| 3155 Terminal.prototype.handleTitle = function(title) { |
| 3156 this.emit('title', title); |
| 3157 }; |
| 3158 |
| 3159 |
| 3160 /** |
| 3161 * ESC |
| 3162 */ |
| 3163 |
| 3164 /** |
| 3165 * ESC D Index (IND is 0x84). |
| 3166 */ |
| 3167 Terminal.prototype.index = function() { |
| 3168 this.y++; |
| 3169 if (this.y > this.scrollBottom) { |
| 3170 this.y--; |
| 3171 this.scroll(); |
| 3172 } |
| 3173 this.state = normal; |
| 3174 }; |
| 3175 |
| 3176 |
| 3177 /** |
| 3178 * ESC M Reverse Index (RI is 0x8d). |
| 3179 */ |
| 3180 Terminal.prototype.reverseIndex = function() { |
| 3181 var j; |
| 3182 this.y--; |
| 3183 if (this.y < this.scrollTop) { |
| 3184 this.y++; |
| 3185 // possibly move the code below to term.reverseScroll(); |
| 3186 // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' |
| 3187 // blankLine(true) is xterm/linux behavior |
| 3188 this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); |
| 3189 j = this.rows - 1 - this.scrollBottom; |
| 3190 this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); |
| 3191 // this.maxRange(); |
| 3192 this.updateRange(this.scrollTop); |
| 3193 this.updateRange(this.scrollBottom); |
| 3194 } |
| 3195 this.state = normal; |
| 3196 }; |
| 3197 |
| 3198 |
| 3199 /** |
| 3200 * ESC c Full Reset (RIS). |
| 3201 */ |
| 3202 Terminal.prototype.reset = function() { |
| 3203 this.options.rows = this.rows; |
| 3204 this.options.cols = this.cols; |
| 3205 var customKeydownHandler = this.customKeydownHandler; |
| 3206 Terminal.call(this, this.options); |
| 3207 this.customKeydownHandler = customKeydownHandler; |
| 3208 this.refresh(0, this.rows - 1); |
| 3209 this.viewport.syncScrollArea(); |
| 3210 }; |
| 3211 |
| 3212 |
| 3213 /** |
| 3214 * ESC H Tab Set (HTS is 0x88). |
| 3215 */ |
| 3216 Terminal.prototype.tabSet = function() { |
| 3217 this.tabs[this.x] = true; |
| 3218 this.state = normal; |
| 3219 }; |
| 3220 |
| 3221 |
| 3222 /** |
| 3223 * CSI |
| 3224 */ |
| 3225 |
| 3226 /** |
| 3227 * CSI Ps A |
| 3228 * Cursor Up Ps Times (default = 1) (CUU). |
| 3229 */ |
| 3230 Terminal.prototype.cursorUp = function(params) { |
| 3231 var param = params[0]; |
| 3232 if (param < 1) param = 1; |
| 3233 this.y -= param; |
| 3234 if (this.y < 0) this.y = 0; |
| 3235 }; |
| 3236 |
| 3237 |
| 3238 /** |
| 3239 * CSI Ps B |
| 3240 * Cursor Down Ps Times (default = 1) (CUD). |
| 3241 */ |
| 3242 Terminal.prototype.cursorDown = function(params) { |
| 3243 var param = params[0]; |
| 3244 if (param < 1) param = 1; |
| 3245 this.y += param; |
| 3246 if (this.y >= this.rows) { |
| 3247 this.y = this.rows - 1; |
| 3248 } |
| 3249 }; |
| 3250 |
| 3251 |
| 3252 /** |
| 3253 * CSI Ps C |
| 3254 * Cursor Forward Ps Times (default = 1) (CUF). |
| 3255 */ |
| 3256 Terminal.prototype.cursorForward = function(params) { |
| 3257 var param = params[0]; |
| 3258 if (param < 1) param = 1; |
| 3259 this.x += param; |
| 3260 if (this.x >= this.cols) { |
| 3261 this.x = this.cols - 1; |
| 3262 } |
| 3263 }; |
| 3264 |
| 3265 |
| 3266 /** |
| 3267 * CSI Ps D |
| 3268 * Cursor Backward Ps Times (default = 1) (CUB). |
| 3269 */ |
| 3270 Terminal.prototype.cursorBackward = function(params) { |
| 3271 var param = params[0]; |
| 3272 if (param < 1) param = 1; |
| 3273 this.x -= param; |
| 3274 if (this.x < 0) this.x = 0; |
| 3275 }; |
| 3276 |
| 3277 |
| 3278 /** |
| 3279 * CSI Ps ; Ps H |
| 3280 * Cursor Position [row;column] (default = [1,1]) (CUP). |
| 3281 */ |
| 3282 Terminal.prototype.cursorPos = function(params) { |
| 3283 var row, col; |
| 3284 |
| 3285 row = params[0] - 1; |
| 3286 |
| 3287 if (params.length >= 2) { |
| 3288 col = params[1] - 1; |
| 3289 } else { |
| 3290 col = 0; |
| 3291 } |
| 3292 |
| 3293 if (row < 0) { |
| 3294 row = 0; |
| 3295 } else if (row >= this.rows) { |
| 3296 row = this.rows - 1; |
| 3297 } |
| 3298 |
| 3299 if (col < 0) { |
| 3300 col = 0; |
| 3301 } else if (col >= this.cols) { |
| 3302 col = this.cols - 1; |
| 3303 } |
| 3304 |
| 3305 this.x = col; |
| 3306 this.y = row; |
| 3307 }; |
| 3308 |
| 3309 |
| 3310 /** |
| 3311 * CSI Ps J Erase in Display (ED). |
| 3312 * Ps = 0 -> Erase Below (default). |
| 3313 * Ps = 1 -> Erase Above. |
| 3314 * Ps = 2 -> Erase All. |
| 3315 * Ps = 3 -> Erase Saved Lines (xterm). |
| 3316 * CSI ? Ps J |
| 3317 * Erase in Display (DECSED). |
| 3318 * Ps = 0 -> Selective Erase Below (default). |
| 3319 * Ps = 1 -> Selective Erase Above. |
| 3320 * Ps = 2 -> Selective Erase All. |
| 3321 */ |
| 3322 Terminal.prototype.eraseInDisplay = function(params) { |
| 3323 var j; |
| 3324 switch (params[0]) { |
| 3325 case 0: |
| 3326 this.eraseRight(this.x, this.y); |
| 3327 j = this.y + 1; |
| 3328 for (; j < this.rows; j++) { |
| 3329 this.eraseLine(j); |
| 3330 } |
| 3331 break; |
| 3332 case 1: |
| 3333 this.eraseLeft(this.x, this.y); |
| 3334 j = this.y; |
| 3335 while (j--) { |
| 3336 this.eraseLine(j); |
| 3337 } |
| 3338 break; |
| 3339 case 2: |
| 3340 j = this.rows; |
| 3341 while (j--) this.eraseLine(j); |
| 3342 break; |
| 3343 case 3: |
| 3344 ; // no saved lines |
| 3345 break; |
| 3346 } |
| 3347 }; |
| 3348 |
| 3349 |
| 3350 /** |
| 3351 * CSI Ps K Erase in Line (EL). |
| 3352 * Ps = 0 -> Erase to Right (default). |
| 3353 * Ps = 1 -> Erase to Left. |
| 3354 * Ps = 2 -> Erase All. |
| 3355 * CSI ? Ps K |
| 3356 * Erase in Line (DECSEL). |
| 3357 * Ps = 0 -> Selective Erase to Right (default). |
| 3358 * Ps = 1 -> Selective Erase to Left. |
| 3359 * Ps = 2 -> Selective Erase All. |
| 3360 */ |
| 3361 Terminal.prototype.eraseInLine = function(params) { |
| 3362 switch (params[0]) { |
| 3363 case 0: |
| 3364 this.eraseRight(this.x, this.y); |
| 3365 break; |
| 3366 case 1: |
| 3367 this.eraseLeft(this.x, this.y); |
| 3368 break; |
| 3369 case 2: |
| 3370 this.eraseLine(this.y); |
| 3371 break; |
| 3372 } |
| 3373 }; |
| 3374 |
| 3375 |
| 3376 /** |
| 3377 * CSI Pm m Character Attributes (SGR). |
| 3378 * Ps = 0 -> Normal (default). |
| 3379 * Ps = 1 -> Bold. |
| 3380 * Ps = 4 -> Underlined. |
| 3381 * Ps = 5 -> Blink (appears as Bold). |
| 3382 * Ps = 7 -> Inverse. |
| 3383 * Ps = 8 -> Invisible, i.e., hidden (VT300). |
| 3384 * Ps = 2 2 -> Normal (neither bold nor faint). |
| 3385 * Ps = 2 4 -> Not underlined. |
| 3386 * Ps = 2 5 -> Steady (not blinking). |
| 3387 * Ps = 2 7 -> Positive (not inverse). |
| 3388 * Ps = 2 8 -> Visible, i.e., not hidden (VT300). |
| 3389 * Ps = 3 0 -> Set foreground color to Black. |
| 3390 * Ps = 3 1 -> Set foreground color to Red. |
| 3391 * Ps = 3 2 -> Set foreground color to Green. |
| 3392 * Ps = 3 3 -> Set foreground color to Yellow. |
| 3393 * Ps = 3 4 -> Set foreground color to Blue. |
| 3394 * Ps = 3 5 -> Set foreground color to Magenta. |
| 3395 * Ps = 3 6 -> Set foreground color to Cyan. |
| 3396 * Ps = 3 7 -> Set foreground color to White. |
| 3397 * Ps = 3 9 -> Set foreground color to default (original). |
| 3398 * Ps = 4 0 -> Set background color to Black. |
| 3399 * Ps = 4 1 -> Set background color to Red. |
| 3400 * Ps = 4 2 -> Set background color to Green. |
| 3401 * Ps = 4 3 -> Set background color to Yellow. |
| 3402 * Ps = 4 4 -> Set background color to Blue. |
| 3403 * Ps = 4 5 -> Set background color to Magenta. |
| 3404 * Ps = 4 6 -> Set background color to Cyan. |
| 3405 * Ps = 4 7 -> Set background color to White. |
| 3406 * Ps = 4 9 -> Set background color to default (original). |
| 3407 * |
| 3408 * If 16-color support is compiled, the following apply. Assume |
| 3409 * that xterm's resources are set so that the ISO color codes are |
| 3410 * the first 8 of a set of 16. Then the aixterm colors are the |
| 3411 * bright versions of the ISO colors: |
| 3412 * Ps = 9 0 -> Set foreground color to Black. |
| 3413 * Ps = 9 1 -> Set foreground color to Red. |
| 3414 * Ps = 9 2 -> Set foreground color to Green. |
| 3415 * Ps = 9 3 -> Set foreground color to Yellow. |
| 3416 * Ps = 9 4 -> Set foreground color to Blue. |
| 3417 * Ps = 9 5 -> Set foreground color to Magenta. |
| 3418 * Ps = 9 6 -> Set foreground color to Cyan. |
| 3419 * Ps = 9 7 -> Set foreground color to White. |
| 3420 * Ps = 1 0 0 -> Set background color to Black. |
| 3421 * Ps = 1 0 1 -> Set background color to Red. |
| 3422 * Ps = 1 0 2 -> Set background color to Green. |
| 3423 * Ps = 1 0 3 -> Set background color to Yellow. |
| 3424 * Ps = 1 0 4 -> Set background color to Blue. |
| 3425 * Ps = 1 0 5 -> Set background color to Magenta. |
| 3426 * Ps = 1 0 6 -> Set background color to Cyan. |
| 3427 * Ps = 1 0 7 -> Set background color to White. |
| 3428 * |
| 3429 * If xterm is compiled with the 16-color support disabled, it |
| 3430 * supports the following, from rxvt: |
| 3431 * Ps = 1 0 0 -> Set foreground and background color to |
| 3432 * default. |
| 3433 * |
| 3434 * If 88- or 256-color support is compiled, the following apply. |
| 3435 * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second |
| 3436 * Ps. |
| 3437 * Ps = 4 8 ; 5 ; Ps -> Set background color to the second |
| 3438 * Ps. |
| 3439 */ |
| 3440 Terminal.prototype.charAttributes = function(params) { |
| 3441 // Optimize a single SGR0. |
| 3442 if (params.length === 1 && params[0] === 0) { |
| 3443 this.curAttr = this.defAttr; |
| 3444 return; |
| 3445 } |
| 3446 |
| 3447 var l = params.length |
| 3448 , i = 0 |
| 3449 , flags = this.curAttr >> 18 |
| 3450 , fg = (this.curAttr >> 9) & 0x1ff |
| 3451 , bg = this.curAttr & 0x1ff |
| 3452 , p; |
| 3453 |
| 3454 for (; i < l; i++) { |
| 3455 p = params[i]; |
| 3456 if (p >= 30 && p <= 37) { |
| 3457 // fg color 8 |
| 3458 fg = p - 30; |
| 3459 } else if (p >= 40 && p <= 47) { |
| 3460 // bg color 8 |
| 3461 bg = p - 40; |
| 3462 } else if (p >= 90 && p <= 97) { |
| 3463 // fg color 16 |
| 3464 p += 8; |
| 3465 fg = p - 90; |
| 3466 } else if (p >= 100 && p <= 107) { |
| 3467 // bg color 16 |
| 3468 p += 8; |
| 3469 bg = p - 100; |
| 3470 } else if (p === 0) { |
| 3471 // default |
| 3472 flags = this.defAttr >> 18; |
| 3473 fg = (this.defAttr >> 9) & 0x1ff; |
| 3474 bg = this.defAttr & 0x1ff; |
| 3475 // flags = 0; |
| 3476 // fg = 0x1ff; |
| 3477 // bg = 0x1ff; |
| 3478 } else if (p === 1) { |
| 3479 // bold text |
| 3480 flags |= 1; |
| 3481 } else if (p === 4) { |
| 3482 // underlined text |
| 3483 flags |= 2; |
| 3484 } else if (p === 5) { |
| 3485 // blink |
| 3486 flags |= 4; |
| 3487 } else if (p === 7) { |
| 3488 // inverse and positive |
| 3489 // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' |
| 3490 flags |= 8; |
| 3491 } else if (p === 8) { |
| 3492 // invisible |
| 3493 flags |= 16; |
| 3494 } else if (p === 22) { |
| 3495 // not bold |
| 3496 flags &= ~1; |
| 3497 } else if (p === 24) { |
| 3498 // not underlined |
| 3499 flags &= ~2; |
| 3500 } else if (p === 25) { |
| 3501 // not blink |
| 3502 flags &= ~4; |
| 3503 } else if (p === 27) { |
| 3504 // not inverse |
| 3505 flags &= ~8; |
| 3506 } else if (p === 28) { |
| 3507 // not invisible |
| 3508 flags &= ~16; |
| 3509 } else if (p === 39) { |
| 3510 // reset fg |
| 3511 fg = (this.defAttr >> 9) & 0x1ff; |
| 3512 } else if (p === 49) { |
| 3513 // reset bg |
| 3514 bg = this.defAttr & 0x1ff; |
| 3515 } else if (p === 38) { |
| 3516 // fg color 256 |
| 3517 if (params[i + 1] === 2) { |
| 3518 i += 2; |
| 3519 fg = matchColor( |
| 3520 params[i] & 0xff, |
| 3521 params[i + 1] & 0xff, |
| 3522 params[i + 2] & 0xff); |
| 3523 if (fg === -1) fg = 0x1ff; |
| 3524 i += 2; |
| 3525 } else if (params[i + 1] === 5) { |
| 3526 i += 2; |
| 3527 p = params[i] & 0xff; |
| 3528 fg = p; |
| 3529 } |
| 3530 } else if (p === 48) { |
| 3531 // bg color 256 |
| 3532 if (params[i + 1] === 2) { |
| 3533 i += 2; |
| 3534 bg = matchColor( |
| 3535 params[i] & 0xff, |
| 3536 params[i + 1] & 0xff, |
| 3537 params[i + 2] & 0xff); |
| 3538 if (bg === -1) bg = 0x1ff; |
| 3539 i += 2; |
| 3540 } else if (params[i + 1] === 5) { |
| 3541 i += 2; |
| 3542 p = params[i] & 0xff; |
| 3543 bg = p; |
| 3544 } |
| 3545 } else if (p === 100) { |
| 3546 // reset fg/bg |
| 3547 fg = (this.defAttr >> 9) & 0x1ff; |
| 3548 bg = this.defAttr & 0x1ff; |
| 3549 } else { |
| 3550 this.error('Unknown SGR attribute: %d.', p); |
| 3551 } |
| 3552 } |
| 3553 |
| 3554 this.curAttr = (flags << 18) | (fg << 9) | bg; |
| 3555 }; |
| 3556 |
| 3557 |
| 3558 /** |
| 3559 * CSI Ps n Device Status Report (DSR). |
| 3560 * Ps = 5 -> Status Report. Result (``OK'') is |
| 3561 * CSI 0 n |
| 3562 * Ps = 6 -> Report Cursor Position (CPR) [row;column]. |
| 3563 * Result is |
| 3564 * CSI r ; c R |
| 3565 * CSI ? Ps n |
| 3566 * Device Status Report (DSR, DEC-specific). |
| 3567 * Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI |
| 3568 * ? r ; c R (assumes page is zero). |
| 3569 * Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready). |
| 3570 * or CSI ? 1 1 n (not ready). |
| 3571 * Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked) |
| 3572 * or CSI ? 2 1 n (locked). |
| 3573 * Ps = 2 6 -> Report Keyboard status as |
| 3574 * CSI ? 2 7 ; 1 ; 0 ; 0 n (North American). |
| 3575 * The last two parameters apply to VT400 & up, and denote key- |
| 3576 * board ready and LK01 respectively. |
| 3577 * Ps = 5 3 -> Report Locator status as |
| 3578 * CSI ? 5 3 n Locator available, if compiled-in, or |
| 3579 * CSI ? 5 0 n No Locator, if not. |
| 3580 */ |
| 3581 Terminal.prototype.deviceStatus = function(params) { |
| 3582 if (!this.prefix) { |
| 3583 switch (params[0]) { |
| 3584 case 5: |
| 3585 // status report |
| 3586 this.send('\x1b[0n'); |
| 3587 break; |
| 3588 case 6: |
| 3589 // cursor position |
| 3590 this.send('\x1b[' |
| 3591 + (this.y + 1) |
| 3592 + ';' |
| 3593 + (this.x + 1) |
| 3594 + 'R'); |
| 3595 break; |
| 3596 } |
| 3597 } else if (this.prefix === '?') { |
| 3598 // modern xterm doesnt seem to |
| 3599 // respond to any of these except ?6, 6, and 5 |
| 3600 switch (params[0]) { |
| 3601 case 6: |
| 3602 // cursor position |
| 3603 this.send('\x1b[?' |
| 3604 + (this.y + 1) |
| 3605 + ';' |
| 3606 + (this.x + 1) |
| 3607 + 'R'); |
| 3608 break; |
| 3609 case 15: |
| 3610 // no printer |
| 3611 // this.send('\x1b[?11n'); |
| 3612 break; |
| 3613 case 25: |
| 3614 // dont support user defined keys |
| 3615 // this.send('\x1b[?21n'); |
| 3616 break; |
| 3617 case 26: |
| 3618 // north american keyboard |
| 3619 // this.send('\x1b[?27;1;0;0n'); |
| 3620 break; |
| 3621 case 53: |
| 3622 // no dec locator/mouse |
| 3623 // this.send('\x1b[?50n'); |
| 3624 break; |
| 3625 } |
| 3626 } |
| 3627 }; |
| 3628 |
| 3629 |
| 3630 /** |
| 3631 * Additions |
| 3632 */ |
| 3633 |
| 3634 /** |
| 3635 * CSI Ps @ |
| 3636 * Insert Ps (Blank) Character(s) (default = 1) (ICH). |
| 3637 */ |
| 3638 Terminal.prototype.insertChars = function(params) { |
| 3639 var param, row, j, ch; |
| 3640 |
| 3641 param = params[0]; |
| 3642 if (param < 1) param = 1; |
| 3643 |
| 3644 row = this.y + this.ybase; |
| 3645 j = this.x; |
| 3646 ch = [this.eraseAttr(), ' ', 1]; // xterm |
| 3647 |
| 3648 while (param-- && j < this.cols) { |
| 3649 this.lines[row].splice(j++, 0, ch); |
| 3650 this.lines[row].pop(); |
| 3651 } |
| 3652 }; |
| 3653 |
| 3654 /** |
| 3655 * CSI Ps E |
| 3656 * Cursor Next Line Ps Times (default = 1) (CNL). |
| 3657 * same as CSI Ps B ? |
| 3658 */ |
| 3659 Terminal.prototype.cursorNextLine = function(params) { |
| 3660 var param = params[0]; |
| 3661 if (param < 1) param = 1; |
| 3662 this.y += param; |
| 3663 if (this.y >= this.rows) { |
| 3664 this.y = this.rows - 1; |
| 3665 } |
| 3666 this.x = 0; |
| 3667 }; |
| 3668 |
| 3669 |
| 3670 /** |
| 3671 * CSI Ps F |
| 3672 * Cursor Preceding Line Ps Times (default = 1) (CNL). |
| 3673 * reuse CSI Ps A ? |
| 3674 */ |
| 3675 Terminal.prototype.cursorPrecedingLine = function(params) { |
| 3676 var param = params[0]; |
| 3677 if (param < 1) param = 1; |
| 3678 this.y -= param; |
| 3679 if (this.y < 0) this.y = 0; |
| 3680 this.x = 0; |
| 3681 }; |
| 3682 |
| 3683 |
| 3684 /** |
| 3685 * CSI Ps G |
| 3686 * Cursor Character Absolute [column] (default = [row,1]) (CHA). |
| 3687 */ |
| 3688 Terminal.prototype.cursorCharAbsolute = function(params) { |
| 3689 var param = params[0]; |
| 3690 if (param < 1) param = 1; |
| 3691 this.x = param - 1; |
| 3692 }; |
| 3693 |
| 3694 |
| 3695 /** |
| 3696 * CSI Ps L |
| 3697 * Insert Ps Line(s) (default = 1) (IL). |
| 3698 */ |
| 3699 Terminal.prototype.insertLines = function(params) { |
| 3700 var param, row, j; |
| 3701 |
| 3702 param = params[0]; |
| 3703 if (param < 1) param = 1; |
| 3704 row = this.y + this.ybase; |
| 3705 |
| 3706 j = this.rows - 1 - this.scrollBottom; |
| 3707 j = this.rows - 1 + this.ybase - j + 1; |
| 3708 |
| 3709 while (param--) { |
| 3710 // test: echo -e '\e[44m\e[1L\e[0m' |
| 3711 // blankLine(true) - xterm/linux behavior |
| 3712 this.lines.splice(row, 0, this.blankLine(true)); |
| 3713 this.lines.splice(j, 1); |
| 3714 } |
| 3715 |
| 3716 // this.maxRange(); |
| 3717 this.updateRange(this.y); |
| 3718 this.updateRange(this.scrollBottom); |
| 3719 }; |
| 3720 |
| 3721 |
| 3722 /** |
| 3723 * CSI Ps M |
| 3724 * Delete Ps Line(s) (default = 1) (DL). |
| 3725 */ |
| 3726 Terminal.prototype.deleteLines = function(params) { |
| 3727 var param, row, j; |
| 3728 |
| 3729 param = params[0]; |
| 3730 if (param < 1) param = 1; |
| 3731 row = this.y + this.ybase; |
| 3732 |
| 3733 j = this.rows - 1 - this.scrollBottom; |
| 3734 j = this.rows - 1 + this.ybase - j; |
| 3735 |
| 3736 while (param--) { |
| 3737 // test: echo -e '\e[44m\e[1M\e[0m' |
| 3738 // blankLine(true) - xterm/linux behavior |
| 3739 this.lines.splice(j + 1, 0, this.blankLine(true)); |
| 3740 this.lines.splice(row, 1); |
| 3741 } |
| 3742 |
| 3743 // this.maxRange(); |
| 3744 this.updateRange(this.y); |
| 3745 this.updateRange(this.scrollBottom); |
| 3746 }; |
| 3747 |
| 3748 |
| 3749 /** |
| 3750 * CSI Ps P |
| 3751 * Delete Ps Character(s) (default = 1) (DCH). |
| 3752 */ |
| 3753 Terminal.prototype.deleteChars = function(params) { |
| 3754 var param, row, ch; |
| 3755 |
| 3756 param = params[0]; |
| 3757 if (param < 1) param = 1; |
| 3758 |
| 3759 row = this.y + this.ybase; |
| 3760 ch = [this.eraseAttr(), ' ', 1]; // xterm |
| 3761 |
| 3762 while (param--) { |
| 3763 this.lines[row].splice(this.x, 1); |
| 3764 this.lines[row].push(ch); |
| 3765 } |
| 3766 }; |
| 3767 |
| 3768 /** |
| 3769 * CSI Ps X |
| 3770 * Erase Ps Character(s) (default = 1) (ECH). |
| 3771 */ |
| 3772 Terminal.prototype.eraseChars = function(params) { |
| 3773 var param, row, j, ch; |
| 3774 |
| 3775 param = params[0]; |
| 3776 if (param < 1) param = 1; |
| 3777 |
| 3778 row = this.y + this.ybase; |
| 3779 j = this.x; |
| 3780 ch = [this.eraseAttr(), ' ', 1]; // xterm |
| 3781 |
| 3782 while (param-- && j < this.cols) { |
| 3783 this.lines[row][j++] = ch; |
| 3784 } |
| 3785 }; |
| 3786 |
| 3787 /** |
| 3788 * CSI Pm ` Character Position Absolute |
| 3789 * [column] (default = [row,1]) (HPA). |
| 3790 */ |
| 3791 Terminal.prototype.charPosAbsolute = function(params) { |
| 3792 var param = params[0]; |
| 3793 if (param < 1) param = 1; |
| 3794 this.x = param - 1; |
| 3795 if (this.x >= this.cols) { |
| 3796 this.x = this.cols - 1; |
| 3797 } |
| 3798 }; |
| 3799 |
| 3800 |
| 3801 /** |
| 3802 * 141 61 a * HPR - |
| 3803 * Horizontal Position Relative |
| 3804 * reuse CSI Ps C ? |
| 3805 */ |
| 3806 Terminal.prototype.HPositionRelative = function(params) { |
| 3807 var param = params[0]; |
| 3808 if (param < 1) param = 1; |
| 3809 this.x += param; |
| 3810 if (this.x >= this.cols) { |
| 3811 this.x = this.cols - 1; |
| 3812 } |
| 3813 }; |
| 3814 |
| 3815 |
| 3816 /** |
| 3817 * CSI Ps c Send Device Attributes (Primary DA). |
| 3818 * Ps = 0 or omitted -> request attributes from terminal. The |
| 3819 * response depends on the decTerminalID resource setting. |
| 3820 * -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'') |
| 3821 * -> CSI ? 1 ; 0 c (``VT101 with No Options'') |
| 3822 * -> CSI ? 6 c (``VT102'') |
| 3823 * -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'') |
| 3824 * The VT100-style response parameters do not mean anything by |
| 3825 * themselves. VT220 parameters do, telling the host what fea- |
| 3826 * tures the terminal supports: |
| 3827 * Ps = 1 -> 132-columns. |
| 3828 * Ps = 2 -> Printer. |
| 3829 * Ps = 6 -> Selective erase. |
| 3830 * Ps = 8 -> User-defined keys. |
| 3831 * Ps = 9 -> National replacement character sets. |
| 3832 * Ps = 1 5 -> Technical characters. |
| 3833 * Ps = 2 2 -> ANSI color, e.g., VT525. |
| 3834 * Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode). |
| 3835 * CSI > Ps c |
| 3836 * Send Device Attributes (Secondary DA). |
| 3837 * Ps = 0 or omitted -> request the terminal's identification |
| 3838 * code. The response depends on the decTerminalID resource set- |
| 3839 * ting. It should apply only to VT220 and up, but xterm extends |
| 3840 * this to VT100. |
| 3841 * -> CSI > Pp ; Pv ; Pc c |
| 3842 * where Pp denotes the terminal type |
| 3843 * Pp = 0 -> ``VT100''. |
| 3844 * Pp = 1 -> ``VT220''. |
| 3845 * and Pv is the firmware version (for xterm, this was originally |
| 3846 * the XFree86 patch number, starting with 95). In a DEC termi- |
| 3847 * nal, Pc indicates the ROM cartridge registration number and is |
| 3848 * always zero. |
| 3849 * More information: |
| 3850 * xterm/charproc.c - line 2012, for more information. |
| 3851 * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) |
| 3852 */ |
| 3853 Terminal.prototype.sendDeviceAttributes = function(params) { |
| 3854 if (params[0] > 0) return; |
| 3855 |
| 3856 if (!this.prefix) { |
| 3857 if (this.is('xterm') |
| 3858 || this.is('rxvt-unicode') |
| 3859 || this.is('screen')) { |
| 3860 this.send('\x1b[?1;2c'); |
| 3861 } else if (this.is('linux')) { |
| 3862 this.send('\x1b[?6c'); |
| 3863 } |
| 3864 } else if (this.prefix === '>') { |
| 3865 // xterm and urxvt |
| 3866 // seem to spit this |
| 3867 // out around ~370 times (?). |
| 3868 if (this.is('xterm')) { |
| 3869 this.send('\x1b[>0;276;0c'); |
| 3870 } else if (this.is('rxvt-unicode')) { |
| 3871 this.send('\x1b[>85;95;0c'); |
| 3872 } else if (this.is('linux')) { |
| 3873 // not supported by linux console. |
| 3874 // linux console echoes parameters. |
| 3875 this.send(params[0] + 'c'); |
| 3876 } else if (this.is('screen')) { |
| 3877 this.send('\x1b[>83;40003;0c'); |
| 3878 } |
| 3879 } |
| 3880 }; |
| 3881 |
| 3882 |
| 3883 /** |
| 3884 * CSI Pm d |
| 3885 * Line Position Absolute [row] (default = [1,column]) (VPA). |
| 3886 */ |
| 3887 Terminal.prototype.linePosAbsolute = function(params) { |
| 3888 var param = params[0]; |
| 3889 if (param < 1) param = 1; |
| 3890 this.y = param - 1; |
| 3891 if (this.y >= this.rows) { |
| 3892 this.y = this.rows - 1; |
| 3893 } |
| 3894 }; |
| 3895 |
| 3896 |
| 3897 /** |
| 3898 * 145 65 e * VPR - Vertical Position Relative |
| 3899 * reuse CSI Ps B ? |
| 3900 */ |
| 3901 Terminal.prototype.VPositionRelative = function(params) { |
| 3902 var param = params[0]; |
| 3903 if (param < 1) param = 1; |
| 3904 this.y += param; |
| 3905 if (this.y >= this.rows) { |
| 3906 this.y = this.rows - 1; |
| 3907 } |
| 3908 }; |
| 3909 |
| 3910 |
| 3911 /** |
| 3912 * CSI Ps ; Ps f |
| 3913 * Horizontal and Vertical Position [row;column] (default = |
| 3914 * [1,1]) (HVP). |
| 3915 */ |
| 3916 Terminal.prototype.HVPosition = function(params) { |
| 3917 if (params[0] < 1) params[0] = 1; |
| 3918 if (params[1] < 1) params[1] = 1; |
| 3919 |
| 3920 this.y = params[0] - 1; |
| 3921 if (this.y >= this.rows) { |
| 3922 this.y = this.rows - 1; |
| 3923 } |
| 3924 |
| 3925 this.x = params[1] - 1; |
| 3926 if (this.x >= this.cols) { |
| 3927 this.x = this.cols - 1; |
| 3928 } |
| 3929 }; |
| 3930 |
| 3931 |
| 3932 /** |
| 3933 * CSI Pm h Set Mode (SM). |
| 3934 * Ps = 2 -> Keyboard Action Mode (AM). |
| 3935 * Ps = 4 -> Insert Mode (IRM). |
| 3936 * Ps = 1 2 -> Send/receive (SRM). |
| 3937 * Ps = 2 0 -> Automatic Newline (LNM). |
| 3938 * CSI ? Pm h |
| 3939 * DEC Private Mode Set (DECSET). |
| 3940 * Ps = 1 -> Application Cursor Keys (DECCKM). |
| 3941 * Ps = 2 -> Designate USASCII for character sets G0-G3 |
| 3942 * (DECANM), and set VT100 mode. |
| 3943 * Ps = 3 -> 132 Column Mode (DECCOLM). |
| 3944 * Ps = 4 -> Smooth (Slow) Scroll (DECSCLM). |
| 3945 * Ps = 5 -> Reverse Video (DECSCNM). |
| 3946 * Ps = 6 -> Origin Mode (DECOM). |
| 3947 * Ps = 7 -> Wraparound Mode (DECAWM). |
| 3948 * Ps = 8 -> Auto-repeat Keys (DECARM). |
| 3949 * Ps = 9 -> Send Mouse X & Y on button press. See the sec- |
| 3950 * tion Mouse Tracking. |
| 3951 * Ps = 1 0 -> Show toolbar (rxvt). |
| 3952 * Ps = 1 2 -> Start Blinking Cursor (att610). |
| 3953 * Ps = 1 8 -> Print form feed (DECPFF). |
| 3954 * Ps = 1 9 -> Set print extent to full screen (DECPEX). |
| 3955 * Ps = 2 5 -> Show Cursor (DECTCEM). |
| 3956 * Ps = 3 0 -> Show scrollbar (rxvt). |
| 3957 * Ps = 3 5 -> Enable font-shifting functions (rxvt). |
| 3958 * Ps = 3 8 -> Enter Tektronix Mode (DECTEK). |
| 3959 * Ps = 4 0 -> Allow 80 -> 132 Mode. |
| 3960 * Ps = 4 1 -> more(1) fix (see curses resource). |
| 3961 * Ps = 4 2 -> Enable Nation Replacement Character sets (DECN- |
| 3962 * RCM). |
| 3963 * Ps = 4 4 -> Turn On Margin Bell. |
| 3964 * Ps = 4 5 -> Reverse-wraparound Mode. |
| 3965 * Ps = 4 6 -> Start Logging. This is normally disabled by a |
| 3966 * compile-time option. |
| 3967 * Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis- |
| 3968 * abled by the titeInhibit resource). |
| 3969 * Ps = 6 6 -> Application keypad (DECNKM). |
| 3970 * Ps = 6 7 -> Backarrow key sends backspace (DECBKM). |
| 3971 * Ps = 1 0 0 0 -> Send Mouse X & Y on button press and |
| 3972 * release. See the section Mouse Tracking. |
| 3973 * Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. |
| 3974 * Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking. |
| 3975 * Ps = 1 0 0 3 -> Use All Motion Mouse Tracking. |
| 3976 * Ps = 1 0 0 4 -> Send FocusIn/FocusOut events. |
| 3977 * Ps = 1 0 0 5 -> Enable Extended Mouse Mode. |
| 3978 * Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt). |
| 3979 * Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt). |
| 3980 * Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit. |
| 3981 * (enables the eightBitInput resource). |
| 3982 * Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num- |
| 3983 * Lock keys. (This enables the numLock resource). |
| 3984 * Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This |
| 3985 * enables the metaSendsEscape resource). |
| 3986 * Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete |
| 3987 * key. |
| 3988 * Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This |
| 3989 * enables the altSendsEscape resource). |
| 3990 * Ps = 1 0 4 0 -> Keep selection even if not highlighted. |
| 3991 * (This enables the keepSelection resource). |
| 3992 * Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables |
| 3993 * the selectToClipboard resource). |
| 3994 * Ps = 1 0 4 2 -> Enable Urgency window manager hint when |
| 3995 * Control-G is received. (This enables the bellIsUrgent |
| 3996 * resource). |
| 3997 * Ps = 1 0 4 3 -> Enable raising of the window when Control-G |
| 3998 * is received. (enables the popOnBell resource). |
| 3999 * Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be |
| 4000 * disabled by the titeInhibit resource). |
| 4001 * Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis- |
| 4002 * abled by the titeInhibit resource). |
| 4003 * Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate |
| 4004 * Screen Buffer, clearing it first. (This may be disabled by |
| 4005 * the titeInhibit resource). This combines the effects of the 1 |
| 4006 * 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based |
| 4007 * applications rather than the 4 7 mode. |
| 4008 * Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. |
| 4009 * Ps = 1 0 5 1 -> Set Sun function-key mode. |
| 4010 * Ps = 1 0 5 2 -> Set HP function-key mode. |
| 4011 * Ps = 1 0 5 3 -> Set SCO function-key mode. |
| 4012 * Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6). |
| 4013 * Ps = 1 0 6 1 -> Set VT220 keyboard emulation. |
| 4014 * Ps = 2 0 0 4 -> Set bracketed paste mode. |
| 4015 * Modes: |
| 4016 * http: *vt100.net/docs/vt220-rm/chapter4.html |
| 4017 */ |
| 4018 Terminal.prototype.setMode = function(params) { |
| 4019 if (typeof params === 'object') { |
| 4020 var l = params.length |
| 4021 , i = 0; |
| 4022 |
| 4023 for (; i < l; i++) { |
| 4024 this.setMode(params[i]); |
| 4025 } |
| 4026 |
| 4027 return; |
| 4028 } |
| 4029 |
| 4030 if (!this.prefix) { |
| 4031 switch (params) { |
| 4032 case 4: |
| 4033 this.insertMode = true; |
| 4034 break; |
| 4035 case 20: |
| 4036 //this.convertEol = true; |
| 4037 break; |
| 4038 } |
| 4039 } else if (this.prefix === '?') { |
| 4040 switch (params) { |
| 4041 case 1: |
| 4042 this.applicationCursor = true; |
| 4043 break; |
| 4044 case 2: |
| 4045 this.setgCharset(0, Terminal.charsets.US); |
| 4046 this.setgCharset(1, Terminal.charsets.US); |
| 4047 this.setgCharset(2, Terminal.charsets.US); |
| 4048 this.setgCharset(3, Terminal.charsets.US); |
| 4049 // set VT100 mode here |
| 4050 break; |
| 4051 case 3: // 132 col mode |
| 4052 this.savedCols = this.cols; |
| 4053 this.resize(132, this.rows); |
| 4054 break; |
| 4055 case 6: |
| 4056 this.originMode = true; |
| 4057 break; |
| 4058 case 7: |
| 4059 this.wraparoundMode = true; |
| 4060 break; |
| 4061 case 12: |
| 4062 // this.cursorBlink = true; |
| 4063 break; |
| 4064 case 66: |
| 4065 this.log('Serial port requested application keypad.'); |
| 4066 this.applicationKeypad = true; |
| 4067 this.viewport.syncScrollArea(); |
| 4068 break; |
| 4069 case 9: // X10 Mouse |
| 4070 // no release, no motion, no wheel, no modifiers. |
| 4071 case 1000: // vt200 mouse |
| 4072 // no motion. |
| 4073 // no modifiers, except control on the wheel. |
| 4074 case 1002: // button event mouse |
| 4075 case 1003: // any event mouse |
| 4076 // any event - sends motion events, |
| 4077 // even if there is no button held down. |
| 4078 this.x10Mouse = params === 9; |
| 4079 this.vt200Mouse = params === 1000; |
| 4080 this.normalMouse = params > 1000; |
| 4081 this.mouseEvents = true; |
| 4082 this.element.style.cursor = 'default'; |
| 4083 this.log('Binding to mouse events.'); |
| 4084 break; |
| 4085 case 1004: // send focusin/focusout events |
| 4086 // focusin: ^[[I |
| 4087 // focusout: ^[[O |
| 4088 this.sendFocus = true; |
| 4089 break; |
| 4090 case 1005: // utf8 ext mode mouse |
| 4091 this.utfMouse = true; |
| 4092 // for wide terminals |
| 4093 // simply encodes large values as utf8 characters |
| 4094 break; |
| 4095 case 1006: // sgr ext mode mouse |
| 4096 this.sgrMouse = true; |
| 4097 // for wide terminals |
| 4098 // does not add 32 to fields |
| 4099 // press: ^[[<b;x;yM |
| 4100 // release: ^[[<b;x;ym |
| 4101 break; |
| 4102 case 1015: // urxvt ext mode mouse |
| 4103 this.urxvtMouse = true; |
| 4104 // for wide terminals |
| 4105 // numbers for fields |
| 4106 // press: ^[[b;x;yM |
| 4107 // motion: ^[[b;x;yT |
| 4108 break; |
| 4109 case 25: // show cursor |
| 4110 this.cursorHidden = false; |
| 4111 break; |
| 4112 case 1049: // alt screen buffer cursor |
| 4113 //this.saveCursor(); |
| 4114 ; // FALL-THROUGH |
| 4115 case 47: // alt screen buffer |
| 4116 case 1047: // alt screen buffer |
| 4117 if (!this.normal) { |
| 4118 var normal = { |
| 4119 lines: this.lines, |
| 4120 ybase: this.ybase, |
| 4121 ydisp: this.ydisp, |
| 4122 x: this.x, |
| 4123 y: this.y, |
| 4124 scrollTop: this.scrollTop, |
| 4125 scrollBottom: this.scrollBottom, |
| 4126 tabs: this.tabs |
| 4127 // XXX save charset(s) here? |
| 4128 // charset: this.charset, |
| 4129 // glevel: this.glevel, |
| 4130 // charsets: this.charsets |
| 4131 }; |
| 4132 this.reset(); |
| 4133 this.normal = normal; |
| 4134 this.showCursor(); |
| 4135 } |
| 4136 break; |
| 4137 } |
| 4138 } |
| 4139 }; |
| 4140 |
| 4141 /** |
| 4142 * CSI Pm l Reset Mode (RM). |
| 4143 * Ps = 2 -> Keyboard Action Mode (AM). |
| 4144 * Ps = 4 -> Replace Mode (IRM). |
| 4145 * Ps = 1 2 -> Send/receive (SRM). |
| 4146 * Ps = 2 0 -> Normal Linefeed (LNM). |
| 4147 * CSI ? Pm l |
| 4148 * DEC Private Mode Reset (DECRST). |
| 4149 * Ps = 1 -> Normal Cursor Keys (DECCKM). |
| 4150 * Ps = 2 -> Designate VT52 mode (DECANM). |
| 4151 * Ps = 3 -> 80 Column Mode (DECCOLM). |
| 4152 * Ps = 4 -> Jump (Fast) Scroll (DECSCLM). |
| 4153 * Ps = 5 -> Normal Video (DECSCNM). |
| 4154 * Ps = 6 -> Normal Cursor Mode (DECOM). |
| 4155 * Ps = 7 -> No Wraparound Mode (DECAWM). |
| 4156 * Ps = 8 -> No Auto-repeat Keys (DECARM). |
| 4157 * Ps = 9 -> Don't send Mouse X & Y on button press. |
| 4158 * Ps = 1 0 -> Hide toolbar (rxvt). |
| 4159 * Ps = 1 2 -> Stop Blinking Cursor (att610). |
| 4160 * Ps = 1 8 -> Don't print form feed (DECPFF). |
| 4161 * Ps = 1 9 -> Limit print to scrolling region (DECPEX). |
| 4162 * Ps = 2 5 -> Hide Cursor (DECTCEM). |
| 4163 * Ps = 3 0 -> Don't show scrollbar (rxvt). |
| 4164 * Ps = 3 5 -> Disable font-shifting functions (rxvt). |
| 4165 * Ps = 4 0 -> Disallow 80 -> 132 Mode. |
| 4166 * Ps = 4 1 -> No more(1) fix (see curses resource). |
| 4167 * Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- |
| 4168 * NRCM). |
| 4169 * Ps = 4 4 -> Turn Off Margin Bell. |
| 4170 * Ps = 4 5 -> No Reverse-wraparound Mode. |
| 4171 * Ps = 4 6 -> Stop Logging. (This is normally disabled by a |
| 4172 * compile-time option). |
| 4173 * Ps = 4 7 -> Use Normal Screen Buffer. |
| 4174 * Ps = 6 6 -> Numeric keypad (DECNKM). |
| 4175 * Ps = 6 7 -> Backarrow key sends delete (DECBKM). |
| 4176 * Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and |
| 4177 * release. See the section Mouse Tracking. |
| 4178 * Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. |
| 4179 * Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. |
| 4180 * Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. |
| 4181 * Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. |
| 4182 * Ps = 1 0 0 5 -> Disable Extended Mouse Mode. |
| 4183 * Ps = 1 0 1 0 -> Don't scroll to bottom on tty output |
| 4184 * (rxvt). |
| 4185 * Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). |
| 4186 * Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables |
| 4187 * the eightBitInput resource). |
| 4188 * Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- |
| 4189 * Lock keys. (This disables the numLock resource). |
| 4190 * Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. |
| 4191 * (This disables the metaSendsEscape resource). |
| 4192 * Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad |
| 4193 * Delete key. |
| 4194 * Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. |
| 4195 * (This disables the altSendsEscape resource). |
| 4196 * Ps = 1 0 4 0 -> Do not keep selection when not highlighted. |
| 4197 * (This disables the keepSelection resource). |
| 4198 * Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables |
| 4199 * the selectToClipboard resource). |
| 4200 * Ps = 1 0 4 2 -> Disable Urgency window manager hint when |
| 4201 * Control-G is received. (This disables the bellIsUrgent |
| 4202 * resource). |
| 4203 * Ps = 1 0 4 3 -> Disable raising of the window when Control- |
| 4204 * G is received. (This disables the popOnBell resource). |
| 4205 * Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen |
| 4206 * first if in the Alternate Screen. (This may be disabled by |
| 4207 * the titeInhibit resource). |
| 4208 * Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be |
| 4209 * disabled by the titeInhibit resource). |
| 4210 * Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor |
| 4211 * as in DECRC. (This may be disabled by the titeInhibit |
| 4212 * resource). This combines the effects of the 1 0 4 7 and 1 0 |
| 4213 * 4 8 modes. Use this with terminfo-based applications rather |
| 4214 * than the 4 7 mode. |
| 4215 * Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. |
| 4216 * Ps = 1 0 5 1 -> Reset Sun function-key mode. |
| 4217 * Ps = 1 0 5 2 -> Reset HP function-key mode. |
| 4218 * Ps = 1 0 5 3 -> Reset SCO function-key mode. |
| 4219 * Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). |
| 4220 * Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. |
| 4221 * Ps = 2 0 0 4 -> Reset bracketed paste mode. |
| 4222 */ |
| 4223 Terminal.prototype.resetMode = function(params) { |
| 4224 if (typeof params === 'object') { |
| 4225 var l = params.length |
| 4226 , i = 0; |
| 4227 |
| 4228 for (; i < l; i++) { |
| 4229 this.resetMode(params[i]); |
| 4230 } |
| 4231 |
| 4232 return; |
| 4233 } |
| 4234 |
| 4235 if (!this.prefix) { |
| 4236 switch (params) { |
| 4237 case 4: |
| 4238 this.insertMode = false; |
| 4239 break; |
| 4240 case 20: |
| 4241 //this.convertEol = false; |
| 4242 break; |
| 4243 } |
| 4244 } else if (this.prefix === '?') { |
| 4245 switch (params) { |
| 4246 case 1: |
| 4247 this.applicationCursor = false; |
| 4248 break; |
| 4249 case 3: |
| 4250 if (this.cols === 132 && this.savedCols) { |
| 4251 this.resize(this.savedCols, this.rows); |
| 4252 } |
| 4253 delete this.savedCols; |
| 4254 break; |
| 4255 case 6: |
| 4256 this.originMode = false; |
| 4257 break; |
| 4258 case 7: |
| 4259 this.wraparoundMode = false; |
| 4260 break; |
| 4261 case 12: |
| 4262 // this.cursorBlink = false; |
| 4263 break; |
| 4264 case 66: |
| 4265 this.log('Switching back to normal keypad.'); |
| 4266 this.applicationKeypad = false; |
| 4267 this.viewport.syncScrollArea(); |
| 4268 break; |
| 4269 case 9: // X10 Mouse |
| 4270 case 1000: // vt200 mouse |
| 4271 case 1002: // button event mouse |
| 4272 case 1003: // any event mouse |
| 4273 this.x10Mouse = false; |
| 4274 this.vt200Mouse = false; |
| 4275 this.normalMouse = false; |
| 4276 this.mouseEvents = false; |
| 4277 this.element.style.cursor = ''; |
| 4278 break; |
| 4279 case 1004: // send focusin/focusout events |
| 4280 this.sendFocus = false; |
| 4281 break; |
| 4282 case 1005: // utf8 ext mode mouse |
| 4283 this.utfMouse = false; |
| 4284 break; |
| 4285 case 1006: // sgr ext mode mouse |
| 4286 this.sgrMouse = false; |
| 4287 break; |
| 4288 case 1015: // urxvt ext mode mouse |
| 4289 this.urxvtMouse = false; |
| 4290 break; |
| 4291 case 25: // hide cursor |
| 4292 this.cursorHidden = true; |
| 4293 break; |
| 4294 case 1049: // alt screen buffer cursor |
| 4295 ; // FALL-THROUGH |
| 4296 case 47: // normal screen buffer |
| 4297 case 1047: // normal screen buffer - clearing it first |
| 4298 if (this.normal) { |
| 4299 this.lines = this.normal.lines; |
| 4300 this.ybase = this.normal.ybase; |
| 4301 this.ydisp = this.normal.ydisp; |
| 4302 this.x = this.normal.x; |
| 4303 this.y = this.normal.y; |
| 4304 this.scrollTop = this.normal.scrollTop; |
| 4305 this.scrollBottom = this.normal.scrollBottom; |
| 4306 this.tabs = this.normal.tabs; |
| 4307 this.normal = null; |
| 4308 // if (params === 1049) { |
| 4309 // this.x = this.savedX; |
| 4310 // this.y = this.savedY; |
| 4311 // } |
| 4312 this.refresh(0, this.rows - 1); |
| 4313 this.showCursor(); |
| 4314 } |
| 4315 break; |
| 4316 } |
| 4317 } |
| 4318 }; |
| 4319 |
| 4320 |
| 4321 /** |
| 4322 * CSI Ps ; Ps r |
| 4323 * Set Scrolling Region [top;bottom] (default = full size of win- |
| 4324 * dow) (DECSTBM). |
| 4325 * CSI ? Pm r |
| 4326 */ |
| 4327 Terminal.prototype.setScrollRegion = function(params) { |
| 4328 if (this.prefix) return; |
| 4329 this.scrollTop = (params[0] || 1) - 1; |
| 4330 this.scrollBottom = (params[1] || this.rows) - 1; |
| 4331 this.x = 0; |
| 4332 this.y = 0; |
| 4333 }; |
| 4334 |
| 4335 |
| 4336 /** |
| 4337 * CSI s |
| 4338 * Save cursor (ANSI.SYS). |
| 4339 */ |
| 4340 Terminal.prototype.saveCursor = function(params) { |
| 4341 this.savedX = this.x; |
| 4342 this.savedY = this.y; |
| 4343 }; |
| 4344 |
| 4345 |
| 4346 /** |
| 4347 * CSI u |
| 4348 * Restore cursor (ANSI.SYS). |
| 4349 */ |
| 4350 Terminal.prototype.restoreCursor = function(params) { |
| 4351 this.x = this.savedX || 0; |
| 4352 this.y = this.savedY || 0; |
| 4353 }; |
| 4354 |
| 4355 |
| 4356 /** |
| 4357 * Lesser Used |
| 4358 */ |
| 4359 |
| 4360 /** |
| 4361 * CSI Ps I |
| 4362 * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). |
| 4363 */ |
| 4364 Terminal.prototype.cursorForwardTab = function(params) { |
| 4365 var param = params[0] || 1; |
| 4366 while (param--) { |
| 4367 this.x = this.nextStop(); |
| 4368 } |
| 4369 }; |
| 4370 |
| 4371 |
| 4372 /** |
| 4373 * CSI Ps S Scroll up Ps lines (default = 1) (SU). |
| 4374 */ |
| 4375 Terminal.prototype.scrollUp = function(params) { |
| 4376 var param = params[0] || 1; |
| 4377 while (param--) { |
| 4378 this.lines.splice(this.ybase + this.scrollTop, 1); |
| 4379 this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine()); |
| 4380 } |
| 4381 // this.maxRange(); |
| 4382 this.updateRange(this.scrollTop); |
| 4383 this.updateRange(this.scrollBottom); |
| 4384 }; |
| 4385 |
| 4386 |
| 4387 /** |
| 4388 * CSI Ps T Scroll down Ps lines (default = 1) (SD). |
| 4389 */ |
| 4390 Terminal.prototype.scrollDown = function(params) { |
| 4391 var param = params[0] || 1; |
| 4392 while (param--) { |
| 4393 this.lines.splice(this.ybase + this.scrollBottom, 1); |
| 4394 this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine()); |
| 4395 } |
| 4396 // this.maxRange(); |
| 4397 this.updateRange(this.scrollTop); |
| 4398 this.updateRange(this.scrollBottom); |
| 4399 }; |
| 4400 |
| 4401 |
| 4402 /** |
| 4403 * CSI Ps ; Ps ; Ps ; Ps ; Ps T |
| 4404 * Initiate highlight mouse tracking. Parameters are |
| 4405 * [func;startx;starty;firstrow;lastrow]. See the section Mouse |
| 4406 * Tracking. |
| 4407 */ |
| 4408 Terminal.prototype.initMouseTracking = function(params) { |
| 4409 // Relevant: DECSET 1001 |
| 4410 }; |
| 4411 |
| 4412 |
| 4413 /** |
| 4414 * CSI > Ps; Ps T |
| 4415 * Reset one or more features of the title modes to the default |
| 4416 * value. Normally, "reset" disables the feature. It is possi- |
| 4417 * ble to disable the ability to reset features by compiling a |
| 4418 * different default for the title modes into xterm. |
| 4419 * Ps = 0 -> Do not set window/icon labels using hexadecimal. |
| 4420 * Ps = 1 -> Do not query window/icon labels using hexadeci- |
| 4421 * mal. |
| 4422 * Ps = 2 -> Do not set window/icon labels using UTF-8. |
| 4423 * Ps = 3 -> Do not query window/icon labels using UTF-8. |
| 4424 * (See discussion of "Title Modes"). |
| 4425 */ |
| 4426 Terminal.prototype.resetTitleModes = function(params) { |
| 4427 ; |
| 4428 }; |
| 4429 |
| 4430 |
| 4431 /** |
| 4432 * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). |
| 4433 */ |
| 4434 Terminal.prototype.cursorBackwardTab = function(params) { |
| 4435 var param = params[0] || 1; |
| 4436 while (param--) { |
| 4437 this.x = this.prevStop(); |
| 4438 } |
| 4439 }; |
| 4440 |
| 4441 |
| 4442 /** |
| 4443 * CSI Ps b Repeat the preceding graphic character Ps times (REP). |
| 4444 */ |
| 4445 Terminal.prototype.repeatPrecedingCharacter = function(params) { |
| 4446 var param = params[0] || 1 |
| 4447 , line = this.lines[this.ybase + this.y] |
| 4448 , ch = line[this.x - 1] || [this.defAttr, ' ', 1]; |
| 4449 |
| 4450 while (param--) line[this.x++] = ch; |
| 4451 }; |
| 4452 |
| 4453 |
| 4454 /** |
| 4455 * CSI Ps g Tab Clear (TBC). |
| 4456 * Ps = 0 -> Clear Current Column (default). |
| 4457 * Ps = 3 -> Clear All. |
| 4458 * Potentially: |
| 4459 * Ps = 2 -> Clear Stops on Line. |
| 4460 * http://vt100.net/annarbor/aaa-ug/section6.html |
| 4461 */ |
| 4462 Terminal.prototype.tabClear = function(params) { |
| 4463 var param = params[0]; |
| 4464 if (param <= 0) { |
| 4465 delete this.tabs[this.x]; |
| 4466 } else if (param === 3) { |
| 4467 this.tabs = {}; |
| 4468 } |
| 4469 }; |
| 4470 |
| 4471 |
| 4472 /** |
| 4473 * CSI Pm i Media Copy (MC). |
| 4474 * Ps = 0 -> Print screen (default). |
| 4475 * Ps = 4 -> Turn off printer controller mode. |
| 4476 * Ps = 5 -> Turn on printer controller mode. |
| 4477 * CSI ? Pm i |
| 4478 * Media Copy (MC, DEC-specific). |
| 4479 * Ps = 1 -> Print line containing cursor. |
| 4480 * Ps = 4 -> Turn off autoprint mode. |
| 4481 * Ps = 5 -> Turn on autoprint mode. |
| 4482 * Ps = 1 0 -> Print composed display, ignores DECPEX. |
| 4483 * Ps = 1 1 -> Print all pages. |
| 4484 */ |
| 4485 Terminal.prototype.mediaCopy = function(params) { |
| 4486 ; |
| 4487 }; |
| 4488 |
| 4489 |
| 4490 /** |
| 4491 * CSI > Ps; Ps m |
| 4492 * Set or reset resource-values used by xterm to decide whether |
| 4493 * to construct escape sequences holding information about the |
| 4494 * modifiers pressed with a given key. The first parameter iden- |
| 4495 * tifies the resource to set/reset. The second parameter is the |
| 4496 * value to assign to the resource. If the second parameter is |
| 4497 * omitted, the resource is reset to its initial value. |
| 4498 * Ps = 1 -> modifyCursorKeys. |
| 4499 * Ps = 2 -> modifyFunctionKeys. |
| 4500 * Ps = 4 -> modifyOtherKeys. |
| 4501 * If no parameters are given, all resources are reset to their |
| 4502 * initial values. |
| 4503 */ |
| 4504 Terminal.prototype.setResources = function(params) { |
| 4505 ; |
| 4506 }; |
| 4507 |
| 4508 |
| 4509 /** |
| 4510 * CSI > Ps n |
| 4511 * Disable modifiers which may be enabled via the CSI > Ps; Ps m |
| 4512 * sequence. This corresponds to a resource value of "-1", which |
| 4513 * cannot be set with the other sequence. The parameter identi- |
| 4514 * fies the resource to be disabled: |
| 4515 * Ps = 1 -> modifyCursorKeys. |
| 4516 * Ps = 2 -> modifyFunctionKeys. |
| 4517 * Ps = 4 -> modifyOtherKeys. |
| 4518 * If the parameter is omitted, modifyFunctionKeys is disabled. |
| 4519 * When modifyFunctionKeys is disabled, xterm uses the modifier |
| 4520 * keys to make an extended sequence of functions rather than |
| 4521 * adding a parameter to each function key to denote the modi- |
| 4522 * fiers. |
| 4523 */ |
| 4524 Terminal.prototype.disableModifiers = function(params) { |
| 4525 ; |
| 4526 }; |
| 4527 |
| 4528 |
| 4529 /** |
| 4530 * CSI > Ps p |
| 4531 * Set resource value pointerMode. This is used by xterm to |
| 4532 * decide whether to hide the pointer cursor as the user types. |
| 4533 * Valid values for the parameter: |
| 4534 * Ps = 0 -> never hide the pointer. |
| 4535 * Ps = 1 -> hide if the mouse tracking mode is not enabled. |
| 4536 * Ps = 2 -> always hide the pointer. If no parameter is |
| 4537 * given, xterm uses the default, which is 1 . |
| 4538 */ |
| 4539 Terminal.prototype.setPointerMode = function(params) { |
| 4540 ; |
| 4541 }; |
| 4542 |
| 4543 |
| 4544 /** |
| 4545 * CSI ! p Soft terminal reset (DECSTR). |
| 4546 * http://vt100.net/docs/vt220-rm/table4-10.html |
| 4547 */ |
| 4548 Terminal.prototype.softReset = function(params) { |
| 4549 this.cursorHidden = false; |
| 4550 this.insertMode = false; |
| 4551 this.originMode = false; |
| 4552 this.wraparoundMode = false; // autowrap |
| 4553 this.applicationKeypad = false; // ? |
| 4554 this.viewport.syncScrollArea(); |
| 4555 this.applicationCursor = false; |
| 4556 this.scrollTop = 0; |
| 4557 this.scrollBottom = this.rows - 1; |
| 4558 this.curAttr = this.defAttr; |
| 4559 this.x = this.y = 0; // ? |
| 4560 this.charset = null; |
| 4561 this.glevel = 0; // ?? |
| 4562 this.charsets = [null]; // ?? |
| 4563 }; |
| 4564 |
| 4565 |
| 4566 /** |
| 4567 * CSI Ps$ p |
| 4568 * Request ANSI mode (DECRQM). For VT300 and up, reply is |
| 4569 * CSI Ps; Pm$ y |
| 4570 * where Ps is the mode number as in RM, and Pm is the mode |
| 4571 * value: |
| 4572 * 0 - not recognized |
| 4573 * 1 - set |
| 4574 * 2 - reset |
| 4575 * 3 - permanently set |
| 4576 * 4 - permanently reset |
| 4577 */ |
| 4578 Terminal.prototype.requestAnsiMode = function(params) { |
| 4579 ; |
| 4580 }; |
| 4581 |
| 4582 |
| 4583 /** |
| 4584 * CSI ? Ps$ p |
| 4585 * Request DEC private mode (DECRQM). For VT300 and up, reply is |
| 4586 * CSI ? Ps; Pm$ p |
| 4587 * where Ps is the mode number as in DECSET, Pm is the mode value |
| 4588 * as in the ANSI DECRQM. |
| 4589 */ |
| 4590 Terminal.prototype.requestPrivateMode = function(params) { |
| 4591 ; |
| 4592 }; |
| 4593 |
| 4594 |
| 4595 /** |
| 4596 * CSI Ps ; Ps " p |
| 4597 * Set conformance level (DECSCL). Valid values for the first |
| 4598 * parameter: |
| 4599 * Ps = 6 1 -> VT100. |
| 4600 * Ps = 6 2 -> VT200. |
| 4601 * Ps = 6 3 -> VT300. |
| 4602 * Valid values for the second parameter: |
| 4603 * Ps = 0 -> 8-bit controls. |
| 4604 * Ps = 1 -> 7-bit controls (always set for VT100). |
| 4605 * Ps = 2 -> 8-bit controls. |
| 4606 */ |
| 4607 Terminal.prototype.setConformanceLevel = function(params) { |
| 4608 ; |
| 4609 }; |
| 4610 |
| 4611 |
| 4612 /** |
| 4613 * CSI Ps q Load LEDs (DECLL). |
| 4614 * Ps = 0 -> Clear all LEDS (default). |
| 4615 * Ps = 1 -> Light Num Lock. |
| 4616 * Ps = 2 -> Light Caps Lock. |
| 4617 * Ps = 3 -> Light Scroll Lock. |
| 4618 * Ps = 2 1 -> Extinguish Num Lock. |
| 4619 * Ps = 2 2 -> Extinguish Caps Lock. |
| 4620 * Ps = 2 3 -> Extinguish Scroll Lock. |
| 4621 */ |
| 4622 Terminal.prototype.loadLEDs = function(params) { |
| 4623 ; |
| 4624 }; |
| 4625 |
| 4626 |
| 4627 /** |
| 4628 * CSI Ps SP q |
| 4629 * Set cursor style (DECSCUSR, VT520). |
| 4630 * Ps = 0 -> blinking block. |
| 4631 * Ps = 1 -> blinking block (default). |
| 4632 * Ps = 2 -> steady block. |
| 4633 * Ps = 3 -> blinking underline. |
| 4634 * Ps = 4 -> steady underline. |
| 4635 */ |
| 4636 Terminal.prototype.setCursorStyle = function(params) { |
| 4637 ; |
| 4638 }; |
| 4639 |
| 4640 |
| 4641 /** |
| 4642 * CSI Ps " q |
| 4643 * Select character protection attribute (DECSCA). Valid values |
| 4644 * for the parameter: |
| 4645 * Ps = 0 -> DECSED and DECSEL can erase (default). |
| 4646 * Ps = 1 -> DECSED and DECSEL cannot erase. |
| 4647 * Ps = 2 -> DECSED and DECSEL can erase. |
| 4648 */ |
| 4649 Terminal.prototype.setCharProtectionAttr = function(params) { |
| 4650 ; |
| 4651 }; |
| 4652 |
| 4653 |
| 4654 /** |
| 4655 * CSI ? Pm r |
| 4656 * Restore DEC Private Mode Values. The value of Ps previously |
| 4657 * saved is restored. Ps values are the same as for DECSET. |
| 4658 */ |
| 4659 Terminal.prototype.restorePrivateValues = function(params) { |
| 4660 ; |
| 4661 }; |
| 4662 |
| 4663 |
| 4664 /** |
| 4665 * CSI Pt; Pl; Pb; Pr; Ps$ r |
| 4666 * Change Attributes in Rectangular Area (DECCARA), VT400 and up. |
| 4667 * Pt; Pl; Pb; Pr denotes the rectangle. |
| 4668 * Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7. |
| 4669 * NOTE: xterm doesn't enable this code by default. |
| 4670 */ |
| 4671 Terminal.prototype.setAttrInRectangle = function(params) { |
| 4672 var t = params[0] |
| 4673 , l = params[1] |
| 4674 , b = params[2] |
| 4675 , r = params[3] |
| 4676 , attr = params[4]; |
| 4677 |
| 4678 var line |
| 4679 , i; |
| 4680 |
| 4681 for (; t < b + 1; t++) { |
| 4682 line = this.lines[this.ybase + t]; |
| 4683 for (i = l; i < r; i++) { |
| 4684 line[i] = [attr, line[i][1]]; |
| 4685 } |
| 4686 } |
| 4687 |
| 4688 // this.maxRange(); |
| 4689 this.updateRange(params[0]); |
| 4690 this.updateRange(params[2]); |
| 4691 }; |
| 4692 |
| 4693 |
| 4694 /** |
| 4695 * CSI Pc; Pt; Pl; Pb; Pr$ x |
| 4696 * Fill Rectangular Area (DECFRA), VT420 and up. |
| 4697 * Pc is the character to use. |
| 4698 * Pt; Pl; Pb; Pr denotes the rectangle. |
| 4699 * NOTE: xterm doesn't enable this code by default. |
| 4700 */ |
| 4701 Terminal.prototype.fillRectangle = function(params) { |
| 4702 var ch = params[0] |
| 4703 , t = params[1] |
| 4704 , l = params[2] |
| 4705 , b = params[3] |
| 4706 , r = params[4]; |
| 4707 |
| 4708 var line |
| 4709 , i; |
| 4710 |
| 4711 for (; t < b + 1; t++) { |
| 4712 line = this.lines[this.ybase + t]; |
| 4713 for (i = l; i < r; i++) { |
| 4714 line[i] = [line[i][0], String.fromCharCode(ch)]; |
| 4715 } |
| 4716 } |
| 4717 |
| 4718 // this.maxRange(); |
| 4719 this.updateRange(params[1]); |
| 4720 this.updateRange(params[3]); |
| 4721 }; |
| 4722 |
| 4723 |
| 4724 /** |
| 4725 * CSI Ps ; Pu ' z |
| 4726 * Enable Locator Reporting (DECELR). |
| 4727 * Valid values for the first parameter: |
| 4728 * Ps = 0 -> Locator disabled (default). |
| 4729 * Ps = 1 -> Locator enabled. |
| 4730 * Ps = 2 -> Locator enabled for one report, then disabled. |
| 4731 * The second parameter specifies the coordinate unit for locator |
| 4732 * reports. |
| 4733 * Valid values for the second parameter: |
| 4734 * Pu = 0 <- or omitted -> default to character cells. |
| 4735 * Pu = 1 <- device physical pixels. |
| 4736 * Pu = 2 <- character cells. |
| 4737 */ |
| 4738 Terminal.prototype.enableLocatorReporting = function(params) { |
| 4739 var val = params[0] > 0; |
| 4740 //this.mouseEvents = val; |
| 4741 //this.decLocator = val; |
| 4742 }; |
| 4743 |
| 4744 |
| 4745 /** |
| 4746 * CSI Pt; Pl; Pb; Pr$ z |
| 4747 * Erase Rectangular Area (DECERA), VT400 and up. |
| 4748 * Pt; Pl; Pb; Pr denotes the rectangle. |
| 4749 * NOTE: xterm doesn't enable this code by default. |
| 4750 */ |
| 4751 Terminal.prototype.eraseRectangle = function(params) { |
| 4752 var t = params[0] |
| 4753 , l = params[1] |
| 4754 , b = params[2] |
| 4755 , r = params[3]; |
| 4756 |
| 4757 var line |
| 4758 , i |
| 4759 , ch; |
| 4760 |
| 4761 ch = [this.eraseAttr(), ' ', 1]; // xterm? |
| 4762 |
| 4763 for (; t < b + 1; t++) { |
| 4764 line = this.lines[this.ybase + t]; |
| 4765 for (i = l; i < r; i++) { |
| 4766 line[i] = ch; |
| 4767 } |
| 4768 } |
| 4769 |
| 4770 // this.maxRange(); |
| 4771 this.updateRange(params[0]); |
| 4772 this.updateRange(params[2]); |
| 4773 }; |
| 4774 |
| 4775 |
| 4776 /** |
| 4777 * CSI P m SP } |
| 4778 * Insert P s Column(s) (default = 1) (DECIC), VT420 and up. |
| 4779 * NOTE: xterm doesn't enable this code by default. |
| 4780 */ |
| 4781 Terminal.prototype.insertColumns = function() { |
| 4782 var param = params[0] |
| 4783 , l = this.ybase + this.rows |
| 4784 , ch = [this.eraseAttr(), ' ', 1] // xterm? |
| 4785 , i; |
| 4786 |
| 4787 while (param--) { |
| 4788 for (i = this.ybase; i < l; i++) { |
| 4789 this.lines[i].splice(this.x + 1, 0, ch); |
| 4790 this.lines[i].pop(); |
| 4791 } |
| 4792 } |
| 4793 |
| 4794 this.maxRange(); |
| 4795 }; |
| 4796 |
| 4797 |
| 4798 /** |
| 4799 * CSI P m SP ~ |
| 4800 * Delete P s Column(s) (default = 1) (DECDC), VT420 and up |
| 4801 * NOTE: xterm doesn't enable this code by default. |
| 4802 */ |
| 4803 Terminal.prototype.deleteColumns = function() { |
| 4804 var param = params[0] |
| 4805 , l = this.ybase + this.rows |
| 4806 , ch = [this.eraseAttr(), ' ', 1] // xterm? |
| 4807 , i; |
| 4808 |
| 4809 while (param--) { |
| 4810 for (i = this.ybase; i < l; i++) { |
| 4811 this.lines[i].splice(this.x, 1); |
| 4812 this.lines[i].push(ch); |
| 4813 } |
| 4814 } |
| 4815 |
| 4816 this.maxRange(); |
| 4817 }; |
| 4818 |
| 4819 /** |
| 4820 * Character Sets |
| 4821 */ |
| 4822 |
| 4823 Terminal.charsets = {}; |
| 4824 |
| 4825 // DEC Special Character and Line Drawing Set. |
| 4826 // http://vt100.net/docs/vt102-ug/table5-13.html |
| 4827 // A lot of curses apps use this if they see TERM=xterm. |
| 4828 // testing: echo -e '\e(0a\e(B' |
| 4829 // The xterm output sometimes seems to conflict with the |
| 4830 // reference above. xterm seems in line with the reference |
| 4831 // when running vttest however. |
| 4832 // The table below now uses xterm's output from vttest. |
| 4833 Terminal.charsets.SCLD = { // (0 |
| 4834 '`': '\u25c6', // '◆' |
| 4835 'a': '\u2592', // '▒' |
| 4836 'b': '\u0009', // '\t' |
| 4837 'c': '\u000c', // '\f' |
| 4838 'd': '\u000d', // '\r' |
| 4839 'e': '\u000a', // '\n' |
| 4840 'f': '\u00b0', // '°' |
| 4841 'g': '\u00b1', // '±' |
| 4842 'h': '\u2424', // '\u2424' (NL) |
| 4843 'i': '\u000b', // '\v' |
| 4844 'j': '\u2518', // '┘' |
| 4845 'k': '\u2510', // '┐' |
| 4846 'l': '\u250c', // '┌' |
| 4847 'm': '\u2514', // '└' |
| 4848 'n': '\u253c', // '┼' |
| 4849 'o': '\u23ba', // '⎺' |
| 4850 'p': '\u23bb', // '⎻' |
| 4851 'q': '\u2500', // '─' |
| 4852 'r': '\u23bc', // '⎼' |
| 4853 's': '\u23bd', // '⎽' |
| 4854 't': '\u251c', // '├' |
| 4855 'u': '\u2524', // '┤' |
| 4856 'v': '\u2534', // '┴' |
| 4857 'w': '\u252c', // '┬' |
| 4858 'x': '\u2502', // '│' |
| 4859 'y': '\u2264', // '≤' |
| 4860 'z': '\u2265', // '≥' |
| 4861 '{': '\u03c0', // 'π' |
| 4862 '|': '\u2260', // '≠' |
| 4863 '}': '\u00a3', // '£' |
| 4864 '~': '\u00b7' // '·' |
| 4865 }; |
| 4866 |
| 4867 Terminal.charsets.UK = null; // (A |
| 4868 Terminal.charsets.US = null; // (B (USASCII) |
| 4869 Terminal.charsets.Dutch = null; // (4 |
| 4870 Terminal.charsets.Finnish = null; // (C or (5 |
| 4871 Terminal.charsets.French = null; // (R |
| 4872 Terminal.charsets.FrenchCanadian = null; // (Q |
| 4873 Terminal.charsets.German = null; // (K |
| 4874 Terminal.charsets.Italian = null; // (Y |
| 4875 Terminal.charsets.NorwegianDanish = null; // (E or (6 |
| 4876 Terminal.charsets.Spanish = null; // (Z |
| 4877 Terminal.charsets.Swedish = null; // (H or (7 |
| 4878 Terminal.charsets.Swiss = null; // (= |
| 4879 Terminal.charsets.ISOLatin = null; // /A |
| 4880 |
| 4881 /** |
| 4882 * Helpers |
| 4883 */ |
| 4884 |
| 4885 function contains(el, arr) { |
| 4886 for (var i = 0; i < arr.length; i += 1) { |
| 4887 if (el === arr[i]) { |
| 4888 return true; |
| 4889 } |
| 4890 } |
| 4891 return false; |
| 4892 } |
| 4893 |
| 4894 function on(el, type, handler, capture) { |
| 4895 if (!Array.isArray(el)) { |
| 4896 el = [el]; |
| 4897 } |
| 4898 el.forEach(function (element) { |
| 4899 element.addEventListener(type, handler, capture || false); |
| 4900 }); |
| 4901 } |
| 4902 |
| 4903 function off(el, type, handler, capture) { |
| 4904 el.removeEventListener(type, handler, capture || false); |
| 4905 } |
| 4906 |
| 4907 function cancel(ev, force) { |
| 4908 if (!this.cancelEvents && !force) { |
| 4909 return; |
| 4910 } |
| 4911 ev.preventDefault(); |
| 4912 ev.stopPropagation(); |
| 4913 return false; |
| 4914 } |
| 4915 |
| 4916 function inherits(child, parent) { |
| 4917 function f() { |
| 4918 this.constructor = child; |
| 4919 } |
| 4920 f.prototype = parent.prototype; |
| 4921 child.prototype = new f; |
| 4922 } |
| 4923 |
| 4924 // if bold is broken, we can't |
| 4925 // use it in the terminal. |
| 4926 function isBoldBroken(document) { |
| 4927 var body = document.getElementsByTagName('body')[0]; |
| 4928 var el = document.createElement('span'); |
| 4929 el.innerHTML = 'hello world'; |
| 4930 body.appendChild(el); |
| 4931 var w1 = el.scrollWidth; |
| 4932 el.style.fontWeight = 'bold'; |
| 4933 var w2 = el.scrollWidth; |
| 4934 body.removeChild(el); |
| 4935 return w1 !== w2; |
| 4936 } |
| 4937 |
| 4938 function indexOf(obj, el) { |
| 4939 var i = obj.length; |
| 4940 while (i--) { |
| 4941 if (obj[i] === el) return i; |
| 4942 } |
| 4943 return -1; |
| 4944 } |
| 4945 |
| 4946 function isThirdLevelShift(term, ev) { |
| 4947 var thirdLevelKey = |
| 4948 (term.isMac && ev.altKey && !ev.ctrlKey && !ev.metaKey) || |
| 4949 (term.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); |
| 4950 |
| 4951 if (ev.type == 'keypress') { |
| 4952 return thirdLevelKey; |
| 4953 } |
| 4954 |
| 4955 // Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress e
vents) |
| 4956 return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47); |
| 4957 } |
| 4958 |
| 4959 function matchColor(r1, g1, b1) { |
| 4960 var hash = (r1 << 16) | (g1 << 8) | b1; |
| 4961 |
| 4962 if (matchColor._cache[hash] != null) { |
| 4963 return matchColor._cache[hash]; |
| 4964 } |
| 4965 |
| 4966 var ldiff = Infinity |
| 4967 , li = -1 |
| 4968 , i = 0 |
| 4969 , c |
| 4970 , r2 |
| 4971 , g2 |
| 4972 , b2 |
| 4973 , diff; |
| 4974 |
| 4975 for (; i < Terminal.vcolors.length; i++) { |
| 4976 c = Terminal.vcolors[i]; |
| 4977 r2 = c[0]; |
| 4978 g2 = c[1]; |
| 4979 b2 = c[2]; |
| 4980 |
| 4981 diff = matchColor.distance(r1, g1, b1, r2, g2, b2); |
| 4982 |
| 4983 if (diff === 0) { |
| 4984 li = i; |
| 4985 break; |
| 4986 } |
| 4987 |
| 4988 if (diff < ldiff) { |
| 4989 ldiff = diff; |
| 4990 li = i; |
| 4991 } |
| 4992 } |
| 4993 |
| 4994 return matchColor._cache[hash] = li; |
| 4995 } |
| 4996 |
| 4997 matchColor._cache = {}; |
| 4998 |
| 4999 // http://stackoverflow.com/questions/1633828 |
| 5000 matchColor.distance = function(r1, g1, b1, r2, g2, b2) { |
| 5001 return Math.pow(30 * (r1 - r2), 2) |
| 5002 + Math.pow(59 * (g1 - g2), 2) |
| 5003 + Math.pow(11 * (b1 - b2), 2); |
| 5004 }; |
| 5005 |
| 5006 function each(obj, iter, con) { |
| 5007 if (obj.forEach) return obj.forEach(iter, con); |
| 5008 for (var i = 0; i < obj.length; i++) { |
| 5009 iter.call(con, obj[i], i, obj); |
| 5010 } |
| 5011 } |
| 5012 |
| 5013 function keys(obj) { |
| 5014 if (Object.keys) return Object.keys(obj); |
| 5015 var key, keys = []; |
| 5016 for (key in obj) { |
| 5017 if (Object.prototype.hasOwnProperty.call(obj, key)) { |
| 5018 keys.push(key); |
| 5019 } |
| 5020 } |
| 5021 return keys; |
| 5022 } |
| 5023 |
| 5024 var wcwidth = (function(opts) { |
| 5025 // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c |
| 5026 // combining characters |
| 5027 var COMBINING = [ |
| 5028 [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], |
| 5029 [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], |
| 5030 [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], |
| 5031 [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], |
| 5032 [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], |
| 5033 [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], |
| 5034 [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], |
| 5035 [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], |
| 5036 [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], |
| 5037 [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], |
| 5038 [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], |
| 5039 [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], |
| 5040 [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], |
| 5041 [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], |
| 5042 [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], |
| 5043 [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], |
| 5044 [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], |
| 5045 [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], |
| 5046 [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], |
| 5047 [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], |
| 5048 [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], |
| 5049 [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], |
| 5050 [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], |
| 5051 [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], |
| 5052 [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], |
| 5053 [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], |
| 5054 [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], |
| 5055 [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], |
| 5056 [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], |
| 5057 [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], |
| 5058 [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], |
| 5059 [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], |
| 5060 [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], |
| 5061 [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], |
| 5062 [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], |
| 5063 [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], |
| 5064 [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], |
| 5065 [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], |
| 5066 [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], |
| 5067 [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], |
| 5068 [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], |
| 5069 [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], |
| 5070 [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB], |
| 5071 [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], |
| 5072 [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], |
| 5073 [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], |
| 5074 [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], |
| 5075 [0xE0100, 0xE01EF] |
| 5076 ]; |
| 5077 // binary search |
| 5078 function bisearch(ucs) { |
| 5079 var min = 0; |
| 5080 var max = COMBINING.length - 1; |
| 5081 var mid; |
| 5082 if (ucs < COMBINING[0][0] || ucs > COMBINING[max][1]) |
| 5083 return false; |
| 5084 while (max >= min) { |
| 5085 mid = Math.floor((min + max) / 2); |
| 5086 if (ucs > COMBINING[mid][1]) |
| 5087 min = mid + 1; |
| 5088 else if (ucs < COMBINING[mid][0]) |
| 5089 max = mid - 1; |
| 5090 else |
| 5091 return true; |
| 5092 } |
| 5093 return false; |
| 5094 } |
| 5095 function wcwidth(ucs) { |
| 5096 // test for 8-bit control characters |
| 5097 if (ucs === 0) |
| 5098 return opts.nul; |
| 5099 if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) |
| 5100 return opts.control; |
| 5101 // binary search in table of non-spacing characters |
| 5102 if (bisearch(ucs)) |
| 5103 return 0; |
| 5104 // if we arrive here, ucs is not a combining or C0/C1 control character |
| 5105 return 1 + |
| 5106 ( |
| 5107 ucs >= 0x1100 && |
| 5108 ( |
| 5109 ucs <= 0x115f || // Hangul Jamo init. consonants |
| 5110 ucs == 0x2329 || |
| 5111 ucs == 0x232a || |
| 5112 (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || // CJK..Yi |
| 5113 (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables |
| 5114 (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs |
| 5115 (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms |
| 5116 (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms |
| 5117 (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms |
| 5118 (ucs >= 0xffe0 && ucs <= 0xffe6) || |
| 5119 (ucs >= 0x20000 && ucs <= 0x2fffd) || |
| 5120 (ucs >= 0x30000 && ucs <= 0x3fffd) |
| 5121 ) |
| 5122 ); |
| 5123 } |
| 5124 return wcwidth; |
| 5125 })({nul: 0, control: 0}); // configurable options |
| 5126 |
| 5127 /** |
| 5128 * Expose |
| 5129 */ |
| 5130 |
| 5131 Terminal.EventEmitter = EventEmitter; |
| 5132 Terminal.CompositionHelper = CompositionHelper; |
| 5133 Terminal.Viewport = Viewport; |
| 5134 Terminal.inherits = inherits; |
| 5135 |
| 5136 /** |
| 5137 * Adds an event listener to the terminal. |
| 5138 * |
| 5139 * @param {string} event The name of the event. TODO: Document all event types |
| 5140 * @param {function} callback The function to call when the event is triggered. |
| 5141 */ |
| 5142 Terminal.on = on; |
| 5143 Terminal.off = off; |
| 5144 Terminal.cancel = cancel; |
| 5145 |
| 5146 module.exports = Terminal; |
OLD | NEW |