--- /dev/null
+/**
+ * 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);
+}
+++ /dev/null
-/**
- * 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 };
--- /dev/null
+import { assert } from 'chai';
+import { Viewport } from './Viewport';
+
+describe('Viewport', () => {
+ var terminal;
+ var viewportElement;
+ var charMeasureElement;
+ var viewport;
+ var scrollAreaElement;
+
+ const CHARACTER_HEIGHT = 10;
+
+ beforeEach(() => {
+ terminal = {
+ lines: [],
+ rows: 0,
+ ydisp: 0,
+ on: () => {},
+ rowContainer: {
+ style: {
+ lineHeight: 0
+ }
+ }
+ };
+ viewportElement = {
+ addEventListener: () => {},
+ style: {
+ height: 0,
+ lineHeight: 0
+ }
+ };
+ scrollAreaElement = {
+ style: {
+ height: 0
+ }
+ };
+ charMeasureElement = {
+ getBoundingClientRect: () => {
+ return { width: null, height: CHARACTER_HEIGHT };
+ }
+ };
+ viewport = new Viewport(terminal, viewportElement, scrollAreaElement, charMeasureElement);
+ });
+
+ 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 = () => {
+ 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', () => {
+ terminal.lines.push('');
+ terminal.lines.push('');
+ terminal.rows = 1;
+ viewport.refresh();
+ assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
+ charMeasureElement.getBoundingClientRect = () => {
+ return { width: null, height: 20 };
+ };
+ viewport.refresh();
+ assert.equal(viewportElement.style.height, 20 + 'px');
+ });
+ });
+
+ describe('syncScrollArea', () => {
+ it('should sync the scroll area', () => {
+ terminal.lines.push('');
+ terminal.rows = 1;
+ assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px');
+ viewport.syncScrollArea();
+ assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
+ assert.equal(scrollAreaElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
+ terminal.lines.push('');
+ viewport.syncScrollArea();
+ assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
+ assert.equal(scrollAreaElement.style.height, 2 * CHARACTER_HEIGHT + 'px');
+ });
+ });
+});
--- /dev/null
+/**
+ * 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();
+ };
+}
+++ /dev/null
-var assert = require('chai').assert;
-var Terminal = require('../xterm');
-
-describe('Viewport', function () {
- var terminal;
- var viewportElement;
- var charMeasureElement;
- var viewport;
- var scrollAreaElement;
-
- var CHARACTER_HEIGHT = 10;
-
- beforeEach(function () {
- terminal = {
- lines: [],
- rows: 0,
- ydisp: 0,
- on: function () {},
- rowContainer: {
- style: {
- lineHeight: 0
- }
- }
- };
- viewportElement = {
- addEventListener: function () {},
- style: {
- height: 0,
- lineHeight: 0
- }
- };
- scrollAreaElement = {
- style: {
- height: 0
- }
- };
- charMeasureElement = {
- getBoundingClientRect: function () {
- return { width: null, height: CHARACTER_HEIGHT };
- }
- };
- viewport = new Terminal.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 () {
- assert.equal(viewportElement.style.lineHeight, CHARACTER_HEIGHT + 'px');
- assert.equal(terminal.rowContainer.style.lineHeight, CHARACTER_HEIGHT + 'px');
- charMeasureElement.getBoundingClientRect = function () {
- 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 () {
- terminal.lines.push('');
- terminal.lines.push('');
- terminal.rows = 1;
- viewport.refresh();
- assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
- charMeasureElement.getBoundingClientRect = function () {
- return { width: null, height: 20 };
- };
- viewport.refresh();
- assert.equal(viewportElement.style.height, 20 + 'px');
- });
- });
-
- describe('syncScrollArea', function () {
- it('should sync the scroll area', function () {
- terminal.lines.push('');
- terminal.rows = 1;
- assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px');
- viewport.syncScrollArea();
- assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
- assert.equal(scrollAreaElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
- terminal.lines.push('');
- viewport.syncScrollArea();
- assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
- assert.equal(scrollAreaElement.style.height, 2 * CHARACTER_HEIGHT + 'px');
- });
- });
-});