]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Merge remote-tracking branch 'upstream/master' into windows_demo
authorDaniel Imms <daimms@microsoft.com>
Thu, 16 Jun 2016 23:28:20 +0000 (16:28 -0700)
committerDaniel Imms <daimms@microsoft.com>
Thu, 16 Jun 2016 23:28:20 +0000 (16:28 -0700)
.gitignore
conf.json [deleted file]
jsdoc.json [new file with mode: 0644]
package.json
src/xterm.css
src/xterm.js
test/addons/test.js [new file with mode: 0644]
test/test.js

index af5c7e2e9acbcb1343aee52c879ac3c84fb9ddda..029efd56b8536c4438a5a46baed01672599cc05a 100644 (file)
@@ -8,5 +8,5 @@ Makefile.gyp
 *.target.gyp.mk
 *.node
 example/*.log
-docs/_build
+docs/
 npm-debug.log
diff --git a/conf.json b/conf.json
deleted file mode 100644 (file)
index b5c8e52..0000000
--- a/conf.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "source": {
-        "include": [
-            "src/xterm.js",
-            "addons/attach/attach.js",
-            "addons/fit/fit.js",
-            "addons/fullscreen/fullscreen.js",
-            "addons/linkify/linkify.js"
-        ]
-    },
-    "opts": {
-        "readme": "README.md"
-    }
-}
\ No newline at end of file
diff --git a/jsdoc.json b/jsdoc.json
new file mode 100644 (file)
index 0000000..28ca16c
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "source": {
+    "include": [
+      "src/xterm.js",
+      "addons/attach/attach.js",
+      "addons/fit/fit.js",
+      "addons/fullscreen/fullscreen.js",
+      "addons/linkify/linkify.js"
+    ]
+  },
+  "opts": {
+    "readme": "README.md",
+    "template": "node_modules/docdash",
+    "encoding": "utf8",
+    "destination": "docs/",
+    "recurse": true,
+    "verbose": true
+  },
+  "templates": {
+    "cleverLinks": false,
+    "monospaceLinks": false
+  }
+}
index e7b66bf8babd29d05af71502583b44d0548bae21..1b98517c286ff8f23fa65dba84505183c0ad2a5a 100644 (file)
     "express-ws": "2.0.0-rc.1",
     "pty.js": "0.3.0",
     "mocha": "2.5.3",
-    "chai": "3.5.0"
+    "chai": "3.5.0",
+    "jsdoc": "3.4.0",
+    "docdash": "0.4.0"
   },
   "scripts": {
-    "start": "node demo/app.js",
-    "test": "node_modules/.bin/mocha --recursive"
+    "start": "bash bin/server",
+    "test": "bash bin/test --recursive",
+    "build:docs": "node_modules/.bin/jsdoc -c jsdoc.json"
   }
 }
index 5a325ed915db00b4567898147dc4af74d3d6968e..1f49d690db9bffadf49e348f1ccd6575c8b770b6 100644 (file)
     background-color: transparent;
 }
 
+.terminal .terminal-cursor.blinking {
+    animation: blink-cursor 1.2s infinite step-end;
+}
+
+@keyframes blink-cursor {
+    0% {
+        background-color: #fff;
+        color: #000;
+    }
+    50% {
+        background-color: transparent;
+        color: #FFF;
+    }
+}
+
 /*
  *  Determine default colors for xterm.js
  */
index 3d29a66f9c9b200a3e78ddba253d8f42b4b1c8f8..6319570d664ccec2021b467ecfd351c9b261247a 100644 (file)
        */
       this.y = 0;
 
+      /**
+       * Used to debounce the refresh function
+       */
+      this.isRefreshing = false;
+
+      /**
+       * Whether there is a full terminal refresh queued
+       */
+
       this.cursorState = 0;
       this.cursorHidden = false;
       this.convertEol;
     });
 
     /**
-     * Focus the terminal.
+     * Focus the terminal. Delegates focus handling to the terminal's DOM element.
      *
      * @public
      */
     Terminal.prototype.focus = function() {
-      if (document.activeElement === this.element) {
-        return;
-      }
+      return this.element.focus();
+    };
 
