UNPKG

ttyplayer

Version:

====

1,159 lines (1,056 loc) 161 kB
(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) // + ';' //