Merge remote-tracking branch 'origin/master' into 426_alt_scroll

This commit is contained in:
Daniel Imms
2018-03-08 09:28:02 -08:00
7 changed files with 255 additions and 255 deletions
+61 -61
View File
@@ -20,43 +20,43 @@ export class CompositionHelper {
* Whether input composition is currently happening, eg. via a mobile keyboard, speech input or * Whether input composition is currently happening, eg. via a mobile keyboard, speech input or
* IME. This variable determines whether the compositionText should be displayed on the UI. * IME. This variable determines whether the compositionText should be displayed on the UI.
*/ */
private isComposing: boolean; private _isComposing: boolean;
/** /**
* The position within the input textarea's value of the current composition. * The position within the input textarea's value of the current composition.
*/ */
private compositionPosition: IPosition; private _compositionPosition: IPosition;
/** /**
* Whether a composition is in the process of being sent, setting this to false will cancel any * Whether a composition is in the process of being sent, setting this to false will cancel any
* in-progress composition. * in-progress composition.
*/ */
private isSendingComposition: boolean; private _isSendingComposition: boolean;
/** /**
* Creates a new CompositionHelper. * Creates a new CompositionHelper.
* @param textarea The textarea that xterm uses for input. * @param _textarea The textarea that xterm uses for input.
* @param compositionView The element to display the in-progress composition in. * @param _compositionView The element to display the in-progress composition in.
* @param terminal The Terminal to forward the finished composition to. * @param _terminal The Terminal to forward the finished composition to.
*/ */
constructor( constructor(
private textarea: HTMLTextAreaElement, private _textarea: HTMLTextAreaElement,
private compositionView: HTMLElement, private _compositionView: HTMLElement,
private terminal: ITerminal private _terminal: ITerminal
) { ) {
this.isComposing = false; this._isComposing = false;
this.isSendingComposition = false; this._isSendingComposition = false;
this.compositionPosition = { start: null, end: null }; this._compositionPosition = { start: null, end: null };
} }
/** /**
* Handles the compositionstart event, activating the composition view. * Handles the compositionstart event, activating the composition view.
*/ */
public compositionstart(): void { public compositionstart(): void {
this.isComposing = true; this._isComposing = true;
this.compositionPosition.start = this.textarea.value.length; this._compositionPosition.start = this._textarea.value.length;
this.compositionView.textContent = ''; this._compositionView.textContent = '';
this.compositionView.classList.add('active'); this._compositionView.classList.add('active');
} }
/** /**
@@ -64,10 +64,10 @@ export class CompositionHelper {
* @param {CompositionEvent} ev The event. * @param {CompositionEvent} ev The event.
*/ */
public compositionupdate(ev: CompositionEvent): void { public compositionupdate(ev: CompositionEvent): void {
this.compositionView.textContent = ev.data; this._compositionView.textContent = ev.data;
this.updateCompositionElements(); this.updateCompositionElements();
setTimeout(() => { setTimeout(() => {
this.compositionPosition.end = this.textarea.value.length; this._compositionPosition.end = this._textarea.value.length;
}, 0); }, 0);
} }
@@ -76,7 +76,7 @@ export class CompositionHelper {
* the handler. * the handler.
*/ */
public compositionend(): void { public compositionend(): void {
this.finalizeComposition(true); this._finalizeComposition(true);
} }
/** /**
@@ -85,7 +85,7 @@ export class CompositionHelper {
* @return Whether the Terminal should continue processing the keydown event. * @return Whether the Terminal should continue processing the keydown event.
*/ */
public keydown(ev: KeyboardEvent): boolean { public keydown(ev: KeyboardEvent): boolean {
if (this.isComposing || this.isSendingComposition) { if (this._isComposing || this._isSendingComposition) {
if (ev.keyCode === 229) { if (ev.keyCode === 229) {
// Continue composing if the keyCode is the "composition character" // Continue composing if the keyCode is the "composition character"
return false; return false;
@@ -95,14 +95,14 @@ export class CompositionHelper {
} else { } else {
// Finish composition immediately. This is mainly here for the case where enter is // Finish composition immediately. This is mainly here for the case where enter is
// pressed and the handler needs to be triggered before the command is executed. // pressed and the handler needs to be triggered before the command is executed.
this.finalizeComposition(false); this._finalizeComposition(false);
} }
} }
if (ev.keyCode === 229) { if (ev.keyCode === 229) {
// If the "composition character" is used but gets to this point it means a non-composition // If the "composition character" is used but gets to this point it means a non-composition
// character (eg. numbers and punctuation) was pressed when the IME was active. // character (eg. numbers and punctuation) was pressed when the IME was active.
this.handleAnyTextareaChanges(); this._handleAnyTextareaChanges();
return false; return false;
} }
@@ -117,22 +117,22 @@ export class CompositionHelper {
* compositionend event is triggered, such as enter, so that the composition is send before * compositionend event is triggered, such as enter, so that the composition is send before
* the command is executed. * the command is executed.
*/ */
private finalizeComposition(waitForPropogation: boolean): void { private _finalizeComposition(waitForPropogation: boolean): void {
this.compositionView.classList.remove('active'); this._compositionView.classList.remove('active');
this.isComposing = false; this._isComposing = false;
this.clearTextareaPosition(); this._clearTextareaPosition();
if (!waitForPropogation) { if (!waitForPropogation) {
// Cancel any delayed composition send requests and send the input immediately. // Cancel any delayed composition send requests and send the input immediately.
this.isSendingComposition = false; this._isSendingComposition = false;
const input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end); const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end);
this.terminal.handler(input); this._terminal.handler(input);
} else { } else {
// Make a deep copy of the composition position here as a new compositionstart event may // Make a deep copy of the composition position here as a new compositionstart event may
// fire before the setTimeout executes. // fire before the setTimeout executes.
const currentCompositionPosition = { const currentCompositionPosition = {
start: this.compositionPosition.start, start: this._compositionPosition.start,
end: this.compositionPosition.end, end: this._compositionPosition.end,
}; };
// Since composition* events happen before the changes take place in the textarea on most // Since composition* events happen before the changes take place in the textarea on most
@@ -143,22 +143,22 @@ export class CompositionHelper {
// - The last compositionupdate event's data property does not always accurately describe // - The last compositionupdate event's data property does not always accurately describe
// the character, a counter example being Korean where an ending consonsant can move to // the character, a counter example being Korean where an ending consonsant can move to
// the following character if the following input is a vowel. // the following character if the following input is a vowel.
this.isSendingComposition = true; this._isSendingComposition = true;
setTimeout(() => { setTimeout(() => {
// Ensure that the input has not already been sent // Ensure that the input has not already been sent
if (this.isSendingComposition) { if (this._isSendingComposition) {
this.isSendingComposition = false; this._isSendingComposition = false;
let input; let input;
if (this.isComposing) { if (this._isComposing) {
// Use the end position to get the string if a new composition has started. // Use the end position to get the string if a new composition has started.
input = this.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end); input = this._textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
} else { } else {
// Don't use the end position here in order to pick up any characters after the // Don't use the end position here in order to pick up any characters after the
// composition has finished, for example when typing a non-composition character // composition has finished, for example when typing a non-composition character
// (eg. 2) after a composition character. // (eg. 2) after a composition character.
input = this.textarea.value.substring(currentCompositionPosition.start); input = this._textarea.value.substring(currentCompositionPosition.start);
} }
this.terminal.handler(input); this._terminal.handler(input);
} }
}, 0); }, 0);
} }
@@ -170,15 +170,15 @@ export class CompositionHelper {
* character" (229) is triggered, in order to allow non-composition text to be entered when an * character" (229) is triggered, in order to allow non-composition text to be entered when an
* IME is active. * IME is active.
*/ */
private handleAnyTextareaChanges(): void { private _handleAnyTextareaChanges(): void {
const oldValue = this.textarea.value; const oldValue = this._textarea.value;
setTimeout(() => { setTimeout(() => {
// Ignore if a composition has started since the timeout // Ignore if a composition has started since the timeout
if (!this.isComposing) { if (!this._isComposing) {
const newValue = this.textarea.value; const newValue = this._textarea.value;
const diff = newValue.replace(oldValue, ''); const diff = newValue.replace(oldValue, '');
if (diff.length > 0) { if (diff.length > 0) {
this.terminal.handler(diff); this._terminal.handler(diff);
} }
} }
}, 0); }, 0);
@@ -191,27 +191,27 @@ export class CompositionHelper {
* necessary as the IME events across browsers are not consistently triggered. * necessary as the IME events across browsers are not consistently triggered.
*/ */
public updateCompositionElements(dontRecurse?: boolean): void { public updateCompositionElements(dontRecurse?: boolean): void {
if (!this.isComposing) { if (!this._isComposing) {
return; return;
} }
if (this.terminal.buffer.isCursorInViewport) { if (this._terminal.buffer.isCursorInViewport) {
const cellHeight = Math.ceil(this.terminal.charMeasure.height * this.terminal.options.lineHeight); const cellHeight = Math.ceil(this._terminal.charMeasure.height * this._terminal.options.lineHeight);
const cursorTop = this.terminal.buffer.y * cellHeight; const cursorTop = this._terminal.buffer.y * cellHeight;
const cursorLeft = this.terminal.buffer.x * this.terminal.charMeasure.width; const cursorLeft = this._terminal.buffer.x * this._terminal.charMeasure.width;
this.compositionView.style.left = cursorLeft + 'px'; this._compositionView.style.left = cursorLeft + 'px';
this.compositionView.style.top = cursorTop + 'px'; this._compositionView.style.top = cursorTop + 'px';
this.compositionView.style.height = cellHeight + 'px'; this._compositionView.style.height = cellHeight + 'px';
this.compositionView.style.lineHeight = cellHeight + 'px'; this._compositionView.style.lineHeight = cellHeight + 'px';
// Sync the textarea to the exact position of the composition view so the IME knows where the // Sync the textarea to the exact position of the composition view so the IME knows where the
// text is. // text is.
const compositionViewBounds = this.compositionView.getBoundingClientRect(); const compositionViewBounds = this._compositionView.getBoundingClientRect();
this.textarea.style.left = cursorLeft + 'px'; this._textarea.style.left = cursorLeft + 'px';
this.textarea.style.top = cursorTop + 'px'; this._textarea.style.top = cursorTop + 'px';
this.textarea.style.width = compositionViewBounds.width + 'px'; this._textarea.style.width = compositionViewBounds.width + 'px';
this.textarea.style.height = compositionViewBounds.height + 'px'; this._textarea.style.height = compositionViewBounds.height + 'px';
this.textarea.style.lineHeight = compositionViewBounds.height + 'px'; this._textarea.style.lineHeight = compositionViewBounds.height + 'px';
} }
if (!dontRecurse) { if (!dontRecurse) {
@@ -223,8 +223,8 @@ export class CompositionHelper {
* Clears the textarea's position so that the cursor does not blink on IE. * Clears the textarea's position so that the cursor does not blink on IE.
* @private * @private
*/ */
private clearTextareaPosition(): void { private _clearTextareaPosition(): void {
this.textarea.style.left = ''; this._textarea.style.left = '';
this.textarea.style.top = ''; this._textarea.style.top = '';
} }
} }
+3 -3
View File
@@ -28,7 +28,7 @@ export class SoundManager implements ISoundManager {
if (this._audioContext) { if (this._audioContext) {
const bellAudioSource = this._audioContext.createBufferSource(); const bellAudioSource = this._audioContext.createBufferSource();
const context = this._audioContext; const context = this._audioContext;
this._audioContext.decodeAudioData(this.base64ToArrayBuffer(this.removeMimeType(this._terminal.options.bellSound)), (buffer) => { this._audioContext.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => {
bellAudioSource.buffer = buffer; bellAudioSource.buffer = buffer;
bellAudioSource.connect(context.destination); bellAudioSource.connect(context.destination);
bellAudioSource.start(0); bellAudioSource.start(0);
@@ -38,7 +38,7 @@ export class SoundManager implements ISoundManager {
} }
} }
private base64ToArrayBuffer(base64: string): ArrayBuffer { private _base64ToArrayBuffer(base64: string): ArrayBuffer {
const binaryString = window.atob(base64); const binaryString = window.atob(base64);
const len = binaryString.length; const len = binaryString.length;
const bytes = new Uint8Array(len); const bytes = new Uint8Array(len);
@@ -50,7 +50,7 @@ export class SoundManager implements ISoundManager {
return bytes.buffer; return bytes.buffer;
} }
private removeMimeType(dataURI: string): string { private _removeMimeType(dataURI: string): string {
// Split the input to get the mime-type and the data itself // Split the input to get the mime-type and the data itself
const splitUri = dataURI.split(','); const splitUri = dataURI.split(',');
+1 -1
View File
@@ -125,7 +125,7 @@ if (os.platform() !== 'win32') {
// Perform a synchronous .write(data) // Perform a synchronous .write(data)
xterm.writeBuffer.push(fromPty); xterm.writeBuffer.push(fromPty);
xterm.innerWrite(); xterm._innerWrite();
let fromEmulator = terminalToString(xterm); let fromEmulator = terminalToString(xterm);
console.log = CONSOLE_LOG; console.log = CONSOLE_LOG;
+2 -2
View File
@@ -29,11 +29,11 @@ describe('term.js addons', () => {
term.refresh = () => {}; term.refresh = () => {};
(<any>term).renderer = new MockRenderer(); (<any>term).renderer = new MockRenderer();
term.viewport = new MockViewport(); term.viewport = new MockViewport();
(<any>term).compositionHelper = new MockCompositionHelper(); (<any>term)._compositionHelper = new MockCompositionHelper();
// Force synchronous writes // Force synchronous writes
term.write = (data) => { term.write = (data) => {
term.writeBuffer.push(data); term.writeBuffer.push(data);
(<any>term).innerWrite(); (<any>term)._innerWrite();
}; };
(<any>term).element = { (<any>term).element = {
classList: { classList: {
+136 -136
View File
@@ -133,30 +133,30 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
/** /**
* The HTMLElement that the terminal is created in, set by Terminal.open. * The HTMLElement that the terminal is created in, set by Terminal.open.
*/ */
private parent: HTMLElement; private _parent: HTMLElement;
private context: Window; private _context: Window;
private document: Document; private _document: Document;
private body: HTMLBodyElement; private _body: HTMLBodyElement;
private viewportScrollArea: HTMLElement; private _viewportScrollArea: HTMLElement;
private viewportElement: HTMLElement; private _viewportElement: HTMLElement;
private helperContainer: HTMLElement; private _helperContainer: HTMLElement;
private compositionView: HTMLElement; private _compositionView: HTMLElement;
private charSizeStyleElement: HTMLStyleElement; private _charSizeStyleElement: HTMLStyleElement;
private visualBellTimer: number; private _visualBellTimer: number;
public browser: IBrowser = <any>Browser; public browser: IBrowser = <any>Browser;
public options: ITerminalOptions; public options: ITerminalOptions;
private colors: any; private _colors: any;
// TODO: This can be changed to an enum or boolean, 0 and 1 seem to be the only options // TODO: This can be changed to an enum or boolean, 0 and 1 seem to be the only options
public cursorState: number; public cursorState: number;
public cursorHidden: boolean; public cursorHidden: boolean;
public convertEol: boolean; public convertEol: boolean;
private sendDataQueue: string; private _sendDataQueue: string;
private customKeyEventHandler: CustomKeyEventHandler; private _customKeyEventHandler: CustomKeyEventHandler;
// modes // modes
public applicationKeypad: boolean; public applicationKeypad: boolean;
@@ -174,10 +174,10 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
public charsets: ICharset[]; public charsets: ICharset[];
// mouse properties // mouse properties
private decLocator: boolean; // This is unstable and never set private _decLocator: boolean; // This is unstable and never set
public x10Mouse: boolean; public x10Mouse: boolean;
public vt200Mouse: boolean; public vt200Mouse: boolean;
private vt300Mouse: boolean; // This is unstable and never set private _vt300Mouse: boolean; // This is unstable and never set
public normalMouse: boolean; public normalMouse: boolean;
public mouseEvents: boolean; public mouseEvents: boolean;
public sendFocus: boolean; public sendFocus: boolean;
@@ -186,13 +186,13 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
public urxvtMouse: boolean; public urxvtMouse: boolean;
// misc // misc
private refreshStart: number; private _refreshStart: number;
private refreshEnd: number; private _refreshEnd: number;
public savedCols: number; public savedCols: number;
// stream // stream
private readable: boolean; private _readable: boolean;
private writable: boolean; private _writable: boolean;
public defAttr: number; public defAttr: number;
public curAttr: number; public curAttr: number;
@@ -204,7 +204,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// user input states // user input states
public writeBuffer: string[]; public writeBuffer: string[];
private writeInProgress: boolean; private _writeInProgress: boolean;
/** /**
* Whether _xterm.js_ sent XOFF in order to catch up with the pty process. * Whether _xterm.js_ sent XOFF in order to catch up with the pty process.
@@ -212,26 +212,26 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* XOFF via ^S that it will not automatically resume when the writeBuffer goes * XOFF via ^S that it will not automatically resume when the writeBuffer goes
* below threshold. * below threshold.
*/ */
private xoffSentToCatchUp: boolean; private _xoffSentToCatchUp: boolean;
/** Whether writing has been stopped as a result of XOFF */ /** Whether writing has been stopped as a result of XOFF */
private writeStopped: boolean; private _writeStopped: boolean;
// leftover surrogate high from previous write invocation // leftover surrogate high from previous write invocation
private surrogateHigh: string; private _surrogateHigh: string;
// Store if user went browsing history in scrollback // Store if user went browsing history in scrollback
private userScrolling: boolean; private _userScrolling: boolean;
private inputHandler: InputHandler; private _inputHandler: InputHandler;
public soundManager: SoundManager; public soundManager: SoundManager;
private parser: Parser; private _parser: Parser;
public renderer: IRenderer; public renderer: IRenderer;
public selectionManager: SelectionManager; public selectionManager: SelectionManager;
public linkifier: ILinkifier; public linkifier: ILinkifier;
public buffers: BufferSet; public buffers: BufferSet;
public viewport: IViewport; public viewport: IViewport;
private compositionHelper: ICompositionHelper; private _compositionHelper: ICompositionHelper;
public charMeasure: CharMeasure; public charMeasure: CharMeasure;
private _mouseZoneManager: IMouseZoneManager; private _mouseZoneManager: IMouseZoneManager;
public mouseHelper: MouseHelper; public mouseHelper: MouseHelper;
@@ -258,10 +258,10 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
) { ) {
super(); super();
this.options = options; this.options = options;
this.setup(); this._setup();
} }
private setup(): void { private _setup(): void {
Object.keys(DEFAULT_OPTIONS).forEach((key) => { Object.keys(DEFAULT_OPTIONS).forEach((key) => {
if (this.options[key] == null) { if (this.options[key] == null) {
this.options[key] = DEFAULT_OPTIONS[key]; this.options[key] = DEFAULT_OPTIONS[key];
@@ -273,7 +273,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// this.context = options.context || window; // this.context = options.context || window;
// this.document = options.document || document; // this.document = options.document || document;
// TODO: WHy not document.body? // TODO: WHy not document.body?
this.parent = document ? document.body : null; this._parent = document ? document.body : null;
this.cols = this.options.cols; this.cols = this.options.cols;
this.rows = this.options.rows; this.rows = this.options.rows;
@@ -284,8 +284,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this.cursorState = 0; this.cursorState = 0;
this.cursorHidden = false; this.cursorHidden = false;
this.sendDataQueue = ''; this._sendDataQueue = '';
this.customKeyEventHandler = null; this._customKeyEventHandler = null;
// modes // modes
this.applicationKeypad = false; this.applicationKeypad = false;
@@ -302,8 +302,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// TODO: Can this be just []? // TODO: Can this be just []?
this.charsets = [null]; this.charsets = [null];
this.readable = true; this._readable = true;
this.writable = true; this._writable = true;
this.defAttr = (0 << 18) | (257 << 9) | (256 << 0); this.defAttr = (0 << 18) | (257 << 9) | (256 << 0);
this.curAttr = (0 << 18) | (257 << 9) | (256 << 0); this.curAttr = (0 << 18) | (257 << 9) | (256 << 0);
@@ -315,15 +315,15 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// user input states // user input states
this.writeBuffer = []; this.writeBuffer = [];
this.writeInProgress = false; this._writeInProgress = false;
this.xoffSentToCatchUp = false; this._xoffSentToCatchUp = false;
this.writeStopped = false; this._writeStopped = false;
this.surrogateHigh = ''; this._surrogateHigh = '';
this.userScrolling = false; this._userScrolling = false;
this.inputHandler = new InputHandler(this); this._inputHandler = new InputHandler(this);
this.parser = new Parser(this.inputHandler, this); this._parser = new Parser(this._inputHandler, this);
// Reuse renderer if the Terminal is being recreated via a reset call. // Reuse renderer if the Terminal is being recreated via a reset call.
this.renderer = this.renderer || null; this.renderer = this.renderer || null;
this.selectionManager = this.selectionManager || null; this.selectionManager = this.selectionManager || null;
@@ -538,8 +538,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
/** /**
* Initialize default behavior * Initialize default behavior
*/ */
private initGlobal(): void { private _initGlobal(): void {
this.bindKeys(); this._bindKeys();
// Bind clipboard functionality // Bind clipboard functionality
on(this.element, 'copy', (event: ClipboardEvent) => { on(this.element, 'copy', (event: ClipboardEvent) => {
@@ -585,7 +585,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
/** /**
* Apply key handling to the terminal * Apply key handling to the terminal
*/ */
private bindKeys(): void { private _bindKeys(): void {
const self = this; const self = this;
on(this.element, 'keydown', function (ev: KeyboardEvent): void { on(this.element, 'keydown', function (ev: KeyboardEvent): void {
if (document.activeElement !== this) { if (document.activeElement !== this) {
@@ -609,11 +609,11 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
on(this.textarea, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true); on(this.textarea, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true);
on(this.textarea, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true); on(this.textarea, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true);
on(this.textarea, 'compositionstart', () => this.compositionHelper.compositionstart()); on(this.textarea, 'compositionstart', () => this._compositionHelper.compositionstart());
on(this.textarea, 'compositionupdate', (e: CompositionEvent) => this.compositionHelper.compositionupdate(e)); on(this.textarea, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper.compositionupdate(e));
on(this.textarea, 'compositionend', () => this.compositionHelper.compositionend()); on(this.textarea, 'compositionend', () => this._compositionHelper.compositionend());
this.on('refresh', () => this.compositionHelper.updateCompositionElements()); this.on('refresh', () => this._compositionHelper.updateCompositionElements());
this.on('refresh', (data) => this.queueLinkification(data.start, data.end)); this.on('refresh', (data) => this._queueLinkification(data.start, data.end));
} }
/** /**
@@ -625,44 +625,44 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
let i = 0; let i = 0;
let div; let div;
this.parent = parent || this.parent; this._parent = parent || this._parent;
if (!this.parent) { if (!this._parent) {
throw new Error('Terminal requires a parent element.'); throw new Error('Terminal requires a parent element.');
} }
// Grab global elements // Grab global elements
this.context = this.parent.ownerDocument.defaultView; this._context = this._parent.ownerDocument.defaultView;
this.document = this.parent.ownerDocument; this._document = this._parent.ownerDocument;
this.body = <HTMLBodyElement>this.document.body; this._body = <HTMLBodyElement>this._document.body;
this._screenDprMonitor = new ScreenDprMonitor(); this._screenDprMonitor = new ScreenDprMonitor();
this._screenDprMonitor.setListener(() => this.emit('dprchange', window.devicePixelRatio)); this._screenDprMonitor.setListener(() => this.emit('dprchange', window.devicePixelRatio));
// Create main element container // Create main element container
this.element = this.document.createElement('div'); this.element = this._document.createElement('div');
this.element.classList.add('terminal'); this.element.classList.add('terminal');
this.element.classList.add('xterm'); this.element.classList.add('xterm');
this.element.setAttribute('tabindex', '0'); this.element.setAttribute('tabindex', '0');
this.parent.appendChild(this.element); this._parent.appendChild(this.element);
// Performance: Use a document fragment to build the terminal // Performance: Use a document fragment to build the terminal
// viewport and helper elements detached from the DOM // viewport and helper elements detached from the DOM
const fragment = document.createDocumentFragment(); const fragment = document.createDocumentFragment();
this.viewportElement = document.createElement('div'); this._viewportElement = document.createElement('div');
this.viewportElement.classList.add('xterm-viewport'); this._viewportElement.classList.add('xterm-viewport');
fragment.appendChild(this.viewportElement); fragment.appendChild(this._viewportElement);
this.viewportScrollArea = document.createElement('div'); this._viewportScrollArea = document.createElement('div');
this.viewportScrollArea.classList.add('xterm-scroll-area'); this._viewportScrollArea.classList.add('xterm-scroll-area');
this.viewportElement.appendChild(this.viewportScrollArea); this._viewportElement.appendChild(this._viewportScrollArea);
this.screenElement = document.createElement('div'); this.screenElement = document.createElement('div');
this.screenElement.classList.add('xterm-screen'); this.screenElement.classList.add('xterm-screen');
// Create the container that will hold helpers like the textarea for // Create the container that will hold helpers like the textarea for
// capturing DOM Events. Then produce the helpers. // capturing DOM Events. Then produce the helpers.
this.helperContainer = document.createElement('div'); this._helperContainer = document.createElement('div');
this.helperContainer.classList.add('xterm-helpers'); this._helperContainer.classList.add('xterm-helpers');
this.screenElement.appendChild(this.helperContainer); this.screenElement.appendChild(this._helperContainer);
fragment.appendChild(this.screenElement); fragment.appendChild(this.screenElement);
this._mouseZoneManager = new MouseZoneManager(this); this._mouseZoneManager = new MouseZoneManager(this);
@@ -680,23 +680,23 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this.textarea.tabIndex = 0; this.textarea.tabIndex = 0;
this.textarea.addEventListener('focus', () => this._onTextAreaFocus()); this.textarea.addEventListener('focus', () => this._onTextAreaFocus());
this.textarea.addEventListener('blur', () => this._onTextAreaBlur()); this.textarea.addEventListener('blur', () => this._onTextAreaBlur());
this.helperContainer.appendChild(this.textarea); this._helperContainer.appendChild(this.textarea);
this.compositionView = document.createElement('div'); this._compositionView = document.createElement('div');
this.compositionView.classList.add('composition-view'); this._compositionView.classList.add('composition-view');
this.compositionHelper = new CompositionHelper(this.textarea, this.compositionView, this); this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this);
this.helperContainer.appendChild(this.compositionView); this._helperContainer.appendChild(this._compositionView);
this.charSizeStyleElement = document.createElement('style'); this._charSizeStyleElement = document.createElement('style');
this.helperContainer.appendChild(this.charSizeStyleElement); this._helperContainer.appendChild(this._charSizeStyleElement);
this.charMeasure = new CharMeasure(document, this.helperContainer); this.charMeasure = new CharMeasure(document, this._helperContainer);
// Performance: Add viewport and helper elements from the fragment // Performance: Add viewport and helper elements from the fragment
this.element.appendChild(fragment); this.element.appendChild(fragment);
this.renderer = new Renderer(this, this.options.theme); this.renderer = new Renderer(this, this.options.theme);
this.options.theme = null; this.options.theme = null;
this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure); this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this.charMeasure);
this.viewport.onThemeChanged(this.renderer.colorManager.colors); this.viewport.onThemeChanged(this.renderer.colorManager.colors);
this.on('cursormove', () => this.renderer.onCursorMove()); this.on('cursormove', () => this.renderer.onCursorMove());
@@ -725,7 +725,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this.viewport.syncScrollArea(); this.viewport.syncScrollArea();
this.selectionManager.refresh(); this.selectionManager.refresh();
}); });
this.viewportElement.addEventListener('scroll', () => this.selectionManager.refresh()); this._viewportElement.addEventListener('scroll', () => this.selectionManager.refresh());
this.mouseHelper = new MouseHelper(this.renderer); this.mouseHelper = new MouseHelper(this.renderer);
@@ -742,7 +742,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
this.refresh(0, this.rows - 1); this.refresh(0, this.rows - 1);
// Initialize global actions that need to be taken on the document. // Initialize global actions that need to be taken on the document.
this.initGlobal(); this._initGlobal();
// Listen for mouse events and translate // Listen for mouse events and translate
// them into terminal mouse protocols. // them into terminal mouse protocols.
@@ -869,7 +869,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// button: button // button: button
// }); // });
if (self.vt300Mouse) { if (self._vt300Mouse) {
// NOTE: Unstable. // NOTE: Unstable.
// http://www.vt100.net/docs/vt3xx-gp/chapter15.html // http://www.vt100.net/docs/vt3xx-gp/chapter15.html
button &= 3; button &= 3;
@@ -886,7 +886,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
return; return;
} }
if (self.decLocator) { if (self._decLocator) {
// NOTE: Unstable. // NOTE: Unstable.
button &= 3; button &= 3;
pos.x -= 32; pos.x -= 32;
@@ -1029,19 +1029,19 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
} }
// bind events // bind events
if (this.normalMouse) on(this.document, 'mousemove', sendMove); if (this.normalMouse) on(this._document, 'mousemove', sendMove);
// x10 compatibility mode can't send button releases // x10 compatibility mode can't send button releases
if (!this.x10Mouse) { if (!this.x10Mouse) {
const handler = (ev: MouseEvent) => { const handler = (ev: MouseEvent) => {
sendButton(ev); sendButton(ev);
// TODO: Seems dangerous calling this on document? // TODO: Seems dangerous calling this on document?
if (this.normalMouse) off(this.document, 'mousemove', sendMove); if (this.normalMouse) off(this._document, 'mousemove', sendMove);
off(this.document, 'mouseup', handler); off(this._document, 'mouseup', handler);
return this.cancel(ev); return this.cancel(ev);
}; };
// TODO: Seems dangerous calling this on document? // TODO: Seems dangerous calling this on document?
on(this.document, 'mouseup', handler); on(this._document, 'mouseup', handler);
} }
return this.cancel(ev); return this.cancel(ev);
@@ -1073,7 +1073,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
} }
return; return;
} }
if (this.x10Mouse || this.vt300Mouse || this.decLocator) return; if (this.x10Mouse || this._vt300Mouse || this._decLocator) return;
sendButton(ev); sendButton(ev);
ev.preventDefault(); ev.preventDefault();
}); });
@@ -1104,8 +1104,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
*/ */
public destroy(): void { public destroy(): void {
super.destroy(); super.destroy();
this.readable = false; this._readable = false;
this.writable = false; this._writable = false;
this.handler = () => {}; this.handler = () => {};
this.write = () => {}; this.write = () => {};
if (this.element && this.element.parentNode) { if (this.element && this.element.parentNode) {
@@ -1131,7 +1131,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* @param {number} start The row to start from (between 0 and this.rows - 1). * @param {number} start The row to start from (between 0 and this.rows - 1).
* @param {number} end The row to end at (between start and this.rows - 1). * @param {number} end The row to end at (between start and this.rows - 1).
*/ */
private queueLinkification(start: number, end: number): void { private _queueLinkification(start: number, end: number): void {
if (this.linkifier) { if (this.linkifier) {
this.linkifier.linkifyRows(start, end); this.linkifier.linkifyRows(start, end);
} }
@@ -1171,13 +1171,13 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
if (!willBufferBeTrimmed) { if (!willBufferBeTrimmed) {
this.buffer.ybase++; this.buffer.ybase++;
// Only scroll the ydisp with ybase if the user has not scrolled up // Only scroll the ydisp with ybase if the user has not scrolled up
if (!this.userScrolling) { if (!this._userScrolling) {
this.buffer.ydisp++; this.buffer.ydisp++;
} }
} else { } else {
// When the buffer is full and the user has scrolled up, keep the text // When the buffer is full and the user has scrolled up, keep the text
// stable unless ydisp is right at the top // stable unless ydisp is right at the top
if (this.userScrolling) { if (this._userScrolling) {
this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0); this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0);
} }
} }
@@ -1191,7 +1191,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// Move the viewport to the bottom of the buffer unless the user is // Move the viewport to the bottom of the buffer unless the user is
// scrolling. // scrolling.
if (!this.userScrolling) { if (!this._userScrolling) {
this.buffer.ydisp = this.buffer.ybase; this.buffer.ydisp = this.buffer.ybase;
} }
@@ -1220,9 +1220,9 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
if (this.buffer.ydisp === 0) { if (this.buffer.ydisp === 0) {
return; return;
} }
this.userScrolling = true; this._userScrolling = true;
} else if (disp + this.buffer.ydisp >= this.buffer.ybase) { } else if (disp + this.buffer.ydisp >= this.buffer.ybase) {
this.userScrolling = false; this._userScrolling = false;
} }
const oldYdisp = this.buffer.ydisp; const oldYdisp = this.buffer.ydisp;
@@ -1272,54 +1272,54 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
// Send XOFF to pause the pty process if the write buffer becomes too large so // Send XOFF to pause the pty process if the write buffer becomes too large so
// xterm.js can catch up before more data is sent. This is necessary in order // xterm.js can catch up before more data is sent. This is necessary in order
// to keep signals such as ^C responsive. // to keep signals such as ^C responsive.
if (this.options.useFlowControl && !this.xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) { if (this.options.useFlowControl && !this._xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) {
// XOFF - stop pty pipe // XOFF - stop pty pipe
// XON will be triggered by emulator before processing data chunk // XON will be triggered by emulator before processing data chunk
this.send(C0.DC3); this.send(C0.DC3);
this.xoffSentToCatchUp = true; this._xoffSentToCatchUp = true;
} }
if (!this.writeInProgress && this.writeBuffer.length > 0) { if (!this._writeInProgress && this.writeBuffer.length > 0) {
// Kick off a write which will write all data in sequence recursively // Kick off a write which will write all data in sequence recursively
this.writeInProgress = true; this._writeInProgress = true;
// Kick off an async innerWrite so more writes can come in while processing data // Kick off an async innerWrite so more writes can come in while processing data
setTimeout(() => { setTimeout(() => {
this.innerWrite(); this._innerWrite();
}); });
} }
} }
private innerWrite(): void { private _innerWrite(): void {
const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE); const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE);
while (writeBatch.length > 0) { while (writeBatch.length > 0) {
const data = writeBatch.shift(); const data = writeBatch.shift();
// If XOFF was sent in order to catch up with the pty process, resume it if // If XOFF was sent in order to catch up with the pty process, resume it if
// the writeBuffer is empty to allow more data to come in. // the writeBuffer is empty to allow more data to come in.
if (this.xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) { if (this._xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) {
this.send(C0.DC1); this.send(C0.DC1);
this.xoffSentToCatchUp = false; this._xoffSentToCatchUp = false;
} }
this.refreshStart = this.buffer.y; this._refreshStart = this.buffer.y;
this.refreshEnd = this.buffer.y; this._refreshEnd = this.buffer.y;
// HACK: Set the parser state based on it's state at the time of return. // HACK: Set the parser state based on it's state at the time of return.
// This works around the bug #662 which saw the parser state reset in the // This works around the bug #662 which saw the parser state reset in the
// middle of parsing escape sequence in two chunks. For some reason the // middle of parsing escape sequence in two chunks. For some reason the
// state of the parser resets to 0 after exiting parser.parse. This change // state of the parser resets to 0 after exiting parser.parse. This change
// just sets the state back based on the correct return statement. // just sets the state back based on the correct return statement.
const state = this.parser.parse(data); const state = this._parser.parse(data);
this.parser.setState(state); this._parser.setState(state);
this.updateRange(this.buffer.y); this.updateRange(this.buffer.y);
this.refresh(this.refreshStart, this.refreshEnd); this.refresh(this._refreshStart, this._refreshEnd);
} }
if (this.writeBuffer.length > 0) { if (this.writeBuffer.length > 0) {
// Allow renderer to catch up before processing the next batch // Allow renderer to catch up before processing the next batch
setTimeout(() => this.innerWrite(), 0); setTimeout(() => this._innerWrite(), 0);
} else { } else {
this.writeInProgress = false; this._writeInProgress = false;
} }
} }
@@ -1341,7 +1341,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* the event should be processed by xterm.js. * the event should be processed by xterm.js.
*/ */
public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void { public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void {
this.customKeyEventHandler = customKeyEventHandler; this._customKeyEventHandler = customKeyEventHandler;
} }
/** /**
@@ -1410,11 +1410,11 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* @param {KeyboardEvent} ev The keydown event to be handled. * @param {KeyboardEvent} ev The keydown event to be handled.
*/ */
protected _keyDown(ev: KeyboardEvent): boolean { protected _keyDown(ev: KeyboardEvent): boolean {
if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {
return false; return false;
} }
if (!this.compositionHelper.keydown(ev)) { if (!this._compositionHelper.keydown(ev)) {
if (this.buffer.ybase !== this.buffer.ydisp) { if (this.buffer.ybase !== this.buffer.ydisp) {
this.scrollToBottom(); this.scrollToBottom();
} }
@@ -1424,9 +1424,9 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
const result = this._evaluateKeyEscapeSequence(ev); const result = this._evaluateKeyEscapeSequence(ev);
if (result.key === C0.DC3) { // XOFF if (result.key === C0.DC3) { // XOFF
this.writeStopped = true; this._writeStopped = true;
} else if (result.key === C0.DC1) { // XON } else if (result.key === C0.DC1) { // XON
this.writeStopped = false; this._writeStopped = false;
} }
if (result.scrollLines) { if (result.scrollLines) {
@@ -1821,7 +1821,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
protected _keyPress(ev: KeyboardEvent): boolean { protected _keyPress(ev: KeyboardEvent): boolean {
let key; let key;
if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) { if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) {
return false; return false;
} }
@@ -1858,14 +1858,14 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* @param {string} data * @param {string} data
*/ */
public send(data: string): void { public send(data: string): void {
if (!this.sendDataQueue) { if (!this._sendDataQueue) {
setTimeout(() => { setTimeout(() => {
this.handler(this.sendDataQueue); this.handler(this._sendDataQueue);
this.sendDataQueue = ''; this._sendDataQueue = '';
}, 1); }, 1);
} }
this.sendDataQueue += data; this._sendDataQueue += data;
} }
/** /**
@@ -1874,14 +1874,14 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
*/ */
public bell(): void { public bell(): void {
this.emit('bell'); this.emit('bell');
if (this.soundBell()) { if (this._soundBell()) {
this.soundManager.playBellSound(); this.soundManager.playBellSound();
} }
if (this.visualBell()) { if (this._visualBell()) {
this.element.classList.add('visual-bell-active'); this.element.classList.add('visual-bell-active');
clearTimeout(this.visualBellTimer); clearTimeout(this._visualBellTimer);
this.visualBellTimer = window.setTimeout(() => { this._visualBellTimer = window.setTimeout(() => {
this.element.classList.remove('visual-bell-active'); this.element.classList.remove('visual-bell-active');
}, 200); }, 200);
} }
@@ -1892,8 +1892,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
*/ */
public log(text: string, data?: any): void { public log(text: string, data?: any): void {
if (!this.options.debug) return; if (!this.options.debug) return;
if (!this.context.console || !this.context.console.log) return; if (!this._context.console || !this._context.console.log) return;
this.context.console.log(text, data); this._context.console.log(text, data);
} }
/** /**
@@ -1901,8 +1901,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
*/ */
public error(text: string, data?: any): void { public error(text: string, data?: any): void {
if (!this.options.debug) return; if (!this.options.debug) return;
if (!this.context.console || !this.context.console.error) return; if (!this._context.console || !this._context.console.error) return;
this.context.console.error(text, data); this._context.console.error(text, data);
} }
/** /**
@@ -1946,8 +1946,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* @param {number} y The number of rows to refresh next. * @param {number} y The number of rows to refresh next.
*/ */
public updateRange(y: number): void { public updateRange(y: number): void {
if (y < this.refreshStart) this.refreshStart = y; if (y < this._refreshStart) this._refreshStart = y;
if (y > this.refreshEnd) this.refreshEnd = y; if (y > this._refreshEnd) this._refreshEnd = y;
// if (y > this.refreshEnd) { // if (y > this.refreshEnd) {
// this.refreshEnd = y; // this.refreshEnd = y;
// if (y > this.rows - 1) { // if (y > this.rows - 1) {
@@ -1960,8 +1960,8 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* Set the range of refreshing to the maximum value * Set the range of refreshing to the maximum value
*/ */
public maxRange(): void { public maxRange(): void {
this.refreshStart = 0; this._refreshStart = 0;
this.refreshEnd = this.rows - 1; this._refreshEnd = this.rows - 1;
} }
/** /**
@@ -2099,7 +2099,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
* Emit the 'title' event and populate the given title. * Emit the 'title' event and populate the given title.
* @param {string} title The title to populate in the event. * @param {string} title The title to populate in the event.
*/ */
private handleTitle(title: string): void { public handleTitle(title: string): void {
/** /**
* This event is emitted when the title of the terminal is changed * This event is emitted when the title of the terminal is changed
* from inside the terminal. The parameter is the new title. * from inside the terminal. The parameter is the new title.
@@ -2154,11 +2154,11 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
public reset(): void { public reset(): void {
this.options.rows = this.rows; this.options.rows = this.rows;
this.options.cols = this.cols; this.options.cols = this.cols;
const customKeyEventHandler = this.customKeyEventHandler; const customKeyEventHandler = this._customKeyEventHandler;
const inputHandler = this.inputHandler; const inputHandler = this._inputHandler;
this.setup(); this._setup();
this.customKeyEventHandler = customKeyEventHandler; this._customKeyEventHandler = customKeyEventHandler;
this.inputHandler = inputHandler; this._inputHandler = inputHandler;
this.refresh(0, this.rows - 1); this.refresh(0, this.rows - 1);
this.viewport.syncScrollArea(); this.viewport.syncScrollArea();
} }
@@ -2186,13 +2186,13 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
return matchColor_(r1, g1, b1); return matchColor_(r1, g1, b1);
} }
private visualBell(): boolean { private _visualBell(): boolean {
return false; return false;
// return this.options.bellStyle === 'visual' || // return this.options.bellStyle === 'visual' ||
// this.options.bellStyle === 'both'; // this.options.bellStyle === 'both';
} }
private soundBell(): boolean { private _soundBell(): boolean {
return this.options.bellStyle === 'sound'; return this.options.bellStyle === 'sound';
// return this.options.bellStyle === 'sound' || // return this.options.bellStyle === 'sound' ||
// this.options.bellStyle === 'both'; // this.options.bellStyle === 'both';
+48 -48
View File
@@ -15,11 +15,11 @@ const FALLBACK_SCROLL_BAR_WIDTH = 15;
*/ */
export class Viewport implements IViewport { export class Viewport implements IViewport {
public scrollBarWidth: number = 0; public scrollBarWidth: number = 0;
private currentRowHeight: number = 0; private _currentRowHeight: number = 0;
private lastRecordedBufferLength: number = 0; private _lastRecordedBufferLength: number = 0;
private lastRecordedViewportHeight: number = 0; private _lastRecordedViewportHeight: number = 0;
private lastRecordedBufferHeight: number = 0; private _lastRecordedBufferHeight: number = 0;
private lastTouchY: number; private _lastTouchY: number;
// Stores a partial line amount when scrolling, this is used to keep track of how much of a line // Stores a partial line amount when scrolling, this is used to keep track of how much of a line
// is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a // is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a
@@ -28,43 +28,43 @@ export class Viewport implements IViewport {
/** /**
* Creates a new Viewport. * Creates a new Viewport.
* @param terminal The terminal this viewport belongs to. * @param _terminal The terminal this viewport belongs to.
* @param viewportElement The DOM element acting as the viewport. * @param _viewportElement The DOM element acting as the viewport.
* @param scrollArea The DOM element acting as the scroll area. * @param _scrollArea The DOM element acting as the scroll area.
* @param charMeasure A DOM element used to measure the character size of. the terminal. * @param _charMeasure A DOM element used to measure the character size of. the terminal.
*/ */
constructor( constructor(
private terminal: ITerminal, private _terminal: ITerminal,
private viewportElement: HTMLElement, private _viewportElement: HTMLElement,
private scrollArea: HTMLElement, private _scrollArea: HTMLElement,
private charMeasure: CharMeasure private _charMeasure: CharMeasure
) { ) {
// Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar. // Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar.
// Unfortunately the overlay scrollbar would be hidden underneath the screen element in that case, // Unfortunately the overlay scrollbar would be hidden underneath the screen element in that case,
// therefore we account for a standard amount to make it visible // therefore we account for a standard amount to make it visible
this.scrollBarWidth = (this.viewportElement.offsetWidth - this.scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH; this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this)); this._viewportElement.addEventListener('scroll', this._onScroll.bind(this));
// Perform this async to ensure the CharMeasure is ready. // Perform this async to ensure the CharMeasure is ready.
setTimeout(() => this.syncScrollArea(), 0); setTimeout(() => this.syncScrollArea(), 0);
} }
public onThemeChanged(colors: IColorSet): void { public onThemeChanged(colors: IColorSet): void {
this.viewportElement.style.backgroundColor = colors.background; this._viewportElement.style.backgroundColor = colors.background;
} }
/** /**
* Refreshes row height, setting line-height, viewport height and scroll area height if * Refreshes row height, setting line-height, viewport height and scroll area height if
* necessary. * necessary.
*/ */
private refresh(): void { private _refresh(): void {
if (this.charMeasure.height > 0) { if (this._charMeasure.height > 0) {
this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio; this._currentRowHeight = this._terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio;
this.lastRecordedViewportHeight = this.viewportElement.offsetHeight; this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;
const newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength) + (this.lastRecordedViewportHeight - this.terminal.renderer.dimensions.canvasHeight); const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._terminal.renderer.dimensions.canvasHeight);
if (this.lastRecordedBufferHeight !== newBufferHeight) { if (this._lastRecordedBufferHeight !== newBufferHeight) {
this.lastRecordedBufferHeight = newBufferHeight; this._lastRecordedBufferHeight = newBufferHeight;
this.scrollArea.style.height = this.lastRecordedBufferHeight + 'px'; this._scrollArea.style.height = this._lastRecordedBufferHeight + 'px';
} }
} }
} }
@@ -73,24 +73,24 @@ export class Viewport implements IViewport {
* Updates dimensions and synchronizes the scroll area if necessary. * Updates dimensions and synchronizes the scroll area if necessary.
*/ */
public syncScrollArea(): void { public syncScrollArea(): void {
if (this.lastRecordedBufferLength !== this.terminal.buffer.lines.length) { if (this._lastRecordedBufferLength !== this._terminal.buffer.lines.length) {
// If buffer height changed // If buffer height changed
this.lastRecordedBufferLength = this.terminal.buffer.lines.length; this._lastRecordedBufferLength = this._terminal.buffer.lines.length;
this.refresh(); this._refresh();
} else if (this.lastRecordedViewportHeight !== (<any>this.terminal).renderer.dimensions.canvasHeight) { } else if (this._lastRecordedViewportHeight !== (<any>this._terminal).renderer.dimensions.canvasHeight) {
// If viewport height changed // If viewport height changed
this.refresh(); this._refresh();
} else { } else {
// If size has changed, refresh viewport // If size has changed, refresh viewport
if (this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this.currentRowHeight) { if (this._terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
this.refresh(); this._refresh();
} }
} }
// Sync scrollTop // Sync scrollTop
const scrollTop = this.terminal.buffer.ydisp * this.currentRowHeight; const scrollTop = this._terminal.buffer.ydisp * this._currentRowHeight;
if (this.viewportElement.scrollTop !== scrollTop) { if (this._viewportElement.scrollTop !== scrollTop) {
this.viewportElement.scrollTop = scrollTop; this._viewportElement.scrollTop = scrollTop;
} }
} }
@@ -99,16 +99,16 @@ export class Viewport implements IViewport {
* terminal to scroll to it. * terminal to scroll to it.
* @param ev The scroll event. * @param ev The scroll event.
*/ */
private onScroll(ev: Event): void { private _onScroll(ev: Event): void {
// Don't attempt to scroll if the element is not visible, otherwise scrollTop will be corrupt // Don't attempt to scroll if the element is not visible, otherwise scrollTop will be corrupt
// which causes the terminal to scroll the buffer to the top // which causes the terminal to scroll the buffer to the top
if (!this.viewportElement.offsetParent) { if (!this._viewportElement.offsetParent) {
return; return;
} }
const newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight); const newRow = Math.round(this._viewportElement.scrollTop / this._currentRowHeight);
const diff = newRow - this.terminal.buffer.ydisp; const diff = newRow - this._terminal.buffer.ydisp;
this.terminal.scrollLines(diff, true); this._terminal.scrollLines(diff, true);
} }
/** /**
@@ -122,7 +122,7 @@ export class Viewport implements IViewport {
if (amount === 0) { if (amount === 0) {
return; return;
} }
this.viewportElement.scrollTop += amount; this._viewportElement.scrollTop += amount;
// Prevent the page from scrolling when the terminal scrolls // Prevent the page from scrolling when the terminal scrolls
ev.preventDefault(); ev.preventDefault();
} }
@@ -136,9 +136,9 @@ export class Viewport implements IViewport {
// Fallback to WheelEvent.DOM_DELTA_PIXEL // Fallback to WheelEvent.DOM_DELTA_PIXEL
let amount = ev.deltaY; let amount = ev.deltaY;
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
amount *= this.currentRowHeight; amount *= this._currentRowHeight;
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
amount *= this.currentRowHeight * this.terminal.rows; amount *= this._currentRowHeight * this._terminal.rows;
} }
return amount; return amount;
} }
@@ -157,12 +157,12 @@ export class Viewport implements IViewport {
// Fallback to WheelEvent.DOM_DELTA_LINE // Fallback to WheelEvent.DOM_DELTA_LINE
let amount = ev.deltaY; let amount = ev.deltaY;
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
amount /= this.currentRowHeight + 0.0; // Prevent integer division amount /= this._currentRowHeight + 0.0; // Prevent integer division
this._wheelPartialScroll += amount; this._wheelPartialScroll += amount;
amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1); amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1);
this._wheelPartialScroll %= 1; this._wheelPartialScroll %= 1;
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
amount *= this.terminal.rows; amount *= this._terminal.rows;
} }
return amount; return amount;
} }
@@ -172,7 +172,7 @@ export class Viewport implements IViewport {
* @param ev The touch event. * @param ev The touch event.
*/ */
public onTouchStart(ev: TouchEvent): void { public onTouchStart(ev: TouchEvent): void {
this.lastTouchY = ev.touches[0].pageY; this._lastTouchY = ev.touches[0].pageY;
} }
/** /**
@@ -180,12 +180,12 @@ export class Viewport implements IViewport {
* @param ev The touch event. * @param ev The touch event.
*/ */
public onTouchMove(ev: TouchEvent): void { public onTouchMove(ev: TouchEvent): void {
let deltaY = this.lastTouchY - ev.touches[0].pageY; let deltaY = this._lastTouchY - ev.touches[0].pageY;
this.lastTouchY = ev.touches[0].pageY; this._lastTouchY = ev.touches[0].pageY;
if (deltaY === 0) { if (deltaY === 0) {
return; return;
} }
this.viewportElement.scrollTop += deltaY; this._viewportElement.scrollTop += deltaY;
ev.preventDefault(); ev.preventDefault();
} }
} }
+4 -4
View File
@@ -237,7 +237,7 @@ class CursorBlinkStateManager {
constructor( constructor(
terminal: ITerminal, terminal: ITerminal,
private renderCallback: () => void private _renderCallback: () => void
) { ) {
this.isCursorVisible = true; this.isCursorVisible = true;
if (terminal.isFocused) { if (terminal.isFocused) {
@@ -272,7 +272,7 @@ class CursorBlinkStateManager {
this.isCursorVisible = true; this.isCursorVisible = true;
if (!this._animationFrame) { if (!this._animationFrame) {
this._animationFrame = window.requestAnimationFrame(() => { this._animationFrame = window.requestAnimationFrame(() => {
this.renderCallback(); this._renderCallback();
this._animationFrame = null; this._animationFrame = null;
}); });
} }
@@ -303,7 +303,7 @@ class CursorBlinkStateManager {
// Hide the cursor // Hide the cursor
this.isCursorVisible = false; this.isCursorVisible = false;
this._animationFrame = window.requestAnimationFrame(() => { this._animationFrame = window.requestAnimationFrame(() => {
this.renderCallback(); this._renderCallback();
this._animationFrame = null; this._animationFrame = null;
}); });
@@ -322,7 +322,7 @@ class CursorBlinkStateManager {
// Invert visibility and render // Invert visibility and render
this.isCursorVisible = !this.isCursorVisible; this.isCursorVisible = !this.isCursorVisible;
this._animationFrame = window.requestAnimationFrame(() => { this._animationFrame = window.requestAnimationFrame(() => {
this.renderCallback(); this._renderCallback();
this._animationFrame = null; this._animationFrame = null;
}); });
}, BLINK_INTERVAL); }, BLINK_INTERVAL);