-      if (this.sendFocus) {
-        this.send('\x1b[I');
-      }
+    /**
+     * Binds the desired focus behavior on a given terminal object.
+     *
+     * @static
+     */
+    Terminal.bindFocus = function (term) {
+      on(term.element, 'focus', function (ev) {
+        if (term.sendFocus) {
+          term.send('\x1b[I');
+        }
 
-      this.showCursor();
-      this.element.focus();
+        term.showCursor();
+        Terminal.focus = term;
+        term.emit('focus', {terminal: term});
+      });
     };
 
+    /**
+     * Blur the terminal. Delegates blur handling to the terminal's DOM element.
+     *
+     * @public
+     */
     Terminal.prototype.blur = function() {
-      if (Terminal.focus !== this) {
-        return;
-      }
-
-      this.cursorState = 0;
-      this.refresh(this.y, this.y);
-      this.element.blur();
+      return terminal.element.blur();
+    };
 
-      if (this.sendFocus) {
-        this.send('\x1b[O');
-      }
-      Terminal.focus = null;
+    /**
+     * Binds the desired blur behavior on a given terminal object.
+     *
+     * @static
+     */
+    Terminal.bindBlur = function (term) {
+      on(term.element, 'blur', function (ev) {
+        if (term.sendFocus) {
+          term.send('\x1b[O');
+        }
+        Terminal.focus = null;
+        term.emit('blur', {terminal: term});
+      });
     };
 
     /**
       Terminal.bindCopy(this);
       Terminal.bindCut(this);
       Terminal.bindDrop(this);
+      Terminal.bindFocus(this);
+      Terminal.bindBlur(this);
     };
 
        /**
       this.element.classList.add('xterm-theme-' + this.theme);
       this.element.setAttribute('tabindex', 0);
       this.element.spellcheck = 'false';
-      this.element.onfocus = function() {
-        self.emit('focus', {terminal: this});
-      };
-      this.element.onblur = function() {
-        self.emit('blur', {terminal: this});
-      };
 
       /*
       * Create the container that will hold the lines of the terminal and then
       // Ensure there is a Terminal.focus.
       this.focus();
 
-      // Start blinking the cursor.
-      this.startBlink();
-
       on(this.element, 'mouseup', function() {
         var selection = document.getSelection(),
             collapsed = selection.isCollapsed,
       this.emit('open');
     };
 
+
+    /**
+     * Attempts to load an add-on using CommonJS or RequireJS (whichever is available).
+     * @param {string} addon The name of the addon to load
+     * @static
+     */
+    Terminal.loadAddon = function(addon, callback) {
+      if (typeof exports === 'object' && typeof module === 'object') {
+        // CommonJS
+        return require(__dirname + '/../addons/' + addon);
+      } else if (typeof define == 'function') {
+        // RequireJS
+        return require(['../addons/' + addon + '/' + addon], callback);
+      } else {
+        console.error('Cannot load a module without a CommonJS or RequireJS environment.');
+        return false;
+      }
+    };
+
+
     // XTerm mouse events
     // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
     // To better understand these
      *
      * @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
      * @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
+     * @param {boolean} queue Whether the refresh should ran right now or be queued
      *
      * @public
      */
