ttyplayer
Version:
====
1,159 lines (1,056 loc) • 161 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["TTYPlayer"] = factory();
else
root["TTYPlayer"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/dist";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
__webpack_require__(1);
var _playerCore = __webpack_require__(5);
var _playerCore2 = __webpack_require__(8).interopRequireDefault(_playerCore);
var _select = __webpack_require__(13);
var _select2 = __webpack_require__(8).interopRequireDefault(_select);
var _component = __webpack_require__(14);
var _component2 = __webpack_require__(8).interopRequireDefault(_component);
var _decode = __webpack_require__(15);
var _decode2 = __webpack_require__(8).interopRequireDefault(_decode);
var _utils = __webpack_require__(7);
var _player = __webpack_require__(16);
var _player2 = __webpack_require__(8).interopRequireDefault(_player);
var defaultCols = 80;
var defaultRows = 20;
var TTYPlayer = function (_Component) {
__webpack_require__(8).inherits(TTYPlayer, _Component);
function TTYPlayer(options) {
__webpack_require__(8).classCallCheck(this, TTYPlayer);
var _this = __webpack_require__(8).possibleConstructorReturn(this, (TTYPlayer.__proto__ || Object.getPrototypeOf(TTYPlayer)).call(this));
var optionsCopy = (0, _utils.assign)({}, options);
if (!optionsCopy.rows) {
optionsCopy.rows = defaultRows;
}
if (!optionsCopy.cols) {
optionsCopy.cols = defaultCols;
}
_this.options = optionsCopy;
_this.mount(options.parent);
_this.createCorePlayer();
_this.delegate();
_this.createSpeedSelect();
_this.bindEvent();
_this.set('isPlaying', false);
return _this;
}
__webpack_require__(8).createClass(TTYPlayer, [{
key: 'onChange',
value: function onChange(key, value) {
if (key === 'isPlaying') {
this.refs.playButton.classList[value ? 'add' : 'remove']('tty-hide');
this.refs.pauseButton.classList[value ? 'remove' : 'add']('tty-hide');
return;
}
}
}, {
key: 'mount',
value: function mount(parentNode) {
var _$ = (0, _utils.element)(_player2.default);
var element = _$.element;
var refs = _$.refs;
parentNode.appendChild(element);
this.element = element;
this.parentNode = parentNode;
this.options.parent = refs.body;
this.refs = refs;
}
}, {
key: 'unmount',
value: function unmount() {
this.parentNode.removeChild(this.element);
}
}, {
key: 'delegate',
value: function delegate() {
var _this2 = this;
var player = this.player;['play', 'resume', 'pause'].forEach(function (method) {
_this2[method] = player[method].bind(player);
});
}
}, {
key: 'bindEvent',
value: function bindEvent() {
var _this3 = this;
this.refs.playButton.addEventListener('click', this.resume);
this.refs.pauseButton.addEventListener('click', this.pause);
this.player.on('play', function () {
_this3.set('isPlaying', true);
});
this.player.on('pause', function () {
_this3.set('isPlaying', false);
});
this.speedSelect.on('change', function (value) {
_this3.player.speed = value;
});
}
}, {
key: 'unbindEvent',
value: function unbindEvent() {
this.refs.playButton.removeEventListener('click', this.resume);
this.refs.pauseButton.removeEventListener('click', this.pause);
}
}, {
key: 'createCorePlayer',
value: function createCorePlayer() {
this.player = new _playerCore2.default(this.options);
}
}, {
key: 'createSpeedSelect',
value: function createSpeedSelect() {
this.speedSelect = new _select2.default(this.refs.speedButton, this.refs.speedSelect);
}
}, {
key: 'load',
value: function load(url) {
var _this4 = this;
(0, _utils.fetchArrayBuffer)(url, function (err, data) {
if (err) {
return _this4.emit('loadError', err);
}
var frames = void 0;
try {
frames = (0, _decode2.default)(data);
} catch (err) {
console.error(err);
return _this4.emit('loadError', err);
}
_this4.play(frames);
});
}
}, {
key: 'destroy',
value: function destroy() {
this.player.destroy();
this.speedSelect.destroy();
this.unbindEvent();
this.removeAllListeners();
this.unmount();
}
}]);
return TTYPlayer;
}(_component2.default);
TTYPlayer.VERSION = ('0.2.1');
module.exports = TTYPlayer;
/***/ },
/* 1 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 2 */,
/* 3 */,
/* 4 */,
/* 5 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _timer = __webpack_require__(6);
var _timer2 = __webpack_require__(8).interopRequireDefault(_timer);
var _xterm = __webpack_require__(9);
var _xterm2 = __webpack_require__(8).interopRequireDefault(_xterm);
var _utils = __webpack_require__(7);
var EventEmitter = _xterm2.default.EventEmitter;
var TTYCorePlayer = function (_EventEmitter) {
__webpack_require__(8).inherits(TTYCorePlayer, _EventEmitter);
function TTYCorePlayer(options) {
__webpack_require__(8).classCallCheck(this, TTYCorePlayer);
var _this = __webpack_require__(8).possibleConstructorReturn(this, (TTYCorePlayer.__proto__ || Object.getPrototypeOf(TTYCorePlayer)).call(this));
var term = new _xterm2.default(options);
term.open();
_this.term = term;
return _this;
}
__webpack_require__(8).createClass(TTYCorePlayer, [{
key: 'atEnd',
value: function atEnd() {
return this.step === this.frames.length;
}
}, {
key: 'play',
value: function play(frames) {
if (frames) {
this.frames = frames;
}
this.term.reset();
this.step = 0;
this.renderFrame();
this.emit('play');
}
}, {
key: 'pause',
value: function pause() {
this._nextTimer.pause();
this.emit('pause');
}
}, {
key: 'resume',
value: function resume() {
this._nextTimer.resume();
this.emit('play');
}
}, {
key: 'renderFrame',
value: function renderFrame() {
var step = this.step;
var frames = this.frames;
var currentFrame = frames[step];
var nextFrame = frames[step + 1];
var str = currentFrame.content;
// It seems to be unnecessary and may cause an unexpected behavior.
// So I ignore it.
if (str !== '\u001b[?1h\u001b=') {
this.term.write(str);
}
this.step = step + 1;
this.next(currentFrame, nextFrame);
}
}, {
key: 'next',
value: function next(currentFrame, nextFrame) {
var _this2 = this;
if (nextFrame) {
this._nextTimer = new _timer2.default(function (_) {
return _this2.renderFrame();
}, nextFrame.time - currentFrame.time, this.speed);
} else if (this.repeat) {
this._nextTimer = new _timer2.default(function (_) {
return _this2.play();
}, this.interval, this.speed);
} else {
this.emit('end');
}
}
}, {
key: 'destroy',
value: function destroy() {
this.term.destroy();
this.removeAllListeners();
this._nextTimer && this._nextTimer.clear();
}
}]);
return TTYCorePlayer;
}(EventEmitter);
exports.default = TTYCorePlayer;
(0, _utils.assign)(TTYCorePlayer.prototype, {
speed: 1,
repeat: true,
interval: 3000
});
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _utils = __webpack_require__(7);
var Timer = function () {
function Timer(callback, time) {
var rate = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
__webpack_require__(8).classCallCheck(this, Timer);
this._rate = rate;
this._setTimeout(callback, time);
}
__webpack_require__(8).createClass(Timer, [{
key: '_setTimeout',
value: function _setTimeout(callback, time) {
var _this = this;
this._timer = setTimeout(function () {
callback();
_this._finish = true;
_this._rest = null;
}, time / this._rate);
this._finish = false;
this._time = time;
this._startTime = new Date();
this._callback = callback;
this._rest = null;
}
}, {
key: 'pause',
value: function pause() {
clearTimeout(this._timer);
this._rest = this.rest;
}
}, {
key: 'resume',
value: function resume() {
if (this._rest != null) {
this._setTimeout(this._callback, this._rest);
}
}
}, {
key: 'clear',
value: function clear() {
this.pause();
this._rest = null;
}
}, {
key: 'rate',
set: function set(x) {
this._rate = x;
if (this._rest == null) {
this.pause();
this.resume();
}
}
}, {
key: 'rest',
get: function get() {
if (this.finish) {
return 0;
} else if (this._rest) {
return this._rest;
} else {
var rest = this._time - (new Date() - this._startTime);
return rest > 0 ? rest : 0;
}
}
}, {
key: 'finish',
get: function get() {
return this._finish;
}
}]);
return Timer;
}();
exports.default = Timer;
/***/ },
/* 7 */
/***/ function(module, exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isArray = isArray;
exports.isString = isString;
exports.assign = assign;
exports.readUtf8 = readUtf8;
exports.fetchArrayBuffer = fetchArrayBuffer;
exports.element = element;
exports.closet = closet;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;
function isArray(arr) {
return toString.call(arr) === '[object Array]';
}
function isString(str) {
return typeof str === 'string';
}
function assign(a, b) {
for (var key in b) {
if (hasOwnProperty.call(b, key)) {
a[key] = b[key];
}
}
return a;
}
/**
* @param {ArrayBuffer} arrayBuffer
* @param {number} start
* @param {number} length
*/
function readUtf8(arrayBuffer, start, length) {
return decodeURIComponent(escape(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer, start, length))));
}
/**
* @param {string} url
* @param {function} callback
*/
function fetchArrayBuffer(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.send();
var error = new Error('XMLHttpRequest error.');
xhr.onload = function (_) {
if (!/^2/.test(xhr.status)) {
return callback(error);
}
callback(null, xhr.response);
};
xhr.onerror = function (_) {
return callback(error);
};
}
var div = document.createElement('div');
function element(template) {
div.innerHTML = template;
var refsElement = div.querySelectorAll('[ref]');
var refs = {};
for (var i = 0, len = refsElement.length; i < len; i++) {
var node = refsElement[i];
var ref = node.getAttribute('ref');
node.removeAttribute('ref');
refs[ref] = node;
}
return {
refs: refs,
element: div.children[0]
};
}
/**
* @param {Element} el
* @param {function(Element): boolean} cond
*/
function closet(el, cond) {
var elem = el;
while (elem && elem !== document) {
if (cond(elem)) {
return elem;
}
elem = elem.parentNode;
}
}
/***/ },
/* 8 */
/***/ function(module, exports) {
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
exports.inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + (typeof superClass === "undefined" ? "undefined" : _typeof(superClass)));
}subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};exports.possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}return call && ((typeof call === "undefined" ? "undefined" : _typeof(call)) === "object" || typeof call === "function") ? call : self;
};exports.createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);
}
}return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
};
}();exports.classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};exports.interopRequireDefault = function (obj) {
return obj && obj.__esModule ? obj : { default: obj };
};
/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {
'use strict';Object.defineProperty(exports,"__esModule",{value:true});__webpack_require__(10);__webpack_require__(11);/**
* xterm.js: xterm, in the browser
* Copyright (c) 2014, sourceLair Limited (www.sourcelair.com (MIT License)
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/exports.default=function(){/**
* Terminal Emulation References:
* http://vt100.net/
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
* http://invisible-island.net/vttest/
* http://www.inwap.com/pdp10/ansicode.txt
* http://linux.die.net/man/4/console_codes
* http://linux.die.net/man/7/urxvt
*/'use strict';/**
* Shared
*/var window=this,document=this.document;/**
* EventEmitter
*/function EventEmitter(){this._events=this._events||{};}EventEmitter.prototype.addListener=function(type,listener){this._events[type]=this._events[type]||[];this._events[type].push(listener);};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.removeListener=function(type,listener){if(!this._events[type])return;var obj=this._events[type],i=obj.length;while(i--){if(obj[i]===listener||obj[i].listener===listener){obj.splice(i,1);return;}}};EventEmitter.prototype.off=EventEmitter.prototype.removeListener;EventEmitter.prototype.removeAllListeners=function(type){if(this._events[type])delete this._events[type];};EventEmitter.prototype.once=function(type,listener){var self=this;function on(){var args=Array.prototype.slice.call(arguments);this.removeListener(type,on);return listener.apply(this,args);}on.listener=listener;return this.on(type,on);};EventEmitter.prototype.emit=function(type){if(!this._events[type])return;var args=Array.prototype.slice.call(arguments,1),obj=this._events[type],l=obj.length,i=0;for(;i<l;i++){obj[i].apply(this,args);}};EventEmitter.prototype.listeners=function(type){return this._events[type]=this._events[type]||[];};/**
* Encapsulates the logic for handling compositionstart, compositionupdate and compositionend
* events, displaying the in-progress composition to the UI and forwarding the final composition
* to the handler.
* @param {HTMLTextAreaElement} textarea The textarea that xterm uses for input.
* @param {HTMLElement} compositionView The element to display the in-progress composition in.
* @param {Terminal} terminal The Terminal to forward the finished composition to.
*/// function CompositionHelper(textarea, compositionView, terminal) {
// this.textarea = textarea
// this.compositionView = compositionView
// this.terminal = terminal
// // 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.
// this.isComposing = false
// // The input currently being composed, eg. via a mobile keyboard, speech input or IME.
// this.compositionText = null
// // The position within the input textarea's value of the current composition.
// this.compositionPosition = { start: null, end: null }
// // Whether a composition is in the process of being sent, setting this to false will cancel
// // any in-progress composition.
// this.isSendingComposition = false
// }
/**
* Handles the compositionstart event, activating the composition view.
*/// CompositionHelper.prototype.compositionstart = function() {
// this.isComposing = true
// this.compositionPosition.start = this.textarea.value.length
// this.compositionView.textContent = ''
// this.compositionView.classList.add('active')
// }
/**
* Handles the compositionupdate event, updating the composition view.
* @param {CompositionEvent} ev The event.
*/// CompositionHelper.prototype.compositionupdate = function(ev) {
// this.compositionView.textContent = ev.data
// this.updateCompositionElements()
// var self = this
// setTimeout(function() {
// self.compositionPosition.end = self.textarea.value.length
// }, 0)
// }
/**
* Handles the compositionend event, hiding the composition view and sending the composition to
* the handler.
*/// CompositionHelper.prototype.compositionend = function() {
// this.finalizeComposition(true)
// }
/**
* Handles the keydown event, routing any necessary events to the CompositionHelper functions.
* @return Whether the Terminal should continue processing the keydown event.
*/// CompositionHelper.prototype.keydown = function(ev) {
// if (this.isComposing || this.isSendingComposition) {
// if (ev.keyCode === 229) {
// // Continue composing if the keyCode is the "composition character"
// return false
// } else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
// // Continue composing if the keyCode is a modifier key
// return false
// } else {
// // 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.
// this.finalizeComposition(false)
// }
// }
// if (ev.keyCode === 229) {
// // 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.
// this.handleAnyTextareaChanges()
// return false
// }
// return true
// }
/**
* Finalizes the composition, resuming regular input actions. This is called when a composition
* is ending.
* @param {boolean} waitForPropogation Whether to wait for events to propogate before sending
* the input. This should be false if a non-composition keystroke is entered before the
* compositionend event is triggered, such as enter, so that the composition is send before
* the command is executed.
*/// CompositionHelper.prototype.finalizeComposition = function(waitForPropogation) {
// this.compositionView.classList.remove('active')
// this.isComposing = false
// this.clearTextareaPosition()
// if (!waitForPropogation) {
// // Cancel any delayed composition send requests and send the input immediately.
// this.isSendingComposition = false
// var input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end)
// this.terminal.handler(input)
// } else {
// // Make a deep copy of the composition position here as a new compositionstart event may
// // fire before the setTimeout executes.
// var currentCompositionPosition = {
// start: this.compositionPosition.start,
// end: this.compositionPosition.end,
// }
// // Since composition* events happen before the changes take place in the textarea on most
// // browsers, use a setTimeout with 0ms time to allow the native compositionend event to
// // complete. This ensures the correct character is retrieved, this solution was used
// // because:
// // - The compositionend event's data property is unreliable, at least on Chromium
// // - 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 following character if the following input is a vowel.
// var self = this
// this.isSendingComposition = true
// setTimeout(function () {
// // Ensure that the input has not already been sent
// if (self.isSendingComposition) {
// self.isSendingComposition = false
// var input
// if (self.isComposing) {
// // Use the end position to get the string if a new composition has started.
// input = self.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end)
// } else {
// // 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
// // (eg. 2) after a composition character.
// input = self.textarea.value.substring(currentCompositionPosition.start)
// }
// self.terminal.handler(input)
// }
// }, 0)
// }
// }
/**
* Apply any changes made to the textarea after the current event chain is allowed to complete.
* This should be called when not currently composing but a keydown event with the "composition
* character" (229) is triggered, in order to allow non-composition text to be entered when an
* IME is active.
*/// CompositionHelper.prototype.handleAnyTextareaChanges = function() {
// var oldValue = this.textarea.value
// var self = this
// setTimeout(function() {
// // Ignore if a composition has started since the timeout
// if (!self.isComposing) {
// var newValue = self.textarea.value
// var diff = newValue.replace(oldValue, '')
// if (diff.length > 0) {
// self.terminal.handler(diff)
// }
// }
// }, 0)
// }
/**
* Positions the composition view on top of the cursor and the textarea just below it (so the
* IME helper dialog is positioned correctly).
*/// CompositionHelper.prototype.updateCompositionElements = function() {
// if (!this.isComposing) {
// return
// }
// var cursor = this.terminal.element.querySelector('.terminal-cursor')
// if (cursor) {
// this.compositionView.style.left = cursor.offsetLeft + 'px'
// this.compositionView.style.top = cursor.offsetTop + 'px'
// this.textarea.style.left = cursor.offsetLeft + 'px'
// this.textarea.style.top = (cursor.offsetTop + cursor.offsetHeight) + 'px'
// }
// }
/**
* Clears the textarea's position so that the cursor does not blink on IE.
* @private
*/// CompositionHelper.prototype.clearTextareaPosition = function() {
// this.textarea.style.left = ''
// this.textarea.style.top = ''
// }
/**
* 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){if(size.height!==this.currentRowHeight){this.currentRowHeight=size.height;this.viewportElement.style.lineHeight=size.height+'px';this.terminal.rowContainer.style.lineHeight=size.height+'px';}if(this.lastRecordedViewportHeight!==this.terminal.rows){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.isApplicationMode){// Fix scroll bar in application mode
this.lastRecordedBufferLength=this.terminal.rows;this.refresh();return;}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;}};/**
* Sets the application mode of the viewport.
* @param {boolean} isApplicationMode Sets whether the terminal is in application mode. true
* for application mode (DECKPAM) and false for normal mode (DECKPNM).
*/Viewport.prototype.setApplicationMode=function(isApplicationMode){this.isApplicationMode=isApplicationMode;this.syncScrollArea();};/**
* 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();};/**
* States
*/var normal=0,escaped=1,csi=2,osc=3,charset=4,dcs=5,ignore=6;/**
* Terminal
*//**
* Creates a new `Terminal` object.
*
* @param {object} options An object containing a set of options, the available options are:
* - cursorBlink (boolean): Whether the terminal cursor blinks
*
* @public
* @class Xterm Xterm
* @alias module:xterm/src/xterm
*/function Terminal(options){var self=this;if(!(this instanceof Terminal)){return new Terminal(arguments[0],arguments[1],arguments[2]);}self.cancel=Terminal.cancel;EventEmitter.call(this);if(typeof options==='number'){options={cols:arguments[0],rows:arguments[1],handler:arguments[2]};}options=options||{};Object.keys(Terminal.defaults).forEach(function(key){if(options[key]==null){options[key]=Terminal.options[key];if(Terminal[key]!==Terminal.defaults[key]){options[key]=Terminal[key];}}self[key]=options[key];});if(options.colors.length===8){options.colors=options.colors.concat(Terminal._colors.slice(8));}else if(options.colors.length===16){options.colors=options.colors.concat(Terminal._colors.slice(16));}else if(options.colors.length===10){options.colors=options.colors.slice(0,-2).concat(Terminal._colors.slice(8,-2),options.colors.slice(-2));}else if(options.colors.length===18){options.colors=options.colors.concat(Terminal._colors.slice(16,-2),options.colors.slice(-2));}this.colors=options.colors;this.options=options;// this.context = options.context || window;
// this.document = options.document || document;
this.parent=options.body||options.parent||(document?document.getElementsByTagName('body')[0]:null);this.cols=options.cols||options.geometry[0];this.rows=options.rows||options.geometry[1];if(options.handler){this.on('data',options.handler);}/**
* The scroll position of the y cursor, ie. ybase + y = the y position within the entire
* buffer
*/this.ybase=0;/**
* The scroll position of the viewport
*/this.ydisp=0;/**
* The cursor's x position after ybase
*/this.x=0;/**
* The cursor's y position after ybase
*/this.y=0;/**
* Used to debounce the refresh function
*/this.isRefreshing=false;/**
* Whether there is a full terminal refresh queued
*/this.cursorState=0;this.cursorHidden=false;this.convertEol;this.state=0;this.queue='';this.scrollTop=0;this.scrollBottom=this.rows-1;this.customKeydownHandler=null;// modes
this.applicationKeypad=false;this.applicationCursor=false;this.originMode=false;this.insertMode=false;this.wraparoundMode=true;// defaults: xterm - true, vt100 - false
this.normal=null;// charset
this.charset=null;this.gcharset=null;this.glevel=0;this.charsets=[null];// mouse properties
this.decLocator;this.x10Mouse;this.vt200Mouse;this.vt300Mouse;this.normalMouse;this.mouseEvents;this.sendFocus;this.utfMouse;this.sgrMouse;this.urxvtMouse;// misc
this.element;this.children;this.refreshStart;this.refreshEnd;this.savedX;this.savedY;this.savedCols;// stream
this.readable=true;this.writable=true;this.defAttr=0<<18|257<<9|256<<0;this.curAttr=this.defAttr;this.params=[];this.currentParam=0;this.prefix='';this.postfix='';// leftover surrogate high from previous write invocation
this.surrogate_high='';/**
* An array of all lines in the entire buffer, including the prompt. The lines are array of
* characters which are 2-length arrays where [0] is an attribute and [1] is the character.
*/this.lines=[];var i=this.rows;while(i--){this.lines.push(this.blankLine());}this.tabs;this.setupStops();}inherits(Terminal,EventEmitter);/**
* back_color_erase feature for xterm.
*/Terminal.prototype.eraseAttr=function(){// if (this.is('screen')) return this.defAttr;
return this.defAttr&~0x1ff|this.curAttr&0x1ff;};/**
* Colors
*/// Colors 0-15
Terminal.tangoColors=[// dark:
'#2e3436','#cc0000','#4e9a06','#c4a000','#3465a4','#75507b','#06989a','#d3d7cf',// bright:
'#555753','#ef2929','#8ae234','#fce94f','#729fcf','#ad7fa8','#34e2e2','#eeeeec'];// Colors 0-15 + 16-255
// Much thanks to TooTallNate for writing this.
Terminal.colors=function(){var colors=Terminal.tangoColors.slice(),r=[0x00,0x5f,0x87,0xaf,0xd7,0xff],i;// 16-231
i=0;for(;i<216;i++){out(r[i/36%6|0],r[i/6%6|0],r[i%6]);}// 232-255 (grey)
i=0;for(;i<24;i++){r=8+i*10;out(r,r,r);}function out(r,g,b){colors.push('#'+hex(r)+hex(g)+hex(b));}function hex(c){c=c.toString(16);return c.length<2?'0'+c:c;}return colors;}();Terminal._colors=Terminal.colors.slice();Terminal.vcolors=function(){var out=[],colors=Terminal.colors,i=0,color;for(;i<256;i++){color=parseInt(colors[i].substring(1),16);out.push([color>>16&0xff,color>>8&0xff,color&0xff]);}return out;}();/**
* Options
*/Terminal.defaults={colors:Terminal.colors,theme:'default',convertEol:false,termName:'xterm',geometry:[80,24],cursorBlink:false,visualBell:false,popOnBell:false,scrollback:1000,screenKeys:false,debug:false,cancelEvents:false// programFeatures: false,
// focusKeys: false,
};Terminal.options={};Terminal.focus=null;each(keys(Terminal.defaults),function(key){Terminal[key]=Terminal.defaults[key];Terminal.options[key]=Terminal.defaults[key];});/**
* Focus the terminal. Delegates focus handling to the terminal's DOM element.
*/Terminal.prototype.focus=function(){return this.textarea.focus();};/**
* Binds the desired focus behavior on a given terminal object.
*
* @static
*/// Terminal.bindFocus = function (term) {
// on(term.textarea, 'focus', function (ev) {
// if (term.sendFocus) {
// term.send('\x1b[I')
// }
// term.element.classList.add('focus')
// term.showCursor()
// Terminal.focus = term
// term.emit('focus', {terminal: term})
// })
// }
/**
* Blur the terminal. Delegates blur handling to the terminal's DOM element.
*/Terminal.prototype.blur=function(){return this.textarea.blur();};/**
* Binds the desired blur behavior on a given terminal object.
*
* @static
*/// Terminal.bindBlur = function (term) {
// on(term.textarea, 'blur', function (ev) {
// term.refresh(term.y, term.y)
// if (term.sendFocus) {
// term.send('\x1b[O')
// }
// term.element.classList.remove('focus')
// Terminal.focus = null
// term.emit('blur', {terminal: term})
// })
// }
/**
* Initialize default behavior
*/Terminal.prototype.initGlobal=function(){var _this=this;// Terminal.bindPaste(this)
// Terminal.bindKeys(this)
// Terminal.bindCopy(this)
// Terminal.bindFocus(this)
// Terminal.bindBlur(this)
setTimeout(function(){_this.send('\x1b[I');_this.element.classList.add('focus');_this.showCursor();});};/**
* Bind to paste event and allow both keyboard and right-click pasting, without having the
* contentEditable value set to true.
*/// Terminal.bindPaste = function(term) {
// on([term.textarea, term.element], 'paste', function(ev) {
// ev.stopPropagation()
// if (ev.clipboardData) {
// var text = ev.clipboardData.getData('text/plain')
// term.handler(text)
// term.textarea.value = ''
// return term.cancel(ev)
// }
// })
// }
/**
* Prepares text copied from terminal selection, to be saved in the clipboard by:
* 1. stripping all trailing white spaces
* 2. converting all non-breaking spaces to regular spaces
* @param {string} text The copied text that needs processing for storing in clipboard
* @returns {string}
* @static
*/Terminal.prepareCopiedTextForClipboard=function(text){var space=String.fromCharCode(32),nonBreakingSpace=String.fromCharCode(160),allNonBreakingSpaces=new RegExp(nonBreakingSpace,'g'),processedText=text.split('\n').map(function(line){/**
* Strip all trailing white spaces and convert all non-breaking spaces to regular
* spaces.
*/var processedLine=line.replace(/\s+$/g,'').replace(allNonBreakingSpaces,space);return processedLine;}).join('\n');return processedText;};/**
* Apply key handling to the terminal
*/// Terminal.bindKeys = function(term) {
// on(term.element, 'keydown', function(ev) {
// if (document.activeElement != this) {
// return
// }
// term.keyDown(ev)
// }, true)
// on(term.element, 'keypress', function(ev) {
// if (document.activeElement != this) {
// return
// }
// term.keyPress(ev)
// }, true)
// on(term.element, 'keyup', term.focus.bind(term))
// on(term.textarea, 'keydown', function(ev) {
// term.keyDown(ev)
// }, true)
// on(term.textarea, 'keypress', function(ev) {
// term.keyPress(ev)
// // Truncate the textarea's value, since it is not needed
// this.value = ''
// }, true)
// on(term.textarea, 'compositionstart', term.compositionHelper.compositionstart.bind(term.compositionHelper))
// on(term.textarea, 'compositionupdate', term.compositionHelper.compositionupdate.bind(term.compositionHelper))
// on(term.textarea, 'compositionend', term.compositionHelper.compositionend.bind(term.compositionHelper))
// term.on('refresh', term.compositionHelper.updateCompositionElements.bind(term.compositionHelper))
// }
/**
* Binds copy functionality to the given terminal.
* @static
*/// Terminal.bindCopy = function(term) {
// on(term.element, 'copy', function(ev) {
// return // temporary
// })
// }
/**
* Insert the given row to the terminal or produce a new one
* if no row argument is passed. Return the inserted row.
* @param {HTMLElement} row (optional) The row to append to the terminal.
*/Terminal.prototype.insertRow=function(row){if((typeof row==='undefined'?'undefined':__webpack_require__(8).typeof(row))!='object'){row=document.createElement('div');}this.rowContainer.appendChild(row);this.children.push(row);return row;};/**
* Opens the terminal within an element.
*
* @param {HTMLElement} parent The element to create the terminal within.
*/Terminal.prototype.open=function(parent){var self=this,i=0,div;this.parent=parent||this.parent;if(!this.parent){throw new Error('Terminal requires a parent element.');}/*
* Grab global elements
*/this.context=this.parent.ownerDocument.defaultView;this.document=this.parent.ownerDocument;this.body=this.document.getElementsByTagName('body')[0];/*
* Parse User-Agent
*/if(this.context.navigator&&this.context.navigator.userAgent){this.isMSIE=!!~this.context.navigator.userAgent.indexOf('MSIE');}/*
* Find the users platform. We use this to interpret the meta key
* and ISO third level shifts.
* http://stackoverflow.com/questions/19877924/what-is-the-list-of-possible-values-for-navigator-platform-as-of-today
*/if(this.context.navigator&&this.context.navigator.platform){this.isMac=contains(this.context.navigator.platform,['Macintosh','MacIntel','MacPPC','Mac68K']);this.isIpad=this.context.navigator.platform==='iPad';this.isIphone=this.context.navigator.platform==='iPhone';this.isMSWindows=contains(this.context.navigator.platform,['Windows','Win16','Win32','WinCE']);}/*
* Create main element container
*/this.element=this.document.createElement('div');this.element.classList.add('terminal');this.element.classList.add('xterm');this.element.classList.add('xterm-theme-'+this.theme);this.element.style.height;this.element.setAttribute('tabindex',0);this.viewportElement=document.createElement('div');this.viewportElement.classList.add('xterm-viewport');this.element.appendChild(this.viewportElement);this.viewportScrollArea=document.createElement('div');this.viewportScrollArea.classList.add('xterm-scroll-area');this.viewportElement.appendChild(this.viewportScrollArea);/*
* Create the container that will hold the lines of the terminal and then
* produce the lines the lines.
*/this.rowContainer=document.createElement('div');this.rowContainer.classList.add('xterm-rows');this.element.appendChild(this.rowContainer);this.children=[];/*
* Create the container that will hold helpers like the textarea for
* capturing DOM Events. Then produce the helpers.
*/this.helperContainer=document.createElement('div');this.helperContainer.classList.add('xterm-helpers');// TODO: This should probably be inserted once it's filled to prevent an additional layout
this.element.appendChild(this.helperContainer);this.textarea=document.createElement('textarea');this.textarea.classList.add('xterm-helper-textarea');this.textarea.setAttribute('autocorrect','off');this.textarea.setAttribute('autocapitalize','off');this.textarea.setAttribute('spellcheck','false');this.textarea.tabIndex=0;// this.textarea.addEventListener('focus', function() {
// self.emit('focus', {terminal: self})
// })
// this.textarea.addEventListener('blur', function() {
// self.emit('blur', {terminal: self})
// })
this.helperContainer.appendChild(this.textarea);this.compositionView=document.createElement('div');this.compositionView.classList.add('composition-view');// this.compositionHelper = new CompositionHelper(this.textarea, this.compositionView, this)
this.helperContainer.appendChild(this.compositionView);this.charMeasureElement=document.createElement('div');this.charMeasureElement.classList.add('xterm-char-measure-element');this.charMeasureElement.innerHTML='W';this.helperContainer.appendChild(this.charMeasureElement);for(;i<this.rows;i++){this.insertRow();}this.parent.appendChild(this.element);this.viewport=new Viewport(this,this.viewportElement,this.viewportScrollArea,this.charMeasureElement);// Draw the screen.
this.refresh(0,this.rows-1);// Initialize global actions that
// need to be taken on the document.
this.initGlobal();// Ensure there is a Terminal.focus.
this.focus();on(this.element,'mouseup',function(){var selection=document.getSelection(),collapsed=selection.isCollapsed,isRange=typeof collapsed=='boolean'?!collapsed:selection.type=='Range';if(!isRange){self.focus();}});// Listen for mouse events and translate
// them into terminal mouse protocols.
// this.bindMouse()
// Figure out whether boldness affects
// the character width of monospace fonts.
if(Terminal.brokenBold==null){Terminal.brokenBold=isBoldBroken(this.document);}this.emit('open');};/**
* XTerm mouse events
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
* To better understand these
* the xterm code is very helpful:
* Relevant files:
* button.c, charproc.c, misc.c
* Relevant functions in xterm/button.c:
* BtnCode, EmitButtonCode, EditorButton, SendMousePosition
*/// Terminal.prototype.bindMouse = function() {
// var el = this.element, self = this, pressed = 32
// // mouseup, mousedown, wheel
// // left click: ^[[M 3<^[[M#3<
// // wheel up: ^[[M`3>
// function sendButton(ev) {
// var button
// , pos
// // get the xterm-style button
// button = getButton(ev)
// // get mouse coordinates
// pos = getCoords(ev)
// if (!pos) return
// sendEvent(button, pos)
// switch (ev.overrideType || ev.type) {
// case 'mousedown':
// pressed = button
// break
// case 'mouseup':
// // keep it at the left
// // button, just in case.
// pressed = 32
// break
// case 'wheel':
// // nothing. don't
// // interfere with
// // `pressed`.
// break
// }
// }
// // motion example of a left click:
// // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
// function sendMove(ev) {
// var button = pressed
// , pos
// pos = getCoords(ev)
// if (!pos) return
// // buttons marked as motions
// // are incremented by 32
// button += 32
// sendEvent(button, pos)
// }
// // encode button and
// // position to characters
// function encode(data, ch) {
// if (!self.utfMouse) {
// if (ch === 255) return data.push(0)
// if (ch > 127) ch = 127
// data.push(ch)
// } else {
// if (ch === 2047) return data.push(0)
// if (ch < 127) {
// data.push(ch)
// } else {
// if (ch > 2047) ch = 2047
// data.push(0xC0 | (ch >> 6))
// data.push(0x80 | (ch & 0x3F))
// }
// }
// }
// // send a mouse event:
// // regular/utf8: ^[[M Cb Cx Cy
// // urxvt: ^[[ Cb ; Cx ; Cy M
// // sgr: ^[[ Cb ; Cx ; Cy M/m
// // vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
// // locator: CSI P e ; P b ; P r ; P c ; P p & w
// function sendEvent(button, pos) {
// // self.emit('mouse', {
// // x: pos.x - 32,
// // y: pos.x - 32,
// // button: button
// // });
// if (self.vt300Mouse) {
// // NOTE: Unstable.
// // http://www.vt100.net/docs/vt3xx-gp/chapter15.html
// button &= 3
// pos.x -= 32
// pos.y -= 32
// var data = '\x1b[24'
// if (button === 0) data += '1'
// else if (button === 1) data += '3'
// else if (button === 2) data += '5'
// else if (button === 3) return
// else data += '0'
// data += '~[' + pos.x + ',' + pos.y + ']\r'
// self.send(data)
// return
// }
// if (self.decLocator) {
// // NOTE: Unstable.
// button &= 3
// pos.x -= 32
// pos.y -= 32
// if (button === 0) button = 2
// else if (button === 1) button = 4
// else if (button === 2) button = 6
// else if (button === 3) button = 3
// self.send('\x1b['
// + button
// + ';'
// + (button === 3 ? 4 : 0)
// + ';'
// + pos.y
// + ';'
// + pos.x
// + ';'
// + (pos.page || 0)
// + '&w')
// return
// }
// if (self.urxvtMouse) {
// pos.x -= 32
// pos.y -= 32
// pos.x++
// pos.y++
// self.send('\x1b[' + button + ';' + pos.x + ';' + pos.y + 'M')
// return
// }
// if (self.sgrMouse) {
// pos.x -= 32
// pos.y -= 32
// self.send('\x1b[<'
// + ((button & 3) === 3 ? button & ~3 : button)
// + ';'
//