]> git.proxmox.com Git - mirror_xterm.js.git/blobdiff - src/xterm.js
Refactor TS build and fix testing
[mirror_xterm.js.git] / src / xterm.js
index 79c11d10b0cadc79975d5b2cefe343396048ac52..ce49d917a76fe7402704db06735ac9822a702e3f 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * xterm.js: xterm, in the browser
- * Copyright (c) 2014, SourceLair Limited <www.sourcelair.com> (MIT License)
+ * Copyright (c) 2014-2014, SourceLair Private Company <www.sourcelair.com> (MIT License)
  * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
  * https://github.com/chjj/term.js
  *
@@ -34,6 +34,7 @@
 import { CompositionHelper } from './CompositionHelper.js';
 import { EventEmitter } from './EventEmitter.js';
 import { Viewport } from './Viewport.js';
+import { rightClickHandler, pasteHandler, copyHandler } from './handlers/Clipboard.js';
 
 /**
  * Terminal Emulation References:
@@ -62,7 +63,9 @@ var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6;
  * Creates a new `Terminal` object.
  *
  * @param {object} options An object containing a set of options, the available options are:
- *   - cursorBlink (boolean): Whether the terminal cursor blinks
+ *   - `cursorBlink` (boolean): Whether the terminal cursor blinks
+ *   - `cols` (number): The number of columns of the terminal (horizontal size)
+ *   - `rows` (number): The number of rows of the terminal (vertical size)
  *
  * @public
  * @class Xterm Xterm
@@ -124,6 +127,7 @@ function Terminal(options) {
 
   this.cols = options.cols || options.geometry[0];
   this.rows = options.rows || options.geometry[1];
+  this.geometry = [this.cols, this.rows];
 
   if (options.handler) {
     this.on('data', options.handler);
@@ -357,6 +361,22 @@ Terminal.prototype.focus = function() {
   return this.textarea.focus();
 };
 
+/**
+ * Retrieves an option's value from the terminal.
+ * @param {string} key The option key.
+ */
+Terminal.prototype.getOption = function(key, value) {
+  if (!(key in Terminal.defaults)) {
+    throw new Error('No option with key "' + key + '"');
+  }
+
+  if (typeof this.options[key] !== 'undefined') {
+    return this.options[key];
+  }
+
+  return this[key];
+};
+
 /**
  * Sets an option on the terminal.
  * @param {string} key The option key.
@@ -415,52 +435,20 @@ Terminal.bindBlur = function (term) {
  * Initialize default behavior
  */
 Terminal.prototype.initGlobal = function() {
-  Terminal.bindPaste(this);
+  var term = this;
+
   Terminal.bindKeys(this);
-  Terminal.bindCopy(this);
   Terminal.bindFocus(this);
   Terminal.bindBlur(this);
-};
 