-    Terminal.prototype.refresh = function(start, end) {
+    Terminal.prototype.refresh = function(start, end, queue) {
+      var self = this;
+
+      // queue defaults to true
+      queue = (typeof queue == 'undefined') ? true : queue;
+
+      /**
+       * The refresh queue allows refresh to execute only approximately 30 times a second. For
+       * commands that pass a significant amount of output to the write function, this prevents the
+       * terminal from maxing out the CPU and making the UI unresponsive. While commands can still
+       * run beyond what they do on the terminal, it is far better with a debounce in place as
+       * every single terminal manipulation does not need to be constructed in the DOM.
+       *
+       * A side-effect of this is that it makes ^C to interrupt a process seem more responsive.
+       */
+      if (queue) {
+        // If refresh should be queued, order the refresh and return.
+        if (this._refreshIsQueued) {
+          // If a refresh has already been queued, just order a full refresh next
+          this._fullRefreshNext = true;
+        } else {
+          setTimeout(function () {
+            self.refresh(start, end, false);
+          }, 34)
+          this._refreshIsQueued = true;
+        }
+        return;
+      }
+
+      // If refresh should be run right now (not be queued), release the lock
+      this._refreshIsQueued = false;
+
+      // If multiple refreshes were requested, make a full refresh.
+      if (this._fullRefreshNext) {
+        start = 0;
+        end = this.rows - 1;
+        this._fullRefreshNext = false // reset lock
+      }
+
       var x, y, i, line, out, ch, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
 
+      // If this is a big refresh, remove the terminal rows from the DOM for faster calculations
       if (end - start >= this.rows / 2) {
         parent = this.element.parentNode;
-        if (parent) parent.removeChild(this.element);
+        if (parent) {
+          this.element.removeChild(this.rowContainer);
+        }
       }
 
       width = this.cols;
       y = start;
 
-      if (end >= this.lines.length) {
+      if (end >= this.rows.length) {
         this.log('`end` is too large. Most likely a bad CSR.');
-        end = this.lines.length - 1;
+        end = this.rows.length - 1;
       }
 
       for (; y <= end; y++) {
             }
             if (data !== this.defAttr) {
               if (data === -1) {
-                out += '<span class="reverse-video terminal-cursor">';
+                out += '<span class="reverse-video terminal-cursor';
+                if (this.cursorBlink) {
+                  out += ' blinking';
+                }
+                out += '">';
               } else {
                 var classNames = [];
 
       }
 
       if (parent) {
-        parent.appendChild(this.element);
+        this.element.appendChild(this.rowContainer);
       }
 
-      /*
-      *  Return focus to previously focused element
-      */
-      focused.focus();
       this.emit('refresh', {element: this.element, start: start, end: end});
     };
 
-    Terminal.prototype._cursorBlink = function() {
-      if (Terminal.focus !== this) return;
-      this.cursorState ^= 1;
-      this.refresh(this.y, this.y);
-    };
-
     Terminal.prototype.showCursor = function() {
       if (!this.cursorState) {
         this.cursorState = 1;
         this.refresh(this.y, this.y);
-      } else {
-        // Temporarily disabled:
-        // this.refreshBlink();
       }
     };
 
-    Terminal.prototype.startBlink = function() {
-      if (!this.cursorBlink) return;
-      var self = this;
-      this._blinker = function() {
-        self._cursorBlink();
-      };
-      this._blink = setInterval(this._blinker, 500);
-    };
-
-    Terminal.prototype.refreshBlink = function() {
-      if (!this.cursorBlink) return;
-      clearInterval(this._blink);
-      this._blink = setInterval(this._blinker, 500);
-    };
-
     Terminal.prototype.scroll = function() {
       var row;
 
diff --git a/test/addons/test.js b/test/addons/test.js
new file mode 100644 (file)
index 0000000..ba689e6
--- /dev/null
@@ -0,0 +1,10 @@
+var assert = require('chai').assert;
+var Terminal = require('../../src/xterm');
+
+describe('xterm.js addons', function() {
+  it('should load addons with Terminal.loadAddon', function () {
+    Terminal.loadAddon('attach');
+    // Test that function was loaded successfully
+    assert.equal(typeof Terminal.prototype.attach, 'function');
+  });
+});
index 6f7a07914d8bc6b85899cdbf2cfc496ac48d8d95..fcfd8832408a11da5abb73e6233ef1a2cd18609a 100644 (file)
@@ -79,7 +79,7 @@ describe('xterm.js', function() {
       xterm.handler = function() {};
       xterm.showCursor = function() {};
       xterm.clearSelection = function() {};
-    })
+    });
 
     describe('On Mac OS', function() {
       beforeEach(function() {