mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge remote-tracking branch 'upstream/master' into 368_custom_handler_before_scroll
This commit is contained in:
@@ -4,7 +4,13 @@
|
||||
tsc
|
||||
|
||||
# Concat all xterm.js files into a single file and output as a UMD to dist/xterm.js
|
||||
browserify out/xterm.js --standalone Terminal -p [ tsify ] --outfile dist/xterm.js
|
||||
browserify ./out/xterm.js --standalone Terminal --debug --outfile ./dist/xterm.js
|
||||
cat ./dist/xterm.js | exorcist ./dist/xterm.js.map -b ./dist > ./dist/xterm.temp.js
|
||||
rm ./dist/xterm.js
|
||||
mv ./dist/xterm.temp.js ./dist/xterm.js
|
||||
|
||||
# Resolve the chain of sourcemaps so that ./dist/xterm.js.map points at ./src
|
||||
sorcery -i dist/xterm.js
|
||||
|
||||
# Copy all CSS files from src/ to dist/
|
||||
cd src
|
||||
|
||||
+4
-1
@@ -10,10 +10,13 @@
|
||||
"repository": "https://github.com/sourcelair/xterm.js",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^3.4.34",
|
||||
"@types/mocha": "^2.2.33",
|
||||
"@types/node": "^6.0.41",
|
||||
"browserify": "^13.1.0",
|
||||
"chai": "3.5.0",
|
||||
"docdash": "0.4.0",
|
||||
"exorcist": "^0.4.0",
|
||||
"express": "4.13.4",
|
||||
"express-ws": "2.0.0-rc.1",
|
||||
"glob": "^7.0.5",
|
||||
@@ -22,7 +25,7 @@
|
||||
"nodemon": "1.10.2",
|
||||
"pty.js": "0.3.1",
|
||||
"sleep": "^3.0.1",
|
||||
"tsify": "^1.0.7",
|
||||
"sorcery": "^0.10.0",
|
||||
"typescript": "^2.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* xterm.js: xterm, in the browser
|
||||
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
|
||||
*/
|
||||
|
||||
export interface ITerminal {
|
||||
rowContainer: HTMLElement;
|
||||
ydisp: number;
|
||||
lines: string[];
|
||||
rows: number;
|
||||
|
||||
on(event: string, callback: () => void);
|
||||
scrollDisp(disp: number, suppressScrollEvent: boolean);
|
||||
}
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
/**
|
||||
* xterm.js: xterm, in the browser
|
||||
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents the viewport of a terminal, the visible area within the larger buffer of output.
|
||||
* Logic for the virtual scroll bar is included in this object.
|
||||
* @param {Terminal} terminal The Terminal object.
|
||||
* @param {HTMLElement} viewportElement The DOM element acting as the viewport
|
||||
* @param {HTMLElement} charMeasureElement A DOM element used to measure the character size of
|
||||
* the terminal.
|
||||
*/
|
||||
function Viewport(terminal, viewportElement, scrollArea, charMeasureElement) {
|
||||
this.terminal = terminal;
|
||||
this.viewportElement = viewportElement;
|
||||
this.scrollArea = scrollArea;
|
||||
this.charMeasureElement = charMeasureElement;
|
||||
this.currentRowHeight = 0;
|
||||
this.lastRecordedBufferLength = 0;
|
||||
this.lastRecordedViewportHeight = 0;
|
||||
|
||||
this.terminal.on('scroll', this.syncScrollArea.bind(this));
|
||||
this.terminal.on('resize', this.syncScrollArea.bind(this));
|
||||
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));
|
||||
|
||||
this.syncScrollArea();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes row height, setting line-height, viewport height and scroll area height if
|
||||
* necessary.
|
||||
* @param {number|undefined} charSize A character size measurement bounding rect object, if it
|
||||
* doesn't exist it will be created.
|
||||
*/
|
||||
Viewport.prototype.refresh = function(charSize) {
|
||||
var size = charSize || this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height > 0) {
|
||||
var rowHeightChanged = size.height !== this.currentRowHeight;
|
||||
if (rowHeightChanged) {
|
||||
this.currentRowHeight = size.height;
|
||||
this.viewportElement.style.lineHeight = size.height + 'px';
|
||||
this.terminal.rowContainer.style.lineHeight = size.height + 'px';
|
||||
}
|
||||
var viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
|
||||
if (rowHeightChanged || viewportHeightChanged) {
|
||||
this.lastRecordedViewportHeight = this.terminal.rows;
|
||||
this.viewportElement.style.height = size.height * this.terminal.rows + 'px';
|
||||
}
|
||||
this.scrollArea.style.height = (size.height * this.lastRecordedBufferLength) + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates dimensions and synchronizes the scroll area if necessary.
|
||||
*/
|
||||
Viewport.prototype.syncScrollArea = function() {
|
||||
if (this.lastRecordedBufferLength !== this.terminal.lines.length) {
|
||||
// If buffer height changed
|
||||
this.lastRecordedBufferLength = this.terminal.lines.length;
|
||||
this.refresh();
|
||||
} else if (this.lastRecordedViewportHeight !== this.terminal.rows) {
|
||||
// If viewport height changed
|
||||
this.refresh();
|
||||
} else {
|
||||
// If size has changed, refresh viewport
|
||||
var size = this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height !== this.currentRowHeight) {
|
||||
this.refresh(size);
|
||||
}
|
||||
}
|
||||
|
||||
// Sync scrollTop
|
||||
var scrollTop = this.terminal.ydisp * this.currentRowHeight;
|
||||
if (this.viewportElement.scrollTop !== scrollTop) {
|
||||
this.viewportElement.scrollTop = scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles scroll events on the viewport, calculating the new viewport and requesting the
|
||||
* terminal to scroll to it.
|
||||
* @param {Event} ev The scroll event.
|
||||
*/
|
||||
Viewport.prototype.onScroll = function(ev) {
|
||||
var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
|
||||
var diff = newRow - this.terminal.ydisp;
|
||||
this.terminal.scrollDisp(diff, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual
|
||||
* scrolling to `onScroll`, this event needs to be attached manually by the consumer of
|
||||
* `Viewport`.
|
||||
* @param {WheelEvent} ev The mouse wheel event.
|
||||
*/
|
||||
Viewport.prototype.onWheel = function(ev) {
|
||||
if (ev.deltaY === 0) {
|
||||
// Do nothing if it's not a vertical scroll event
|
||||
return;
|
||||
}
|
||||
// Fallback to WheelEvent.DOM_DELTA_PIXEL
|
||||
var multiplier = 1;
|
||||
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
||||
multiplier = this.currentRowHeight;
|
||||
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
||||
multiplier = this.currentRowHeight * this.terminal.rows;
|
||||
}
|
||||
this.viewportElement.scrollTop += ev.deltaY * multiplier;
|
||||
// Prevent the page from scrolling when the terminal scrolls
|
||||
ev.preventDefault();
|
||||
};
|
||||
|
||||
export { Viewport };
|
||||
@@ -1,21 +1,21 @@
|
||||
var assert = require('chai').assert;
|
||||
var Terminal = require('../xterm');
|
||||
import { assert } from 'chai';
|
||||
import { Viewport } from './Viewport';
|
||||
|
||||
describe('Viewport', function () {
|
||||
describe('Viewport', () => {
|
||||
var terminal;
|
||||
var viewportElement;
|
||||
var charMeasureElement;
|
||||
var viewport;
|
||||
var scrollAreaElement;
|
||||
|
||||
var CHARACTER_HEIGHT = 10;
|
||||
const CHARACTER_HEIGHT = 10;
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(() => {
|
||||
terminal = {
|
||||
lines: [],
|
||||
rows: 0,
|
||||
ydisp: 0,
|
||||
on: function () {},
|
||||
on: () => {},
|
||||
rowContainer: {
|
||||
style: {
|
||||
lineHeight: 0
|
||||
@@ -23,7 +23,7 @@ describe('Viewport', function () {
|
||||
}
|
||||
};
|
||||
viewportElement = {
|
||||
addEventListener: function () {},
|
||||
addEventListener: () => {},
|
||||
style: {
|
||||
height: 0,
|
||||
lineHeight: 0
|
||||
@@ -35,37 +35,31 @@ describe('Viewport', function () {
|
||||
}
|
||||
};
|
||||
charMeasureElement = {
|
||||
getBoundingClientRect: function () {
|
||||
getBoundingClientRect: () => {
|
||||
return { width: null, height: CHARACTER_HEIGHT };
|
||||
}
|
||||
};
|
||||
viewport = new Terminal.Viewport(terminal, viewportElement, scrollAreaElement, charMeasureElement);
|
||||
viewport = new Viewport(terminal, viewportElement, scrollAreaElement, charMeasureElement);
|
||||
});
|
||||
|
||||
describe('Public API', function () {
|
||||
it('should define Viewport.prototype.onWheel', function () {
|
||||
assert.isDefined(Terminal.Viewport.prototype.onWheel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('refresh', function () {
|
||||
it('should set the line-height of the terminal', function () {
|
||||
describe('refresh', () => {
|
||||
it('should set the line-height of the terminal', () => {
|
||||
assert.equal(viewportElement.style.lineHeight, CHARACTER_HEIGHT + 'px');
|
||||
assert.equal(terminal.rowContainer.style.lineHeight, CHARACTER_HEIGHT + 'px');
|
||||
charMeasureElement.getBoundingClientRect = function () {
|
||||
charMeasureElement.getBoundingClientRect = () => {
|
||||
return { width: null, height: 1 };
|
||||
};
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.lineHeight, '1px');
|
||||
assert.equal(terminal.rowContainer.style.lineHeight, '1px');
|
||||
});
|
||||
it('should set the height of the viewport when the line-height changed', function () {
|
||||
it('should set the height of the viewport when the line-height changed', () => {
|
||||
terminal.lines.push('');
|
||||
terminal.lines.push('');
|
||||
terminal.rows = 1;
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
|
||||
charMeasureElement.getBoundingClientRect = function () {
|
||||
charMeasureElement.getBoundingClientRect = () => {
|
||||
return { width: null, height: 20 };
|
||||
};
|
||||
viewport.refresh();
|
||||
@@ -73,8 +67,8 @@ describe('Viewport', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('syncScrollArea', function () {
|
||||
it('should sync the scroll area', function () {
|
||||
describe('syncScrollArea', () => {
|
||||
it('should sync the scroll area', () => {
|
||||
terminal.lines.push('');
|
||||
terminal.rows = 1;
|
||||
assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px');
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* xterm.js: xterm, in the browser
|
||||
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
|
||||
*/
|
||||
|
||||
import { ITerminal } from './Interfaces';
|
||||
|
||||
/**
|
||||
* Represents the viewport of a terminal, the visible area within the larger buffer of output.
|
||||
* Logic for the virtual scroll bar is included in this object.
|
||||
* @param viewportElement The DOM element acting as the viewport.
|
||||
* @param scrollArea The DOM element acting as the scroll area.
|
||||
* @param charMeasureElement A DOM element used to measure the character size of. the terminal.
|
||||
*/
|
||||
export class Viewport {
|
||||
private currentRowHeight: number;
|
||||
private lastRecordedBufferLength: number;
|
||||
private lastRecordedViewportHeight: number;
|
||||
|
||||
constructor(
|
||||
private terminal: ITerminal,
|
||||
private viewportElement: HTMLElement,
|
||||
private scrollArea: HTMLElement,
|
||||
private charMeasureElement: HTMLElement
|
||||
) {
|
||||
this.currentRowHeight = 0;
|
||||
this.lastRecordedBufferLength = 0;
|
||||
this.lastRecordedViewportHeight = 0;
|
||||
|
||||
this.terminal.on('scroll', this.syncScrollArea.bind(this));
|
||||
this.terminal.on('resize', this.syncScrollArea.bind(this));
|
||||
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));
|
||||
|
||||
this.syncScrollArea();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes row height, setting line-height, viewport height and scroll area height if
|
||||
* necessary.
|
||||
* @param charSize A character size measurement bounding rect object, if it doesn't exist it will
|
||||
* be created.
|
||||
*/
|
||||
private refresh(charSize?: ClientRect): void {
|
||||
var size = charSize || this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height > 0) {
|
||||
var rowHeightChanged = size.height !== this.currentRowHeight;
|
||||
if (rowHeightChanged) {
|
||||
this.currentRowHeight = size.height;
|
||||
this.viewportElement.style.lineHeight = size.height + 'px';
|
||||
this.terminal.rowContainer.style.lineHeight = size.height + 'px';
|
||||
}
|
||||
var viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
|
||||
if (rowHeightChanged || viewportHeightChanged) {
|
||||
this.lastRecordedViewportHeight = this.terminal.rows;
|
||||
this.viewportElement.style.height = size.height * this.terminal.rows + 'px';
|
||||
}
|
||||
this.scrollArea.style.height = (size.height * this.lastRecordedBufferLength) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates dimensions and synchronizes the scroll area if necessary.
|
||||
*/
|
||||
public syncScrollArea(): void {
|
||||
if (this.lastRecordedBufferLength !== this.terminal.lines.length) {
|
||||
// If buffer height changed
|
||||
this.lastRecordedBufferLength = this.terminal.lines.length;
|
||||
this.refresh();
|
||||
} else if (this.lastRecordedViewportHeight !== this.terminal.rows) {
|
||||
// If viewport height changed
|
||||
this.refresh();
|
||||
} else {
|
||||
// If size has changed, refresh viewport
|
||||
var size = this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height !== this.currentRowHeight) {
|
||||
this.refresh(size);
|
||||
}
|
||||
}
|
||||
|
||||
// Sync scrollTop
|
||||
var scrollTop = this.terminal.ydisp * this.currentRowHeight;
|
||||
if (this.viewportElement.scrollTop !== scrollTop) {
|
||||
this.viewportElement.scrollTop = scrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles scroll events on the viewport, calculating the new viewport and requesting the
|
||||
* terminal to scroll to it.
|
||||
* @param ev The scroll event.
|
||||
*/
|
||||
private onScroll(ev: Event) {
|
||||
var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
|
||||
var diff = newRow - this.terminal.ydisp;
|
||||
this.terminal.scrollDisp(diff, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual
|
||||
* scrolling to `onScroll`, this event needs to be attached manually by the consumer of
|
||||
* `Viewport`.
|
||||
* @param ev The mouse wheel event.
|
||||
*/
|
||||
public onWheel(ev: WheelEvent) {
|
||||
if (ev.deltaY === 0) {
|
||||
// Do nothing if it's not a vertical scroll event
|
||||
return;
|
||||
}
|
||||
// Fallback to WheelEvent.DOM_DELTA_PIXEL
|
||||
var multiplier = 1;
|
||||
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
||||
multiplier = this.currentRowHeight;
|
||||
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
||||
multiplier = this.currentRowHeight * this.terminal.rows;
|
||||
}
|
||||
this.viewportElement.scrollTop += ev.deltaY * multiplier;
|
||||
// Prevent the page from scrolling when the terminal scrolls
|
||||
ev.preventDefault();
|
||||
};
|
||||
}
|
||||
+2
-1
@@ -193,6 +193,7 @@ describe('xterm.js', function() {
|
||||
};
|
||||
var event = {
|
||||
type: 'keydown',
|
||||
keyCode: 0,
|
||||
preventDefault: function(){},
|
||||
stopPropagation: function(){}
|
||||
};
|
||||
@@ -218,7 +219,7 @@ describe('xterm.js', function() {
|
||||
assert.equal(xterm.ydisp, startYDisp);
|
||||
xterm.scrollDisp(-1);
|
||||
assert.equal(xterm.ydisp, startYDisp - 1);
|
||||
xterm.keyDown();
|
||||
xterm.keyDown({ keyCode: 0 });
|
||||
assert.equal(xterm.ydisp, startYDisp - 1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { assert } from 'chai';
|
||||
import * as Keyboard from './Keyboard';
|
||||
|
||||
describe('Keyboard', () => {
|
||||
describe('isModifierOnlyKeyboardEvent', () => {
|
||||
it('should return true when only modifier keys are used', () => {
|
||||
// Note that KeyboardEvent.keyCode is deprecated but we're using it to improve browser
|
||||
// compatibility. This helper returns the `any` type because KeyboardEvent doesn't exist under
|
||||
// NodeJS.
|
||||
function createEvent(keyCode: number): any {
|
||||
return { keyCode };
|
||||
}
|
||||
assert.isTrue(Keyboard.isModifierOnlyKeyboardEvent(createEvent(16)));
|
||||
assert.isTrue(Keyboard.isModifierOnlyKeyboardEvent(createEvent(17)));
|
||||
assert.isTrue(Keyboard.isModifierOnlyKeyboardEvent(createEvent(18)));
|
||||
assert.isTrue(Keyboard.isModifierOnlyKeyboardEvent(createEvent(91)));
|
||||
assert.isFalse(Keyboard.isModifierOnlyKeyboardEvent(createEvent(19)));
|
||||
assert.isFalse(Keyboard.isModifierOnlyKeyboardEvent(createEvent(90)));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* xterm.js: xterm, in the browser
|
||||
* Copyright (c) 2016, SourceLair Private Company <www.sourcelair.com> (MIT License)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Keyboard utilities module. This module contains utilities for dealing with keyboard interaction.
|
||||
* @module xterm/utils/Keyboard
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets whether a KeyboardEvent is made up entirely of modifier keys.
|
||||
*
|
||||
* @param event The event to check.
|
||||
* @return Whether the KeyboardEvent is made up entirely of modifier keys.
|
||||
*/
|
||||
export function isModifierOnlyKeyboardEvent(event: KeyboardEvent): boolean {
|
||||
return event.keyCode === 16 || // Shift
|
||||
event.keyCode === 17 || // Control
|
||||
event.keyCode === 18 || // Alt
|
||||
event.keyCode === 91; // Meta
|
||||
}
|
||||
+2
-1
@@ -36,6 +36,7 @@ import { EventEmitter } from './EventEmitter.js';
|
||||
import { Viewport } from './Viewport.js';
|
||||
import { rightClickHandler, pasteHandler, copyHandler } from './handlers/Clipboard.js';
|
||||
import * as Browser from './utils/Browser';
|
||||
import * as Keyboard from './utils/Keyboard';
|
||||
|
||||
/**
|
||||
* Terminal Emulation References:
|
||||
@@ -2430,7 +2431,7 @@ Terminal.prototype.keyDown = function(ev) {
|
||||
}
|
||||
|
||||
// Scroll down to prompt, whenever the user presses a key.
|
||||
if (this.ybase !== this.ydisp) {
|
||||
if (!Keyboard.isModifierOnlyKeyboardEvent(ev) && this.ybase !== this.ydisp) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@
|
||||
"target": "es5",
|
||||
"rootDir": "src",
|
||||
"allowJs": true,
|
||||
"outDir": "out"
|
||||
"outDir": "out",
|
||||
"sourceMap": true
|
||||
},
|
||||
"exclude": [
|
||||
"addons",
|
||||
|
||||
Reference in New Issue
Block a user