-/**
- * Bind to paste event and allow both keyboard and right-click pasting, without having the
- * contentEditable value set to true.
- */
-Terminal.bindPaste = function(term) {
-  on([term.textarea, term.element], 'paste', function(ev) {
-    ev.stopPropagation();
-    if (ev.clipboardData) {
-      var text = ev.clipboardData.getData('text/plain');
-      term.handler(text);
-      term.textarea.value = '';
-      return term.cancel(ev);
-    }
+  // Bind clipboard functionality
+  on(this.element, 'copy', copyHandler);
+  on(this.textarea, 'paste', function (ev) {
+    pasteHandler.call(this, ev, term);
+  });
+  on(this.element, 'contextmenu', function (ev) {
+    rightClickHandler.call(this, ev, term);
   });
-};
-
-/**
- * Prepares text copied from terminal selection, to be saved in the clipboard by:
- *   1. stripping all trailing white spaces
- *   2. converting all non-breaking spaces to regular spaces
- * @param {string} text The copied text that needs processing for storing in clipboard
- * @returns {string}
- * @static
- */
-Terminal.prepareCopiedTextForClipboard = function (text) {
-  var space = String.fromCharCode(32),
-      nonBreakingSpace = String.fromCharCode(160),
-      allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'),
-      processedText = text.split('\n').map(function (line) {
-        /**
-         * Strip all trailing white spaces and convert all non-breaking spaces to regular
-         * spaces.
-         */
-        var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
-
-        return processedLine;
-      }).join('\n');
-
-  return processedText;
 };
 
 /**
@@ -499,16 +487,6 @@ Terminal.bindKeys = function(term) {
   term.on('refresh', term.compositionHelper.updateCompositionElements.bind(term.compositionHelper));
 };
 
-/**
- * Binds copy functionality to the given terminal.
- * @static
- */
-Terminal.bindCopy = function(term) {
-  on(term.element, 'copy', function(ev) {
-    return; // temporary
-  });
-};
-
 
 /**
  * Insert the given row to the terminal or produce a new one
@@ -667,10 +645,10 @@ Terminal.prototype.open = function(parent) {
 Terminal.loadAddon = function(addon, callback) {
   if (typeof exports === 'object' && typeof module === 'object') {
     // CommonJS
-    return require(__dirname + '/../addons/' + addon);
+    return require('./addons/' + addon + '/' + addon);
   } else if (typeof define == 'function') {
     // RequireJS
-    return require(['../addons/' + addon + '/' + addon], callback);
+    return require(['./addons/' + addon + '/' + addon], callback);
   } else {
     console.error('Cannot load a module without a CommonJS or RequireJS environment.');
     return false;
@@ -1001,7 +979,6 @@ Terminal.prototype.bindMouse = function() {
   // the shell for example
   on(el, 'wheel', function(ev) {
     if (self.mouseEvents) return;
-    if (self.applicationKeypad) return;
     self.viewport.onWheel(ev);
     return self.cancel(ev);
   });
@@ -1333,6 +1310,28 @@ Terminal.prototype.scrollDisp = function(disp, suppressScrollEvent) {
   this.refresh(0, this.rows - 1);
 };
 
+/**
+ * Scroll the display of the terminal by a number of pages.
+ * @param {number} pageCount The number of pages to scroll (negative scrolls up).
+ */
+Terminal.prototype.scrollPages = function(pageCount) {
+  this.scrollDisp(pageCount * (this.rows - 1));
+}
+
+/**
+ * Scrolls the display of the terminal to the top.
+ */
+Terminal.prototype.scrollToTop = function() {
+  this.scrollDisp(-this.ydisp);
+}
+
+/**
+ * Scrolls the display of the terminal to the bottom.
+ */
+Terminal.prototype.scrollToBottom = function() {
+  this.scrollDisp(this.ybase - this.ydisp);
+}
+
 /**
  * Writes text to the terminal.
  * @param {string} text The text to write to the terminal.
@@ -1676,7 +1675,7 @@ Terminal.prototype.write = function(data) {
           case '=':
             this.log('Serial port requested application keypad.');
             this.applicationKeypad = true;
-            this.viewport.setApplicationMode(true);
+            this.viewport.syncScrollArea();
             this.state = normal;
             break;
 
@@ -1684,7 +1683,7 @@ Terminal.prototype.write = function(data) {
           case '>':
             this.log('Switching back to normal keypad.');
             this.applicationKeypad = false;
-            this.viewport.setApplicationMode(false);
+            this.viewport.syncScrollArea();
             this.state = normal;
             break;
 
@@ -2427,14 +2426,14 @@ Terminal.prototype.keyDown = function(ev) {
 
   if (result.scrollDisp) {
     this.scrollDisp(result.scrollDisp);
-    return this.cancel(ev);
+    return this.cancel(ev, true);
   }
 
   if (isThirdLevelShift(this, ev)) {
     return true;
   }
 
-  if (result.cancel ) {
+  if (result.cancel) {
     // The event is canceled at the end already, is this necessary?
     this.cancel(ev, true);
   }
@@ -2470,16 +2469,16 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
   };
   var modifiers = ev.shiftKey << 0 | ev.altKey << 1 | ev.ctrlKey << 2 | ev.metaKey << 3;
   switch (ev.keyCode) {
-      // backspace
     case 8:
+      // backspace
       if (ev.shiftKey) {
         result.key = '\x08'; // ^H
         break;
       }
       result.key = '\x7f'; // ^?
       break;
-      // tab
     case 9:
+      // tab
       if (ev.shiftKey) {
         result.key = '\x1b[Z';
         break;
@@ -2487,18 +2486,18 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
       result.key = '\t';
       result.cancel = true;
       break;
-      // return/enter
     case 13:
+      // return/enter
       result.key = '\r';
       result.cancel = true;
       break;
-      // escape
     case 27:
+      // escape
       result.key = '\x1b';
       result.cancel = true;
       break;
-      // left-arrow
     case 37:
+      // left-arrow
       if (modifiers) {
         result.key = '\x1b[1;' + (modifiers + 1) + 'D';
         // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards
@@ -2512,8 +2511,8 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
         result.key = '\x1b[D';
       }
       break;
-      // right-arrow
     case 39:
+      // right-arrow
       if (modifiers) {
         result.key = '\x1b[1;' + (modifiers + 1) + 'C';
         // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward
@@ -2527,8 +2526,8 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
         result.key = '\x1b[C';
       }
       break;
-      // up-arrow
     case 38:
+      // up-arrow
       if (modifiers) {
         result.key = '\x1b[1;' + (modifiers + 1) + 'A';
         // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
@@ -2542,8 +2541,8 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
         result.key = '\x1b[A';
       }
       break;
-      // down-arrow
     case 40:
+      // down-arrow
       if (modifiers) {
         result.key = '\x1b[1;' + (modifiers + 1) + 'B';
         // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
@@ -2557,24 +2556,24 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
         result.key = '\x1b[B';
       }
       break;
-      // insert
     case 45:
+      // insert
       if (!ev.shiftKey && !ev.ctrlKey) {
         // <Ctrl> or <Shift> + <Insert> are used to
         // copy-paste on some systems.
         result.key = '\x1b[2~';
       }
       break;
-      // delete
     case 46:
+      // delete
       if (modifiers) {
         result.key = '\x1b[3;' + (modifiers + 1) + '~';
       } else {
         result.key = '\x1b[3~';
       }
       break;
-      // home
     case 36:
+      // home
       if (modifiers)
         result.key = '\x1b[1;' + (modifiers + 1) + 'H';
       else if (this.applicationCursor)
@@ -2582,8 +2581,8 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
       else
         result.key = '\x1b[H';
       break;
-      // end
     case 35:
+      // end
       if (modifiers)
         result.key = '\x1b[1;' + (modifiers + 1) + 'F';
       else if (this.applicationCursor)
@@ -2591,24 +2590,24 @@ Terminal.prototype.evaluateKeyEscapeSequence = function(ev) {
       else
         result.key = '\x1b[F';
       break;
-      // page up
     case 33:
+      // page up
       if (ev.shiftKey) {
         result.scrollDisp = -(this.rows - 1);
       } else {
         result.key = '\x1b[5~';
       }
       break;
-      // page down
     case 34:
+      // page down
       if (ev.shiftKey) {
         result.scrollDisp = this.rows - 1;
       } else {
         result.key = '\x1b[6~';
       }
       break;
-      // F1-F12
     case 112:
+      // F1-F12
       if (modifiers) {
         result.key = '\x1b[1;' + (modifiers + 1) + 'P';
       } else {
@@ -2946,6 +2945,7 @@ Terminal.prototype.resize = function(x, y) {
 
   this.normal = null;
 
+  this.geometry = [this.cols, this.rows];
   this.emit('resize', {terminal: this, cols: x, rows: y});
 };
 
@@ -3058,10 +3058,14 @@ Terminal.prototype.eraseLeft = function(x, y) {
  * Clears the entire buffer, making the prompt line the new first line.
  */
 Terminal.prototype.clear = function() {
+  if (this.ybase === 0 && this.y === 0) {
+    // Don't clear if it's already clear
+    return;
+  }
+  this.lines = [this.lines[this.ybase + this.y]];
   this.ydisp = 0;
   this.ybase = 0;
   this.y = 0;
-  this.lines = [this.lines[this.lines.length - 1]];
   for (var i = 1; i < this.rows; i++) {
     this.lines.push(this.blankLine());
   }
@@ -3121,9 +3125,9 @@ Terminal.prototype.is = function(term) {
 
 
 /**
    * Emit the 'data' event and populate the given data.
    * @param {string} data The data to populate in the event.
    */
+ * Emit the 'data' event and populate the given data.
+ * @param {string} data The data to populate in the event.
+ */
 Terminal.prototype.handler = function(data) {
   this.emit('data', data);
 };
@@ -3187,6 +3191,7 @@ Terminal.prototype.reset = function() {
   Terminal.call(this, this.options);
   this.customKeydownHandler = customKeydownHandler;
   this.refresh(0, this.rows - 1);
+  this.viewport.syncScrollArea();
 };
 
 
@@ -4044,7 +4049,7 @@ Terminal.prototype.setMode = function(params) {
       case 66:
         this.log('Serial port requested application keypad.');
         this.applicationKeypad = true;
-        this.viewport.setApplicationMode(true);
+        this.viewport.syncScrollArea();
         break;
       case 9: // X10 Mouse
         // no release, no motion, no wheel, no modifiers.
@@ -4244,7 +4249,7 @@ Terminal.prototype.resetMode = function(params) {
       case 66:
         this.log('Switching back to normal keypad.');
         this.applicationKeypad = false;
-        this.viewport.setApplicationMode(false);
+        this.viewport.syncScrollArea();
         break;
       case 9: // X10 Mouse
       case 1000: // vt200 mouse
@@ -4365,8 +4370,8 @@ Terminal.prototype.scrollUp = function(params) {
 
 
 /**
    * CSI Ps T  Scroll down Ps lines (default = 1) (SD).
    */
+ * CSI Ps T  Scroll down Ps lines (default = 1) (SD).
+ */
 Terminal.prototype.scrollDown = function(params) {
   var param = params[0] || 1;
   while (param--) {
@@ -4531,7 +4536,7 @@ Terminal.prototype.softReset = function(params) {
   this.originMode = false;
   this.wraparoundMode = false; // autowrap
   this.applicationKeypad = false; // ?
-  this.viewport.setApplicationMode(false);
+  this.viewport.syncScrollArea();
   this.applicationCursor = false;
   this.scrollTop = 0;
   this.scrollBottom = this.rows - 1;