pax_global_header00006660000000000000000000000064134151461200014507gustar00rootroot0000000000000052 comment=711752a56a31375d63a71886dbe34e259c5a3bf8 xterm.js-3.8.1/000077500000000000000000000000001341514612000132725ustar00rootroot00000000000000xterm.js-3.8.1/.dockerignore000066400000000000000000000002331341514612000157440ustar00rootroot00000000000000node_modules/ *.swp .lock-wscript lib/ Makefile.gyp *.Makefile *.target.gyp.mk *.node example/*.log docs/ npm-debug.log /.idea/ .env build/ .vscode/ .git/ xterm.js-3.8.1/.editorconfig000066400000000000000000000003471341514612000157530ustar00rootroot00000000000000root = true [*] indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true end_of_line = lf [*.{j,t}s] max_line_length = 100 [*.css] indent_size = 4 [*.md] trim_trailing_whitespace = false xterm.js-3.8.1/.gitattributes000066400000000000000000000000141341514612000161600ustar00rootroot00000000000000* text=auto xterm.js-3.8.1/.github/000077500000000000000000000000001341514612000146325ustar00rootroot00000000000000xterm.js-3.8.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001341514612000170155ustar00rootroot00000000000000xterm.js-3.8.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000004311341514612000215050ustar00rootroot00000000000000--- name: Bug report about: Create a bug report --- ## Details - Browser and browser version: - OS version: - xterm.js version: ### Steps to reproduce 1. 2. xterm.js-3.8.1/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000003021341514612000225350ustar00rootroot00000000000000--- name: Feature request about: Suggest a feature or enhancement --- xterm.js-3.8.1/.github/ISSUE_TEMPLATE/question.md000066400000000000000000000005641341514612000212130ustar00rootroot00000000000000--- name: Question about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/xtermjs or https://gitter.im/sourcelair/xterm.js. --- 🛑 The issue tracker is not for questions 🛑 If you have a question, please ask it on https://stackoverflow.com/questions/tagged/xtermjs or https://gitter.im/sourcelair/xterm.js. xterm.js-3.8.1/.gitignore000066400000000000000000000004741341514612000152670ustar00rootroot00000000000000node_modules/ *.swp .lock-wscript lib/ Makefile.gyp *.Makefile *.target.gyp.mk *.node example/*.log docs/ npm-debug.log /.idea/ .env build/ .vscode/ .DS_Store fixtures/typings-test/*.js package-lock.json # Directories needed for code coverage coverage/ .nyc_output/ # Keep bundled code out of Git dist/ demo/dist/ xterm.js-3.8.1/.gitmodules000066400000000000000000000000001341514612000154350ustar00rootroot00000000000000xterm.js-3.8.1/.mailmap000066400000000000000000000005521341514612000147150ustar00rootroot00000000000000Antonis Kalipetis Antonis Kalipetis Daniel Imms Paris Kasidiaris Paris Kasidiaris Thanasis Daglis Thanasis Daglis xterm.js-3.8.1/.npmignore000066400000000000000000000011551341514612000152730ustar00rootroot00000000000000# Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - entries to be included must be negated with "!" !*.js !*.json # Whitelist - dist/ !dist/**/*.js !dist/**/*.js.map !dist/**/*.css # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.css # Whitelist - src/ !src/**/*.ts !src/**/*.d.ts !src/**/*.js !src/**/*.js.map !src/**/*.css # Whitelist - typings/ !typings/*.d.ts # Blacklist - (normal behavior) these will override any whitelist *.test.ts *.test.d.ts *.test.js *.test.js.map lib/test/ docs/ /.idea/ .vscode/ build/ fixtures/ coverage/ demo/ xterm.js-3.8.1/.npmrc000066400000000000000000000000221341514612000144040ustar00rootroot00000000000000package-lock=falsexterm.js-3.8.1/AUTHORS000066400000000000000000000116231341514612000143450ustar00rootroot00000000000000List of xterm.js contributors. Updated before every release. 7PH 7PH Adrian Labbé Aleksandr Andrienko Aleksandr Andriienko Alessandro Nadalin Alexander Olsson Alexandre Petit-Pas Alexey Kontsevoy Andres Mejia Anish Athalye Anthony Lapenna Antonin Stefanutti Antonis Kalipetis Anton Skshidlevsky Anton Yurovskykh Artem Arbatskiy Austin Robertson ayapi Ben Hall Benjamin Fischer Benjamin Raymond Benjamin Woodruff Bill Church Bob Reid bottleofwater Brandon Bayer Brian Mock Bruno Ribeiro Bruno Ribeito Carson Anderson CHaBou Christian Budde Christensen Christof Marti Christopher Jeffrey coderaiser Damien Tournoud Dan Brown Daniel Griffen Daniel Griffen Daniel Imms Daniel Risacher Dan Kaplun Darin Morrison dcylabs Dominik Csapak Edgar Andrés Margffoy Tuay Elliot Saba Exile Felipe Gasper Felix <30559812+felixse@users.noreply.github.com> ficristo Gary Ritchie hiro-su Ian Lewis imoses InDieTasten irokas Jakob Gillich Jan Kuri Jean Bruenn Jeff Principe Jeremy Danyow Jianhui Zhao Joao Moreno Joao Moreno Johannes Zellner Jon Austin Jon Kohler Jon Masters Jörg Breitbart Jose Anton jpoth Justin Luk Justin Mecham Kirill Merkushev Krasimir Tsonev Ledion Bitincka Linus Unnebäck Luca Lucian Buzzo Lukas Drgon Lukas Geiger Maël Nison Marc Dumais Marek Libra Markus F.X.J. Oberhumer Martin Chloride Martin Koppehel Martin Wang Matt Bierner Matthew James Michael Irwin Mikko Karvonen mofux muji Nicolas Ramz npezza93 Oleksandr Andriienko Paris Kasidiaris Paris Kasidiaris Peng Xiao Peter Baumgarten Philip Olson PowerHat <31401273+7PH@users.noreply.github.com> PowerHat pro-src <34285059+pro-src@users.noreply.github.com> pro-src Rick Baker runarberg Saad Malik Samuel Williams Saswat Das Saul Costa Segev Finer Shuanglei Tao sitzmar Steven Silvester stuicey Sven-Hendrik Haase t-amqi Thanasis Daglis thomas Thomas Zilz Tine Jozelj Tyler Jewell Vincent Woo Viraj Sinha yutaka YuviPanda ZHAO Xudong 赵雪珂 xterm.js-3.8.1/CODE_OF_CONDUCT.md000066400000000000000000000063251341514612000160770ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting [@Tyriar](https://twitter.com/Tyriar) or [@pariskasid](https://twitter.com/pariskasid) on Twitter. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org xterm.js-3.8.1/CONTRIBUTING.md000066400000000000000000000045341341514612000155310ustar00rootroot00000000000000# How to contribute to xterm.js - [Opening issues for bug reports or feature requests](#opening-issues) - [Contributing code](#contributing-code) ## Opening issues The preferred way to report bugs or request features is to use [GitHub issues](http://github.com/sourcelair/xterm.js/issues). Before opening an issue, read these pointers. ### Opening issues effectively - Include information about **the browser in which the problem occurred**. Even if you tested several browsers, and the problem occurred in all of them, mention this fact in the bug report. Also include browser version numbers and the operating system that you're on. - Mention which release of xterm.js you're using. Preferably, try also with the current HEAD of the master branch, to ensure the problem has not already been fixed. - Mention precisely what went wrong. What did you expect to happen? What happened instead? Describe the exact steps a maintainer has to take to make the problem occur. - If the problem can not be reproduced in the [demo of xterm.js](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo), please provide an HTML document that demonstrates the problem. - Be polite. Issues with an indignant or belligerent tone tend to be moved to the bottom of the pile. ## Contributing code - Make sure you have a [GitHub account](https://github.com/join) - Fork [xterm.js](https://github.com/sourcelair/xterm.js/) ([how to fork a repo](https://help.github.com/articles/fork-a-repo)) - Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running - Make your changes - If your changes are easy to test or likely to regress, add tests. Tests go into `test`, directory. - Follow the general code style of the rest of the project (see below). - Submit a pull request ([how to create a pull request](https://help.github.com/articles/fork-a-repo)). Don't put more than one feature/fix in a single pull request. By contributing code to xterm.js you - agree to license the contributed code under xterm.js' [MIT license](LICENSE). - confirm that you have the right to contribute and license the code in question. (Either you hold all rights on the code, or the rights holder has explicitly granted the right to use it like this, through a compatible open source license or through a direct agreement with you.) xterm.js-3.8.1/Dockerfile000066400000000000000000000010651341514612000152660ustar00rootroot00000000000000FROM node:8 MAINTAINER Paris Kasidiaris # Set the working directory WORKDIR /usr/src/app # Set an entrypoint, to automatically install node modules ENTRYPOINT ["/bin/bash", "-c", "if [[ ! -d node_modules ]]; then npm install; fi; exec \"${@:0}\";"] CMD ["npm", "run", "start"] # First, install dependencies to improve layer caching COPY package.json /usr/src/app/ RUN npm install # Add the code COPY . /usr/src/app # Run the tests and build, to make sure everything is working nicely RUN npm run build && npm run webpack && npm run test xterm.js-3.8.1/LICENSE000066400000000000000000000023551341514612000143040ustar00rootroot00000000000000Copyright (c) 2017-2018, The xterm.js authors (https://github.com/xtermjs/xterm.js) Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com) Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/) 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. xterm.js-3.8.1/Procfile000066400000000000000000000000461341514612000147600ustar00rootroot00000000000000web: npm start webpack: npm run watch xterm.js-3.8.1/README.md000066400000000000000000000315131341514612000145540ustar00rootroot00000000000000# [![xterm.js logo](logo-full.png)](https://xtermjs.org) [![Build Status](https://dev.azure.com/xtermjs/xterm.js/_apis/build/status/xtermjs.xterm.js)](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3) [![Coverage Status](https://coveralls.io/repos/github/xtermjs/xterm.js/badge.svg?branch=master)](https://coveralls.io/github/xtermjs/xterm.js?branch=master) [![Gitter](https://badges.gitter.im/sourcelair/xterm.js.svg)](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![jsDelivr Hits](https://data.jsdelivr.com/v1/package/npm/xterm/badge?style=rounded)](https://www.jsdelivr.com/package/npm/xterm) Xterm.js is a terminal front-end component written in JavaScript that works in the browser. It enables applications to provide fully featured terminals to their users and create great development experiences. ## Features - **Text-based application support**: Use xterm.js to work with applications like `bash`, `git` etc. - **Curses-based application support**: Use xterm.js to work with applications like `vim`, `tmux` etc. - **Mouse events support**: Xterm.js captures mouse events like click and scroll and passes them to the terminal's back-end controlling process - **CJK (Chinese, Japanese, Korean) character support**: Xterm.js renders CJK characters seamlessly - **IME support**: Insert international (including CJK) characters using IME input with your keyboard - **Self-contained library**: Xterm.js works on its own. It does not require any external libraries like jQuery or React to work - **Modular, event-based API**: Lets you build addons and themes with ease ## What xterm.js is not - Xterm.js is not a terminal application that you can download and use on your computer - Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output) ## Getting Started First you need to install the module, we ship exclusively through [npm](https://www.npmjs.com/) so you need that installed and then add xterm.js as a dependency by running: ``` npm install xterm ``` To start using xterm.js on your browser, add the `xterm.js` and `xterm.css` to the head of your html page. Then create a `
` onto which xterm can attach itself. ```html
``` Finally instantiate the `Terminal` object and then call the `open` function with the DOM object of the `div`. ### Importing The proposed way to load xterm.js is via the ES6 module syntax. ```javascript import { Terminal } from 'xterm'; ``` ### API The full API for xterm.js is contained within the [TypeScript declaration file](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), use the branch/tag picker in GitHub (`w`) to navigate to the correct version of the API. Note that some APIs are marked *experimental*, these are added so we can experiment with new ideas without committing to support it like a normal semver API. Note that these APIs can change radically between versions so be sure to read release notes if you plan on using experimental APIs. ### Addons Addons are JavaScript modules that extend the `Terminal` prototype with new methods and attributes to provide additional functionality. There are a handful available in the main repository in the `src/addons` directory and you can even write your own, by using xterm.js' public API. To use an addon, just import the JavaScript module and pass it to `Terminal`'s `applyAddon` method: ```javascript import { Terminal } from xterm; import * as fit from 'xterm/lib/addons/fit/fit'; Terminal.applyAddon(fit); var xterm = new Terminal(); // Instantiate the terminal xterm.fit(); // Use the `fit` method, provided by the `fit` addon ``` You will also need to include the addon's CSS file if it has one in the folder. #### Importing Addons in TypeScript There are currently no typings for addons if they are accessed via extending Terminal prototype, so you will need to upcast if using TypeScript, eg. `(xterm).fit()`. Alternatively, you can import addon function and enhance the terminal on demand. This would have better typing support and is friendly to treeshaking. E.g.: ```typescript import { Terminal } from 'xterm'; import { fit } from 'xterm/lib/addons/fit/fit'; const xterm = new Terminal(); // Fit the terminal when necessary: fit(xterm); ``` #### Third party addons There are also the following third party addons available: - [xterm-webfont](https://www.npmjs.com/package/xterm-webfont) ## Browser Support Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Here is a list of the versions we aim to support: - Chrome latest - Edge latest - Firefox latest - Safari latest - IE11 Xterm.js works seamlessly in Electron apps and may even work on earlier versions of the browsers but these are the browsers we strive to keep working. ## API The current full API documentation is available in the [TypeScript declaration file on the repository](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), switch the tag (press `w` when viewing the file) to point at the specific version tag you're using. ## Real-world uses Xterm.js is used in several world-class applications to provide great terminal experiences. - [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js - [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile and powerful open source code editor that provides an integrated terminal based on xterm.js - [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js - [**Katacoda**](https://www.katacoda.com/): Katacoda is an Interactive Learning Platform for software developers, covering the latest Cloud Native technologies. - [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. - [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams. - [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by `xterm.js`. - [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using `xterm.js`, socket.io, and ssh2. - [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. - [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. - [**Codevolve**](https://www.codevolve.com "Codevolve"): Online platform for interactive coding and web development courses. Live container-backed terminal uses `xterm.js`. - [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. - [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. - [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy and run in the cloud. - [**Gravitational Teleport**](https://github.com/gravitational/teleport): Gravitational Teleport is a modern SSH server for remotely accessing clusters of Linux servers via SSH or HTTPS. - [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job. - [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scallable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers. - [**Portainer**](https://portainer.io): Simple management UI for Docker. - [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising `xterm.js`, SJCL & websockets. - [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. - [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. - [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. - [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. - [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. - [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. - [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. - [**electerm**](http://electerm.html5beta.com): electerm is a terminal/ssh/sftp client(mac, win, linux) based on electron/node-pty/xterm. - [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. - [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. - [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. - [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server. - [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS - [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. - [**Microsoft SQL Operations Studio**](https://github.com/Microsoft/sqlopsstudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux - [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users - [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies. - [**Hyper**](https://hyper.is): A terminal built on web technologies - [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter. - [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js. - [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator. - [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice. - [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js - [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP. - [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere. - [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only. ## Releases Xterm.js follows a monthly release cycle roughly. The existing releases are available at this GitHub repo's [Releases](https://github.com/sourcelair/xterm.js/releases), while the roadmap is available as [Milestones](https://github.com/sourcelair/xterm.js/milestones). ## Contributing You can read the [guide on the wiki](https://github.com/xtermjs/xterm.js/wiki/Contributing) to learn how to contribute and setup xterm.js for development. ## License Agreement If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work. Copyright (c) 2017-2018, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)
Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) xterm.js-3.8.1/azure-pipelines.yml000066400000000000000000000027731341514612000171420ustar00rootroot00000000000000# Node.js # Build a general Node.js application with npm. # Add steps that analyze code, save build artifacts, deploy, and more: # https://docs.microsoft.com/vsts/pipelines/languages/javascript jobs: - job: Linux pool: vmImage: 'ubuntu-16.04' steps: - task: NodeTool@0 inputs: versionSpec: '8.x' displayName: 'Install Node.js' - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.9.4" displayName: 'Install Yarn' - script: | yarn displayName: 'Install dependencies and build' - script: | yarn mocha displayName: 'Test' - script: | yarn lint displayName: 'Lint' - job: macOS pool: vmImage: 'xcode9-macos10.13' steps: - task: NodeTool@0 inputs: versionSpec: '8.x' displayName: 'Install Node.js' - script: | yarn displayName: 'Install dependencies and build' - script: | yarn mocha displayName: 'Test' - script: | yarn lint displayName: 'Lint' - script: | yarn test-coverage export COVERALLS_GIT_BRANCH=$BUILD_SOURCEBRANCH yarn coveralls displayName: 'Generate and publish coverage' - job: Windows pool: vmImage: 'vs2017-win2016' steps: - task: NodeTool@0 inputs: versionSpec: '8.x' displayName: 'Install Node.js' - script: | yarn displayName: 'Install dependencies and build' - script: | yarn mocha displayName: 'Test' - script: | yarn lint displayName: 'Lint' xterm.js-3.8.1/bin/000077500000000000000000000000001341514612000140425ustar00rootroot00000000000000xterm.js-3.8.1/bin/generate-authors000077500000000000000000000003471341514612000172510ustar00rootroot00000000000000#! /usr/bin/env sh tail --lines=+3 AUTHORS > AUTHORS.tmp git log --format='%aN <%aE>' >> AUTHORS.tmp echo "List of xterm.js contributors. Updated before every release.\n" > AUTHORS sort -u AUTHORS.tmp >> AUTHORS rm -f AUTHORS.tmp xterm.js-3.8.1/bin/prepare-release000077500000000000000000000011341341514612000170430ustar00rootroot00000000000000#! /usr/bin/env sh # Usage: ./bin/prepare-release x.y.z # x.y.z should be semver (e.g. 1.0.0) set -e NEW_VERSION=$1 CURRENT_PACKAGE_JSON_VERSION=$(cat package.json \ | grep version \ | head -1 \ | awk -F: '{ print $2 }' \ | sed 's/[",]//g' \ | tr -d '[[:space:]]') # Build xterm.js into `dist` export BUILD_DIR=dist npm run build # Update AUTHORS file sh bin/generate-authors # Update version in package.json sed -i "s/\"version\": \"$CURRENT_PACKAGE_JSON_VERSION\"/\"version\": \"$NEW_VERSION\"/g" package.json git commit -S -s -a -m "Bump version to $NEW_VERSION" git tag $NEW_VERSION xterm.js-3.8.1/bin/release000077500000000000000000000005371341514612000154150ustar00rootroot00000000000000#! /usr/bin/env sh # Usage: ./bin/release x.y.z # x.y.z should be semver (e.g. 1.0.0) set -e if [ -z "$1" ]; then echo "No version supplied. Please a version argument\n" echo "Usage: $0 VERSION\n" echo "Example: $0 1.0.0" exit fi NEW_VERSION=$1 ./bin/prepare-release $NEW_VERSION git push && \ git push --tags && \ npm publish xterm.js-3.8.1/demo/000077500000000000000000000000001341514612000142165ustar00rootroot00000000000000xterm.js-3.8.1/demo/client.ts000066400000000000000000000231051341514612000160450ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT * * This file is the entry point for browserify. */ /// import { Terminal } from '../lib/public/Terminal'; import * as attach from '../lib/addons/attach/attach'; import * as fit from '../lib/addons/fit/fit'; import * as fullscreen from '../lib/addons/fullscreen/fullscreen'; import * as search from '../lib/addons/search/search'; import * as webLinks from '../lib/addons/webLinks/webLinks'; import * as winptyCompat from '../lib/addons/winptyCompat/winptyCompat'; // Pulling in the module's types relies on the above, it's looks a // little weird here as we're importing "this" module import { Terminal as TerminalType } from 'xterm'; export interface IWindowWithTerminal extends Window { term: TerminalType; } declare let window: IWindowWithTerminal; Terminal.applyAddon(attach); Terminal.applyAddon(fit); Terminal.applyAddon(fullscreen); Terminal.applyAddon(search); Terminal.applyAddon(webLinks); Terminal.applyAddon(winptyCompat); let term; let protocol; let socketURL; let socket; let pid; const terminalContainer = document.getElementById('terminal-container'); const actionElements = { findNext: document.querySelector('#find-next'), findPrevious: document.querySelector('#find-previous') }; const paddingElement = document.getElementById('padding'); function setPadding(): void { term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px'; term.fit(); } createTerminal(); const disposeRecreateButtonHandler = () => { // If the terminal exists dispose of it, otherwise recreate it if (term) { term.dispose(); term = null; window.term = null; socket = null; document.getElementById('dispose').innerHTML = 'Recreate Terminal'; } else { createTerminal(); document.getElementById('dispose').innerHTML = 'Dispose terminal'; } }; document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler); function createTerminal(): void { // Clean terminal while (terminalContainer.children.length) { terminalContainer.removeChild(terminalContainer.children[0]); } term = new Terminal({}); window.term = term; // Expose `term` to window for debugging purposes term.on('resize', (size: { cols: number, rows: number }) => { if (!pid) { return; } const cols = size.cols; const rows = size.rows; const url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows; fetch(url, {method: 'POST'}); }); protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/'; term.open(terminalContainer); term.winptyCompatInit(); term.webLinksInit(); term.fit(); term.focus(); addDomListener(paddingElement, 'change', setPadding); addDomListener(actionElements.findNext, 'keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); const searchOptions = { regex: (document.getElementById('regex') as HTMLInputElement).checked, wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked, caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked }; term.findNext(actionElements.findNext.value, searchOptions); } }); addDomListener(actionElements.findPrevious, 'keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); const searchOptions = { regex: (document.getElementById('regex') as HTMLInputElement).checked, wholeWord: (document.getElementById('whole-word') as HTMLInputElement).checked, caseSensitive: (document.getElementById('case-sensitive') as HTMLInputElement).checked }; term.findPrevious(actionElements.findPrevious.value, searchOptions); } }); // fit is called within a setTimeout, cols and rows need this. setTimeout(() => { initOptions(term); // TODO: Clean this up, opt-cols/rows doesn't exist anymore (document.getElementById(`opt-cols`)).value = term.cols; (document.getElementById(`opt-rows`)).value = term.rows; paddingElement.value = '0'; // Set terminal size again to set the specific dimensions on the demo updateTerminalSize(); fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then((res) => { res.text().then((processId) => { pid = processId; socketURL += processId; socket = new WebSocket(socketURL); socket.onopen = runRealTerminal; socket.onclose = runFakeTerminal; socket.onerror = runFakeTerminal; }); }); }, 0); } function runRealTerminal(): void { term.attach(socket); term._initialized = true; } function runFakeTerminal(): void { if (term._initialized) { return; } term._initialized = true; term.prompt = () => { term.write('\r\n$ '); }; term.writeln('Welcome to xterm.js'); term.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); term.writeln('Type some keys and commands to play around.'); term.writeln(''); term.prompt(); term._core.register(term.addDisposableListener('key', (key, ev) => { const printable = !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey; if (ev.keyCode === 13) { term.prompt(); } else if (ev.keyCode === 8) { // Do not delete the prompt if (term.x > 2) { term.write('\b \b'); } } else if (printable) { term.write(key); } })); term._core.register(term.addDisposableListener('paste', (data, ev) => { term.write(data); })); } function initOptions(term: TerminalType): void { const blacklistedOptions = [ // Internal only options 'cancelEvents', 'convertEol', 'debug', 'handler', 'screenKeys', 'termName', 'useFlowControl', // Complex option 'theme' ]; const stringOptions = { bellSound: null, bellStyle: ['none', 'sound'], cursorStyle: ['block', 'underline', 'bar'], experimentalCharAtlas: ['none', 'static', 'dynamic'], fontFamily: null, fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], rendererType: ['dom', 'canvas'] }; const options = Object.keys((term)._core.options); const booleanOptions = []; const numberOptions = []; options.filter(o => blacklistedOptions.indexOf(o) === -1).forEach(o => { switch (typeof term.getOption(o)) { case 'boolean': booleanOptions.push(o); break; case 'number': numberOptions.push(o); break; default: if (Object.keys(stringOptions).indexOf(o) === -1) { console.warn(`Unrecognized option: "${o}"`); } } }); let html = ''; html += '
'; booleanOptions.forEach(o => { html += `
`; }); html += '
'; numberOptions.forEach(o => { html += `
`; }); html += '
'; Object.keys(stringOptions).forEach(o => { if (stringOptions[o]) { html += `
`; } else { html += `
`; } }); html += '
'; const container = document.getElementById('options-container'); container.innerHTML = html; // Attach listeners booleanOptions.forEach(o => { const input = document.getElementById(`opt-${o}`); addDomListener(input, 'change', () => { console.log('change', o, input.checked); term.setOption(o, input.checked); }); }); numberOptions.forEach(o => { const input = document.getElementById(`opt-${o}`); addDomListener(input, 'change', () => { console.log('change', o, input.value); if (o === 'cols' || o === 'rows') { updateTerminalSize(); } else { term.setOption(o, parseInt(input.value, 10)); } }); }); Object.keys(stringOptions).forEach(o => { const input = document.getElementById(`opt-${o}`); addDomListener(input, 'change', () => { console.log('change', o, input.value); term.setOption(o, input.value); }); }); } function addDomListener(element: HTMLElement, type: string, handler: (...args: any[]) => any): void { element.addEventListener(type, handler); term._core.register({ dispose: () => element.removeEventListener(type, handler) }); } function updateTerminalSize(): void { const cols = parseInt((document.getElementById(`opt-cols`)).value, 10); const rows = parseInt((document.getElementById(`opt-rows`)).value, 10); const width = (cols * term._core.renderer.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toString() + 'px'; const height = (rows * term._core.renderer.dimensions.actualCellHeight).toString() + 'px'; terminalContainer.style.width = width; terminalContainer.style.height = height; term.fit(); } xterm.js-3.8.1/demo/index.html000066400000000000000000000034761341514612000162250ustar00rootroot00000000000000 xterm.js demo

xterm.js: A terminal for the web

Actions

Options

These options can be set in the Terminal constructor or using the Terminal.setOption function.

Style


Attention: The demo is a barebones implementation and is designed for the development and evaluation of xterm.js only. Exposing the demo to the public as is would introduce security risks for the host.

xterm.js-3.8.1/demo/server.js000066400000000000000000000042371341514612000160700ustar00rootroot00000000000000var express = require('express'); var app = express(); var expressWs = require('express-ws')(app); var os = require('os'); var pty = require('node-pty'); var terminals = {}, logs = {}; app.use('/build', express.static(__dirname + '/../build')); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); app.get('/style.css', function(req, res){ res.sendFile(__dirname + '/style.css'); }); app.get('/dist/client-bundle.js', function(req, res){ res.sendFile(__dirname + '/dist/client-bundle.js'); }); app.post('/terminals', function (req, res) { var cols = parseInt(req.query.cols), rows = parseInt(req.query.rows), term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { name: 'xterm-color', cols: cols || 80, rows: rows || 24, cwd: process.env.PWD, env: process.env }); console.log('Created terminal with PID: ' + term.pid); terminals[term.pid] = term; logs[term.pid] = ''; term.on('data', function(data) { logs[term.pid] += data; }); res.send(term.pid.toString()); res.end(); }); app.post('/terminals/:pid/size', function (req, res) { var pid = parseInt(req.params.pid), cols = parseInt(req.query.cols), rows = parseInt(req.query.rows), term = terminals[pid]; term.resize(cols, rows); console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.'); res.end(); }); app.ws('/terminals/:pid', function (ws, req) { var term = terminals[parseInt(req.params.pid)]; console.log('Connected to terminal ' + term.pid); ws.send(logs[term.pid]); term.on('data', function(data) { try { ws.send(data); } catch (ex) { // The WebSocket is not open, ignore } }); ws.on('message', function(msg) { term.write(msg); }); ws.on('close', function () { term.kill(); console.log('Closed terminal ' + term.pid); // Clean things up delete terminals[term.pid]; delete logs[term.pid]; }); }); var port = process.env.PORT || 3000, host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; console.log('App listening to http://' + host + ':' + port); app.listen(port, host); xterm.js-3.8.1/demo/start.js000066400000000000000000000020261341514612000157110ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT * * This file is the entry point for browserify. */ const cp = require('child_process'); const path = require('path'); const webpack = require('webpack'); // Launch server cp.spawn('node', [path.resolve(__dirname, 'server.js')], { stdio: 'inherit' }); // Build/watch client source const clientConfig = { entry: path.resolve(__dirname, 'client.ts'), devtool: 'inline-source-map', module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ } ] }, resolve: { extensions: [ '.tsx', '.ts', '.js' ] }, output: { filename: 'client-bundle.js', path: path.resolve(__dirname, 'dist') }, mode: 'development', watch: true }; const compiler = webpack(clientConfig); compiler.watch({ // Example watchOptions aggregateTimeout: 300, poll: undefined }, (err, stats) => { // Print watch/build result here... console.log(stats.toString({ colors: true })); }); xterm.js-3.8.1/demo/style.css000066400000000000000000000006731341514612000160760ustar00rootroot00000000000000body { font-family: helvetica, sans-serif, arial; font-size: 1em; color: #111; } h1 { text-align: center; } #terminal-container { width: 800px; height: 450px; margin: 0 auto; padding: 2px; } p { font-size: 0.9em; font-style: italic } #option-container { display: flex; justify-content: center; } .option-group { display: inline-block; padding-left: 20px; vertical-align: top; } xterm.js-3.8.1/demo/tsconfig.json000066400000000000000000000002711341514612000167250ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "rootDir": ".", "sourceMap": true }, "include": [ "client.ts", "../typings/xterm.d.ts" ] } xterm.js-3.8.1/demo/zmodem/000077500000000000000000000000001341514612000155115ustar00rootroot00000000000000xterm.js-3.8.1/demo/zmodem/app.js000066400000000000000000000044651341514612000166400ustar00rootroot00000000000000var express = require('express'); var app = express(); var expressWs = require('express-ws')(app); var os = require('os'); var pty = require('node-pty'); var terminals = {}, logs = {}; app.use('/build', express.static(__dirname + '/../../build')); app.use('/demo', express.static(__dirname + '/../../demo')); app.use('/zmodemjs', express.static(__dirname + '/../../node_modules/zmodem.js/dist')); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); app.get('/style.css', function(req, res){ res.sendFile(__dirname + '../style.css'); }); app.get('/main.js', function(req, res){ res.sendFile(__dirname + '/main.js'); }); app.post('/terminals', function (req, res) { var cols = parseInt(req.query.cols), rows = parseInt(req.query.rows), term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { encoding: null, name: 'xterm-color', cols: cols || 80, rows: rows || 24, cwd: process.env.PWD, env: process.env }); console.log('Created terminal with PID: ' + term.pid); terminals[term.pid] = term; logs[term.pid] = ''; term.on('data', function(data) { logs[term.pid] += data; }); res.send(term.pid.toString()); res.end(); }); app.post('/terminals/:pid/size', function (req, res) { var pid = parseInt(req.params.pid), cols = parseInt(req.query.cols), rows = parseInt(req.query.rows), term = terminals[pid]; term.resize(cols, rows); console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.'); res.end(); }); app.ws('/terminals/:pid', function (ws, req) { var term = terminals[parseInt(req.params.pid)]; console.log('Connected to terminal ' + term.pid); ws.send(logs[term.pid]); term.on('data', function(data) { try { ws.send(data); } catch (ex) { // The WebSocket is not open, ignore } }); ws.on('message', function(msg) { term.write(msg); }); ws.on('close', function () { term.kill(); console.log('Closed terminal ' + term.pid); // Clean things up delete terminals[term.pid]; delete logs[term.pid]; }); }); var port = process.env.PORT || 3000, host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0'; console.log('App listening to http://' + host + ':' + port); app.listen(port, host); xterm.js-3.8.1/demo/zmodem/index.html000066400000000000000000000132571341514612000175160ustar00rootroot00000000000000 xterm.js demo

xterm.js: xterm, in the browser

Actions

Options

Size

Attention: The demo is a barebones implementation and is designed for xterm.js evaluation purposes only. Exposing the demo to the public as is would introduce security risks for the host.

* ZMODEM file transfers are supported via an addon. To try it out, install lrzsz onto the remote peer, then run rz to send from your browser or sz <file> to send from the remote peer.

xterm.js-3.8.1/demo/zmodem/main.js000066400000000000000000000266641341514612000170110ustar00rootroot00000000000000"use strict"; var term, protocol, socketURL, socket, pid; Terminal.applyAddon(fit); Terminal.applyAddon(attach); Terminal.applyAddon(zmodem); Terminal.applyAddon(search); var terminalContainer = document.getElementById('terminal-container'), actionElements = { findNext: document.querySelector('#find-next'), findPrevious: document.querySelector('#find-previous') }, optionElements = { cursorBlink: document.querySelector('#option-cursor-blink'), cursorStyle: document.querySelector('#option-cursor-style'), scrollback: document.querySelector('#option-scrollback'), tabstopwidth: document.querySelector('#option-tabstopwidth'), bellStyle: document.querySelector('#option-bell-style') }, colsElement = document.getElementById('cols'), rowsElement = document.getElementById('rows'); function setTerminalSize() { var cols = parseInt(colsElement.value, 10); var rows = parseInt(rowsElement.value, 10); var viewportElement = document.querySelector('.xterm-viewport'); var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth; var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px'; var height = (rows * term.charMeasure.height).toString() + 'px'; terminalContainer.style.width = width; terminalContainer.style.height = height; term.resize(cols, rows); } colsElement.addEventListener('change', setTerminalSize); rowsElement.addEventListener('change', setTerminalSize); actionElements.findNext.addEventListener('keypress', function (e) { if (e.key === "Enter") { e.preventDefault(); term.findNext(actionElements.findNext.value); } }); actionElements.findPrevious.addEventListener('keypress', function (e) { if (e.key === "Enter") { e.preventDefault(); term.findPrevious(actionElements.findPrevious.value); } }); optionElements.cursorBlink.addEventListener('change', function () { term.setOption('cursorBlink', optionElements.cursorBlink.checked); }); optionElements.cursorStyle.addEventListener('change', function () { term.setOption('cursorStyle', optionElements.cursorStyle.value); }); optionElements.bellStyle.addEventListener('change', function () { term.setOption('bellStyle', optionElements.bellStyle.value); }); optionElements.scrollback.addEventListener('change', function () { term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10)); }); optionElements.tabstopwidth.addEventListener('change', function () { term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10)); }); createTerminal(); function createTerminal() { // Clean terminal while (terminalContainer.children.length) { terminalContainer.removeChild(terminalContainer.children[0]); } term = new Terminal({ cursorBlink: optionElements.cursorBlink.checked, scrollback: parseInt(optionElements.scrollback.value, 10), tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10) }); term.on('resize', function (size) { if (!pid) { return; } var cols = size.cols, rows = size.rows, url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows; fetch(url, {method: 'POST'}); }); protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://'; socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/'; term.open(terminalContainer); term.fit(); // fit is called within a setTimeout, cols and rows need this. setTimeout(function () { colsElement.value = term.cols; rowsElement.value = term.rows; // Set terminal size again to set the specific dimensions on the demo setTerminalSize(); fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) { res.text().then(function (pid) { window.pid = pid; socketURL += pid; socket = new WebSocket(socketURL); socket.onopen = runRealTerminal; socket.onclose = runFakeTerminal; socket.onerror = runFakeTerminal; term.zmodemAttach(socket, { noTerminalWriteOutsideSession: true, } ); term.on("zmodemRetract", () => { start_form.style.display = "none"; start_form.onsubmit = null; }); term.on("zmodemDetect", (detection) => { function do_zmodem() { term.detach(); let zsession = detection.confirm(); var promise; if (zsession.type === "receive") { promise = _handle_receive_session(zsession); } else { promise = _handle_send_session(zsession); } promise.catch( console.error.bind(console) ).then( () => { term.attach(socket); } ); } if (_auto_zmodem()) { do_zmodem(); } else { start_form.style.display = ""; start_form.onsubmit = function(e) { start_form.style.display = "none"; if (document.getElementById("zmstart_yes").checked) { do_zmodem(); } else { detection.deny(); } }; } }); }); }); }, 0); } //---------------------------------------------------------------------- // UI STUFF function _show_file_info(xfer) { var file_info = xfer.get_details(); document.getElementById("name").textContent = file_info.name; document.getElementById("size").textContent = file_info.size; document.getElementById("mtime").textContent = file_info.mtime; document.getElementById("files_remaining").textContent = file_info.files_remaining; document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining; document.getElementById("mode").textContent = "0" + file_info.mode.toString(8); var xfer_opts = xfer.get_options(); ["conversion", "management", "transport", "sparse"].forEach( (lbl) => { document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl]; } ); document.getElementById("zm_file").style.display = ""; } function _hide_file_info() { document.getElementById("zm_file").style.display = "none"; } function _save_to_disk(xfer, buffer) { return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name); } var skipper_button = document.getElementById("zm_progress_skipper"); var skipper_button_orig_text = skipper_button.textContent; function _show_progress() { skipper_button.disabled = false; skipper_button.textContent = skipper_button_orig_text; document.getElementById("bytes_received").textContent = 0; document.getElementById("percent_received").textContent = 0; document.getElementById("zm_progress").style.display = ""; } function _update_progress(xfer) { var total_in = xfer.get_offset(); document.getElementById("bytes_received").textContent = total_in; var percent_received = 100 * total_in / xfer.get_details().size; document.getElementById("percent_received").textContent = percent_received.toFixed(2); } function _hide_progress() { document.getElementById("zm_progress").style.display = "none"; } var start_form = document.getElementById("zm_start"); function _auto_zmodem() { return document.getElementById("zmodem-auto").checked; } // END UI STUFF //---------------------------------------------------------------------- function _handle_receive_session(zsession) { zsession.on("offer", function(xfer) { current_receive_xfer = xfer; _show_file_info(xfer); var offer_form = document.getElementById("zm_offer"); function on_form_submit() { offer_form.style.display = "none"; //START //if (offer_form.zmaccept.value) { if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) { _show_progress(); var FILE_BUFFER = []; xfer.on("input", (payload) => { _update_progress(xfer); FILE_BUFFER.push( new Uint8Array(payload) ); }); xfer.accept().then( () => { _save_to_disk(xfer, FILE_BUFFER); }, console.error.bind(console) ); } else { xfer.skip(); } //END } if (_auto_zmodem()) { on_form_submit(); } else { offer_form.onsubmit = on_form_submit; offer_form.style.display = ""; } } ); var promise = new Promise( (res) => { zsession.on("session_end", () => { _hide_file_info(); _hide_progress(); res(); } ); } ); zsession.start(); return promise; } function _handle_send_session(zsession) { var choose_form = document.getElementById("zm_choose"); choose_form.style.display = ""; var file_el = document.getElementById("zm_files"); var promise = new Promise( (res) => { file_el.onchange = function(e) { choose_form.style.display = "none"; var files_obj = file_el.files; Zmodem.Browser.send_files( zsession, files_obj, { on_offer_response(obj, xfer) { if (xfer) _show_progress(); //console.log("offer", xfer ? "accepted" : "skipped"); }, on_progress(obj, xfer) { _update_progress(xfer); }, on_file_complete(obj) { //console.log("COMPLETE", obj); _hide_progress(); }, } ).then(_hide_progress).then( zsession.close.bind(zsession), console.error.bind(console) ).then( () => { _hide_file_info(); _hide_progress(); res(); } ); }; } ); return promise; } //This is here to allow canceling of an in-progress ZMODEM transfer. var current_receive_xfer; //Called from HTML directly. function skip_current_file() { current_receive_xfer.skip(); skipper_button.disabled = true; skipper_button.textContent = "Waiting for server to acknowledge skip …"; } function runRealTerminal() { term.attach(socket); term._initialized = true; } function runFakeTerminal() { if (term._initialized) { return; } term._initialized = true; var shellprompt = '$ '; term.prompt = function () { term.write('\r\n' + shellprompt); }; term.writeln('Welcome to xterm.js'); term.writeln('This is a local terminal emulation, without a real terminal in the back-end.'); term.writeln('Type some keys and commands to play around.'); term.writeln(''); term.prompt(); term.on('key', function (key, ev) { var printable = ( !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey ); if (ev.keyCode == 13) { term.prompt(); } else if (ev.keyCode == 8) { // Do not delete the prompt if (term.x > 2) { term.write('\b \b'); } } else if (printable) { term.write(key); } }); term.on('paste', function (data, ev) { term.write(data); }); } xterm.js-3.8.1/docker-compose.yml000066400000000000000000000004401341514612000167250ustar00rootroot00000000000000version: "3" services: web: image: xtermjs/xterm.js:latest build: . volumes: - ./:/usr/src/app ports: - ${XTERMJS_PORT:3000}:3000 command: ["npm", "start"] watch: build: . volumes: - ./:/usr/src/app command: ["npm", "run", "watch"] xterm.js-3.8.1/fixtures/000077500000000000000000000000001341514612000151435ustar00rootroot00000000000000xterm.js-3.8.1/fixtures/escape_sequence_files/000077500000000000000000000000001341514612000214555ustar00rootroot00000000000000xterm.js-3.8.1/fixtures/escape_sequence_files/NOTES000066400000000000000000000011711341514612000222700ustar00rootroot00000000000000All tests are made for 80x25 terminal. Make sure to run tests with 80x25. Create .text files from xterm (expected output) - open xterm - resize xterm to 80x25 - run `python run_tests.py` - copy & paste whole window output into editor - add 26th empty line (due to line handling in toString) - not a bug, a feature ;) - advance to next test with ^D Known problems ############## t0031-HBP: - no documentation at all about CSIj found - skipping t0050-ICH: - bug in xterm? (cant ICH last real char, always sticks to last col) - text used from https://github.com/MarkLodato/vt100-parser/blob/master/test/t0050-ICH.text xterm.js-3.8.1/fixtures/escape_sequence_files/t0001-all_printable.in000066400000000000000000000001451341514612000253570ustar00rootroot00000000000000 !"#$%&'()*+,-./ 0123456789:;<=>? @ABCDEFGHIJKLMNO PQRSTUVWXYZ[\]^_ `abcdefghijklmno pqrstuvwxyz{|}~ xterm.js-3.8.1/fixtures/escape_sequence_files/t0001-all_printable.text000066400000000000000000000001701341514612000257330ustar00rootroot00000000000000 !"#$%&'()*+,-./ 0123456789:;<=>? @ABCDEFGHIJKLMNO PQRSTUVWXYZ[\]^_ `abcdefghijklmno pqrstuvwxyz{|}~ xterm.js-3.8.1/fixtures/escape_sequence_files/t0002-history.in000066400000000000000000000002761341514612000242560ustar00rootroot00000000000000 ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ xterm.js-3.8.1/fixtures/escape_sequence_files/t0002-history.text000066400000000000000000000000611341514612000246240ustar00rootroot00000000000000g h i j k l m n o p q r s t u v w x y z { | } ~ xterm.js-3.8.1/fixtures/escape_sequence_files/t0002j-simple_string.in000066400000000000000000000000441341514612000255770ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz0123456789xterm.js-3.8.1/fixtures/escape_sequence_files/t0002j-simple_string.text000066400000000000000000000000751341514612000261610ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz0123456789 xterm.js-3.8.1/fixtures/escape_sequence_files/t0003-line_wrap.in000066400000000000000000000067611341514612000245430ustar00rootroot00000000000000a ab abc abcd abcde abcdef abcdefg abcdefgh abcdefghi abcdefghij abcdefghijk abcdefghijkl abcdefghijklm abcdefghijklmn abcdefghijklmno abcdefghijklmnop abcdefghijklmnopq abcdefghijklmnopqr abcdefghijklmnopqrs abcdefghijklmnopqrst abcdefghijklmnopqrstu abcdefghijklmnopqrstuv abcdefghijklmnopqrstuvw abcdefghijklmnopqrstuvwx abcdefghijklmnopqrstuvwxy abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyzA abcdefghijklmnopqrstuvwxyzAB abcdefghijklmnopqrstuvwxyzABC abcdefghijklmnopqrstuvwxyzABCD abcdefghijklmnopqrstuvwxyzABCDE abcdefghijklmnopqrstuvwxyzABCDEF abcdefghijklmnopqrstuvwxyzABCDEFG abcdefghijklmnopqrstuvwxyzABCDEFGH abcdefghijklmnopqrstuvwxyzABCDEFGHI abcdefghijklmnopqrstuvwxyzABCDEFGHIJ abcdefghijklmnopqrstuvwxyzABCDEFGHIJK abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890a abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ab abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abc abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdef abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijk abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkl abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrs abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrst xterm.js-3.8.1/fixtures/escape_sequence_files/t0003-line_wrap.text000066400000000000000000000030261341514612000251100ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890a abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ab abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abc abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdef abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijk abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkl abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq r abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq rs abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq rst xterm.js-3.8.1/fixtures/escape_sequence_files/t0003j-LF.in000066400000000000000000000001051341514612000232200ustar00rootroot000000000000001 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 xterm.js-3.8.1/fixtures/escape_sequence_files/t0003j-LF.text000066400000000000000000000001021341514612000235730ustar00rootroot000000000000003 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 xterm.js-3.8.1/fixtures/escape_sequence_files/t0004-LF.in000066400000000000000000000002461341514612000230550ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t xterm.js-3.8.1/fixtures/escape_sequence_files/t0004-LF.text000066400000000000000000000000611341514612000234260ustar00rootroot000000000000007 8 9 0 a b c d e f g h i j k l m n o p q r s t xterm.js-3.8.1/fixtures/escape_sequence_files/t0004j-CR.in000066400000000000000000000003021341514612000232230ustar00rootroot000000000000001 x 2 x 3 x 4 x 5 x 6 x 7 xxterm.js-3.8.1/fixtures/escape_sequence_files/t0004j-CR.text000066400000000000000000000003071341514612000236060ustar00rootroot00000000000000x x2 x 3 x 4 x 5 x 6 x 7 xterm.js-3.8.1/fixtures/escape_sequence_files/t0005-CR.in000066400000000000000000000072231341514612000230630ustar00rootroot00000000000000b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a xterm.js-3.8.1/fixtures/escape_sequence_files/t0005-CR.text000066400000000000000000000030761341514612000234430ustar00rootroot00000000000000a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a b a ab xterm.js-3.8.1/fixtures/escape_sequence_files/t0006-IND.in000066400000000000000000000003701341514612000231660ustar00rootroot00000000000000aDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDuDvDwDxDyDzDADBDCDDDEDFDGDHDIDJDKDLDMDNDODPDQDRDSDTDUDVDWDXDYDZD0D1D2D3D4D5D6D7D8D9D0DaDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDt xterm.js-3.8.1/fixtures/escape_sequence_files/t0006-IND.text000066400000000000000000000033071341514612000235470ustar00rootroot00000000000000 7 8 9 0 a b c d e f g h i j k l m n o p q r s t xterm.js-3.8.1/fixtures/escape_sequence_files/t0007-space_at_end.in000066400000000000000000000005601341514612000251630ustar00rootroot000000000000000 space: 1 space: 2 space: 3 space: 70 space: 71 space: 72 space: 73 space: xterm.js-3.8.1/fixtures/escape_sequence_files/t0007-space_at_end.text000066400000000000000000000006011341514612000255350ustar00rootroot000000000000000 space: 1 space: 2 space: 3 space: 70 space: 71 space: 72 space: 73 space: xterm.js-3.8.1/fixtures/escape_sequence_files/t0008-BS.in000066400000000000000000000006221341514612000230620ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz! abc@ # abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop$ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq% abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr^ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrs& xterm.js-3.8.1/fixtures/escape_sequence_files/t0008-BS.text000066400000000000000000000005711341514612000234430ustar00rootroot00000000000000abcdefghijklmnopqrst!vwxyz @bc # abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij$lmnop abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij%lmnopq abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq ^ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq &s xterm.js-3.8.1/fixtures/escape_sequence_files/t0009-NEL.in000066400000000000000000000071031341514612000231760ustar00rootroot00000000000000aEabEabcEabcdEabcdeEabcdefEabcdefgEabcdefghEabcdefghiEabcdefghijEabcdefghijkEabcdefghijklEabcdefghijklmEabcdefghijklmnEabcdefghijklmnoEabcdefghijklmnopEabcdefghijklmnopqEabcdefghijklmnopqrEabcdefghijklmnopqrsEabcdefghijklmnopqrstEabcdefghijklmnopqrstuEabcdefghijklmnopqrstuvEabcdefghijklmnopqrstuvwEabcdefghijklmnopqrstuvwxEabcdefghijklmnopqrstuvwxyEabcdefghijklmnopqrstuvwxyzEabcdefghijklmnopqrstuvwxyzAEabcdefghijklmnopqrstuvwxyzABEabcdefghijklmnopqrstuvwxyzABCEabcdefghijklmnopqrstuvwxyzABCDEabcdefghijklmnopqrstuvwxyzABCDEEabcdefghijklmnopqrstuvwxyzABCDEFEabcdefghijklmnopqrstuvwxyzABCDEFGEabcdefghijklmnopqrstuvwxyzABCDEFGHEabcdefghijklmnopqrstuvwxyzABCDEFGHIEabcdefghijklmnopqrstuvwxyzABCDEFGHIJEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQREabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890aEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdeEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghiEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnoEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrsEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrst xterm.js-3.8.1/fixtures/escape_sequence_files/t0009-NEL.text000066400000000000000000000030261341514612000235540ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890a abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ab abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abc abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdef abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijk abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkl abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq r abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq rs abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq rst xterm.js-3.8.1/fixtures/escape_sequence_files/t0010-RI.in000066400000000000000000000001501341514612000230550ustar00rootroot00000000000000a b c dMeMfMg h i j....................................................................kMlMmMn xterm.js-3.8.1/fixtures/escape_sequence_files/t0010-RI.text000066400000000000000000000004671341514612000234460ustar00rootroot00000000000000a g n h f m ie l j....................................................................k xterm.js-3.8.1/fixtures/escape_sequence_files/t0011-RI_scroll.in000066400000000000000000000003641341514612000244430ustar00rootroot00000000000000And the third. This should be the last line. This one should be lost. This one's a goner, too. MMMMMMMMMMMMMMMMMMMMMMMMThis is second line. MThis should be the first line. xterm.js-3.8.1/fixtures/escape_sequence_files/t0011-RI_scroll.text000066400000000000000000000002161341514612000250150ustar00rootroot00000000000000This should be the first line. This is second line. And the third. This should be the last line. This one should be lost. xterm.js-3.8.1/fixtures/escape_sequence_files/t0012-VT.in000066400000000000000000000002471341514612000231050ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t xterm.js-3.8.1/fixtures/escape_sequence_files/t0012-VT.text000066400000000000000000000033071341514612000234630ustar00rootroot00000000000000 7 8 9 0 a b c d e f g h i j k l m n o p q r s t xterm.js-3.8.1/fixtures/escape_sequence_files/t0013-FF.in000066400000000000000000000002501341514612000230420ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 0 a b c d e f g h i j k l m n o p q r s t xterm.js-3.8.1/fixtures/escape_sequence_files/t0013-FF.text000066400000000000000000000032131341514612000234220ustar00rootroot00000000000000 8 9 0 a b c d e f g h i j k l m n o p q r s t xterm.js-3.8.1/fixtures/escape_sequence_files/t0014-CAN.in000066400000000000000000000001631341514612000231540ustar00rootroot00000000000000abcdDefgh abcdDefgh abcd!Defgh abcd!*Defgh abcd[Defgh abcd[!Defgh abcd[2Defgh abcd[*2;Defgh xterm.js-3.8.1/fixtures/escape_sequence_files/t0014-CAN.text000066400000000000000000000001411341514612000235260ustar00rootroot00000000000000abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh xterm.js-3.8.1/fixtures/escape_sequence_files/t0015-SUB.in000066400000000000000000000001631341514612000232050ustar00rootroot00000000000000abcdDefgh abcdDefgh abcd!Defgh abcd!*Defgh abcd[Defgh abcd[!Defgh abcd[2Defgh abcd[*2;Defgh xterm.js-3.8.1/fixtures/escape_sequence_files/t0015-SUB.text000066400000000000000000000001411341514612000235570ustar00rootroot00000000000000abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh abcdDefgh xterm.js-3.8.1/fixtures/escape_sequence_files/t0016-SU.in000066400000000000000000000015771341514612000231160ustar00rootroot00000000000000HelloGoodbye UpDown x  -----------------------------------------------------------------------------x ------------------------------------------------------------------------------x -------------------------------------------------------------------------------x --------------------------------------------------------------------------------x ---------------------------------------------------------------------------------x .............................................................................x ..............................................................................x ...............................................................................x ................................................................................x .................................................................................x  The End. xterm.js-3.8.1/fixtures/escape_sequence_files/t0016-SU.text000066400000000000000000000000411341514612000234550ustar00rootroot00000000000000 The End. xterm.js-3.8.1/fixtures/escape_sequence_files/t0017-SD.in000066400000000000000000000020561341514612000230670ustar00rootroot00000000000000A B C D E F G H I J K L M N O P Q R S T U V W X a b c d e f g h ------------------------------------------------------------------------------1 -------------------------------------------------------------------------------2 --------------------------------------------------------------------------------3 ---------------------------------------------------------------------------------4 ..............................................................................5 ...............................................................................6 ................................................................................7 .................................................................................8 xterm.js-3.8.1/fixtures/escape_sequence_files/t0017-SD.text000066400000000000000000000011251341514612000234410ustar00rootroot00000000000000 a b f g h 1 2 3 -4------------------------------------------------------------------------------ 5 6 7 8............................................................................... xterm.js-3.8.1/fixtures/escape_sequence_files/t0020-CUF.in000066400000000000000000000013501341514612000231640ustar00rootroot00000000000000abcdefghijkl abcdefghijkl abcdefghijkl abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ x x abcd xterm.js-3.8.1/fixtures/escape_sequence_files/t0020-CUF.text000066400000000000000000000015711341514612000235470ustar00rootroot00000000000000abcdefg hijkl abcdefg hijkl abcdefg hijkl abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq r @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ x x abcd xterm.js-3.8.1/fixtures/escape_sequence_files/t0021-CUB.in000066400000000000000000000006031341514612000231610ustar00rootroot00000000000000abcdefg!@ abcdefg!@ abcdefg!@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr@ x xterm.js-3.8.1/fixtures/escape_sequence_files/t0021-CUB.text000066400000000000000000000005561341514612000235460ustar00rootroot00000000000000abcdef!@ !@cdefg abcde!@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@q abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq @ x xterm.js-3.8.1/fixtures/escape_sequence_files/t0022-CUU.in000066400000000000000000000002231341514612000232030ustar00rootroot00000000000000a b c defg h i j....................................................................klmn 0123 xterm.js-3.8.1/fixtures/escape_sequence_files/t0022-CUU.text000066400000000000000000000005011341514612000235600ustar00rootroot00000000000000a g n h f m ie l j....................................................................k 3 2 1 0 xterm.js-3.8.1/fixtures/escape_sequence_files/t0023-CUU_scroll.in000066400000000000000000000003421341514612000245640ustar00rootroot00000000000000This is the first line. This is the second line. And the third. This line should be deleted. Penultimate line. This should be the last line. I have gone up all the way... xterm.js-3.8.1/fixtures/escape_sequence_files/t0023-CUU_scroll.text000066400000000000000000000002001341514612000251330ustar00rootroot00000000000000I have gone up all the way... This line should be deleted. Penultimate line. This should be the last line. xterm.js-3.8.1/fixtures/escape_sequence_files/t0024-CUD.in000066400000000000000000000002321341514612000231640ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x012345Bottom line. ABCDE  xterm.js-3.8.1/fixtures/escape_sequence_files/t0024-CUD.text000066400000000000000000000007411341514612000235470ustar00rootroot00000000000000b 1 c 2 d 3 e f 4 g h i 5 j k l m n o p q A r B s C t D u E v w x Bottom line. xterm.js-3.8.1/fixtures/escape_sequence_files/t0025-CUP.in000066400000000000000000000000751341514612000232060ustar00rootroot00000000000000abcdefg xterm.js-3.8.1/fixtures/escape_sequence_files/t0025-CUP.text000066400000000000000000000002071341514612000235610ustar00rootroot00000000000000 b e d g f xterm.js-3.8.1/fixtures/escape_sequence_files/t0026-CNL.in000066400000000000000000000001721341514612000231720ustar00rootroot00000000000000abcdefghi -------------------------------------------------------------------------abcdefghijlast line xterm.js-3.8.1/fixtures/escape_sequence_files/t0026-CNL.text000066400000000000000000000001731341514612000235510ustar00rootroot00000000000000def ghi -------------------------------------------------------------------------abcdefg hij last line xterm.js-3.8.1/fixtures/escape_sequence_files/t0027-CPL.in000066400000000000000000000000571341514612000231770ustar00rootroot00000000000000erasedreplacement line fourline two xterm.js-3.8.1/fixtures/escape_sequence_files/t0027-CPL.text000066400000000000000000000000651341514612000235540ustar00rootroot00000000000000replacement line two line four xterm.js-3.8.1/fixtures/escape_sequence_files/t0030-HPR.in000066400000000000000000000013501341514612000232010ustar00rootroot00000000000000abcdefghijkl abcdefghijkl abcdefghijkl abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm@ x x abcd xterm.js-3.8.1/fixtures/escape_sequence_files/t0030-HPR.text000066400000000000000000000015711341514612000235640ustar00rootroot00000000000000abcdefg hijkl abcdefg hijkl abcdefg hijkl abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq r @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @ x x abcd xterm.js-3.8.1/fixtures/escape_sequence_files/t0031-HPB.in_000066400000000000000000000006031341514612000233210ustar00rootroot00000000000000abcdefg!@ abcdefg!@ abcdefg!@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr@ x xterm.js-3.8.1/fixtures/escape_sequence_files/t0031-HPB.text000066400000000000000000000005671341514612000235510ustar00rootroot00000000000000abcdefg!@ abcdefg!@ abcdefg!@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq @ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq r@ x xterm.js-3.8.1/fixtures/escape_sequence_files/t0032-VPB.in000066400000000000000000000002231341514612000231770ustar00rootroot00000000000000a b c defg h i j....................................................................klmn 0123 xterm.js-3.8.1/fixtures/escape_sequence_files/t0032-VPB.text000066400000000000000000000001561341514612000235620ustar00rootroot00000000000000b c defg h i j....................................................................klmn 0123 xterm.js-3.8.1/fixtures/escape_sequence_files/t0033-VPB_scroll.in000066400000000000000000000003421341514612000245600ustar00rootroot00000000000000This is the first line. This is the second line. And the third. This line should be deleted. Penultimate line. This should be the last line. I have gone up all the way... xterm.js-3.8.1/fixtures/escape_sequence_files/t0033-VPB_scroll.text000066400000000000000000000000661341514612000251410ustar00rootroot00000000000000I have gone up all the way... xterm.js-3.8.1/fixtures/escape_sequence_files/t0034-VPR.in000066400000000000000000000002321341514612000232210ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x012345Bottom line. ABCDE  xterm.js-3.8.1/fixtures/escape_sequence_files/t0034-VPR.text000066400000000000000000000007411341514612000236040ustar00rootroot00000000000000b 1 c 2 d 3 e f 4 g h i 5 j k l m n o p q A r B s C t D u E v w x Bottom line. xterm.js-3.8.1/fixtures/escape_sequence_files/t0035-HVP.in000066400000000000000000000000751341514612000232150ustar00rootroot00000000000000abcdefg xterm.js-3.8.1/fixtures/escape_sequence_files/t0035-HVP.text000066400000000000000000000002071341514612000235700ustar00rootroot00000000000000 b e d g f xterm.js-3.8.1/fixtures/escape_sequence_files/t0040-REP.in000066400000000000000000000004441341514612000232020ustar00rootroot00000000000000x < abcdefg abcdefg! @ . ?- xterm.js-3.8.1/fixtures/escape_sequence_files/t0040-REP.text000066400000000000000000000004601341514612000235560ustar00rootroot00000000000000xxxxxx < abcdefg abcd!fg @@@@@@@@@ @@@@@@@@@@@@ . .... ? ?- xterm.js-3.8.1/fixtures/escape_sequence_files/t0050-ICH.in000066400000000000000000000027121341514612000231600ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz[15@ abcdefghijklmnopqrstuvwxyz[80@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[17@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[18@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[19@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[20@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[21@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop[5@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq[5@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq[5@ ICH at end:[5@ abcdefghijklmnopqrstuvwxyz[15@!@# abcdefghijklmnopqrstuvwxyz[80@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[17@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[18@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[19@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[20@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[21@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop[5@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq[5@!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq[5@!@# ICH at end:[5@!@# xterm.js-3.8.1/fixtures/escape_sequence_files/t0050-ICH.text000066400000000000000000000024761341514612000235450ustar00rootroot00000000000000abcdefghijklmnopqrstu abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567 89 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567 89 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567 8 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW XYZ01234567890abcdefghijkl ICH at end: abcdefghijklmnopqrstu!@# vwxyz abcdefghijklmnopqrstu!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567!@# 89 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567!@# 89 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567!@# 8 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567!@# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop! @# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop! @# abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW!@# XYZ01234567890abcdefghijkl ICH at end:!@# xterm.js-3.8.1/fixtures/escape_sequence_files/t0051-IL.in000066400000000000000000000003241341514612000230570ustar00rootroot00000000000000ab cd ef gh ij kl mn opQRST 1 2 3 4 5------------------------------------------------------------------------------ab 6 7 8 9 10 11 12 13 14 15 xterm.js-3.8.1/fixtures/escape_sequence_files/t0051-IL.text000066400000000000000000000002061341514612000234340ustar00rootroot00000000000000 ef gh ij QR kl mn op 1 2 3 b 4------------------------------------------------------------------------------a 5 6 7 8 9 10 11 12 xterm.js-3.8.1/fixtures/escape_sequence_files/t0052-DL.in000066400000000000000000000000731341514612000230540ustar00rootroot00000000000000a b c d e f g hijklmnop 1 2 3 4 x xterm.js-3.8.1/fixtures/escape_sequence_files/t0052-DL.text000066400000000000000000000000511341514612000234260ustar00rootroot00000000000000a b c ijklmnop g h 1 x 4 xterm.js-3.8.1/fixtures/escape_sequence_files/t0053-DCH.in000066400000000000000000000016411341514612000231560ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz> abcdefghijklmnopqrstuvwxyz>! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>! ------------------------------------------------------------------------------? -------------------------------------------------------------------------------? -------------------------------------------------------------------------------? ---------------------------------------------------------------------------------? .............................................................................. ............................................................................... ............................................................................... ................................................................................. xterm.js-3.8.1/fixtures/escape_sequence_files/t0053-DCH.text000066400000000000000000000015231341514612000235330ustar00rootroot00000000000000abcdefghijklmnopqr>vwxyz abcdefghijklmnopqr>!wxyz >!mnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh abcdefghijklmnopqrstuvwxyz0123456789ABC>!PQRSTUVWXYZ0123456789abcdefgh ------------------------------------------------------------------------------? -------------------------------------------------------------------------------? -------------------------------------------------------------------------------? -------------------------------------------------------------------------------- -? .............................................................................. ............................................................................... ............................................................................... ................................................................................ . xterm.js-3.8.1/fixtures/escape_sequence_files/t0054-ECH.in000066400000000000000000000016411341514612000231600ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz> abcdefghijklmnopqrstuvwxyz>! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>! ------------------------------------------------------------------------------? -------------------------------------------------------------------------------? -------------------------------------------------------------------------------? ---------------------------------------------------------------------------------? .............................................................................. ............................................................................... ............................................................................... ................................................................................. xterm.js-3.8.1/fixtures/escape_sequence_files/t0054-ECH.text000066400000000000000000000015531341514612000235400ustar00rootroot00000000000000abcdefghijklmnopqr> vwxyz abcdefghijklmnopqr>! vwxyz >! lmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh abcdefghijklmnopqrstuvwxyz0123456789ABC>! OPQRSTUVWXYZ0123456789abcdefgh ------------------------------------------------------------------------------? -------------------------------------------------------------------------------? -------------------------------------------------------------------------------? -------------------------------------------------------------------------------- -? .............................................................................. ............................................................................... ............................................................................... ................................................................................ . xterm.js-3.8.1/fixtures/escape_sequence_files/t0055-EL.in000066400000000000000000000013411341514612000230570ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>< abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>< abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>< abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh>< abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh><! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh><! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh><! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh! xterm.js-3.8.1/fixtures/escape_sequence_files/t0055-EL.text000066400000000000000000000006531341514612000234420ustar00rootroot00000000000000abcdefghijklmnopqrstuvwxyz0123456789ABC> abcdefghijklmnopqrstuvwxyz0123456789ABC> FGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh abcdefghijklmnopqrstuvwxyz0123456789ABC>! !FGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh ! abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefg! xterm.js-3.8.1/fixtures/escape_sequence_files/t0056-ED.in000066400000000000000000000023761341514612000230610ustar00rootroot00000000000000a ab abc abcd abcde abcdef abcdefg abcdefgh abcdefghi abcdefghij abcdefghijk abcdefghijkl abcdefghijklm abcdefghijklmn abcdefghijklmno abcdefghijklmnop abcdefghijklmnopq abcdefghijklmnopqr abcdefghijklmnopqrs abcdefghijklmnopqrst abcdefghijklmnopqrstu abcdefghijklmnopqrstuv abcdefghijklmnopqrstuvw abcdefghijklmnopqrstuvwx abcdefghijklmnopqrstuvwxy abcdefghijklmnopqrstuvwxyz A AB ABC ABCD ABCDE ABCDEF ABCDEFG ABCDEFGH ABCDEFGHI ABCDEFGHIJ ABCDEFGHIJK ABCDEFGHIJKL ABCDEFGHIJKLM ABCDEFGHIJKLMN ABCDEFGHIJKLMNO ABCDEFGHIJKLMNOP ABCDEFGHIJKLMNOPQ ABCDEFGHIJKLMNOPQR ABCDEFGHIJKLMNOPQRS ABCDEFGHIJKLMNOPQRST ABCDEFGHIJKLMNOPQRSTU ABCDEFGHIJKLMNOPQRSTUV ABCDEFGHIJKLMNOPQRSTUVW ABCDEFGHIJKLMNOPQRSTUVWX ABCDEFGHIJKLMNOPQRSTUVWXY!  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaav^ the end xterm.js-3.8.1/fixtures/escape_sequence_files/t0056-ED.text000066400000000000000000000005461341514612000234340ustar00rootroot00000000000000 ! aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaav aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^ the end xterm.js-3.8.1/fixtures/escape_sequence_files/t0057-ED3.in000066400000000000000000000013531341514612000231370ustar00rootroot00000000000000a ab abc abcd abcde abcdef abcdefg abcdefgh abcdefghi abcdefghij abcdefghijk abcdefghijkl abcdefghijklm abcdefghijklmn abcdefghijklmno abcdefghijklmnop abcdefghijklmnopq abcdefghijklmnopqr abcdefghijklmnopqrs abcdefghijklmnopqrst abcdefghijklmnopqrstu abcdefghijklmnopqrstuv abcdefghijklmnopqrstuvw abcdefghijklmnopqrstuvwx abcdefghijklmnopqrstuvwxy abcdefghijklmnopqrstuvwxyz A AB ABC ABCD ABCDE ABCDEF ABCDEFG ABCDEFGH ABCDEFGHI ABCDEFGHIJ ABCDEFGHIJK ABCDEFGHIJKL ABCDEFGHIJKLM ABCDEFGHIJKLMN ABCDEFGHIJKLMNO ABCDEFGHIJKLMNOP ABCDEFGHIJKLMNOPQ ABCDEFGHIJKLMNOPQR ABCDEFGHIJKLMNOPQRS ABCDEFGHIJKLMNOPQRST ABCDEFGHIJKLMNOPQRSTU ABCDEFGHIJKLMNOPQRSTUV ABCDEFGHIJKLMNOPQRSTUVW ABCDEFGHIJKLMNOPQRSTUVWX ABCDEFGHIJKLMNOPQRSTUVWXY this is the end xterm.js-3.8.1/fixtures/escape_sequence_files/t0057-ED3.note000066400000000000000000000005771341514612000235050ustar00rootroot00000000000000Xterm behaves oddly with CSI 3 J. This function is supposed to clear the saved lines in history. Xterm does this, but a small number of lines of history are not cleared. The number seems to vary with how high the window is and how much output has recently been saved. There is no reason to simulate this behavior, so the expected outputs are as if the entire history was erased. xterm.js-3.8.1/fixtures/escape_sequence_files/t0057-ED3.text000066400000000000000000000005331341514612000235140ustar00rootroot00000000000000ABCDEF ABCDEFG ABCDEFGH ABCDEFGHI ABCDEFGHIJ ABCDEFGHIJK ABCDEFGHIJKL ABCDEFGHIJKLM ABCDEFGHIJKLMN ABCDEFGHIJKLMNO ABCDEFGHIJKLMNOP ABCDEFGHIJKLMNOPQ ABCDEFGHIJKLMNOPQR ABCDEFGHIJKLMNOPQRS ABCDEFGHIJKLMNOPQRST ABCDEFGHIJKLMNOPQRSTU ABCDEFGHIJKLMNOPQRSTUV ABCDEFGHIJKLMNOPQRSTUVW ABCDEFGHIJKLMNOPQRSTUVWX ABCDEFGHIJKLMNOPQRSTUVWXY this is the end xterm.js-3.8.1/fixtures/escape_sequence_files/t0060-DECSC.in000066400000000000000000000002021341514612000233670ustar00rootroot00000000000000 7v 12 38^ v7 ...ooo8^ xterm.js-3.8.1/fixtures/escape_sequence_files/t0060-DECSC.text000066400000000000000000000003001341514612000237440ustar00rootroot00000000000000 3 v ... ^ ooo xterm.js-3.8.1/fixtures/escape_sequence_files/t0061-CSI_s.in000066400000000000000000000002061341514612000235130ustar00rootroot00000000000000 v 12 3^ v ...ooo^ xterm.js-3.8.1/fixtures/escape_sequence_files/t0061-CSI_s.text000066400000000000000000000003001341514612000240640ustar00rootroot00000000000000 3 v ... ^ ooo xterm.js-3.8.1/fixtures/escape_sequence_files/t0070-DECSTBM_LF.in000066400000000000000000000001541341514612000242170ustar00rootroot000000000000001 2 3 4 5 6 7 8 9ABCDEFa b c d e f g h i j k l m n o p q r s t uvwxyz  The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0070-DECSTBM_LF.text000066400000000000000000000006241341514612000245770ustar00rootroot000000000000001 2 6 7 8 9 ABC DEF a b c d e f g h i j k l m n o p yz qrstu vwx The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0071-DECSTBM_IND.in000066400000000000000000000002101341514612000243220ustar00rootroot000000000000001D2D3D4D5D6D7D8D9ABCDEFaDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDuvwxyz  The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0071-DECSTBM_IND.text000066400000000000000000000006371341514612000247150ustar00rootroot00000000000000 2 6 7 8 9 ABC DEF a b c d e f g h i j k l m n o p q The end. rstu vwx xterm.js-3.8.1/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.in000066400000000000000000000001621341514612000243350ustar00rootroot000000000000001E2E3E4E5E6E7E8E9aEbEcEdEeEfEgEhEiEjEkElEmEnEoEpEqErEsEtEu The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0072-DECSTBM_NEL.text000066400000000000000000000000711341514612000247120ustar00rootroot000000000000002 5 6 7 8 9 a b c d e f g h i j k l m n o p q The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0073-DECSTBM_RI.in000066400000000000000000000005321341514612000242330ustar00rootroot00000000000000TOP 1 TOP 2 TOP 3 TOP 4 TOP 5 TOP 6 - GONE BOTTOM 6 - GONE BOTTOM 5 BOTTOM 4 BOTTOM 3 BOTTOM 2 BOTTOM 1 And the third. This should be the last line. This one should be lost. This one's a goner, too. MMMMMMMMMMMMMMThis is second line. MThis should be the first line. The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0073-DECSTBM_RI.text000066400000000000000000000002721341514612000246120ustar00rootroot00000000000000TOP 2 TOP 3 TOP 4 TOP 5 This should be the first line. This is second line. And the third. This should be the last line. BOTTOM 5 BOTTOM 4 BOTTOM 3 BOTTOM 2 BOTTOM 1 The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.in000066400000000000000000000001151341514612000246340ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x xterm.js-3.8.1/fixtures/escape_sequence_files/t0074-DECSTBM_SU_SD.text000066400000000000000000000004001341514612000252070ustar00rootroot00000000000000a b c d f g h i j k l m n o p q r s w x xterm.js-3.8.1/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.in000066400000000000000000000001211341514612000250440ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x12 xterm.js-3.8.1/fixtures/escape_sequence_files/t0075-DECSTBM_CUU_CUD.text000066400000000000000000000000621341514612000254260ustar00rootroot00000000000000a b c d e f g h i j2 k l m n o p q r 1 t u v w x xterm.js-3.8.1/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.in000066400000000000000000000011111341514612000246010ustar00rootroot00000000000000 1 (IL on line 2, expect nothing) 2 (DL on line 22, expect nothing) 3 vvvv IL on line 5, expected: A_BC 4 A 5 B 6 C 7 D 8 ^^^^ 9 vvvv DL on line 11, expected: ACD_ 10 A 11 B 12 C 13 D 14 ^^^^ 15 vvvv IL on line 17, expected: A_ 16 A 17 B 18 ^^^^ 19 vvvv IL on line 21, expected: A_ 20 A 21 B 22 ^^^^ 23 vvvv IL on line 24, expected: _A 24 A  25 B 26 ^^^^ 27 vvvv DL on line 28, expected: B_ 28 A 29 B 30 ^^^^ 31 32 xterm.js-3.8.1/fixtures/escape_sequence_files/t0076-DECSTBM_IL_DL.text000066400000000000000000000004171341514612000251670ustar00rootroot00000000000000 8 ^^^^ 9 vvvv DL on line 11, expected: ACD_ 10 A 12 C 13 D 14 ^^^^ 15 vvvv IL on line 17, expected: A_ 16 A 18 ^^^^ 19 vvvv IL on line 21, expected: A_ 20 A 22 ^^^^ 23 vvvv IL on line 24, expected: _A 25 B 26 ^^^^ 28 A 29 B 30 ^^^^ 31 32 xterm.js-3.8.1/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.in000066400000000000000000000003571341514612000252500ustar00rootroot00000000000000 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24A B C D E F G  Done. xterm.js-3.8.1/fixtures/escape_sequence_files/t0077-DECSTBM_quirks.text000066400000000000000000000001511341514612000256160ustar00rootroot00000000000000 3 4 A 5 7 C 8 9 10 11 12 13 14 18 19 20 21 23 24 B D F G Done. xterm.js-3.8.1/fixtures/escape_sequence_files/t0080-HT.in000066400000000000000000000010171341514612000230700ustar00rootroot00000000000000a b c d e f g h i j k l x 1 2 3 Tab before EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh@ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh @ tab (2):abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg @ Tab at EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi@ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi @ with clipping: 4567890abcdefghi MD@ xterm.js-3.8.1/fixtures/escape_sequence_files/t0080-HT.text000066400000000000000000000012201341514612000234420ustar00rootroot00000000000000a b c d e f g h i j k l x 1 3 2 Tab before EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh@ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh@ tab (2):abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg @ Tab at EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi @ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi @ with clipping: 4567890abcdefgh@ xterm.js-3.8.1/fixtures/escape_sequence_files/t0081-TBC.in000066400000000000000000000005541341514612000231730ustar00rootroot00000000000000default: > 1 2 3 4 5 6 7 8 9 0 clear non-existant: x x > 1 2 3 4 5 6 7 8 9 0 clear 2 and 4: x x > 1 3 5 6 7 8 9 0 clear 0: x > 1 3 5 6 7 8 9 0 clear after 0: .x > 1 3 5 6 7 8 9 0 clear all: > 0done TBC at end with clipping: ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdeMD! xterm.js-3.8.1/fixtures/escape_sequence_files/t0081-TBC.text000066400000000000000000000015161341514612000235500ustar00rootroot00000000000000default: > 1 2 3 4 5 6 7 8 9 0 clear non-existant: x x > 1 2 3 4 5 6 7 8 9 0 clear 2 and 4: x x > 1 3 5 6 7 8 9 0 clear 0: x > 1 3 5 6 7 8 9 0 clear after 0: . x > 1 3 5 6 7 8 9 0 clear all: > 0 done TBC at end with clipping: ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd! xterm.js-3.8.1/fixtures/escape_sequence_files/t0082-HTS.in000066400000000000000000000004001341514612000232100ustar00rootroot00000000000000 1 2 3 4 abcdeHFghijklmnopqrstuvHWxyz H 1 2 W 3 4 HTS at end: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdeHfgh H 1 2 W 3 4 5 6 7 8 9 a b HTS at end with clipping: ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdeHMD! xterm.js-3.8.1/fixtures/escape_sequence_files/t0082-HTS.text000066400000000000000000000005511341514612000235750ustar00rootroot00000000000000 1 2 3 4 abcdeFghijklmnopqrstuvWxyz H 1 2 W 3 4 HTS at end: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde fgh H 1 2 W 3 4 5 6 7 8 9 a b HTS at end with clipping: ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd! xterm.js-3.8.1/fixtures/escape_sequence_files/t0083-CHT.in000066400000000000000000000011301341514612000231720ustar00rootroot00000000000000abcdefghijkl x 12 3 CHT before EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh@ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh@ tab (2):abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg@ CHT at EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi@ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi@ with clipping: 4567890abcdefghiMD@ aeg e h x xterm.js-3.8.1/fixtures/escape_sequence_files/t0083-CHT.text000066400000000000000000000015121341514612000235540ustar00rootroot00000000000000a b c d e f g h i j k l x 1 3 2 CHT before EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh@ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh@ tab (2):abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg @ CHT at EOL: no tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi @ tab: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi @ with clipping: 4567890abcdefgh@ a e g e h x xterm.js-3.8.1/fixtures/escape_sequence_files/t0084-CBT.in000066400000000000000000000010661341514612000231750ustar00rootroot00000000000000a b c d e f g h i j k <Iihfa a default tab stops: near end: abcdefg! at end: abcdefgh! at end (2): abcdefgh! at end with clipping: abcdefghMD! at end with clipping (2): abcdefghMD! set tab stop at column 80: Hv at end: abcdefgh! at end (2): abcdefgh! at end with clipping: abcdefghMD! at end with clipping (2): abcdefghMD! xterm.js-3.8.1/fixtures/escape_sequence_files/t0084-CBT.text000066400000000000000000000017441341514612000235560ustar00rootroot00000000000000a b c d e f g h i j k a f h i < a default tab stops: near end: !bcdefg at end: abcdefgh ! at end (2): abcdefgh ! at end with clipping: !bcdefgh at end with clipping (2): ! abcdefgh set tab stop at column 80: v at end: abcdefgh ! at end (2): abcdefgh ! at end with clipping: !bcdefgh at end with clipping (2): ! abcdefgh xterm.js-3.8.1/fixtures/escape_sequence_files/t0084-CBT.text-xterm000066400000000000000000000017371341514612000247150ustar00rootroot00000000000000a b c d e f g h i j k a f h i < a default tab stops: near end: !bcdefg at end: abcdefgh ! at end (2): abcdefgh ! at end with clipping: !bcdefgh at end with clipping (2): ! abcdefgh set tab stop at column 80: v at end: abcdefgh ! at end (2): abcdefgh ! at end with clipping: !bcdefgh at end with clipping (2): ! abcdefgh xterm.js-3.8.1/fixtures/escape_sequence_files/t0090-alt_screen.in000066400000000000000000000005121341514612000246740ustar00rootroot00000000000000a[?47h b c d e f g h i j k l m n o p q r s t u v w x y[?47lz A[?47h B C D E F G H I J K L M N O P Q R S T U V W X Y[?47lZ --->[?47hhello[?47l<-- --->[?47h[?47l<--- fin xterm.js-3.8.1/fixtures/escape_sequence_files/t0090-alt_screen.text000066400000000000000000000003041341514612000252510ustar00rootroot00000000000000 z AZ <-- ---> ---> <--- fin xterm.js-3.8.1/fixtures/escape_sequence_files/t0091-alt_screen_ED3.in000066400000000000000000000001731341514612000253330ustar00rootroot00000000000000a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 10 [?47h one two three four five six  [?47l 11 xterm.js-3.8.1/fixtures/escape_sequence_files/t0091-alt_screen_ED3.text000066400000000000000000000012261341514612000257110ustar00rootroot00000000000000 n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9 10 11 xterm.js-3.8.1/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.in000066400000000000000000000001161341514612000255770ustar00rootroot00000000000000a b7 c[?47h >7 xxxxxxx8[?47l!8< The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0092-alt_screen_DECSC.text000066400000000000000000000000651341514612000261600ustar00rootroot00000000000000a b< c ! The end. xterm.js-3.8.1/fixtures/escape_sequence_files/t0100-IRM.in000066400000000000000000000006731341514612000232040ustar00rootroot00000000000000-------- insert single '!' --------------------------------------------abcdefghi jklmnop! -------- insert 0-9, with wraparound ----------------------------------abcdefghi jklmnop0123456789 -------- repeat 3 '!' -------------------------------------------------abcdefghi jklmnop! -------- repeat 10 '!', with wraparound -------------------------------abcdefghi jklmnop! xterm.js-3.8.1/fixtures/escape_sequence_files/t0100-IRM.text000066400000000000000000000005751341514612000235630ustar00rootroot00000000000000-------- insert single '!' --------------------------------------------abc!defgh jklmnop -------- insert 0-9, with wraparound ----------------------------------abc012345 6789jklmnop -------- repeat 3 '!' -------------------------------------------------abc!!!def jklmnop -------- repeat 10 '!', with wraparound -------------------------------abc!!!!!! !!!!jklmnop xterm.js-3.8.1/fixtures/escape_sequence_files/t0101-NLM.in000066400000000000000000000000531341514612000231740ustar00rootroot00000000000000a b c d e f g h i j k l m n o p xterm.js-3.8.1/fixtures/escape_sequence_files/t0101-NLM.text000066400000000000000000000000521341514612000235510ustar00rootroot00000000000000a b c d e f g h k l m n o p xterm.js-3.8.1/fixtures/escape_sequence_files/t0102-DECAWM.in000066400000000000000000000005021341514612000235060ustar00rootroot00000000000000-------- default: wraparound ----------------------------------------------abcdefgh[?7h -------- set: wraparound ----------------------------------------------abcdefgh[?7l -------- unset: no wraparound -------------------------------------------abcdefgh this should be immediately below "no wraparound"[?7h xterm.js-3.8.1/fixtures/escape_sequence_files/t0102-DECAWM.text000066400000000000000000000005041341514612000240660ustar00rootroot00000000000000-------- default: wraparound ----------------------------------------------abcd efgh -------- set: wraparound ----------------------------------------------abcd efgh -------- unset: no wraparound -------------------------------------------abcd this should be immediately below "no wraparound" xterm.js-3.8.1/fixtures/escape_sequence_files/t0103-reverse_wrap.in000066400000000000000000000000721341514612000252550ustar00rootroot00000000000000[?45ha bA c-B dC!the endreally![?45l xterm.js-3.8.1/fixtures/escape_sequence_files/t0103-reverse_wrap.text000066400000000000000000000003011341514612000256260ustar00rootroot00000000000000c C ! the end really! xterm.js-3.8.1/fixtures/escape_sequence_files/t0200-SGR.html000066400000000000000000000015741341514612000235500ustar00rootroot00000000000000
Implemented non-color attributes:

 1: This is bold.

 3: This is italic.

 4: This is underlined.

 5: This is slowly blinking.

 6: This is rapidly blinking.

 7: This is inverse.

 8: This is hidden.

 9: This is struck out.

21: This is double underlined.

53: This is overlined.



Unimplemented non-color attributes:

 2: weight:feint

20: style:fraktur

51: frame:box

52: frame:circle
xterm.js-3.8.1/fixtures/escape_sequence_files/t0200-SGR.in_000066400000000000000000000007211341514612000233420ustar00rootroot00000000000000Implemented non-color attributes: 1: This is bold. 3: This is italic. 4: This is underlined. 5: This is slowly blinking. 6: This is rapidly blinking. 7: This is inverse. 8: This is hidden. 9: This is struck out. 21: This is double underlined. 53: This is overlined. Unimplemented non-color attributes: 2: weight:feint 20: style:fraktur 51: frame:box 52: frame:circle xterm.js-3.8.1/fixtures/escape_sequence_files/t0220-SGR_inverse.html000066400000000000000000000013431341514612000252770ustar00rootroot00000000000000
This is inverse text with default fg and bg.
This is inverse text with red fg and default bg.
This is inverse text with default fg and red bg.
This is inverse text with green fg and red bg.
xterm.js-3.8.1/fixtures/escape_sequence_files/t0220-SGR_inverse.in_000066400000000000000000000003751341514612000251040ustar00rootroot00000000000000This is inverse text with default fg and bg. This is inverse text with red fg and default bg. This is inverse text with default fg and red bg. This is inverse text with green fg and red bg. xterm.js-3.8.1/fixtures/escape_sequence_files/t0500-bash_long_line.in000066400000000000000000000007541341514612000255240ustar00rootroot00000000000000Script started on Fri 27 Mar 2009 08:53:29 PM EDT dircolors: /etc/DIR_COLORS: No such file or directory ]0;mark@mark-desktop:~/vt100-to-htmlmark@mark-desktop:~/vt100-to-html$ bcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST UVWXYZ1234567890abcdefghijklmnopqrstuvcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuv ]0;mark@mark-desktop:~/vt100-to-htmlmark@mark-desktop:~/vt100-to-html$ exit Script done on Fri 27 Mar 2009 08:53:33 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0500-bash_long_line.text000066400000000000000000000005111341514612000260710ustar00rootroot00000000000000Script started on Fri 27 Mar 2009 08:53:29 PM EDT dircolors: /etc/DIR_COLORS: No such file or directory mark@mark-desktop:~/vt100-to-html$ cdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU VWXYZ1234567890abcdefghijklmnopqrstuv mark@mark-desktop:~/vt100-to-html$ exit Script done on Fri 27 Mar 2009 08:53:33 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0501-bash_ls.in000066400000000000000000000012431341514612000241670ustar00rootroot00000000000000Script started on Sun 17 May 2009 06:20:41 PM EDT dircolors: /etc/DIR_COLORS: No such file or directory ]0;mark@mark-desktop:~/vt100-to-html/scriptsmark@mark-desktop:~/vt100-to-html/scripts$ ls bash.script ]0;mark@mark-desktop:~/vt100-to-html/scriptsmark@mark-desktop:~/vt100-to-html/scripts$ ls / bin dev initrd.img lib64 opt selinux usr boot etc initrd.img.old lost+found proc srv var boot2 home lib media root sys vmlinuz cdrom initrd lib32 mnt sbin tmp vmlinuz.old ]0;mark@mark-desktop:~/vt100-to-html/scriptsmark@mark-desktop:~/vt100-to-html/scripts$ exit Script done on Sun 17 May 2009 06:20:52 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0501-bash_ls.text000066400000000000000000000011031341514612000245400ustar00rootroot00000000000000Script started on Sun 17 May 2009 06:20:41 PM EDT dircolors: /etc/DIR_COLORS: No such file or directory mark@mark-desktop:~/vt100-to-html/scripts$ ls bash.script mark@mark-desktop:~/vt100-to-html/scripts$ ls / bin dev initrd.img lib64 opt selinux usr boot etc initrd.img.old lost+found proc srv var boot2 home lib media root sys vmlinuz cdrom initrd lib32 mnt sbin tmp vmlinuz.old mark@mark-desktop:~/vt100-to-html/scripts$ exit Script done on Sun 17 May 2009 06:20:52 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0502-bash_ls_color.in000066400000000000000000000017311341514612000253700ustar00rootroot00000000000000Script started on Sun 17 May 2009 06:21:05 PM EDT dircolors: /etc/DIR_COLORS: No such file or directory ]0;mark@mark-desktop:~/vt100-to-html/scriptsmark@mark-desktop:~/vt100-to-html/scripts$ ls --color=auto / bin dev initrd.img lib64 opt selinux usr boot etc initrd.img.old lost+found proc srv var boot2 home lib media root sys vmlinuz cdrom initrd lib32 mnt sbin tmp vmlinuz.old ]0;mark@mark-desktop:~/vt100-to-html/scriptsmark@mark-desktop:~/vt100-to-html/scripts$ exit Script done on Sun 17 May 2009 06:21:11 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0502-bash_ls_color.text000066400000000000000000000010301341514612000257360ustar00rootroot00000000000000Script started on Sun 17 May 2009 06:21:05 PM EDT dircolors: /etc/DIR_COLORS: No such file or directory mark@mark-desktop:~/vt100-to-html/scripts$ ls --color=auto / bin dev initrd.img lib64 opt selinux usr boot etc initrd.img.old lost+found proc srv var boot2 home lib media root sys vmlinuz cdrom initrd lib32 mnt sbin tmp vmlinuz.old mark@mark-desktop:~/vt100-to-html/scripts$ exit Script done on Sun 17 May 2009 06:21:11 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0503-zsh_ls_color.in000066400000000000000000000023001341514612000252510ustar00rootroot00000000000000Script started on Sun 17 May 2009 06:21:21 PM EDT ]2;mark-desktop - ~/vt100-to-html/scripts% $ ~/vt100-to-html/scriptslls / ]2;mark-desktop - ls /bin dev initrd.img lib64 opt selinux usr boot etc initrd.img.old lost+found proc srv var boot2 home lib media root sys vmlinuz cdrom initrd lib32 mnt sbin tmp vmlinuz.old ]2;mark-desktop - ~/vt100-to-html/scripts% $ ~/vt100-to-html/scripts Script done on Sun 17 May 2009 06:21:27 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0503-zsh_ls_color.text000066400000000000000000000010261341514612000256330ustar00rootroot00000000000000Script started on Sun 17 May 2009 06:21:21 PM EDT $ ls / ~/vt100-to-html/scripts bin dev initrd.img lib64 opt selinux usr boot etc initrd.img.old lost+found proc srv var boot2 home lib media root sys vmlinuz cdrom initrd lib32 mnt sbin tmp vmlinuz.old $ ~/vt100-to-html/scripts Script done on Sun 17 May 2009 06:21:27 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0504-vim.in000066400000000000000000000336471341514612000233670ustar00rootroot00000000000000Script started on Sun 15 Aug 2010 11:53:27 PM EDT ]0;mark-desktop - ~/vt100-to-html/test% $ ~/vt100-to-html/testlls ]0;mark-desktop - lsexpected_text t0016-SU.text t0050-ICH.text input t0017-SD.in t0051-IL.in run_all.py t0017-SD.text t0051-IL.text t0001-all_printable.in t0020-CUF.in t0052-DL.in t0001-all_printable.text t0020-CUF.text t0052-DL.text t0002-history.in t0021-CUB.in t0053-DCH.in t0002-history.text t0021-CUB.text t0053-DCH.text t0003-line_wrap.in t0022-CUU.in t0054-ECH.in t0003-line_wrap.text t0022-CUU.text t0054-ECH.text t0004-LF.in t0023-CUU_scroll.in t0055-EL.in t0004-LF.text t0023-CUU_scroll.text t0055-EL.text t0005-CR.in t0024-CUD.in t0056-ED.in t0005-CR.text t0024-CUD.text t0056-ED.text t0006-IND.in t0025-CUP.in t0057-ED3.in t0006-IND.text t0025-CUP.text t0057-ED3.note t0007-space_at_end.in t0026-CNL.in t0057-ED3.text t0007-space_at_end.text t0026-CNL.text t0060-DECSC.in t0008-BS.in t0027-CPL.in t0060-DECSC.text t0008-BS.text t0027-CPL.text t0061-CSI_s.in t0009-NEL.in t0030-HPR.in t0061-CSI_s.text t0009-NEL.text t0030-HPR.text t008x-alt_screen_ED.in t0010-RI.in t0031-HPB.in t008x-IRM.in t0010-RI.text t0031-HPB.text t008x-NLM.in t0011-RI_scroll.in t0032-VPB.in t008x-save_cursor_mode.in t0011-RI_scroll.text t0032-VPB.text t0500-bash_long_line.in t0012-VT.in t0033-VPB_scroll.in t0500-bash_long_line.text t0012-VT.text t0033-VPB_scroll.text t0501-bash_ls.in t0013-FF.in t0034-VPR.in t0501-bash_ls.text t0013-FF.text t0034-VPR.text t0502-bash_ls_color.in t0014-CAN.in t0035-HVP.in t0502-bash_ls_color.text t0014-CAN.text t0035-HVP.text t0503-zsh_ls_color.in t0015-SUB.in t0040-REP.in t0503-zsh_ls_color.text t0015-SUB.text t0040-REP.text typescript t0016-SU.in t0050-ICH.in ]0;mark-desktop - ~/vt100-to-html/test% $ ~/vt100-to-html/testvvim ]0;mark-desktop - vim[?1000h[?1049h[?1h=[?12;25h[?12l[?25h[>c[?25l~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ [No Name] 0,0-1 AllVIM - Vi IMprovedversion 7.2.267by Bram Moolenaar et al.Vim is open source and freely distributableBecome a registered Vim user!type :help register for information type :q to exit type :help or  for on-line helptype :help version7 for version info]2;[No Name] - VIM]1;[No Name][?12l[?25h[?1000l[?1002hP+q436f\P+q6b75\P+q6b64\P+q6b72\P+q6b6c\P+q2332\P+q2334\P+q2569\P+q2a37\P+q6b31\[?25l-- INSERT --1 [?12l[?25h[?25lT         [+]1,2 ]2;[No Name] + - VIM]1;[No Name][?12l[?25h[?25lTh3 [?12l[?25h[?25lhi4 [?12l[?25h[?25lis5 [?12l[?25h[?25l6 [?12l[?25h[?25l i7 [?12l[?25h[?25lis8 [?12l[?25h[?25l9 [?12l[?25h[?25l a10[?12l[?25h[?25l1 [?12l[?25h[?25l t2 [?12l[?25h[?25lte3 [?12l[?25h[?25les4 [?12l[?25h[?25lst5 [?12l[?25h[?25lt.6 [?12l[?25h[?25l2,1 [?12l[?25h[?25l3,[?12l[?25h[?25lH2 [?12l[?25h[?25lHo3 [?12l[?25h[?25loe4 [?12l[?25h[?25l5 [?12l[?25h[?25l f6 [?12l[?25h[?25lfu7 [?12l[?25h[?25l6 [?12l[?25h[?25l5 [?12l[?25h[?25l4 [?12l[?25h[?25l3 [?12l[?25h[?25lop4 [?12l[?25h[?25lpe5 [?12l[?25h[?25lef6 [?12l[?25h[?25lfu7 [?12l[?25h[?25lul8 [?12l[?25h[?25lll9 [?12l[?25h[?25lly10[?12l[?25h[?25l1 [?12l[?25h[?25l i2 [?12l[?25h[?25lit3 [?12l[?25h[?25l4 [?12l[?25h[?25l w5 [?12l[?25h[?25lwi6 [?12l[?25h[?25lil7 [?12l[?25h[?25lll8 [?12l[?25h[?25l9 [?12l[?25h[?25l w20 [?12l[?25h[?25lwo1 [?12l[?25h[?25lor2 [?12l[?25h[?25lrk3 [?12l[?25h[?25lk.4 [?12l[?25h[?25l4,1 [?12l[?25h[?25l5,[?12l[?25h[?25l6,[?12l[?25h[?25l7,[?12l[?25h[?25l8,[?12l[?25h[?25l9,[?12l[?25h[?25l10,1[?12l[?25h[?25l1,[?12l[?25h[?25l2,[?12l[?25h[?25l3,[?12l[?25h[?25l4,[?12l[?25h[?25l5,[?12l[?25h[?25l6,[?12l[?25h[?25l7,[?12l[?25h[?25l8,[?12l[?25h[?25l9,[?12l[?25h[?25l20,[?12l[?25h[?25l1,[?12l[?25h[?25l2,[?12l[?25h[?25l 3,Bot[?12l[?25h[?25l 4,[?12l[?25h[?25l 5,[?12l[?25h[?25l 6,[?12l[?25h[?25l 7,[?12l[?25h[?25l 8,[?12l[?25h[?25l 9,[?12l[?25h[?25l 30,[?12l[?25h[?25l 1,[?12l[?25h[?25l 2,[?12l[?25h[?25l 3,[?12l[?25h[?25lI2 [?12l[?25h[?25l3 [?12l[?25h[?25l b4 [?12l[?25h[?25lbe5 [?12l[?25h[?25let6 [?12l[?25h[?25l7 [?12l[?25h[?25l i8 [?12l[?25h[?25lit9 [?12l[?25h[?25l10[?12l[?25h[?25l w1 [?12l[?25h[?25lwi2 [?12l[?25h[?25lil3 [?12l[?25h[?25lll4 [?12l[?25h[?25ll.5 [?12l[?25h[?25l4 [?12l[?25h[?25lThis is a test. Hopefully it will work.1,1 Top[?12l[?25h[?25lI bet it will. ~ ~ ~ ~ ~ ~ ~ ~ ~ [No Name] [+] 21,0-1 Bot[?12l[?25h[?25l0,[?12l[?25h[?25l19,[?12l[?25h[?25l8,[?12l[?25h[?25l7,[?12l[?25h[?25l6,[?12l[?25h[?25l5,[?12l[?25h[?25l:[?12l[?25hw[?25l[?12l[?25hq[?25l[?12l[?25h [?25lE32: No file name[?12l[?25h[?25l:[?12l[?25hw[?25l[?12l[?25h[?25l [?12l[?25hf[?25l[?12l[?25ho[?25l[?12l[?25ho[?25l[?12l[?25h [?25l"foo" [New] 33L, 85C writtenfoo ]2;foo (~/vt100-to-html/test) - VIM]1;foo[?12l[?25h[?25l:[?12l[?25hq[?25l[?12l[?25h [?25l[?1002l]2;mark-desktop - vim]1;mark-desktop - vim[?1l>[?12l[?25h[?1049l]0;mark-desktop - ~/vt100-to-html/test% $ ~/vt100-to-html/testeecho Yes! ]0;mark-desktop - echo Yes!Yes! ]0;mark-desktop - ~/vt100-to-html/test% $ ~/vt100-to-html/test Script done on Sun 15 Aug 2010 11:54:14 PM EDT xterm.js-3.8.1/fixtures/escape_sequence_files/t0504-vim.text000066400000000000000000000032141341514612000237300ustar00rootroot00000000000000t0005-CR.in t0024-CUD.in t0056-ED.in t0005-CR.text t0024-CUD.text t0056-ED.text t0006-IND.in t0025-CUP.in t0057-ED3.in t0006-IND.text t0025-CUP.text t0057-ED3.note t0007-space_at_end.in t0026-CNL.in t0057-ED3.text t0007-space_at_end.text t0026-CNL.text t0060-DECSC.in t0008-BS.in t0027-CPL.in t0060-DECSC.text t0008-BS.text t0027-CPL.text t0061-CSI_s.in t0009-NEL.in t0030-HPR.in t0061-CSI_s.text t0009-NEL.text t0030-HPR.text t008x-alt_screen_ED.in t0010-RI.in t0031-HPB.in t008x-IRM.in t0010-RI.text t0031-HPB.text t008x-NLM.in t0011-RI_scroll.in t0032-VPB.in t008x-save_cursor_mode.in t0011-RI_scroll.text t0032-VPB.text t0500-bash_long_line.in t0012-VT.in t0033-VPB_scroll.in t0500-bash_long_line.text t0012-VT.text t0033-VPB_scroll.text t0501-bash_ls.in t0013-FF.in t0034-VPR.in t0501-bash_ls.text t0013-FF.text t0034-VPR.text t0502-bash_ls_color.in t0014-CAN.in t0035-HVP.in t0502-bash_ls_color.text t0014-CAN.text t0035-HVP.text t0503-zsh_ls_color.in t0015-SUB.in t0040-REP.in t0503-zsh_ls_color.text t0015-SUB.text t0040-REP.text typescript t0016-SU.in t0050-ICH.in $ vim ~/vt100-to-html/test Script done on Sun 15 Aug 2010 11:54:14 PM EDT ~/vt100-to-html/test xterm.js-3.8.1/fixtures/typings-test/000077500000000000000000000000001341514612000176155ustar00rootroot00000000000000xterm.js-3.8.1/fixtures/typings-test/tsconfig.json000066400000000000000000000002701341514612000223230ustar00rootroot00000000000000{ "files": [ "typings-test.ts" ], "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6" ], "noEmit": true } } xterm.js-3.8.1/fixtures/typings-test/typings-test.ts000066400000000000000000000173671341514612000226550ustar00rootroot00000000000000/** * @license MIT */ /// import { Terminal } from 'xterm'; namespace constructor { { new Terminal(); new Terminal({}); new Terminal({ cols: 1, rows: 1 }); new Terminal({ 'cols': 1, 'cursorBlink': true, 'cursorStyle': 'block', 'disableStdin': false, 'rows': 1, 'scrollback': 10, 'tabStopWidth': 2, }); } } namespace properties { { const t: Terminal = new Terminal(); const element: HTMLElement = t.element; const textarea: HTMLTextAreaElement = t.textarea; } } namespace static_methods { { Terminal.applyAddon({}); Terminal.applyAddon({}); Terminal.applyAddon({}); Terminal.applyAddon({}); Terminal.applyAddon({}); Terminal.applyAddon({}); } } namespace methods_core { { const t: Terminal = new Terminal(); t.blur(); t.focus(); t.destroy(); t.clear(); t.refresh(0, 1); t.reset(); t.resize(1, 1); t.write('foo'); t.writeln('foo'); } { const t: Terminal = new Terminal(); // no arg t.on('blur', () => {}); t.on('focus', () => {}); t.on('linefeed', () => {}); t.on('selection', () => {}); // args t.on('data', () => {}); t.on('data', (data: string) => console.log(data)); t.on('key', () => {}); t.on('key', (key: string) => console.log(key, event)); t.on('key', (key: string, event: KeyboardEvent) => console.log(key, event)); t.on('keydown', () => {}); t.on('keydown', (event: KeyboardEvent) => console.log(event)); t.on('keypress', () => {}); t.on('keypress', (event: KeyboardEvent) => console.log(event)); t.on('refresh', () => {}); t.on('refresh', (data: {start: number, end: number}) => console.log(data)); t.on('resize', () => {}); t.on('resize', (data: {cols: number, rows: number}) => console.log(data)); t.on('scroll', () => {}); t.on('scroll', (ydisp: number) => console.log(ydisp)); t.on('title', () => {}); t.on('title', (title: string) => console.log(title)); } { const t: Terminal = new Terminal(); // no arg t.off('blur', () => {}); t.off('focus', () => {}); t.off('linefeed', () => {}); t.off('selection', () => {}); // args t.off('data', () => {}); t.off('data', (data: string) => console.log(data)); t.off('key', () => {}); t.off('key', (key: string) => console.log(key, event)); t.off('key', (key: string, event: KeyboardEvent) => console.log(key, event)); t.off('keydown', () => {}); t.off('keydown', (event: KeyboardEvent) => console.log(event)); t.off('keypress', () => {}); t.off('keypress', (event: KeyboardEvent) => console.log(event)); t.off('refresh', () => {}); t.off('refresh', (data: {element: HTMLElement, start: number, end: number}) => console.log(data)); t.off('resize', () => {}); t.off('resize', (data: {terminal: Terminal, cols: number, rows: number}) => console.log(data)); t.off('scroll', () => {}); t.off('scroll', (ydisp: number) => console.log(ydisp)); t.off('title', () => {}); t.off('title', (title: string) => console.log(title)); } { const t: Terminal = new Terminal(); const e: HTMLElement = null; t.open(e); } { const t: Terminal = new Terminal(); t.attachCustomKeyEventHandler((e: KeyboardEvent) => true); t.attachCustomKeyEventHandler((e: KeyboardEvent) => false); } namespace options { { const t: Terminal = new Terminal(); const r01: string = t.getOption('cursorStyle'); const r02: string = t.getOption('termName'); const r03: boolean = t.getOption('cancelEvents'); const r04: boolean = t.getOption('convertEol'); const r05: boolean = t.getOption('cursorBlink'); const r06: boolean = t.getOption('debug'); const r07: boolean = t.getOption('disableStdin'); const r08: boolean = t.getOption('popOnBell'); const r09: boolean = t.getOption('screenKeys'); const r10: boolean = t.getOption('useFlowControl'); const r11: boolean = t.getOption('visualBell'); const r12: string[] = t.getOption('colors'); const r13: number = t.getOption('cols'); const r14: number = t.getOption('rows'); const r15: number = t.getOption('tabStopWidth'); const r16: number = t.getOption('scrollback'); const r18: (data: string) => void = t.getOption('handler'); const r19: string = t.getOption('bellSound'); const r20: string = t.getOption('bellStyle'); const r21: boolean = t.getOption('enableBold'); const r22: number = t.getOption('letterSpacing'); const r23: boolean = t.getOption('macOptionIsMeta'); const r24: string = t.getOption('fontWeight'); const r25: string = t.getOption('fontWeightBold'); const r26: boolean = t.getOption('allowTransparency'); const r27: boolean = t.getOption('rightClickSelectsWord'); } { const t: Terminal = new Terminal(); t.setOption('cursorStyle', 'bar'); t.setOption('cursorStyle', 'block'); t.setOption('cursorStyle', 'underline'); t.setOption('termName', 'foo'); t.setOption('cancelEvents', true); t.setOption('convertEol', true); t.setOption('cursorBlink', true); t.setOption('debug', true); t.setOption('disableStdin', true); t.setOption('enableBold', true); t.setOption('fontWeight', 'normal'); t.setOption('fontWeight', 'bold'); t.setOption('fontWeightBold', 'normal'); t.setOption('fontWeightBold', 'bold'); t.setOption('popOnBell', true); t.setOption('screenKeys', true); t.setOption('useFlowControl', true); t.setOption('allowTransparency', true); t.setOption('visualBell', true); t.setOption('colors', ['a', 'b']); t.setOption('letterSpacing', 1); t.setOption('cols', 1); t.setOption('rows', 1); t.setOption('tabStopWidth', 1); t.setOption('scrollback', 1); t.setOption('handler', (data: string) => console.log(data)); t.setOption('bellSound', 'foo'); t.setOption('bellStyle', 'none'); // t.setOption('bellStyle', 'visual'); t.setOption('bellStyle', 'sound'); // t.setOption('bellStyle', 'both'); t.setOption('fontSize', 1); t.setOption('lineHeight', 1); t.setOption('fontFamily', 'foo'); t.setOption('theme', {background: '#ff0000'}); t.setOption('macOptionIsMeta', true); t.setOption('rightClickSelectsWord', false); } } namespace scrolling { { const t: Terminal = new Terminal(); t.scrollLines(-1); t.scrollLines(1); t.scrollLines(-1); t.scrollLines(1); t.scrollToTop(); t.scrollToBottom(); } } namespace selection { { const t: Terminal = new Terminal(); const r1: boolean = t.hasSelection(); const r2: string = t.getSelection(); t.clearSelection(); t.selectAll(); } } } namespace methods_experimental { { const t: Terminal = new Terminal(); t.registerLinkMatcher(/foo/, () => {}); t.registerLinkMatcher(new RegExp('foo'), () => {}); t.registerLinkMatcher(/foo/, () => {}, {}); t.registerLinkMatcher(/foo/, (event: MouseEvent, uri: string) => { console.log(event, uri); return void 0; }, {}); t.registerLinkMatcher(/foo/, () => true, {}); t.registerLinkMatcher(/foo/, () => false, {}); t.registerLinkMatcher(/foo/, () => true, { matchIndex: 1 }); t.registerLinkMatcher(/foo/, () => true, { matchIndex: 1, priority: 1, validationCallback: (uri: string, callback: (isValid: boolean) => void) => { console.log(uri, callback); }, tooltipCallback: (e: MouseEvent, uri: string) => { console.log(e, uri); }, leaveCallback: () => {} }); t.deregisterLinkMatcher(1); } } xterm.js-3.8.1/gulpfile.js000066400000000000000000000101571341514612000154430ustar00rootroot00000000000000/** * @license MIT */ const browserify = require('browserify'); const buffer = require('vinyl-buffer'); const fs = require('fs-extra'); const gulp = require('gulp'); const path = require('path'); const merge = require('merge-stream'); const mocha = require('gulp-mocha'); const sorcery = require('sorcery'); const source = require('vinyl-source-stream'); const sourcemaps = require('gulp-sourcemaps'); const ts = require('gulp-typescript'); const util = require('gulp-util'); const buildDir = process.env.BUILD_DIR || 'build'; const tsProject = ts.createProject('tsconfig.json'); let srcDir = tsProject.config.compilerOptions.rootDir; let outDir = tsProject.config.compilerOptions.outDir; const addons = fs.readdirSync(`${__dirname}/src/addons`); const TEST_PATHS = [ `${outDir}/*test.js`, `${outDir}/**/*test.js`, `${outDir}/*integration.js`, `${outDir}/**/*integration.js` ]; // Under some environments like TravisCI, this comes out at absolute which can // break the build. This ensures that the outDir is absolute. if (path.normalize(outDir).indexOf(__dirname) !== 0) { outDir = `${__dirname}/${path.normalize(outDir)}`; } gulp.task('css', function() { return gulp.src(`${srcDir}/**/*.css`).pipe(gulp.dest(outDir)); }); gulp.task('watch-css', function() { return gulp.watch(`${srcDir}/**/*.css`, ['css']); }); /** * Bundle JavaScript files produced by the `tsc` task, into a single file named `xterm.js` with * Browserify. */ gulp.task('browserify', function() { // Ensure that the build directory exists fs.ensureDirSync(buildDir); let browserifyOptions = { basedir: buildDir, debug: true, entries: [`${outDir}/xterm.js`], standalone: 'Terminal', cache: {}, packageCache: {} }; let bundleStream = browserify(browserifyOptions) .bundle() .pipe(source('xterm.js')) .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true, sourceRoot: '..'})) .pipe(sourcemaps.write('./')) .pipe(gulp.dest(buildDir)); // Copy stylesheets from ${outDir}/ to ${buildDir}/ let copyStylesheets = gulp.src(`${outDir}/**/*.css`).pipe(gulp.dest(buildDir)); return merge(bundleStream, copyStylesheets); }); gulp.task('browserify-addons', function() { const bundles = addons.map((addon) => { const addonOptions = { basedir: `${buildDir}/addons/${addon}`, debug: true, entries: [`${outDir}/addons/${addon}/${addon}.js`], standalone: addon, cache: {}, packageCache: {} }; const addonBundle = browserify(addonOptions) .external(path.join(outDir, 'Terminal.js')) .bundle() .pipe(source(`./addons/${addon}/${addon}.js`)) .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true, sourceRoot: ''})) .pipe(sourcemaps.write('./')) .pipe(gulp.dest(buildDir)); return addonBundle; }); return merge(...bundles); }); gulp.task('mocha', function () { return gulp.src(TEST_PATHS, {read: false}) .pipe(mocha()) .once('error', () => process.exit(1)); }); /** * Run single test suite (file) by file name (without file extension). Example of the command: * gulp mocha-suite --test InputHandler.test */ gulp.task('mocha-suite', [], function () { let testName = util.env.test; util.log("Run test by Name: " + testName); return gulp.src([`${outDir}/${testName}.js`, `${outDir}/**/${testName}.js`], {read: false}) .pipe(mocha()) .once('error', () => process.exit(1)); }); /** * Use `sorcery` to resolve the source map chain and point back to the TypeScript files. * (Without this task the source maps produced for the JavaScript bundle points into the * compiled JavaScript files in ${outDir}/). */ gulp.task('sorcery', ['browserify'], function () { let chain = sorcery.loadSync(`${buildDir}/xterm.js`); chain.apply(); chain.writeSync(); }); gulp.task('sorcery-addons', ['browserify-addons'], function () { addons.forEach((addon) => { const chain = sorcery.loadSync(`${buildDir}/addons/${addon}/${addon}.js`); chain.apply(); chain.writeSync(); }) }); gulp.task('build', ['sorcery', 'sorcery-addons']); gulp.task('test', ['mocha']); gulp.task('default', ['build']); xterm.js-3.8.1/logo-full.png000066400000000000000000000265631341514612000157140ustar00rootroot00000000000000PNG  IHDR." IDATx^yUOu$ȢEK$2 @Ya $==(|AA"^v}ɕmzf"KP W3&]Ub /$L t~jfdϳ_שsCK! hTJ:%B@@D^n! (XB@yB@R"t`[B@! DB@!0J ȏҁn ! {@! (% "?JV%Bv$LӾ)鸍x.oC72u+!B "  ' ӑe- dtXw0hz\F=q%B@$Kn%`fo9eU(AJ !VL'W ! @(~Eۘ675oQ`p/o?oóf$3&W ! @HEލjɐ3,h#ߢ03J';uO%W ! @xD k -| >=.i5g7|~ҝ 3;ڴۓ+B E 7)qa\۴oAc{v u]?e՟ƾ$;! D c?t8 1c4u[p)î~<юhl:B@"Zg͚^z ´Z"`eY߆pwRg^&g1&4_)ѸL! "'Z݌ 4Y9 ;cJIlٲkw}]c M(ڧ9N! b ?!Ǫ.x6cgl@" >gķB@$G 2wSuTߑ\4 Glw:0j>|ZgF[t\'T45CKg-˺5l*E-vX ! !B@oUU'+)rv9,q|PPGq;}$rh=;0:.R! @l"/~ SVkO,p\#ߜ[[wAbt޿#%@"2ZΤ1pm/µ $+1OGT! F EebV|ϲBxh>D WnzB@pHo#)F\q_eԹ#B#D~̙6m-3<U7a?mJ/L '֭giVLnh<:g'(1[>O/~xqӦҡ˗/_}\mig+Fk)ޙm})N!D#"0`8Х7W}j.όħ_wPB@h GӭqmmwDXy뛵F"]({r.}ģB`taV 0_.86%x:oO2@,~.]|\(9Dc+pBL+%YHN|>L%GeyǕB@E@D>2eʇ!钮S9$/.knfhqK6딩k+ф#|ci4R?v10tKGܓRA|\T޹vM8B@H"1֪H'c{7M;ٮ}_ͯrнmӞJB@T" fhBmj*\eܶ.*a?}5iI(ى/! @D"n |7p!Yd{{w fN^>Ȩ'N ! F:GP׵m EP昦j G8%;3:;lq%O`֬YիWbw;́yX\{7~/PD>^[y9s6 _٦i1Hܶ\qWR ;j5=xϾԇ !0: ;Y,׍Nz%"Sd %Ȝzs*ۑRMڮB˝bGK\!0z0NGSEr~\heZm,v3qgqhG×K!T:u+_T*=wٲeo_iu]?w$NJ.*Jz֙$F !0" /ll꛽gŪѱWq-˺V][8Vh?onOb~Ȳ Dw&"^( *zz7Ny5LGYhٮ#.eԓ#U <6V>%,'@]vqXrяdyUUO)ZD>ɦ¯Csl^R- qw 67aΝs$ˡv4ڌHSAoR! <:)|Jܭuh:ߕ1bjqZ{QYyS~7άOU\#OI!/0u! ,n7." 㣵j[w-YτZ|NѦODJzpl,mܟ ˥?UBKDFZ3 n]c孚賝oj_k4f8gtfE3J_a|pGY| !ukڕ \>α-쳝A8V;/m/mfL@[+"_!B@ "VRM C` zL8jW[wabU])˴|%m`3f̘T*=¬N(|taY3J? `@aMs7'hںe:g +O B (b3 cW=;j+ Wjq݅}x 5[:-IKӴiPmV'=OD@KD9 Y&|6]^'>Gvdďm[[[f8?"OA)&fG'+Nb̙3===}A~S+lCAF@DL3ATm`:۴/巭p!jBgm4u ^M:#I~=wnݺ ?6+J 3=ADM1cNrKa`ajLlĩƏdťn+2 Ce* #\k֯_JU<[dj)jop,[r7u T*5wosVo0ЫagST>tDp 9l5Ng<۴oeUs#kJsC̴0I^, ǀ[` +4w|zj^a1 9B%''O3nܘ}=WWZhs]Mq)d1.ze|tFݹZNQ}p"V(|=8+b? ;fU(AJtu7Ľ>Cl6f>ղ =u  '^Y 8<`G}`:Ͳ{FsŶ"~usl O)_ݻwe@D÷[*M  |b5'/>QrcvR}{jҹ!m:D~֬YիWgA|GΌ7~{ʕnTڦe` ēhR^Z[䉯^n؛@8(4nmmQY-NHnX{MGȓaWY>+M|/gy^U)ES9֬TiҥK_N{o&z |\WVuK^|4};;|܅M/_:D̶?cƌ)+ӷC%.gzCy-W3ؼ}. &Lh"svƽ#XnZAM.|{F V(bԜ>:[7M4e{gx&ύO 1fqsoGI+ w1sOPpd6ءh6IQx_Ω6K@Sr>(S}ki'H5bWE8D$[>Y Qg/0ӕ%pT}`t?cBHa|󶩨{ ai9F"?p{ ތ 3f0khۗJD/W3ٯK}y7oo0iv5E5i,|\FVF\kվBN "|;@|mCQ{*r0#{G\KMS)nͬ 7xP(T:f̘1\ErtJˊu\@|q-&zS)r]n#NnA0ƣI? .z=~ei%/ri;"~5H{ЮeF.7QmT*򆡶3M̈D˲~waa ?E+ءCm~9xU.L/<;9 ^XVX_^)9a¤+>7 `=VyJW(G@Ӵϒ?=߶M^~kWJZs}977M?0X_ýv_ "?2n1$/w SGI&EFiA͇^qj}7 ǭ?tגtoҥ/U]Y-~zwbV1(SaXxQE*'+x,k@x9=j C;afbvaC+t)|DHsOy }PYk mmG{ c{hlH" fYQX7H0cl𠕷1& 'v =ݐضjTp<;f-&L GlVJ"[o`e ES[\~aa6m~սmc<ֲm] & "(#2]W !7'/ 7uWZ4O>}r侞PT>4 G"/Sܠ5<_taFx{zUFfSna|5 ^f(pa."^~`ųdOwR}6wbJW|-rODzAJo0m}=)ד~D5MK ȝw72]; [V%8_6 A|Vy^VX<6"a &o_w{iƮm9\WJt &}|z `i^]x=;(SRqVi[u-a7ڮʇj"_iIkP/w~Xx!ψ_9DPKK'wHZe=v("_}u ^O"qҍwE{@ rx9]p(AR~Q.{(=4|<]"^5C $l L8E޾;".:|NL ! JDXN#R/sn$ܤ3B;oG/. ,"/" (meO9p;Nz:̻|Qaڅ \&teZ,Όz]#;׿b8~'H7IfLdI;'w߽pg6]ֆcPw҉0m֊mCLuY=Ml^#DVL.YBae[=ڵ. PAPOi}ܷq|:@[Wݒ'"vL e_ dsʥb.,|NJ713>lEkK5舄$"?<]_qE0}<X |L7^nM馔[Ծs S , `2c3-I6h5Ȼh(nED^D>jо+v}H [Oj E佒s;U{#N 'yflW> :h.tQīO<\ iZ|+$Z6""?t߲3g~c3 3N9Iw"~:[[tV6횋ڻ 2pa#9#H'U.k+a""QuI7sqY=ak᫈|w[ t]&uUlv-zٮ9n.̴G2)iTgTU۞Ci wQODqD08HX?rʍQ}FȻ pEv㮋ȧwFγ6Mflu4<uf.?6lk W䰄f̏ Co { (|C2xn61E>k֬B>0fY=,PImӾ clFAD"_a+ͯE~?ڌcY֓Q}6;y_c""fhpzaqO7LӮY*p ."?~ԑQψW> C=Aa)9g&S>-2_aBQ ؃QA|}ERUJZ`ӤE^@+jW +|MbRo#[63qU]njȸŲlw|իp63n|#9yI(^p0mû}X@*`?4ySbx :@ncউ'^ ח{K$E~9w-R!7 L?tg}C:]ݍ9)%򆡶3^/7umZyfH1L NqPgY+ȡ%lJTR.+E)U[ZZ>ha'"E)X*_'MOW?Jޮ9s:B!5`:ʲ_u""aI֪5#KiȴE .IDATrl;^Z;4UvB {[6̡LGyڲʣ&tlI:4gҥWs]hF Tn5GUۓ[ߖXl{S߆Ok{h^́qڵxW6l[|E^W:~Ki>}yo _*l_ץ޿a,"V m5M()xʭ}j.mw>vRxxcs ;?r)VO"4+;0u EBrS@"0MmI:7gҗ>C&- O+rU4gW>6._D>-΁nhܡfXV摵ٮd~AL~˨1LfԩW_:tie%mLi[6>FD>&ӧOߡܺn.RMBA#տ7ubqMMm='DJ0! "%NC ]*9ba|/e×o!!oiV)0npH"݅$bm藹-&W ! EiR/9q͟M5R):T.L ;NA'" mw$v3Ӳ ׄA ׬åǦ|O.챱4BhYRܢ`KimN9)"1ag"7 =鎁٦w7 ^3ɨ]renş0 iSR$U=#͡G8(Z60}MiaW|vُ4۷aHpD^OLU̅0y7vG. Yr?#9j܈G4ap0." P2M bjR'SJneog ǕpB@'RNr>w{2qrb^p\-E䃳{rҒ/m!eRwWܭujiMRuԵzgB@NUNDAij fz\.u'8G0? ieY3upzL?˨;.BHD׵;AJAEi_t\/ں paܕkS? N3! B v[-DYy,/&UH{ [j$x'B.byPaO㧖e'=H?: O alCa|B@46D~#`LMXp@u41ewApDu)kX ! @,";o,M`ٶ$ḡe;(<Ɍ7~yCsZT,B "=C~IwǚfaDNAuC$jx6Q!CުSfɄ }}q,|zX˨E%'kVmaX}Nq ]{灁:3j=`KTZ ! gs@5dq۴]Cvx}Ȩ$! D" E)S@:tFX}J-_|?n~ %@O2SM-&Wb ! @<B̙36mXЧI׷iCWr]I0W[~֗p\ '-"Ϋ;&ck6 ]рRe/`tB@%N鉓d ?L7:)U @v3wd(O klb^?=_$W ! @D^7|pxL,+B;תx$1?oa3~kSI<B@\$B@!!a+! D@DFCrB@!!a+! D@DFCrB@!!a+! D@DFCrB@!!a+! D@DFCrB@!!a+! D+|sIENDB`xterm.js-3.8.1/package.json000066400000000000000000000055561341514612000155730ustar00rootroot00000000000000{ "name": "xterm", "description": "Full xterm terminal, in your browser", "version": "3.8.1", "main": "lib/public/Terminal.js", "types": "typings/xterm.d.ts", "repository": "https://github.com/xtermjs/xterm.js", "license": "MIT", "devDependencies": { "@types/chai": "^3.4.34", "@types/glob": "^5.0.35", "@types/jsdom": "11.0.1", "@types/mocha": "^2.2.33", "@types/node": "6.0.108", "@types/webpack": "^4.4.11", "browserify": "^13.3.0", "chai": "3.5.0", "concurrently": "^3.5.1", "coveralls": "^3.0.1", "express": "4.13.4", "express-ws": "2.0.0-rc.1", "fs-extra": "^1.0.0", "glob": "^7.0.5", "gulp": "3.9.1", "gulp-cli": "^1.2.2", "gulp-concat": "^2.6.1", "gulp-mocha": "^3.0.1", "gulp-sourcemaps": "1.9.1", "gulp-typescript": "^3.1.3", "gulp-util": "3.0.8", "jsdoc": "3.4.3", "jsdom": "^11.11.0", "merge-stream": "^1.0.1", "node-pty": "0.7.6", "nodemon": "1.10.2", "nyc": "^11.8.0", "sorcery": "^0.10.0", "source-map-loader": "^0.2.3", "ts-loader": "^4.5.0", "tslint": "^5.9.1", "tslint-consistent-codestyle": "^1.13.0", "typescript": "3.0", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", "webpack": "^4.17.1", "webpack-cli": "^3.1.0", "zmodem.js": "^0.1.5" }, "scripts": { "start": "node demo/start", "start-zmodem": "node demo/zmodem/app", "lint": "tslint 'src/**/*.ts' './demo/**/*.ts'", "test": "npm run mocha", "posttest": "npm run lint", "test-debug": "node --inspect-brk node_modules/.bin/gulp test", "test-suite": "gulp mocha-suite --test", "test-coverage": "nyc -x gulpfile.js -x '**/*test*' npm run mocha", "mocha": "gulp test", "build:docs": "jsdoc -c jsdoc.json", "tsc": "tsc", "prebuild": "concurrently --kill-others-on-fail --names \"lib,attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem,css\" \"tsc\" \"tsc -p ./src/addons/attach\" \"tsc -p ./src/addons/fit\" \"tsc -p ./src/addons/fullscreen\" \"tsc -p ./src/addons/search\" \"tsc -p ./src/addons/terminado\" \"tsc -p ./src/addons/webLinks\" \"tsc -p ./src/addons/winptyCompat\" \"tsc -p ./src/addons/zmodem\" \"gulp css\"", "build": "gulp build", "prepublish": "npm run build", "coveralls": "nyc report --reporter=text-lcov | coveralls", "webpack": "gulp webpack", "watch": "concurrently --kill-others-on-fail --names \"lib,css\" \"tsc -w\" \"gulp watch-css\"", "watch-addons": "concurrently --kill-others-on-fail --names \"attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem\" \"tsc -w -p ./src/addons/attach\" \"tsc -w -p ./src/addons/fit\" \"tsc -w -p ./src/addons/fullscreen\" \"tsc -w -p ./src/addons/search\" \"tsc -w -p ./src/addons/terminado\" \"tsc -w -p ./src/addons/webLinks\" \"tsc -w -p ./src/addons/winptyCompat\" \"tsc -w -p ./src/addons/zmodem\"" } } xterm.js-3.8.1/src/000077500000000000000000000000001341514612000140615ustar00rootroot00000000000000xterm.js-3.8.1/src/AccessibilityManager.ts000066400000000000000000000261131341514612000205160ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import * as Strings from './Strings'; import { ITerminal, IBuffer } from './Types'; import { isMac } from './shared/utils/Browser'; import { RenderDebouncer } from './ui/RenderDebouncer'; import { addDisposableDomListener } from './ui/Lifecycle'; import { Disposable } from './common/Lifecycle'; const MAX_ROWS_TO_READ = 20; const enum BoundaryPosition { TOP, BOTTOM } export class AccessibilityManager extends Disposable { private _accessibilityTreeRoot: HTMLElement; private _rowContainer: HTMLElement; private _rowElements: HTMLElement[]; private _liveRegion: HTMLElement; private _liveRegionLineCount: number = 0; private _renderRowsDebouncer: RenderDebouncer; private _topBoundaryFocusListener: (e: FocusEvent) => void; private _bottomBoundaryFocusListener: (e: FocusEvent) => void; /** * This queue has a character pushed to it for keys that are pressed, if the * next character added to the terminal is equal to the key char then it is * not announced (added to live region) because it has already been announced * by the textarea event (which cannot be canceled). There are some race * condition cases if there is typing while data is streaming, but this covers * the main case of typing into the prompt and inputting the answer to a * question (Y/N, etc.). */ private _charsToConsume: string[] = []; constructor(private _terminal: ITerminal) { super(); this._accessibilityTreeRoot = document.createElement('div'); this._accessibilityTreeRoot.classList.add('xterm-accessibility'); this._rowContainer = document.createElement('div'); this._rowContainer.classList.add('xterm-accessibility-tree'); this._rowElements = []; for (let i = 0; i < this._terminal.rows; i++) { this._rowElements[i] = this._createAccessibilityTreeNode(); this._rowContainer.appendChild(this._rowElements[i]); } this._topBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.TOP); this._bottomBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.BOTTOM); this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener); this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); this._refreshRowsDimensions(); this._accessibilityTreeRoot.appendChild(this._rowContainer); this._renderRowsDebouncer = new RenderDebouncer(this._terminal, this._renderRows.bind(this)); this._refreshRows(); this._liveRegion = document.createElement('div'); this._liveRegion.classList.add('live-region'); this._liveRegion.setAttribute('aria-live', 'assertive'); this._accessibilityTreeRoot.appendChild(this._liveRegion); this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityTreeRoot); this.register(this._renderRowsDebouncer); this.register(this._terminal.addDisposableListener('resize', data => this._onResize(data.rows))); this.register(this._terminal.addDisposableListener('refresh', data => this._refreshRows(data.start, data.end))); this.register(this._terminal.addDisposableListener('scroll', data => this._refreshRows())); // Line feed is an issue as the prompt won't be read out after a command is run this.register(this._terminal.addDisposableListener('a11y.char', (char) => this._onChar(char))); this.register(this._terminal.addDisposableListener('linefeed', () => this._onChar('\n'))); this.register(this._terminal.addDisposableListener('a11y.tab', spaceCount => this._onTab(spaceCount))); this.register(this._terminal.addDisposableListener('key', keyChar => this._onKey(keyChar))); this.register(this._terminal.addDisposableListener('blur', () => this._clearLiveRegion())); // TODO: Maybe renderer should fire an event on terminal when the characters change and that // should be listened to instead? That would mean that the order of events are always // guarenteed this.register(this._terminal.addDisposableListener('dprchange', () => this._refreshRowsDimensions())); this.register(this._terminal.renderer.addDisposableListener('resize', () => this._refreshRowsDimensions())); // This shouldn't be needed on modern browsers but is present in case the // media query that drives the dprchange event isn't supported this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions())); } public dispose(): void { super.dispose(); this._terminal.element.removeChild(this._accessibilityTreeRoot); this._rowElements.length = 0; } private _onBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void { const boundaryElement = e.target; const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2]; // Don't scroll if the buffer top has reached the end in that direction const posInSet = boundaryElement.getAttribute('aria-posinset'); const lastRowPos = position === BoundaryPosition.TOP ? '1' : `${this._terminal.buffer.lines.length}`; if (posInSet === lastRowPos) { return; } // Don't scroll when the last focused item was not the second row (focus is going the other // direction) if (e.relatedTarget !== beforeBoundaryElement) { return; } // Remove old boundary element from array let topBoundaryElement: HTMLElement; let bottomBoundaryElement: HTMLElement; if (position === BoundaryPosition.TOP) { topBoundaryElement = boundaryElement; bottomBoundaryElement = this._rowElements.pop()!; this._rowContainer.removeChild(bottomBoundaryElement); } else { topBoundaryElement = this._rowElements.shift()!; bottomBoundaryElement = boundaryElement; this._rowContainer.removeChild(topBoundaryElement); } // Remove listeners from old boundary elements topBoundaryElement.removeEventListener('focus', this._topBoundaryFocusListener); bottomBoundaryElement.removeEventListener('focus', this._bottomBoundaryFocusListener); // Add new element to array/DOM if (position === BoundaryPosition.TOP) { const newElement = this._createAccessibilityTreeNode(); this._rowElements.unshift(newElement); this._rowContainer.insertAdjacentElement('afterbegin', newElement); } else { const newElement = this._createAccessibilityTreeNode(); this._rowElements.push(newElement); this._rowContainer.appendChild(newElement); } // Add listeners to new boundary elements this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener); this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); // Scroll up this._terminal.scrollLines(position === BoundaryPosition.TOP ? -1 : 1); // Focus new boundary before element this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2].focus(); // Prevent the standard behavior e.preventDefault(); e.stopImmediatePropagation(); } private _onResize(rows: number): void { // Remove bottom boundary listener this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener); // Grow rows as required for (let i = this._rowContainer.children.length; i < this._terminal.rows; i++) { this._rowElements[i] = this._createAccessibilityTreeNode(); this._rowContainer.appendChild(this._rowElements[i]); } // Shrink rows as required while (this._rowElements.length > rows) { this._rowContainer.removeChild(this._rowElements.pop()!); } // Add bottom boundary listener this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener); this._refreshRowsDimensions(); } private _createAccessibilityTreeNode(): HTMLElement { const element = document.createElement('div'); element.setAttribute('role', 'listitem'); element.tabIndex = -1; this._refreshRowDimensions(element); return element; } private _onTab(spaceCount: number): void { for (let i = 0; i < spaceCount; i++) { this._onChar(' '); } } private _onChar(char: string): void { if (this._liveRegionLineCount < MAX_ROWS_TO_READ + 1) { if (this._charsToConsume.length > 0) { // Have the screen reader ignore the char if it was just input const shiftedChar = this._charsToConsume.shift(); if (shiftedChar !== char) { this._announceCharacter(char); } } else { this._announceCharacter(char); } if (char === '\n') { this._liveRegionLineCount++; if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) { this._liveRegion.textContent += Strings.tooMuchOutput; } } // Only detach/attach on mac as otherwise messages can go unaccounced if (isMac) { if (this._liveRegion.textContent && this._liveRegion.textContent.length > 0 && !this._liveRegion.parentNode) { setTimeout(() => { this._accessibilityTreeRoot.appendChild(this._liveRegion); }, 0); } } } } private _clearLiveRegion(): void { this._liveRegion.textContent = ''; this._liveRegionLineCount = 0; // Only detach/attach on mac as otherwise messages can go unaccounced if (isMac) { if (this._liveRegion.parentNode) { this._accessibilityTreeRoot.removeChild(this._liveRegion); } } } private _onKey(keyChar: string): void { this._clearLiveRegion(); this._charsToConsume.push(keyChar); } private _refreshRows(start?: number, end?: number): void { this._renderRowsDebouncer.refresh(start, end); } private _renderRows(start: number, end: number): void { const buffer: IBuffer = this._terminal.buffer; const setSize = buffer.lines.length.toString(); for (let i = start; i <= end; i++) { const lineData = buffer.translateBufferLineToString(buffer.ydisp + i, true); const posInSet = (buffer.ydisp + i + 1).toString(); const element = this._rowElements[i]; element.textContent = lineData.length === 0 ? Strings.blankLine : lineData; element.setAttribute('aria-posinset', posInSet); element.setAttribute('aria-setsize', setSize); } } private _refreshRowsDimensions(): void { if (!this._terminal.renderer.dimensions.actualCellHeight) { return; } if (this._rowElements.length !== this._terminal.rows) { this._onResize(this._terminal.rows); } for (let i = 0; i < this._terminal.rows; i++) { this._refreshRowDimensions(this._rowElements[i]); } } private _refreshRowDimensions(element: HTMLElement): void { element.style.height = `${this._terminal.renderer.dimensions.actualCellHeight}px`; } private _announceCharacter(char: string): void { if (char === ' ') { // Always use nbsp for spaces in order to preserve the space between characters in // voiceover's caption window this._liveRegion.innerHTML += ' '; } else { this._liveRegion.textContent += char; } } } xterm.js-3.8.1/src/Buffer.test.ts000066400000000000000000000540361341514612000166300ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import { ITerminal } from './Types'; import { Buffer, DEFAULT_ATTR, CHAR_DATA_CHAR_INDEX } from './Buffer'; import { CircularList } from './common/CircularList'; import { MockTerminal, TestTerminal } from './utils/TestUtils.test'; import { BufferLine } from './BufferLine'; const INIT_COLS = 80; const INIT_ROWS = 24; describe('Buffer', () => { let terminal: ITerminal; let buffer: Buffer; beforeEach(() => { terminal = new MockTerminal(); terminal.cols = INIT_COLS; terminal.rows = INIT_ROWS; terminal.options.scrollback = 1000; buffer = new Buffer(terminal, true); }); describe('constructor', () => { it('should create a CircularList with max length equal to rows + scrollback, for its lines', () => { assert.instanceOf(buffer.lines, CircularList); assert.equal(buffer.lines.maxLength, terminal.rows + terminal.options.scrollback); }); it('should set the Buffer\'s scrollBottom value equal to the terminal\'s rows -1', () => { assert.equal(buffer.scrollBottom, terminal.rows - 1); }); }); describe('fillViewportRows', () => { it('should fill the buffer with blank lines based on the size of the viewport', () => { const blankLineChar = BufferLine.blankLine(terminal.cols, DEFAULT_ATTR).get(0); buffer.fillViewportRows(); assert.equal(buffer.lines.length, INIT_ROWS); for (let y = 0; y < INIT_ROWS; y++) { assert.equal(buffer.lines.get(y).length, INIT_COLS); for (let x = 0; x < INIT_COLS; x++) { assert.deepEqual(buffer.lines.get(y).get(x), blankLineChar); } } }); }); describe('getWrappedRangeForLine', () => { describe('non-wrapped', () => { it('should return a single row for the first row', () => { buffer.fillViewportRows(); assert.deepEqual(buffer.getWrappedRangeForLine(0), { first: 0, last: 0 }); }); it('should return a single row for a middle row', () => { buffer.fillViewportRows(); assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 12, last: 12 }); }); it('should return a single row for the last row', () => { buffer.fillViewportRows(); assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 1), { first: 23, last: 23 }); }); }); describe('wrapped', () => { it('should return a range for the first row', () => { buffer.fillViewportRows(); buffer.lines.get(1).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(0), { first: 0, last: 1 }); }); it('should return a range for a middle row wrapping upwards', () => { buffer.fillViewportRows(); buffer.lines.get(12).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 11, last: 12 }); }); it('should return a range for a middle row wrapping downwards', () => { buffer.fillViewportRows(); buffer.lines.get(13).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 12, last: 13 }); }); it('should return a range for a middle row wrapping both ways', () => { buffer.fillViewportRows(); buffer.lines.get(11).isWrapped = true; buffer.lines.get(12).isWrapped = true; buffer.lines.get(13).isWrapped = true; buffer.lines.get(14).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(12), { first: 10, last: 14 }); }); it('should return a range for the last row', () => { buffer.fillViewportRows(); buffer.lines.get(23).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 1), { first: 22, last: 23 }); }); it('should return a range for a row that wraps upward to first row', () => { buffer.fillViewportRows(); buffer.lines.get(1).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(1), { first: 0, last: 1 }); }); it('should return a range for a row that wraps downward to last row', () => { buffer.fillViewportRows(); buffer.lines.get(buffer.lines.length - 1).isWrapped = true; assert.deepEqual(buffer.getWrappedRangeForLine(buffer.lines.length - 2), { first: 22, last: 23 }); }); }); }); describe('resize', () => { describe('column size is reduced', () => { it('should not trim the data in the buffer', () => { buffer.fillViewportRows(); buffer.resize(INIT_COLS / 2, INIT_ROWS); assert.equal(buffer.lines.length, INIT_ROWS); for (let i = 0; i < INIT_ROWS; i++) { assert.equal(buffer.lines.get(i).length, INIT_COLS); } }); }); describe('column size is increased', () => { it('should add pad columns', () => { buffer.fillViewportRows(); buffer.resize(INIT_COLS + 10, INIT_ROWS); assert.equal(buffer.lines.length, INIT_ROWS); for (let i = 0; i < INIT_ROWS; i++) { assert.equal(buffer.lines.get(i).length, INIT_COLS + 10); } }); }); describe('row size reduced', () => { it('should trim blank lines from the end', () => { buffer.fillViewportRows(); buffer.resize(INIT_COLS, INIT_ROWS - 10); assert.equal(buffer.lines.length, INIT_ROWS - 10); }); it('should move the viewport down when it\'s at the end', () => { buffer.fillViewportRows(); // Set cursor y to have 5 blank lines below it buffer.y = INIT_ROWS - 5 - 1; buffer.resize(INIT_COLS, INIT_ROWS - 10); // Trim 5 rows assert.equal(buffer.lines.length, INIT_ROWS - 5); // Shift the viewport down 5 rows assert.equal(buffer.ydisp, 5); assert.equal(buffer.ybase, 5); }); describe('no scrollback', () => { it('should trim from the top of the buffer when the cursor reaches the bottom', () => { terminal.options.scrollback = 0; buffer = new Buffer(terminal, true); assert.equal(buffer.lines.maxLength, INIT_ROWS); buffer.y = INIT_ROWS - 1; buffer.fillViewportRows(); buffer.lines.get(5).get(0)[1] = 'a'; buffer.lines.get(INIT_ROWS - 1).get(0)[1] = 'b'; buffer.resize(INIT_COLS, INIT_ROWS - 5); assert.equal(buffer.lines.get(0).get(0)[1], 'a'); assert.equal(buffer.lines.get(INIT_ROWS - 1 - 5).get(0)[1], 'b'); }); }); }); describe('row size increased', () => { describe('empty buffer', () => { it('should add blank lines to end', () => { buffer.fillViewportRows(); assert.equal(buffer.ydisp, 0); buffer.resize(INIT_COLS, INIT_ROWS + 10); assert.equal(buffer.ydisp, 0); assert.equal(buffer.lines.length, INIT_ROWS + 10); }); }); describe('filled buffer', () => { it('should show more of the buffer above', () => { buffer.fillViewportRows(); // Create 10 extra blank lines for (let i = 0; i < 10; i++) { buffer.lines.push(BufferLine.blankLine(terminal.cols, DEFAULT_ATTR)); } // Set cursor to the bottom of the buffer buffer.y = INIT_ROWS - 1; // Scroll down 10 lines buffer.ybase = 10; buffer.ydisp = 10; assert.equal(buffer.lines.length, INIT_ROWS + 10); buffer.resize(INIT_COLS, INIT_ROWS + 5); // Should be should 5 more lines assert.equal(buffer.ydisp, 5); assert.equal(buffer.ybase, 5); // Should not trim the buffer assert.equal(buffer.lines.length, INIT_ROWS + 10); }); it('should show more of the buffer below when the viewport is at the top of the buffer', () => { buffer.fillViewportRows(); // Create 10 extra blank lines for (let i = 0; i < 10; i++) { buffer.lines.push(BufferLine.blankLine(terminal.cols, DEFAULT_ATTR)); } // Set cursor to the bottom of the buffer buffer.y = INIT_ROWS - 1; // Scroll down 10 lines buffer.ybase = 10; buffer.ydisp = 0; assert.equal(buffer.lines.length, INIT_ROWS + 10); buffer.resize(INIT_COLS, INIT_ROWS + 5); // The viewport should remain at the top assert.equal(buffer.ydisp, 0); // The buffer ybase should move up 5 lines assert.equal(buffer.ybase, 5); // Should not trim the buffer assert.equal(buffer.lines.length, INIT_ROWS + 10); }); }); }); describe('row and column increased', () => { it('should resize properly', () => { buffer.fillViewportRows(); buffer.resize(INIT_COLS + 5, INIT_ROWS + 5); assert.equal(buffer.lines.length, INIT_ROWS + 5); for (let i = 0; i < INIT_ROWS + 5; i++) { assert.equal(buffer.lines.get(i).length, INIT_COLS + 5); } }); }); }); describe('buffer marked to have no scrollback', () => { it('should always have a scrollback of 0', () => { assert.equal(terminal.options.scrollback, 1000); // Test size on initialization buffer = new Buffer(terminal, false); buffer.fillViewportRows(); assert.equal(buffer.lines.maxLength, INIT_ROWS); // Test size on buffer increase buffer.resize(INIT_COLS, INIT_ROWS * 2); assert.equal(buffer.lines.maxLength, INIT_ROWS * 2); // Test size on buffer decrease buffer.resize(INIT_COLS, INIT_ROWS / 2); assert.equal(buffer.lines.maxLength, INIT_ROWS / 2); }); }); describe('addMarker', () => { it('should adjust a marker line when the buffer is trimmed', () => { terminal.options.scrollback = 0; buffer = new Buffer(terminal, true); buffer.fillViewportRows(); const marker = buffer.addMarker(buffer.lines.length - 1); assert.equal(marker.line, buffer.lines.length - 1); buffer.lines.emit('trim', 1); assert.equal(marker.line, buffer.lines.length - 2); }); it('should dispose of a marker if it is trimmed off the buffer', () => { terminal.options.scrollback = 0; buffer = new Buffer(terminal, true); buffer.fillViewportRows(); assert.equal(buffer.markers.length, 0); const marker = buffer.addMarker(0); assert.equal(marker.isDisposed, false); assert.equal(buffer.markers.length, 1); buffer.lines.emit('trim', 1); assert.equal(marker.isDisposed, true); assert.equal(buffer.markers.length, 0); }); }); describe ('translateBufferLineToString', () => { it('should handle selecting a section of ascii text', () => { const line = new BufferLine(); line.push([ null, 'a', 1, 'a'.charCodeAt(0)]); line.push([ null, 'b', 1, 'b'.charCodeAt(0)]); line.push([ null, 'c', 1, 'c'.charCodeAt(0)]); line.push([ null, 'd', 1, 'd'.charCodeAt(0)]); buffer.lines.set(0, line); const str = buffer.translateBufferLineToString(0, true, 0, 2); assert.equal(str, 'ab'); }); it('should handle a cut-off double width character by including it', () => { const line = new BufferLine(); line.push([ null, '語', 2, 35486 ]); line.push([ null, '', 0, null]); line.push([ null, 'a', 1, 'a'.charCodeAt(0)]); buffer.lines.set(0, line); const str1 = buffer.translateBufferLineToString(0, true, 0, 1); assert.equal(str1, '語'); }); it('should handle a zero width character in the middle of the string by not including it', () => { const line = new BufferLine(); line.push([ null, '語', 2, '語'.charCodeAt(0) ]); line.push([ null, '', 0, null]); line.push([ null, 'a', 1, 'a'.charCodeAt(0)]); buffer.lines.set(0, line); const str0 = buffer.translateBufferLineToString(0, true, 0, 1); assert.equal(str0, '語'); const str1 = buffer.translateBufferLineToString(0, true, 0, 2); assert.equal(str1, '語'); const str2 = buffer.translateBufferLineToString(0, true, 0, 3); assert.equal(str2, '語a'); }); it('should handle single width emojis', () => { const line = new BufferLine(); line.push([ null, '😁', 1, '😁'.charCodeAt(0) ]); line.push([ null, 'a', 1, 'a'.charCodeAt(0)]); buffer.lines.set(0, line); const str1 = buffer.translateBufferLineToString(0, true, 0, 1); assert.equal(str1, '😁'); const str2 = buffer.translateBufferLineToString(0, true, 0, 2); assert.equal(str2, '😁a'); }); it('should handle double width emojis', () => { const line = new BufferLine(); line.push([ null, '😁', 2, '😁'.charCodeAt(0) ]); line.push([ null, '', 0, null]); buffer.lines.set(0, line); const str1 = buffer.translateBufferLineToString(0, true, 0, 1); assert.equal(str1, '😁'); const str2 = buffer.translateBufferLineToString(0, true, 0, 2); assert.equal(str2, '😁'); const line2 = new BufferLine(); line2.push([ null, '😁', 2, '😁'.charCodeAt(0) ]); line2.push([ null, '', 0, null]); line2.push([ null, 'a', 1, 'a'.charCodeAt(0)]); buffer.lines.set(0, line2); const str3 = buffer.translateBufferLineToString(0, true, 0, 3); assert.equal(str3, '😁a'); }); }); describe('stringIndexToBufferIndex', () => { let terminal: TestTerminal; beforeEach(() => { terminal = new TestTerminal({rows: 5, cols: 10}); }); it('multiline ascii', () => { const input = 'This is ASCII text spanning multiple lines.'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); for (let i = 0; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex); } }); it('combining e\u0301 in a sentence', () => { const input = 'Sitting in the cafe\u0301 drinking coffee.'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); for (let i = 0; i < 19; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex); } // string index 18 & 19 point to combining char e\u0301 ---> same buffer Index assert.deepEqual( terminal.buffer.stringIndexToBufferIndex(0, 18), terminal.buffer.stringIndexToBufferIndex(0, 19)); // after the combining char every string index has an offset of -1 for (let i = 19; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex); } }); it('multiline combining e\u0301', () => { const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); // every buffer cell index contains 2 string indices for (let i = 0; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex); } }); it('surrogate char in a sentence', () => { const input = 'The 𝄞 is a clef widely used in modern notation.'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); for (let i = 0; i < 5; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex); } // string index 4 & 5 point to surrogate char 𝄞 ---> same buffer Index assert.deepEqual( terminal.buffer.stringIndexToBufferIndex(0, 4), terminal.buffer.stringIndexToBufferIndex(0, 5)); // after the combining char every string index has an offset of -1 for (let i = 5; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex); } }); it('multiline surrogate char', () => { const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); // every buffer cell index contains 2 string indices for (let i = 0; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex); } }); it('surrogate char with combining', () => { // eye of Ra with acute accent - string length of 3 const input = '𓂀\u0301 - the eye hiroglyph with an acute accent.'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); // index 0..2 should map to 0 assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 1)); assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 2)); for (let i = 2; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([((i - 2) / terminal.cols) | 0, (i - 2) % terminal.cols], bufferIndex); } }); it('multiline surrogate with combining', () => { const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); // every buffer cell index contains 3 string indices for (let i = 0; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([(((i / 3) | 0) / terminal.cols) | 0, ((i / 3) | 0) % terminal.cols], bufferIndex); } }); it('fullwidth chars', () => { const input = 'These 123 are some fat numbers.'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); for (let i = 0; i < 6; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex); } // string index 6, 7, 8 take 2 cells assert.deepEqual([0, 8], terminal.buffer.stringIndexToBufferIndex(0, 7)); assert.deepEqual([1, 0], terminal.buffer.stringIndexToBufferIndex(0, 8)); // rest of the string has offset of +3 for (let i = 9; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([((i + 3) / terminal.cols) | 0, (i + 3) % terminal.cols], bufferIndex); } }); it('multiline fullwidth chars', () => { const input = '12345678901234567890'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); for (let i = 9; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i); assert.deepEqual([((i << 1) / terminal.cols) | 0, (i << 1) % terminal.cols], bufferIndex); } }); it('fullwidth combining with emoji - match emoji cell', () => { const input = 'Lots of ¥\u0301 make me 😃.'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); const stringIndex = s.match(/😃/).index; const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex); assert(terminal.buffer.lines.get(bufferIndex[0]).get(bufferIndex[1])[CHAR_DATA_CHAR_INDEX], '😃'); }); it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => { const input = 'a12345678901234567890'; // the 'a' at the beginning moves all fullwidth chars one to the right // now the end of the line contains a dangling empty cell since // the next fullwidth char has to wrap early // the dangling last cell is wrongly added in the string // --> fixable after resolving #1685 terminal.writeSync(input); // TODO: reenable after fix // const s = terminal.buffer.contents(true).toArray()[0]; // assert.equal(input, s); for (let i = 10; i < input.length; ++i) { const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i + 1); // TODO: remove +1 after fix const j = (i - 0) << 1; assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex); } }); }); describe('BufferStringIterator', function(): void { it('iterator does not ovrflow buffer limits', function(): void { const terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5}); const data = [ 'aaaaaaaaaa', 'aaaaaaaaa\n', 'aaaaaaaaaa', 'aaaaaaaaa\n', 'aaaaaaaaaa', 'aaaaaaaaaa', 'aaaaaaaaaa', 'aaaaaaaaa\n', 'aaaaaaaaaa', 'aaaaaaaaaa' ]; terminal.writeSync(data.join('')); // brute force test with insane values expect(() => { for (let overscan = 0; overscan < 20; ++overscan) { for (let start = -10; start < 20; ++start) { for (let end = -10; end < 20; ++end) { const it = terminal.buffer.iterator(false, start, end, overscan, overscan); while (it.hasNext()) { it.next(); } } } } }).to.not.throw(); }); }); }); xterm.js-3.8.1/src/Buffer.ts000066400000000000000000000366401341514612000156530ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { CircularList } from './common/CircularList'; import { CharData, ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult } from './Types'; import { EventEmitter } from './common/EventEmitter'; import { IMarker } from 'xterm'; import { BufferLine } from './BufferLine'; export const DEFAULT_ATTR = (0 << 18) | (257 << 9) | (256 << 0); export const CHAR_DATA_ATTR_INDEX = 0; export const CHAR_DATA_CHAR_INDEX = 1; export const CHAR_DATA_WIDTH_INDEX = 2; export const CHAR_DATA_CODE_INDEX = 3; export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1 export const NULL_CELL_CHAR = ' '; export const NULL_CELL_WIDTH = 1; export const NULL_CELL_CODE = 32; /** * This class represents a terminal buffer (an internal state of the terminal), where the * following information is stored (in high-level): * - text content of this particular buffer * - cursor position * - scroll position */ export class Buffer implements IBuffer { public lines: CircularList; public ydisp: number; public ybase: number; public y: number; public x: number; public scrollBottom: number; public scrollTop: number; public tabs: any; public savedY: number; public savedX: number; public markers: Marker[] = []; /** * Create a new Buffer. * @param _terminal The terminal the Buffer will belong to. * @param _hasScrollback Whether the buffer should respect the scrollback of * the terminal. */ constructor( private _terminal: ITerminal, private _hasScrollback: boolean ) { this.clear(); } public get hasScrollback(): boolean { return this._hasScrollback && this.lines.maxLength > this._terminal.rows; } public get isCursorInViewport(): boolean { const absoluteY = this.ybase + this.y; const relativeY = absoluteY - this.ydisp; return (relativeY >= 0 && relativeY < this._terminal.rows); } /** * Gets the correct buffer length based on the rows provided, the terminal's * scrollback and whether this buffer is flagged to have scrollback or not. * @param rows The terminal rows to use in the calculation. */ private _getCorrectBufferLength(rows: number): number { if (!this._hasScrollback) { return rows; } const correctBufferLength = rows + this._terminal.options.scrollback; return correctBufferLength > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : correctBufferLength; } /** * Fills the buffer's viewport with blank lines. */ public fillViewportRows(): void { if (this.lines.length === 0) { let i = this._terminal.rows; while (i--) { this.lines.push(BufferLine.blankLine(this._terminal.cols, DEFAULT_ATTR)); } } } /** * Clears the buffer to it's initial state, discarding all previous data. */ public clear(): void { this.ydisp = 0; this.ybase = 0; this.y = 0; this.x = 0; this.lines = new CircularList(this._getCorrectBufferLength(this._terminal.rows)); this.scrollTop = 0; this.scrollBottom = this._terminal.rows - 1; this.setupTabStops(); } /** * Resizes the buffer, adjusting its data accordingly. * @param newCols The new number of columns. * @param newRows The new number of rows. */ public resize(newCols: number, newRows: number): void { // Increase max length if needed before adjustments to allow space to fill // as required. const newMaxLength = this._getCorrectBufferLength(newRows); if (newMaxLength > this.lines.maxLength) { this.lines.maxLength = newMaxLength; } // The following adjustments should only happen if the buffer has been // initialized/filled. if (this.lines.length > 0) { // Deal with columns increasing (we don't do anything when columns reduce) if (this._terminal.cols < newCols) { const ch: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // does xterm use the default attr? for (let i = 0; i < this.lines.length; i++) { while (this.lines.get(i).length < newCols) { this.lines.get(i).push(ch); } } } // Resize rows in both directions as needed let addToY = 0; if (this._terminal.rows < newRows) { for (let y = this._terminal.rows; y < newRows; y++) { if (this.lines.length < newRows + this.ybase) { if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { // There is room above the buffer and there are no empty elements below the line, // scroll up this.ybase--; addToY++; if (this.ydisp > 0) { // Viewport is at the top of the buffer, must increase downwards this.ydisp--; } } else { // Add a blank line if there is no buffer left at the top to scroll to, or if there // are blank lines after the cursor this.lines.push(BufferLine.blankLine(newCols, DEFAULT_ATTR)); } } } } else { // (this._terminal.rows >= newRows) for (let y = this._terminal.rows; y > newRows; y--) { if (this.lines.length > newRows + this.ybase) { if (this.lines.length > this.ybase + this.y + 1) { // The line is a blank line below the cursor, remove it this.lines.pop(); } else { // The line is the cursor, scroll down this.ybase++; this.ydisp++; } } } } // Reduce max length if needed after adjustments, this is done after as it // would otherwise cut data from the bottom of the buffer. if (newMaxLength < this.lines.maxLength) { // Trim from the top of the buffer and adjust ybase and ydisp. const amountToTrim = this.lines.length - newMaxLength; if (amountToTrim > 0) { this.lines.trimStart(amountToTrim); this.ybase = Math.max(this.ybase - amountToTrim, 0); this.ydisp = Math.max(this.ydisp - amountToTrim, 0); } this.lines.maxLength = newMaxLength; } // Make sure that the cursor stays on screen this.x = Math.min(this.x, newCols - 1); this.y = Math.min(this.y, newRows - 1); if (addToY) { this.y += addToY; } this.savedY = Math.min(this.savedY, newRows - 1); this.savedX = Math.min(this.savedX, newCols - 1); this.scrollTop = 0; } this.scrollBottom = newRows - 1; } /** * Translates a string index back to a BufferIndex. * To get the correct buffer position the string must start at `startCol` 0 * (default in translateBufferLineToString). * The method also works on wrapped line strings given rows were not trimmed. * The method operates on the CharData string length, there are no * additional content or boundary checks. Therefore the string and the buffer * should not be altered in between. * TODO: respect trim flag after fixing #1685 * @param lineIndex line index the string was retrieved from * @param stringIndex index within the string * @param startCol column offset the string was retrieved from */ public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): BufferIndex { while (stringIndex) { const line = this.lines.get(lineIndex); if (!line) { [-1, -1]; } for (let i = 0; i < line.length; ++i) { stringIndex -= line.get(i)[CHAR_DATA_CHAR_INDEX].length; if (stringIndex < 0) { return [lineIndex, i]; } } lineIndex++; } return [lineIndex, 0]; } /** * Translates a buffer line to a string, with optional start and end columns. * Wide characters will count as two columns in the resulting string. This * function is useful for getting the actual text underneath the raw selection * position. * @param line The line being translated. * @param trimRight Whether to trim whitespace to the right. * @param startCol The column to start at. * @param endCol The column to end at. */ public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol: number = 0, endCol: number = null): string { // Get full line let lineString = ''; const line = this.lines.get(lineIndex); if (!line) { return ''; } // Initialize column and index values. Column values represent the actual // cell column, indexes represent the index in the string. Indexes are // needed here because some chars are 0 characters long (eg. after wide // chars) and some chars are longer than 1 characters long (eg. emojis). let startIndex = startCol; // Only set endCol to the line length when it is null. 0 is a valid column. if (endCol === null) { endCol = line.length; } let endIndex = endCol; for (let i = 0; i < line.length; i++) { const char = line.get(i); lineString += char[CHAR_DATA_CHAR_INDEX]; // Adjust start and end cols for wide characters if they affect their // column indexes if (char[CHAR_DATA_WIDTH_INDEX] === 0) { if (startCol >= i) { startIndex--; } if (endCol > i) { endIndex--; } } else { // Adjust the columns to take glyphs that are represented by multiple // code points into account. if (char[CHAR_DATA_CHAR_INDEX].length > 1) { if (startCol > i) { startIndex += char[CHAR_DATA_CHAR_INDEX].length - 1; } if (endCol > i) { endIndex += char[CHAR_DATA_CHAR_INDEX].length - 1; } } } } // Calculate the final end col by trimming whitespace on the right of the // line if needed. if (trimRight) { const rightWhitespaceIndex = lineString.search(/\s+$/); if (rightWhitespaceIndex !== -1) { endIndex = Math.min(endIndex, rightWhitespaceIndex); } // Return the empty string if only trimmed whitespace is selected if (endIndex <= startIndex) { return ''; } } return lineString.substring(startIndex, endIndex); } public getWrappedRangeForLine(y: number): { first: number, last: number } { let first = y; let last = y; // Scan upwards for wrapped lines while (first > 0 && this.lines.get(first).isWrapped) { first--; } // Scan downwards for wrapped lines while (last + 1 < this.lines.length && this.lines.get(last + 1).isWrapped) { last++; } return { first, last }; } /** * Setup the tab stops. * @param i The index to start setting up tab stops from. */ public setupTabStops(i?: number): void { if (i !== null && i !== undefined) { if (!this.tabs[i]) { i = this.prevStop(i); } } else { this.tabs = {}; i = 0; } for (; i < this._terminal.cols; i += this._terminal.options.tabStopWidth) { this.tabs[i] = true; } } /** * Move the cursor to the previous tab stop from the given position (default is current). * @param x The position to move the cursor to the previous tab stop. */ public prevStop(x?: number): number { if (x === null || x === undefined) { x = this.x; } while (!this.tabs[--x] && x > 0); return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; } /** * Move the cursor one tab stop forward from the given position (default is current). * @param x The position to move the cursor one tab stop forward. */ public nextStop(x?: number): number { if (x === null || x === undefined) { x = this.x; } while (!this.tabs[++x] && x < this._terminal.cols); return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; } public addMarker(y: number): Marker { const marker = new Marker(y); this.markers.push(marker); marker.register(this.lines.addDisposableListener('trim', amount => { marker.line -= amount; // The marker should be disposed when the line is trimmed from the buffer if (marker.line < 0) { marker.dispose(); } })); marker.register(marker.addDisposableListener('dispose', () => this._removeMarker(marker))); return marker; } private _removeMarker(marker: Marker): void { // TODO: This could probably be optimized by relying on sort order and trimming the array using .length this.markers.splice(this.markers.indexOf(marker), 1); } public iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator { return new BufferStringIterator(this, trimRight, startIndex, endIndex, startOverscan, endOverscan); } } export class Marker extends EventEmitter implements IMarker { private static _nextId = 1; private _id: number = Marker._nextId++; public isDisposed: boolean = false; public get id(): number { return this._id; } constructor( public line: number ) { super(); } public dispose(): void { if (this.isDisposed) { return; } this.isDisposed = true; // Emit before super.dispose such that dispose listeners get a change to react this.emit('dispose'); super.dispose(); } } /** * Iterator to get unwrapped content strings from the buffer. * The iterator returns at least the string data between the borders * `startIndex` and `endIndex` (exclusive) and will expand the lines * by `startOverscan` to the top and by `endOverscan` to the bottom, * if no new line was found in between. * It will never read/return string data beyond `startIndex - startOverscan` * or `endIndex + endOverscan`. Therefore the first and last line might be truncated. * It is possible to always get the full string for the first and last line as well * by setting the overscan values to the actual buffer length. This not recommended * since it might return the whole buffer within a single string in a worst case scenario. */ export class BufferStringIterator implements IBufferStringIterator { private _current: number; constructor ( private _buffer: IBuffer, private _trimRight: boolean, private _startIndex: number = 0, private _endIndex: number = _buffer.lines.length, private _startOverscan: number = 0, private _endOverscan: number = 0 ) { if (this._startIndex < 0) { this._startIndex = 0; } if (this._endIndex > this._buffer.lines.length) { this._endIndex = this._buffer.lines.length; } this._current = this._startIndex; } public hasNext(): boolean { return this._current < this._endIndex; } public next(): IBufferStringIteratorResult { const range = this._buffer.getWrappedRangeForLine(this._current); // limit search window to overscan value at both borders if (range.first < this._startIndex - this._startOverscan) { range.first = this._startIndex - this._startOverscan; } if (range.last > this._endIndex + this._endOverscan) { range.last = this._endIndex + this._endOverscan; } // limit to current buffer length range.first = Math.max(range.first, 0); range.last = Math.min(range.last, this._buffer.lines.length); let result = ''; for (let i = range.first; i <= range.last; ++i) { // TODO: always apply trimRight after fixing #1685 result += this._buffer.translateBufferLineToString(i, (this._trimRight) ? i === range.last : false); } this._current = range.last + 1; return {range: range, content: result}; } } xterm.js-3.8.1/src/BufferLine.test.ts000066400000000000000000000071001341514612000174260ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import * as chai from 'chai'; import { BufferLine } from './BufferLine'; import { CharData, IBufferLine } from './Types'; import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, CHAR_DATA_ATTR_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX } from './Buffer'; class TestBufferLine extends BufferLine { public toArray(): CharData[] { return this._data; } } describe('BufferLine', function(): void { it('ctor', function(): void { let line: IBufferLine = new TestBufferLine(); chai.expect(line.length).equals(0); chai.expect(line.pop()).equals(undefined); chai.expect(line.isWrapped).equals(false); line = new TestBufferLine(10); chai.expect(line.length).equals(10); chai.expect(line.pop()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); chai.expect(line.isWrapped).equals(false); line = new TestBufferLine(10, null, true); chai.expect(line.length).equals(10); chai.expect(line.pop()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); chai.expect(line.isWrapped).equals(true); line = new TestBufferLine(10, [123, 'a', 456, 789], true); chai.expect(line.length).equals(10); chai.expect(line.pop()).eql([123, 'a', 456, 789]); chai.expect(line.isWrapped).equals(true); }); it('splice', function(): void { const line = new TestBufferLine(); const data: CharData[] = [ [1, 'a', 0, 0], [2, 'b', 0, 0], [3, 'c', 0, 0] ]; for (let i = 0; i < data.length; ++i) line.push(data[i]); chai.expect(line.length).equals(data.length); const removed1 = line.splice(1, 1, [4, 'd', 0, 0]); const removed2 = data.splice(1, 1, [4, 'd', 0, 0]); chai.expect(removed1).eql(removed2); chai.expect(line.toArray()).eql(data); }); it('TerminalLine.blankLine', function(): void { const line = TestBufferLine.blankLine(5, 123); chai.expect(line.length).equals(5); chai.expect(line.isWrapped).equals(false); const ch = line.get(0); chai.expect(ch[CHAR_DATA_ATTR_INDEX]).equals(123); chai.expect(ch[CHAR_DATA_CHAR_INDEX]).equals(NULL_CELL_CHAR); chai.expect(ch[CHAR_DATA_WIDTH_INDEX]).equals(NULL_CELL_WIDTH); chai.expect(ch[CHAR_DATA_CODE_INDEX]).equals(NULL_CELL_CODE); }); it('insertCells', function(): void { const line = new TestBufferLine(); const data: CharData[] = [ [1, 'a', 0, 0], [2, 'b', 0, 0], [3, 'c', 0, 0] ]; for (let i = 0; i < data.length; ++i) line.push(data[i]); line.insertCells(1, 3, [4, 'd', 0, 0]); chai.expect(line.toArray()).eql([[1, 'a', 0, 0], [4, 'd', 0, 0], [4, 'd', 0, 0]]); }); it('deleteCells', function(): void { const line = new TestBufferLine(); const data: CharData[] = [ [1, 'a', 0, 0], [2, 'b', 0, 0], [3, 'c', 0, 0], [4, 'd', 0, 0], [5, 'e', 0, 0] ]; for (let i = 0; i < data.length; ++i) line.push(data[i]); line.deleteCells(1, 2, [6, 'f', 0, 0]); chai.expect(line.toArray()).eql([[1, 'a', 0, 0], [4, 'd', 0, 0], [5, 'e', 0, 0], [6, 'f', 0, 0], [6, 'f', 0, 0]]); }); it('replaceCells', function(): void { const line = new TestBufferLine(); const data: CharData[] = [ [1, 'a', 0, 0], [2, 'b', 0, 0], [3, 'c', 0, 0], [4, 'd', 0, 0], [5, 'e', 0, 0] ]; for (let i = 0; i < data.length; ++i) line.push(data[i]); line.replaceCells(2, 4, [6, 'f', 0, 0]); chai.expect(line.toArray()).eql([[1, 'a', 0, 0], [2, 'b', 0, 0], [6, 'f', 0, 0], [6, 'f', 0, 0], [5, 'e', 0, 0]]); }); }); xterm.js-3.8.1/src/BufferLine.ts000066400000000000000000000045251341514612000164600ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { CharData, IBufferLine } from './Types'; import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from './Buffer'; /** * Class representing a terminal line. */ export class BufferLine implements IBufferLine { static blankLine(cols: number, attr: number, isWrapped?: boolean): IBufferLine { const ch: CharData = [attr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; return new BufferLine(cols, ch, isWrapped); } protected _data: CharData[]; public isWrapped = false; public length: number; constructor(cols?: number, ch?: CharData, isWrapped?: boolean) { this._data = []; this.length = this._data.length; if (cols) { if (!ch) { ch = [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; } for (let i = 0; i < cols; i++) { this.push(ch); // Note: the ctor ch is not cloned (resembles old behavior) } } if (isWrapped) { this.isWrapped = true; } } public get(index: number): CharData { return this._data[index]; } public set(index: number, data: CharData): void { this._data[index] = data; } public pop(): CharData | undefined { const data = this._data.pop(); this.length = this._data.length; return data; } public push(data: CharData): void { this._data.push(data); this.length = this._data.length; } public splice(start: number, deleteCount: number, ...items: CharData[]): CharData[] { const removed = this._data.splice(start, deleteCount, ...items); this.length = this._data.length; return removed; } /** insert n cells ch at pos, right cells are lost (stable length) */ public insertCells(pos: number, n: number, ch: CharData): void { while (n--) { this.splice(pos, 0, ch); this.pop(); } } /** delete n cells at pos, right side is filled with fill (stable length) */ public deleteCells(pos: number, n: number, fill: CharData): void { while (n--) { this.splice(pos, 1); this.push(fill); } } /** replace cells from pos to pos + n - 1 with fill */ public replaceCells(start: number, end: number, fill: CharData): void { while (start < end && start < this.length) { this.set(start++, fill); // Note: fill is not cloned (resembles old behavior) } } } xterm.js-3.8.1/src/BufferSet.test.ts000066400000000000000000000025211341514612000172740ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { ITerminal } from './Types'; import { BufferSet } from './BufferSet'; import { Buffer } from './Buffer'; import { MockTerminal } from './utils/TestUtils.test'; describe('BufferSet', () => { let terminal: ITerminal; let bufferSet: BufferSet; beforeEach(() => { terminal = new MockTerminal(); terminal.cols = 80; terminal.rows = 24; terminal.options.scrollback = 1000; bufferSet = new BufferSet(terminal); }); describe('constructor', () => { it('should create two different buffers: alt and normal', () => { assert.instanceOf(bufferSet.normal, Buffer); assert.instanceOf(bufferSet.alt, Buffer); assert.notEqual(bufferSet.normal, bufferSet.alt); }); }); describe('activateNormalBuffer', () => { beforeEach(() => { bufferSet.activateNormalBuffer(); }); it('should set the normal buffer as the currently active buffer', () => { assert.equal(bufferSet.active, bufferSet.normal); }); }); describe('activateAltBuffer', () => { beforeEach(() => { bufferSet.activateAltBuffer(); }); it('should set the alt buffer as the currently active buffer', () => { assert.equal(bufferSet.active, bufferSet.alt); }); }); }); xterm.js-3.8.1/src/BufferSet.ts000066400000000000000000000057321341514612000163250ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal, IBufferSet } from './Types'; import { Buffer } from './Buffer'; import { EventEmitter } from './common/EventEmitter'; /** * The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and * provides also utilities for working with them. */ export class BufferSet extends EventEmitter implements IBufferSet { private _normal: Buffer; private _alt: Buffer; private _activeBuffer: Buffer; /** * Create a new BufferSet for the given terminal. * @param _terminal - The terminal the BufferSet will belong to */ constructor(private _terminal: ITerminal) { super(); this._normal = new Buffer(this._terminal, true); this._normal.fillViewportRows(); // The alt buffer should never have scrollback. // See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer this._alt = new Buffer(this._terminal, false); this._activeBuffer = this._normal; this.setupTabStops(); } /** * Returns the alt Buffer of the BufferSet */ public get alt(): Buffer { return this._alt; } /** * Returns the normal Buffer of the BufferSet */ public get active(): Buffer { return this._activeBuffer; } /** * Returns the currently active Buffer of the BufferSet */ public get normal(): Buffer { return this._normal; } /** * Sets the normal Buffer of the BufferSet as its currently active Buffer */ public activateNormalBuffer(): void { if (this._activeBuffer === this._normal) { return; } // The alt buffer should always be cleared when we switch to the normal // buffer. This frees up memory since the alt buffer should always be new // when activated. this._alt.clear(); this._activeBuffer = this._normal; this.emit('activate', { activeBuffer: this._normal, inactiveBuffer: this._alt }); } /** * Sets the alt Buffer of the BufferSet as its currently active Buffer */ public activateAltBuffer(): void { if (this._activeBuffer === this._alt) { return; } // Since the alt buffer is always cleared when the normal buffer is // activated, we want to fill it when switching to it. this._alt.fillViewportRows(); this._activeBuffer = this._alt; this.emit('activate', { activeBuffer: this._alt, inactiveBuffer: this._normal }); } /** * Resizes both normal and alt buffers, adjusting their data accordingly. * @param newCols The new number of columns. * @param newRows The new number of rows. */ public resize(newCols: number, newRows: number): void { this._normal.resize(newCols, newRows); this._alt.resize(newCols, newRows); } /** * Setup the tab stops. * @param i The index to start setting up tab stops from. */ public setupTabStops(i?: number): void { this._normal.setupTabStops(i); this._alt.setupTabStops(i); } } xterm.js-3.8.1/src/CharWidth.test.ts000066400000000000000000000060131341514612000172640ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { TestTerminal } from './utils/TestUtils.test'; import { assert } from 'chai'; import { getStringCellWidth } from './CharWidth'; import { IBuffer } from './Types'; import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer'; describe('getStringCellWidth', function(): void { let terminal: TestTerminal; beforeEach(() => { terminal = new TestTerminal({rows: 5, cols: 30}); }); function sumWidths(buffer: IBuffer, start: number, end: number, sentinel: string): number { let result = 0; for (let i = start; i < end; ++i) { const line = buffer.lines.get(i); for (let j = 0; j < line.length; ++j) { // TODO: change to trimBorder with multiline const ch = line.get(j); result += ch[CHAR_DATA_WIDTH_INDEX]; // return on sentinel if (ch[CHAR_DATA_CHAR_INDEX] === sentinel) { return result; } } } return result; } it('ASCII chars', function(): void { const input = 'This is just ASCII text.#'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); }); it('combining chars', function(): void { const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301#'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); }); it('surrogate chars', function(): void { const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞#'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); }); it('surrogate combining chars', function(): void { const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301#'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); }); it('fullwidth chars', function(): void { const input = '1234567890#'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); }); it('fullwidth chars offset 1', function(): void { const input = 'a1234567890#'; terminal.writeSync(input); const s = terminal.buffer.iterator(true).next().content; assert.equal(input, s); assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); }); // TODO: multiline tests once #1685 is resolved }); xterm.js-3.8.1/src/CharWidth.ts000066400000000000000000000170051341514612000163110ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ export const wcwidth = (function(opts: {nul: number, control: number}): (ucs: number) => number { // extracted from https://www.cl.cam.ac.uk/%7Emgk25/ucs/wcwidth.c // combining characters const COMBINING_BMP = [ [0x0300, 0x036F], [0x0483, 0x0486], [0x0488, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0603], [0x0610, 0x0615], [0x064B, 0x065E], [0x0670, 0x0670], [0x06D6, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x0901, 0x0902], [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0954], [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], [0x0A70, 0x0A71], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], [0x0B3F, 0x0B3F], [0x0B41, 0x0B43], [0x0B4D, 0x0B4D], [0x0B56, 0x0B56], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], [0x0D41, 0x0D43], [0x0D4D, 0x0D4D], [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], [0x0EB4, 0x0EB9], [0x0EBB, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F90, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], [0x1032, 0x1032], [0x1036, 0x1037], [0x1039, 0x1039], [0x1058, 0x1059], [0x1160, 0x11FF], [0x135F, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], [0x180B, 0x180D], [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], [0x1DC0, 0x1DCA], [0x1DFE, 0x1DFF], [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2063], [0x206A, 0x206F], [0x20D0, 0x20EF], [0x302A, 0x302F], [0x3099, 0x309A], [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], [0xFE20, 0xFE23], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] ]; const COMBINING_HIGH = [ [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x1D167, 0x1D169], [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], [0x1D242, 0x1D244], [0xE0001, 0xE0001], [0xE0020, 0xE007F], [0xE0100, 0xE01EF] ]; // binary search function bisearch(ucs: number, data: number[][]): boolean { let min = 0; let max = data.length - 1; let mid; if (ucs < data[0][0] || ucs > data[max][1]) { return false; } while (max >= min) { mid = (min + max) >> 1; if (ucs > data[mid][1]) { min = mid + 1; } else if (ucs < data[mid][0]) { max = mid - 1; } else { return true; } } return false; } function wcwidthBMP(ucs: number): number { // test for 8-bit control characters if (ucs === 0) { return opts.nul; } if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) { return opts.control; } // binary search in table of non-spacing characters if (bisearch(ucs, COMBINING_BMP)) { return 0; } // if we arrive here, ucs is not a combining or C0/C1 control character if (isWideBMP(ucs)) { return 2; } return 1; } function isWideBMP(ucs: number): boolean { return ( ucs >= 0x1100 && ( ucs <= 0x115f || // Hangul Jamo init. consonants ucs === 0x2329 || ucs === 0x232a || (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs !== 0x303f) || // CJK..Yi (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compat Ideographs (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compat Forms (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms (ucs >= 0xffe0 && ucs <= 0xffe6))); } function wcwidthHigh(ucs: number): 0 | 1 | 2 { if (bisearch(ucs, COMBINING_HIGH)) { return 0; } if ((ucs >= 0x20000 && ucs <= 0x2fffd) || (ucs >= 0x30000 && ucs <= 0x3fffd)) { return 2; } return 1; } const control = opts.control | 0; let table: number[] | Uint32Array = null; function initTable(): number[] | Uint32Array { // lookup table for BMP const CODEPOINTS = 65536; // BMP holds 65536 codepoints const BITWIDTH = 2; // a codepoint can have a width of 0, 1 or 2 const ITEMSIZE = 32; // using uint32_t const CONTAINERSIZE = CODEPOINTS * BITWIDTH / ITEMSIZE; const CODEPOINTS_PER_ITEM = ITEMSIZE / BITWIDTH; table = (typeof Uint32Array === 'undefined') ? new Array(CONTAINERSIZE) : new Uint32Array(CONTAINERSIZE); for (let i = 0; i < CONTAINERSIZE; ++i) { let num = 0; let pos = CODEPOINTS_PER_ITEM; while (pos--) { num = (num << 2) | wcwidthBMP(CODEPOINTS_PER_ITEM * i + pos); } table[i] = num; } return table; } // get width from lookup table // position in container : num / CODEPOINTS_PER_ITEM // ==> n = table[Math.floor(num / 16)] // ==> n = table[num >> 4] // 16 codepoints per number: FFEEDDCCBBAA99887766554433221100 // position in number : (num % CODEPOINTS_PER_ITEM) * BITWIDTH // ==> m = (n % 16) * 2 // ==> m = (num & 15) << 1 // right shift to position m // ==> n = n >> m e.g. m=12 000000000000FFEEDDCCBBAA99887766 // we are only interested in 2 LSBs, cut off higher bits // ==> n = n & 3 e.g. 000000000000000000000000000000XX return function (num: number): number { num = num | 0; // get asm.js like optimization under V8 if (num < 32) { return control | 0; } if (num < 127) { return 1; } const t = table || initTable(); if (num < 65536) { return t[num >> 4] >> ((num & 15) << 1) & 3; } // do a full search for high codepoints return wcwidthHigh(num); }; })({nul: 0, control: 0}); // configurable options /** * Get the terminal cell width for a string. */ export function getStringCellWidth(s: string): number { let result = 0; for (let i = 0; i < s.length; ++i) { let code = s.charCodeAt(i); if (0xD800 <= code && code <= 0xDBFF) { const low = s.charCodeAt(i + 1); if (isNaN(low)) { return result; } code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; } if (0xDC00 <= code && code <= 0xDFFF) { continue; } result += wcwidth(code); } return result; } xterm.js-3.8.1/src/CompositionHelper.test.ts000066400000000000000000000227501341514612000210600ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { CompositionHelper } from './CompositionHelper'; import { ITerminal } from './Types'; describe('CompositionHelper', () => { let terminal: ITerminal; let compositionHelper: CompositionHelper; let compositionView: HTMLElement; let textarea: HTMLTextAreaElement; let handledText: string; beforeEach(() => { compositionView = { classList: { add: () => {}, remove: () => {} }, getBoundingClientRect: () => { return { width: 0 }; }, style: { left: 0, top: 0 }, textContent: '' } as any; textarea = { value: '', style: { left: 0, top: 0 } } as any; terminal = { element: { querySelector: () => { return { offsetLeft: 0, offsetTop: 0 }; } }, handler: (text: string) => { handledText += text; }, buffer: { isCursorInViewport: true }, charMeasure: { height: 10, width: 10 }, options: { lineHeight: 1 } } as any; handledText = ''; compositionHelper = new CompositionHelper(textarea, compositionView, terminal); }); describe('Input', () => { it('Should insert simple characters', (done) => { // First character 'ㅇ' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, 'ㅇ'); // Second character 'ㅇ' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, 'ㅇㅇ'); done(); }, 0); }, 0); }, 0); }, 0); }); it('Should insert complex characters', (done) => { // First character '앙' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: '아' }); textarea.value = '아'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: '앙' }); textarea.value = '앙'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, '앙'); // Second character '앙' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = '앙ㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: '아' }); textarea.value = '앙아'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: '앙' }); textarea.value = '앙앙'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, '앙앙'); done(); }, 0); }, 0); }, 0); }, 0); }, 0); }, 0); }, 0); }, 0); }); it('Should insert complex characters that change with following character', (done) => { // First character '아' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: '아' }); textarea.value = '아'; setTimeout(() => { // wait for any textarea updates // Start second character '아' in first character compositionHelper.compositionupdate({ data: '앙' }); textarea.value = '앙'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: '아' }); textarea.value = '아아'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, '아아'); done(); }, 0); }, 0); }, 0); }, 0); }, 0); }); it('Should insert multi-characters compositions', (done) => { // First character 'だ' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'd' }); textarea.value = 'd'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: 'だ' }); textarea.value = 'だ'; setTimeout(() => { // wait for any textarea updates // Second character 'あ' compositionHelper.compositionupdate({ data: 'だあ' }); textarea.value = 'だあ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, 'だあ'); done(); }, 0); }, 0); }, 0); }, 0); }); it('Should insert multi-character compositions that are converted to other characters with the same length', (done) => { // First character 'だ' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'd' }); textarea.value = 'd'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: 'だ' }); textarea.value = 'だ'; setTimeout(() => { // wait for any textarea updates // Second character 'ー' compositionHelper.compositionupdate({ data: 'だー' }); textarea.value = 'だー'; setTimeout(() => { // wait for any textarea updates // Convert to katakana 'ダー' compositionHelper.compositionupdate({ data: 'ダー' }); textarea.value = 'ダー'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, 'ダー'); done(); }, 0); }, 0); }, 0); }, 0); }, 0); }); it('Should insert multi-character compositions that are converted to other characters with different lengths', (done) => { // First character 'い' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'い' }); textarea.value = 'い'; setTimeout(() => { // wait for any textarea updates // Second character 'ま' compositionHelper.compositionupdate({ data: 'いm' }); textarea.value = 'いm'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionupdate({ data: 'いま' }); textarea.value = 'いま'; setTimeout(() => { // wait for any textarea updates // Convert to kanji '今' compositionHelper.compositionupdate({ data: '今' }); textarea.value = '今'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); setTimeout(() => { // wait for any textarea updates assert.equal(handledText, '今'); done(); }, 0); }, 0); }, 0); }, 0); }, 0); }); it('Should insert non-composition characters input immediately after composition characters', (done) => { // First character 'ㅇ' compositionHelper.compositionstart(); compositionHelper.compositionupdate({ data: 'ㅇ' }); textarea.value = 'ㅇ'; setTimeout(() => { // wait for any textarea updates compositionHelper.compositionend(); // Second character '1' (a non-composition character) textarea.value = 'ㅇ1'; setTimeout(() => { // wait for any textarea updates assert.equal(handledText, 'ㅇ1'); done(); }, 0); }, 0); }); }); }); xterm.js-3.8.1/src/CompositionHelper.ts000066400000000000000000000214111341514612000200730ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal } from './Types'; interface IPosition { start: number; end: number; } /** * 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. */ export class CompositionHelper { /** * 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. */ private _isComposing: boolean; /** * The position within the input textarea's value of the current composition. */ private _compositionPosition: IPosition; /** * Whether a composition is in the process of being sent, setting this to false will cancel any * in-progress composition. */ private _isSendingComposition: boolean; /** * Creates a new CompositionHelper. * @param _textarea The textarea that xterm uses for input. * @param _compositionView The element to display the in-progress composition in. * @param _terminal The Terminal to forward the finished composition to. */ constructor( private _textarea: HTMLTextAreaElement, private _compositionView: HTMLElement, private _terminal: ITerminal ) { this._isComposing = false; this._isSendingComposition = false; this._compositionPosition = { start: null, end: null }; } /** * Handles the compositionstart event, activating the composition view. */ public compositionstart(): void { 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 ev The event. */ public compositionupdate(ev: CompositionEvent): void { this._compositionView.textContent = ev.data; this.updateCompositionElements(); setTimeout(() => { this._compositionPosition.end = this._textarea.value.length; }, 0); } /** * Handles the compositionend event, hiding the composition view and sending the composition to * the handler. */ public compositionend(): void { this._finalizeComposition(true); } /** * Handles the keydown event, routing any necessary events to the CompositionHelper functions. * @param ev The keydown event. * @return Whether the Terminal should continue processing the keydown event. */ public keydown(ev: KeyboardEvent): boolean { 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; } // 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 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. */ private _finalizeComposition(waitForPropogation: boolean): void { 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; const 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. const 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. this._isSendingComposition = true; setTimeout(() => { // Ensure that the input has not already been sent if (this._isSendingComposition) { this._isSendingComposition = false; let input; if (this._isComposing) { // Use the end position to get the string if a new composition has started. input = this._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 = this._textarea.value.substring(currentCompositionPosition.start); } this._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. */ private _handleAnyTextareaChanges(): void { const oldValue = this._textarea.value; setTimeout(() => { // Ignore if a composition has started since the timeout if (!this._isComposing) { const newValue = this._textarea.value; const diff = newValue.replace(oldValue, ''); if (diff.length > 0) { this._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). * @param dontRecurse Whether to use setTimeout to recursively trigger another update, this is * necessary as the IME events across browsers are not consistently triggered. */ public updateCompositionElements(dontRecurse?: boolean): void { if (!this._isComposing) { return; } if (this._terminal.buffer.isCursorInViewport) { const cellHeight = Math.ceil(this._terminal.charMeasure.height * this._terminal.options.lineHeight); const cursorTop = this._terminal.buffer.y * cellHeight; const cursorLeft = this._terminal.buffer.x * this._terminal.charMeasure.width; this._compositionView.style.left = cursorLeft + 'px'; this._compositionView.style.top = cursorTop + 'px'; this._compositionView.style.height = cellHeight + 'px'; this._compositionView.style.lineHeight = cellHeight + 'px'; // Sync the textarea to the exact position of the composition view so the IME knows where the // text is. const compositionViewBounds = this._compositionView.getBoundingClientRect(); this._textarea.style.left = cursorLeft + 'px'; this._textarea.style.top = cursorTop + 'px'; this._textarea.style.width = compositionViewBounds.width + 'px'; this._textarea.style.height = compositionViewBounds.height + 'px'; this._textarea.style.lineHeight = compositionViewBounds.height + 'px'; } if (!dontRecurse) { setTimeout(() => this.updateCompositionElements(true), 0); } } /** * Clears the textarea's position so that the cursor does not blink on IE. * @private */ private _clearTextareaPosition(): void { this._textarea.style.left = ''; this._textarea.style.top = ''; } } xterm.js-3.8.1/src/EscapeSequenceParser.test.ts000066400000000000000000001352171341514612000214660ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { ParserState, IDcsHandler, IParsingState } from './Types'; import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from './EscapeSequenceParser'; import * as chai from 'chai'; function r(a: number, b: number): string[] { let c = b - a; const arr = new Array(c); while (c--) { arr[c] = String.fromCharCode(--b); } return arr; } // derived parser with access to internal states class TestEscapeSequenceParser extends EscapeSequenceParser { public get osc(): string { return this._osc; } public set osc(value: string) { this._osc = value; } public get params(): number[] { return this._params; } public set params(value: number[]) { this._params = value; } public get collect(): string { return this._collect; } public set collect(value: string) { this._collect = value; } public mockActiveDcsHandler(): void { this._activeDcsHandler = this._dcsHandlerFb; } } // test object to collect parser actions and compare them with expected values const testTerminal: any = { calls: [], clear: function (): void { this.calls = []; }, compare: function (value: any): void { chai.expect(this.calls.slice()).eql(value); // weird bug w'o slicing here }, print: function (data: string, start: number, end: number): void { this.calls.push(['print', data.substring(start, end)]); }, actionOSC: function (s: string): void { this.calls.push(['osc', s]); }, actionExecute: function (flag: string): void { this.calls.push(['exe', flag]); }, actionCSI: function (collect: string, params: number[], flag: string): void { this.calls.push(['csi', collect, params, flag]); }, actionESC: function (collect: string, flag: string): void { this.calls.push(['esc', collect, flag]); }, actionDCSHook: function (collect: string, params: number[], flag: string): void { this.calls.push(['dcs hook', collect, params, flag]); }, actionDCSPrint: function (data: string, start: number, end: number): void { this.calls.push(['dcs put', data.substring(start, end)]); }, actionDCSUnhook: function (): void { this.calls.push(['dcs unhook']); } }; // dcs handler to map dcs actions into the test object `testTerminal` class DcsTest implements IDcsHandler { hook(collect: string, params: number[], flag: number): void { testTerminal.actionDCSHook(collect, params, String.fromCharCode(flag)); } put(data: string, start: number, end: number): void { testTerminal.actionDCSPrint(data, start, end); } unhook(): void { testTerminal.actionDCSUnhook(); } } const states: number[] = [ ParserState.GROUND, ParserState.ESCAPE, ParserState.ESCAPE_INTERMEDIATE, ParserState.CSI_ENTRY, ParserState.CSI_PARAM, ParserState.CSI_INTERMEDIATE, ParserState.CSI_IGNORE, ParserState.SOS_PM_APC_STRING, ParserState.OSC_STRING, ParserState.DCS_ENTRY, ParserState.DCS_PARAM, ParserState.DCS_IGNORE, ParserState.DCS_INTERMEDIATE, ParserState.DCS_PASSTHROUGH ]; let state: any; // parser with Uint8Array based transition table const parserUint = new TestEscapeSequenceParser(VT500_TRANSITION_TABLE); parserUint.setPrintHandler(testTerminal.print.bind(testTerminal)); parserUint.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { testTerminal.actionCSI(collect, params, String.fromCharCode(flag)); }); parserUint.setEscHandlerFallback((collect: string, flag: number) => { testTerminal.actionESC(collect, String.fromCharCode(flag)); }); parserUint.setExecuteHandlerFallback((code: number) => { testTerminal.actionExecute(String.fromCharCode(code)); }); parserUint.setOscHandlerFallback((identifier: number, data: string) => { if (identifier === -1) testTerminal.actionOSC(data); // handle error condition silently else testTerminal.actionOSC('' + identifier + ';' + data); }); parserUint.setDcsHandlerFallback(new DcsTest()); // array based transition table const VT500_TRANSITION_TABLE_ARRAY = new TransitionTable(VT500_TRANSITION_TABLE.table.length); VT500_TRANSITION_TABLE_ARRAY.table = new Array(VT500_TRANSITION_TABLE.table.length); for (let i = 0; i < VT500_TRANSITION_TABLE.table.length; ++i) { VT500_TRANSITION_TABLE_ARRAY.table[i] = VT500_TRANSITION_TABLE.table[i]; } // parser with array based transition table const parserArray = new TestEscapeSequenceParser(VT500_TRANSITION_TABLE_ARRAY); parserArray.setPrintHandler(testTerminal.print.bind(testTerminal)); parserArray.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { testTerminal.actionCSI(collect, params, String.fromCharCode(flag)); }); parserArray.setEscHandlerFallback((collect: string, flag: number) => { testTerminal.actionESC(collect, String.fromCharCode(flag)); }); parserArray.setExecuteHandlerFallback((code: number) => { testTerminal.actionExecute(String.fromCharCode(code)); }); parserArray.setOscHandlerFallback((identifier: number, data: string) => { if (identifier === -1) testTerminal.actionOSC(data); // handle error condition silently else testTerminal.actionOSC('' + identifier + ';' + data); }); parserArray.setDcsHandlerFallback(new DcsTest()); interface IRun { tableType: string; parser: TestEscapeSequenceParser; } describe('EscapeSequenceParser', function (): void { let parser: TestEscapeSequenceParser | null = null; const runs: IRun[] = [ { tableType: 'Uint8Array', parser: parserUint }, { tableType: 'Array', parser: parserArray } ]; runs.forEach(function (run: IRun): void { describe('Parser init and methods / ' + run.tableType, function (): void { before(function(): void { parser = run.parser; }); it('constructor', function (): void { let p: EscapeSequenceParser = new EscapeSequenceParser(); chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE); p = new EscapeSequenceParser(VT500_TRANSITION_TABLE); chai.expect(p.TRANSITIONS).equal(VT500_TRANSITION_TABLE); const tansitions: TransitionTable = new TransitionTable(10); p = new EscapeSequenceParser(tansitions); chai.expect(p.TRANSITIONS).equal(tansitions); }); it('inital states', function (): void { chai.expect(parser.initialState).equal(ParserState.GROUND); chai.expect(parser.currentState).equal(ParserState.GROUND); chai.expect(parser.osc).equal(''); chai.expect(parser.params).eql([0]); chai.expect(parser.collect).equal(''); }); it('reset states', function (): void { parser.currentState = 124; parser.osc = '#'; parser.params = [123]; parser.collect = '#'; parser.reset(); chai.expect(parser.currentState).equal(ParserState.GROUND); chai.expect(parser.osc).equal(''); chai.expect(parser.params).eql([0]); chai.expect(parser.collect).equal(''); }); }); }); runs.forEach(function (run: IRun): void { describe('state transitions and actions / ' + run.tableType, function (): void { before(function(): void { parser = run.parser; }); it('state GROUND execute action', function (): void { parser.reset(); testTerminal.clear(); const exes = r(0x00, 0x18); exes.concat(['\x19']); exes.concat(r(0x1c, 0x20)); for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.GROUND; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); it('state GROUND print action', function (): void { parser.reset(); testTerminal.clear(); const printables = r(0x20, 0x7f); // NOTE: DEL excluded for (let i = 0; i < printables.length; ++i) { parser.currentState = ParserState.GROUND; parser.parse(printables[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([['print', printables[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans ANYWHERE --> GROUND with actions', function (): void { const exes = [ '\x18', '\x1a', '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87', '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97', '\x99', '\x9a' ]; const exceptions: { [key: number]: { [key: string]: any[] } } = { 8: { '\x18': [], '\x1a': [] } // simply abort osc state }; parser.reset(); testTerminal.clear(); for (state in states) { for (let i = 0; i < exes.length; ++i) { parser.currentState = state; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare((state in exceptions ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } parser.parse('\x9c'); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([]); parser.reset(); testTerminal.clear(); } }); it('trans ANYWHERE --> ESCAPE with clear', function (): void { parser.reset(); for (state in states) { parser.currentState = state; parser.osc = '#'; parser.params = [23]; parser.collect = '#'; parser.parse('\x1b'); chai.expect(parser.currentState).equal(ParserState.ESCAPE); chai.expect(parser.osc).equal(''); chai.expect(parser.params).eql([0]); chai.expect(parser.collect).equal(''); parser.reset(); } }); it('state ESCAPE execute rules', function (): void { parser.reset(); testTerminal.clear(); const exes = r(0x00, 0x18); exes.concat(['\x19']); exes.concat(r(0x1c, 0x20)); for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.ESCAPE; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.ESCAPE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); it('state ESCAPE ignore', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.ESCAPE; parser.parse('\x7f'); chai.expect(parser.currentState).equal(ParserState.ESCAPE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); it('trans ESCAPE --> GROUND with ecs_dispatch action', function (): void { parser.reset(); testTerminal.clear(); const dispatches = r(0x30, 0x50); dispatches.concat(r(0x51, 0x58)); dispatches.concat(['\x59', '\x5a', '\x5c']); dispatches.concat(r(0x60, 0x7f)); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.ESCAPE; parser.parse(dispatches[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([['esc', '', dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans ESCAPE --> ESCAPE_INTERMEDIATE with collect action', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.ESCAPE; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('state ESCAPE_INTERMEDIATE execute rules', function (): void { parser.reset(); testTerminal.clear(); const exes = r(0x00, 0x18); exes.concat(['\x19']); exes.concat(r(0x1c, 0x20)); for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); it('state ESCAPE_INTERMEDIATE ignore', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parser.parse('\x7f'); chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); it('state ESCAPE_INTERMEDIATE collect action', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('trans ESCAPE_INTERMEDIATE --> GROUND with esc_dispatch action', function (): void { parser.reset(); testTerminal.clear(); const collect = r(0x30, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([['esc', '', collect[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans ANYWHERE/ESCAPE --> CSI_ENTRY with clear', function (): void { parser.reset(); // C0 parser.currentState = ParserState.ESCAPE; parser.osc = '#'; parser.params = [123]; parser.collect = '#'; parser.parse('['); chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); chai.expect(parser.osc).equal(''); chai.expect(parser.params).eql([0]); chai.expect(parser.collect).equal(''); parser.reset(); // C1 for (state in states) { parser.currentState = state; parser.osc = '#'; parser.params = [123]; parser.collect = '#'; parser.parse('\x9b'); chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); chai.expect(parser.osc).equal(''); chai.expect(parser.params).eql([0]); chai.expect(parser.collect).equal(''); parser.reset(); } }); it('state CSI_ENTRY execute rules', function (): void { parser.reset(); testTerminal.clear(); const exes = r(0x00, 0x18); exes.concat(['\x19']); exes.concat(r(0x1c, 0x20)); for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); it('state CSI_ENTRY ignore', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_ENTRY; parser.parse('\x7f'); chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); it('trans CSI_ENTRY --> GROUND with csi_dispatch action', function (): void { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parser.parse(dispatches[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([['csi', '', [0], dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans CSI_ENTRY --> CSI_PARAM with param/collect actions', function (): void { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parser.parse(params[i]); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.CSI_ENTRY; parser.parse('\x3b'); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); chai.expect(parser.params).eql([0, 0]); parser.reset(); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('state CSI_PARAM execute rules', function (): void { parser.reset(); testTerminal.clear(); const exes = r(0x00, 0x18); exes.concat(['\x19']); exes.concat(r(0x1c, 0x20)); for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); it('state CSI_PARAM param action', function (): void { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parser.parse(params[i]); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.CSI_PARAM; parser.parse('\x3b'); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); chai.expect(parser.params).eql([0, 0]); parser.reset(); }); it('state CSI_PARAM ignore', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_PARAM; parser.parse('\x7f'); chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); it('trans CSI_PARAM --> GROUND with csi_dispatch action', function (): void { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parser.params = [0, 1]; parser.parse(dispatches[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans CSI_ENTRY --> CSI_INTERMEDIATE with collect action', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('trans CSI_PARAM --> CSI_INTERMEDIATE with collect action', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('state CSI_INTERMEDIATE execute rules', function (): void { parser.reset(); testTerminal.clear(); const exes = r(0x00, 0x18); exes.concat(['\x19']); exes.concat(r(0x1c, 0x20)); for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); it('state CSI_INTERMEDIATE collect', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('state CSI_INTERMEDIATE ignore', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_INTERMEDIATE; parser.parse('\x7f'); chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); it('trans CSI_INTERMEDIATE --> GROUND with csi_dispatch action', function (): void { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parser.params = [0, 1]; parser.parse(dispatches[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans CSI_ENTRY --> CSI_IGNORE', function (): void { parser.reset(); parser.currentState = ParserState.CSI_ENTRY; parser.parse('\x3a'); chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); parser.reset(); }); it('trans CSI_PARAM --> CSI_IGNORE', function (): void { parser.reset(); const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parser.parse('\x3b' + chars[i]); chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); chai.expect(parser.params).eql([0, 0]); parser.reset(); } }); it('trans CSI_INTERMEDIATE --> CSI_IGNORE', function (): void { parser.reset(); const chars = r(0x30, 0x40); for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parser.parse(chars[i]); chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); chai.expect(parser.params).eql([0]); parser.reset(); } }); it('state CSI_IGNORE execute rules', function (): void { parser.reset(); testTerminal.clear(); const exes = r(0x00, 0x18); exes.concat(['\x19']); exes.concat(r(0x1c, 0x20)); for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_IGNORE; parser.parse(exes[i]); chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); it('state CSI_IGNORE ignore', function (): void { parser.reset(); testTerminal.clear(); const ignored = r(0x20, 0x40); ignored.concat(['\x7f']); for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.CSI_IGNORE; parser.parse(ignored[i]); chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); } }); it('trans CSI_IGNORE --> GROUND', function (): void { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_IGNORE; parser.params = [0, 1]; parser.parse(dispatches[i]); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([]); parser.reset(); testTerminal.clear(); } }); it('trans ANYWHERE/ESCAPE --> SOS_PM_APC_STRING', function (): void { parser.reset(); // C0 let initializers = ['\x58', '\x5e', '\x5f']; for (let i = 0; i < initializers.length; ++i) { parser.parse('\x1b' + initializers[i]); chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); parser.reset(); } // C1 for (state in states) { parser.currentState = state; initializers = ['\x98', '\x9e', '\x9f']; for (let i = 0; i < initializers.length; ++i) { parser.parse(initializers[i]); chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); parser.reset(); } } }); it('state SOS_PM_APC_STRING ignore rules', function (): void { parser.reset(); const ignored = r(0x00, 0x18); ignored.concat(['\x19']); ignored.concat(r(0x1c, 0x20)); ignored.concat(r(0x20, 0x80)); for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.SOS_PM_APC_STRING; parser.parse(ignored[i]); chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); parser.reset(); } }); it('trans ANYWHERE/ESCAPE --> OSC_STRING', function (): void { parser.reset(); // C0 parser.parse('\x1b]'); chai.expect(parser.currentState).equal(ParserState.OSC_STRING); parser.reset(); // C1 for (state in states) { parser.currentState = state; parser.parse('\x9d'); chai.expect(parser.currentState).equal(ParserState.OSC_STRING); parser.reset(); } }); it('state OSC_STRING ignore rules', function (): void { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', /*'\x07',*/ '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f']; for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.OSC_STRING; parser.parse(ignored[i]); chai.expect(parser.currentState).equal(ParserState.OSC_STRING); chai.expect(parser.osc).equal(''); parser.reset(); } }); it('state OSC_STRING put action', function (): void { parser.reset(); const puts = r(0x20, 0x80); for (let i = 0; i < puts.length; ++i) { parser.currentState = ParserState.OSC_STRING; parser.parse(puts[i]); chai.expect(parser.currentState).equal(ParserState.OSC_STRING); chai.expect(parser.osc).equal(puts[i]); parser.reset(); } }); it('state DCS_ENTRY', function (): void { parser.reset(); // C0 parser.parse('\x1bP'); chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); parser.reset(); // C1 for (state in states) { parser.currentState = state; parser.parse('\x90'); chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); parser.reset(); } }); it('state DCS_ENTRY ignore rules', function (): void { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parser.parse(ignored[i]); chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); parser.reset(); } }); it('state DCS_ENTRY --> DCS_PARAM with param/collect actions', function (): void { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parser.parse(params[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.DCS_ENTRY; parser.parse('\x3b'); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); chai.expect(parser.params).eql([0, 0]); parser.reset(); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('state DCS_PARAM ignore rules', function (): void { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parser.parse(ignored[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); parser.reset(); } }); it('state DCS_PARAM param action', function (): void { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parser.parse(params[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.DCS_PARAM; parser.parse('\x3b'); chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); chai.expect(parser.params).eql([0, 0]); parser.reset(); }); it('trans DCS_ENTRY --> DCS_IGNORE', function (): void { parser.reset(); parser.currentState = ParserState.DCS_ENTRY; parser.parse('\x3a'); chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); parser.reset(); }); it('trans DCS_PARAM --> DCS_IGNORE', function (): void { parser.reset(); const chars = ['\x3a', '\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parser.parse('\x3b' + chars[i]); chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); chai.expect(parser.params).eql([0, 0]); parser.reset(); } }); it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { parser.reset(); const chars = r(0x30, 0x40); for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parser.parse(chars[i]); chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); parser.reset(); } }); it('state DCS_IGNORE ignore rules', function (): void { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; ignored.concat(r(0x20, 0x80)); for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_IGNORE; parser.parse(ignored[i]); chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); parser.reset(); } }); it('trans DCS_ENTRY --> DCS_INTERMEDIATE with collect action', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('trans DCS_PARAM --> DCS_INTERMEDIATE with collect action', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('state DCS_INTERMEDIATE ignore rules', function (): void { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x19', '\x1c', '\x1d', '\x1e', '\x1f', '\x7f']; for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parser.parse(ignored[i]); chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); parser.reset(); } }); it('state DCS_INTERMEDIATE collect action', function (): void { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); chai.expect(parser.collect).equal(collect[i]); parser.reset(); } }); it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { parser.reset(); const chars = r(0x30, 0x40); for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parser.parse('\x20' + chars[i]); chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); chai.expect(parser.collect).equal('\x20'); parser.reset(); } }); it('trans DCS_ENTRY --> DCS_PASSTHROUGH with hook', function (): void { parser.reset(); testTerminal.clear(); const collect = r(0x40, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', '', [0], collect[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans DCS_PARAM --> DCS_PASSTHROUGH with hook', function (): void { parser.reset(); testTerminal.clear(); const collect = r(0x40, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', '', [0], collect[i]]]); parser.reset(); testTerminal.clear(); } }); it('trans DCS_INTERMEDIATE --> DCS_PASSTHROUGH with hook', function (): void { parser.reset(); testTerminal.clear(); const collect = r(0x40, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parser.parse(collect[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', '', [0], collect[i]]]); parser.reset(); testTerminal.clear(); } }); it('state DCS_PASSTHROUGH put action', function (): void { parser.reset(); testTerminal.clear(); const puts = r(0x00, 0x18); puts.concat(['\x19']); puts.concat(r(0x1c, 0x20)); puts.concat(r(0x20, 0x7f)); for (let i = 0; i < puts.length; ++i) { parser.currentState = ParserState.DCS_PASSTHROUGH; parser.mockActiveDcsHandler(); parser.parse(puts[i]); chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs put', puts[i]]]); parser.reset(); testTerminal.clear(); } }); it('state DCS_PASSTHROUGH ignore', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.DCS_PASSTHROUGH; parser.parse('\x7f'); chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); }); }); runs.forEach(function (run: IRun): void { let test: Function | null = null; describe('escape sequence examples / ' + run.tableType, function (): void { before(function(): void { parser = run.parser; test = function(s: string, value: any, noReset: any): void { if (!noReset) { parser.reset(); testTerminal.clear(); } parser.parse(s); testTerminal.compare(value); }; }); it('CSI with print and execute', function (): void { test('\x1b[<31;5mHello World! öäü€\nabc', [ ['csi', '<', [31, 5], 'm'], ['print', 'Hello World! öäü€'], ['exe', '\n'], ['print', 'abc'] ], null); }); it('OSC', function (): void { test('\x1b]0;abc123€öäü\x07', [ ['osc', '0;abc123€öäü'] ], null); }); it('single DCS', function (): void { test('\x1bP1;2;3+$abc;de\x9c', [ ['dcs hook', '+$', [1, 2, 3], 'a'], ['dcs put', 'bc;de'], ['dcs unhook'] ], null); }); it('multi DCS', function (): void { test('\x1bP1;2;3+$abc;de', [ ['dcs hook', '+$', [1, 2, 3], 'a'], ['dcs put', 'bc;de'] ], null); testTerminal.clear(); test('abc\x9c', [ ['dcs put', 'abc'], ['dcs unhook'] ], true); }); it('print + DCS(C1)', function (): void { test('abc\x901;2;3+$abc;de\x9c', [ ['print', 'abc'], ['dcs hook', '+$', [1, 2, 3], 'a'], ['dcs put', 'bc;de'], ['dcs unhook'] ], null); }); it('print + PM(C1) + print', function (): void { test('abc\x98123tzf\x9cdefg', [ ['print', 'abc'], ['print', 'defg'] ], null); }); it('print + OSC(C1) + print', function (): void { test('abc\x9d123tzf\x9cdefg', [ ['print', 'abc'], ['osc', '123tzf'], ['print', 'defg'] ], null); }); it('error recovery', function (): void { test('\x1b[1€abcdefg\x9b<;c', [ ['print', 'abcdefg'], ['csi', '<', [0, 0], 'c'] ], null); }); }); }); describe('coverage tests', function (): void { it('CSI_IGNORE error', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_IGNORE; parser.parse('€öäü'); chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); it('DCS_IGNORE error', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.DCS_IGNORE; parser.parse('€öäü'); chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); it('DCS_PASSTHROUGH error', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.DCS_PASSTHROUGH; parser.parse('\x901;2;3+$a€öäü'); chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', '+$', [1, 2, 3], 'a'], ['dcs put', '€öäü']]); parser.reset(); testTerminal.clear(); }); it('error else of if (code > 159)', function (): void { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.GROUND; parser.parse('\x1e'); chai.expect(parser.currentState).equal(ParserState.GROUND); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); }); describe('set/clear handler', function (): void { const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1b]1;foo=bar\x1b\\'; let parser2: TestEscapeSequenceParser = null; let print = ''; const esc: string[] = []; const csi: [string, number[], string][] = []; const exe: string[] = []; const osc: [number, string][] = []; const dcs: ([string] | [string, string] | [string, string, number[], number])[] = []; function clearAccu(): void { print = ''; esc.length = 0; csi.length = 0; exe.length = 0; osc.length = 0; dcs.length = 0; } beforeEach(function (): void { parser2 = new TestEscapeSequenceParser(); clearAccu(); }); it('print handler', function (): void { parser2.setPrintHandler(function (data: string, start: number, end: number): void { print += data.substring(start, end); }); parser2.parse(INPUT); chai.expect(print).equal('hello world!$>'); parser2.clearPrintHandler(); parser2.clearPrintHandler(); // should not throw clearAccu(); parser2.parse(INPUT); chai.expect(print).equal(''); }); it('ESC handler', function (): void { parser2.setEscHandler('%G', function (): void { esc.push('%G'); }); parser2.setEscHandler('E', function (): void { esc.push('E'); }); parser2.parse(INPUT); chai.expect(esc).eql(['%G', 'E']); parser2.clearEscHandler('%G'); parser2.clearEscHandler('%G'); // should not throw clearAccu(); parser2.parse(INPUT); chai.expect(esc).eql(['E']); parser2.clearEscHandler('E'); clearAccu(); parser2.parse(INPUT); chai.expect(esc).eql([]); }); it('CSI handler', function (): void { parser2.setCsiHandler('m', function (params: number[], collect: string): void { csi.push(['m', params, collect]); }); parser2.parse(INPUT); chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]); parser2.clearCsiHandler('m'); parser2.clearCsiHandler('m'); // should not throw clearAccu(); parser2.parse(INPUT); chai.expect(csi).eql([]); }); it('EXECUTE handler', function (): void { parser2.setExecuteHandler('\n', function (): void { exe.push('\n'); }); parser2.setExecuteHandler('\r', function (): void { exe.push('\r'); }); parser2.parse(INPUT); chai.expect(exe).eql(['\r', '\n']); parser2.clearExecuteHandler('\r'); parser2.clearExecuteHandler('\r'); // should not throw clearAccu(); parser2.parse(INPUT); chai.expect(exe).eql(['\n']); }); it('OSC handler', function (): void { parser2.setOscHandler(1, function (data: string): void { osc.push([1, data]); }); parser2.parse(INPUT); chai.expect(osc).eql([[1, 'foo=bar']]); parser2.clearOscHandler(1); parser2.clearOscHandler(1); // should not throw clearAccu(); parser2.parse(INPUT); chai.expect(osc).eql([]); }); it('DCS handler', function (): void { parser2.setDcsHandler('+p', { hook: function (collect: string, params: number[], flag: number): void { dcs.push(['hook', collect, params, flag]); }, put: function (data: string, start: number, end: number): void { dcs.push(['put', data.substring(start, end)]); }, unhook: function (): void { dcs.push(['unhook']); } }); parser2.parse('\x1bP1;2;3+pabc'); parser2.parse(';de\x9c'); chai.expect(dcs).eql([ ['hook', '+', [1, 2, 3], 'p'.charCodeAt(0)], ['put', 'abc'], ['put', ';de'], ['unhook'] ]); parser2.clearDcsHandler('+p'); parser2.clearDcsHandler('+p'); // should not throw clearAccu(); parser2.parse('\x1bP1;2;3+pabc'); parser2.parse(';de\x9c'); chai.expect(dcs).eql([]); }); it('ERROR handler', function (): void { let errorState: IParsingState = null; parser2.setErrorHandler(function (state: IParsingState): IParsingState { errorState = state; return state; }); parser2.parse('\x1b[1;2;€;3m'); // faulty escape sequence chai.expect(errorState).eql({ position: 6, code: '€'.charCodeAt(0), currentState: ParserState.CSI_PARAM, print: -1, dcs: -1, osc: '', collect: '', params: [1, 2, 0], // extra zero here abort: false }); parser2.clearErrorHandler(); parser2.clearErrorHandler(); // should not throw errorState = null; parser2.parse('\x1b[1;2;a;3m'); chai.expect(errorState).eql(null); }); }); // TODO: error conditions and error recovery (not implemented yet in parser) }); xterm.js-3.8.1/src/EscapeSequenceParser.ts000066400000000000000000000604251341514612000205060ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { ParserState, ParserAction, IParsingState, IDcsHandler, IEscapeSequenceParser } from './Types'; import { Disposable } from './common/Lifecycle'; /** * Returns an array filled with numbers between the low and high parameters (right exclusive). * @param low The low number. * @param high The high number. */ function r(low: number, high: number): number[] { let c = high - low; const arr = new Array(c); while (c--) { arr[c] = --high; } return arr; } /** * Transition table for EscapeSequenceParser. * NOTE: data in the underlying table is packed like this: * currentState << 8 | characterCode --> action << 4 | nextState */ export class TransitionTable { public table: Uint8Array | number[]; constructor(length: number) { this.table = (typeof Uint8Array === 'undefined') ? new Array(length) : new Uint8Array(length); } /** * Add a transition to the transition table. * @param code input character code * @param state current parser state * @param action parser action to be done * @param next next parser state */ add(code: number, state: number, action: number | null, next: number | null): void { this.table[state << 8 | code] = ((action | 0) << 4) | ((next === undefined) ? state : next); } /** * Add transitions for multiple input character codes. * @param codes input character code array * @param state current parser state * @param action parser action to be done * @param next next parser state */ addMany(codes: number[], state: number, action: number | null, next: number | null): void { for (let i = 0; i < codes.length; i++) { this.add(codes[i], state, action, next); } } } /** * Default definitions for the VT500_TRANSITION_TABLE. */ const PRINTABLES = r(0x20, 0x7f); const EXECUTABLES = r(0x00, 0x18); EXECUTABLES.push(0x19); EXECUTABLES.concat(r(0x1c, 0x20)); const DEFAULT_TRANSITION = ParserAction.ERROR << 4 | ParserState.GROUND; /** * VT500 compatible transition table. * Taken from https://vt100.net/emu/dec_ansi_parser. */ export const VT500_TRANSITION_TABLE = (function (): TransitionTable { const table: TransitionTable = new TransitionTable(4095); const states: number[] = r(ParserState.GROUND, ParserState.DCS_PASSTHROUGH + 1); let state: any; // table with default transition [any] --> DEFAULT_TRANSITION for (state in states) { // NOTE: table lookup is capped at 0xa0 in parse to keep the table small for (let code = 0; code < 160; ++code) { table.add(code, state, ParserAction.ERROR, ParserState.GROUND); } } // printables table.addMany(PRINTABLES, ParserState.GROUND, ParserAction.PRINT, ParserState.GROUND); // global anywhere rules for (state in states) { table.addMany([0x18, 0x1a, 0x99, 0x9a], state, ParserAction.EXECUTE, ParserState.GROUND); table.addMany(r(0x80, 0x90), state, ParserAction.EXECUTE, ParserState.GROUND); table.addMany(r(0x90, 0x98), state, ParserAction.EXECUTE, ParserState.GROUND); table.add(0x9c, state, ParserAction.IGNORE, ParserState.GROUND); // ST as terminator table.add(0x1b, state, ParserAction.CLEAR, ParserState.ESCAPE); // ESC table.add(0x9d, state, ParserAction.OSC_START, ParserState.OSC_STRING); // OSC table.addMany([0x98, 0x9e, 0x9f], state, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING); table.add(0x9b, state, ParserAction.CLEAR, ParserState.CSI_ENTRY); // CSI table.add(0x90, state, ParserAction.CLEAR, ParserState.DCS_ENTRY); // DCS } // rules for executables and 7f table.addMany(EXECUTABLES, ParserState.GROUND, ParserAction.EXECUTE, ParserState.GROUND); table.addMany(EXECUTABLES, ParserState.ESCAPE, ParserAction.EXECUTE, ParserState.ESCAPE); table.add(0x7f, ParserState.ESCAPE, ParserAction.IGNORE, ParserState.ESCAPE); table.addMany(EXECUTABLES, ParserState.OSC_STRING, ParserAction.IGNORE, ParserState.OSC_STRING); table.addMany(EXECUTABLES, ParserState.CSI_ENTRY, ParserAction.EXECUTE, ParserState.CSI_ENTRY); table.add(0x7f, ParserState.CSI_ENTRY, ParserAction.IGNORE, ParserState.CSI_ENTRY); table.addMany(EXECUTABLES, ParserState.CSI_PARAM, ParserAction.EXECUTE, ParserState.CSI_PARAM); table.add(0x7f, ParserState.CSI_PARAM, ParserAction.IGNORE, ParserState.CSI_PARAM); table.addMany(EXECUTABLES, ParserState.CSI_IGNORE, ParserAction.EXECUTE, ParserState.CSI_IGNORE); table.addMany(EXECUTABLES, ParserState.CSI_INTERMEDIATE, ParserAction.EXECUTE, ParserState.CSI_INTERMEDIATE); table.add(0x7f, ParserState.CSI_INTERMEDIATE, ParserAction.IGNORE, ParserState.CSI_INTERMEDIATE); table.addMany(EXECUTABLES, ParserState.ESCAPE_INTERMEDIATE, ParserAction.EXECUTE, ParserState.ESCAPE_INTERMEDIATE); table.add(0x7f, ParserState.ESCAPE_INTERMEDIATE, ParserAction.IGNORE, ParserState.ESCAPE_INTERMEDIATE); // osc table.add(0x5d, ParserState.ESCAPE, ParserAction.OSC_START, ParserState.OSC_STRING); table.addMany(PRINTABLES, ParserState.OSC_STRING, ParserAction.OSC_PUT, ParserState.OSC_STRING); table.add(0x7f, ParserState.OSC_STRING, ParserAction.OSC_PUT, ParserState.OSC_STRING); table.addMany([0x9c, 0x1b, 0x18, 0x1a, 0x07], ParserState.OSC_STRING, ParserAction.OSC_END, ParserState.GROUND); table.addMany(r(0x1c, 0x20), ParserState.OSC_STRING, ParserAction.IGNORE, ParserState.OSC_STRING); // sos/pm/apc does nothing table.addMany([0x58, 0x5e, 0x5f], ParserState.ESCAPE, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING); table.addMany(PRINTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING); table.addMany(EXECUTABLES, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING); table.add(0x9c, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.GROUND); // csi entries table.add(0x5b, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.CSI_ENTRY); table.addMany(r(0x40, 0x7f), ParserState.CSI_ENTRY, ParserAction.CSI_DISPATCH, ParserState.GROUND); table.addMany(r(0x30, 0x3a), ParserState.CSI_ENTRY, ParserAction.PARAM, ParserState.CSI_PARAM); table.add(0x3b, ParserState.CSI_ENTRY, ParserAction.PARAM, ParserState.CSI_PARAM); table.addMany([0x3c, 0x3d, 0x3e, 0x3f], ParserState.CSI_ENTRY, ParserAction.COLLECT, ParserState.CSI_PARAM); table.addMany(r(0x30, 0x3a), ParserState.CSI_PARAM, ParserAction.PARAM, ParserState.CSI_PARAM); table.add(0x3b, ParserState.CSI_PARAM, ParserAction.PARAM, ParserState.CSI_PARAM); table.addMany(r(0x40, 0x7f), ParserState.CSI_PARAM, ParserAction.CSI_DISPATCH, ParserState.GROUND); table.addMany([0x3a, 0x3c, 0x3d, 0x3e, 0x3f], ParserState.CSI_PARAM, ParserAction.IGNORE, ParserState.CSI_IGNORE); table.addMany(r(0x20, 0x40), ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.CSI_IGNORE); table.add(0x7f, ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.CSI_IGNORE); table.addMany(r(0x40, 0x7f), ParserState.CSI_IGNORE, ParserAction.IGNORE, ParserState.GROUND); table.add(0x3a, ParserState.CSI_ENTRY, ParserAction.IGNORE, ParserState.CSI_IGNORE); table.addMany(r(0x20, 0x30), ParserState.CSI_ENTRY, ParserAction.COLLECT, ParserState.CSI_INTERMEDIATE); table.addMany(r(0x20, 0x30), ParserState.CSI_INTERMEDIATE, ParserAction.COLLECT, ParserState.CSI_INTERMEDIATE); table.addMany(r(0x30, 0x40), ParserState.CSI_INTERMEDIATE, ParserAction.IGNORE, ParserState.CSI_IGNORE); table.addMany(r(0x40, 0x7f), ParserState.CSI_INTERMEDIATE, ParserAction.CSI_DISPATCH, ParserState.GROUND); table.addMany(r(0x20, 0x30), ParserState.CSI_PARAM, ParserAction.COLLECT, ParserState.CSI_INTERMEDIATE); // esc_intermediate table.addMany(r(0x20, 0x30), ParserState.ESCAPE, ParserAction.COLLECT, ParserState.ESCAPE_INTERMEDIATE); table.addMany(r(0x20, 0x30), ParserState.ESCAPE_INTERMEDIATE, ParserAction.COLLECT, ParserState.ESCAPE_INTERMEDIATE); table.addMany(r(0x30, 0x7f), ParserState.ESCAPE_INTERMEDIATE, ParserAction.ESC_DISPATCH, ParserState.GROUND); table.addMany(r(0x30, 0x50), ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND); table.addMany(r(0x51, 0x58), ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND); table.addMany([0x59, 0x5a, 0x5c], ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND); table.addMany(r(0x60, 0x7f), ParserState.ESCAPE, ParserAction.ESC_DISPATCH, ParserState.GROUND); // dcs entry table.add(0x50, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.DCS_ENTRY); table.addMany(EXECUTABLES, ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_ENTRY); table.add(0x7f, ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_ENTRY); table.addMany(r(0x1c, 0x20), ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_ENTRY); table.addMany(r(0x20, 0x30), ParserState.DCS_ENTRY, ParserAction.COLLECT, ParserState.DCS_INTERMEDIATE); table.add(0x3a, ParserState.DCS_ENTRY, ParserAction.IGNORE, ParserState.DCS_IGNORE); table.addMany(r(0x30, 0x3a), ParserState.DCS_ENTRY, ParserAction.PARAM, ParserState.DCS_PARAM); table.add(0x3b, ParserState.DCS_ENTRY, ParserAction.PARAM, ParserState.DCS_PARAM); table.addMany([0x3c, 0x3d, 0x3e, 0x3f], ParserState.DCS_ENTRY, ParserAction.COLLECT, ParserState.DCS_PARAM); table.addMany(EXECUTABLES, ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE); table.addMany(r(0x20, 0x80), ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE); table.addMany(r(0x1c, 0x20), ParserState.DCS_IGNORE, ParserAction.IGNORE, ParserState.DCS_IGNORE); table.addMany(EXECUTABLES, ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_PARAM); table.add(0x7f, ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_PARAM); table.addMany(r(0x1c, 0x20), ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_PARAM); table.addMany(r(0x30, 0x3a), ParserState.DCS_PARAM, ParserAction.PARAM, ParserState.DCS_PARAM); table.add(0x3b, ParserState.DCS_PARAM, ParserAction.PARAM, ParserState.DCS_PARAM); table.addMany([0x3a, 0x3c, 0x3d, 0x3e, 0x3f], ParserState.DCS_PARAM, ParserAction.IGNORE, ParserState.DCS_IGNORE); table.addMany(r(0x20, 0x30), ParserState.DCS_PARAM, ParserAction.COLLECT, ParserState.DCS_INTERMEDIATE); table.addMany(EXECUTABLES, ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_INTERMEDIATE); table.add(0x7f, ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_INTERMEDIATE); table.addMany(r(0x1c, 0x20), ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_INTERMEDIATE); table.addMany(r(0x20, 0x30), ParserState.DCS_INTERMEDIATE, ParserAction.COLLECT, ParserState.DCS_INTERMEDIATE); table.addMany(r(0x30, 0x40), ParserState.DCS_INTERMEDIATE, ParserAction.IGNORE, ParserState.DCS_IGNORE); table.addMany(r(0x40, 0x7f), ParserState.DCS_INTERMEDIATE, ParserAction.DCS_HOOK, ParserState.DCS_PASSTHROUGH); table.addMany(r(0x40, 0x7f), ParserState.DCS_PARAM, ParserAction.DCS_HOOK, ParserState.DCS_PASSTHROUGH); table.addMany(r(0x40, 0x7f), ParserState.DCS_ENTRY, ParserAction.DCS_HOOK, ParserState.DCS_PASSTHROUGH); table.addMany(EXECUTABLES, ParserState.DCS_PASSTHROUGH, ParserAction.DCS_PUT, ParserState.DCS_PASSTHROUGH); table.addMany(PRINTABLES, ParserState.DCS_PASSTHROUGH, ParserAction.DCS_PUT, ParserState.DCS_PASSTHROUGH); table.add(0x7f, ParserState.DCS_PASSTHROUGH, ParserAction.IGNORE, ParserState.DCS_PASSTHROUGH); table.addMany([0x1b, 0x9c], ParserState.DCS_PASSTHROUGH, ParserAction.DCS_UNHOOK, ParserState.GROUND); return table; })(); /** * Dummy DCS handler as default fallback. */ class DcsDummy implements IDcsHandler { hook(collect: string, params: number[], flag: number): void { } put(data: string, start: number, end: number): void { } unhook(): void { } } /** * EscapeSequenceParser. * This class implements the ANSI/DEC compatible parser described by * Paul Williams (https://vt100.net/emu/dec_ansi_parser). * To implement custom ANSI compliant escape sequences it is not needed to * alter this parser, instead consider registering a custom handler. * For non ANSI compliant sequences change the transition table with * the optional `transitions` contructor argument and * reimplement the `parse` method. * NOTE: The parameter element notation is currently not supported. * TODO: implement error recovery hook via error handler return values */ export class EscapeSequenceParser extends Disposable implements IEscapeSequenceParser { public initialState: number; public currentState: number; // buffers over several parse calls protected _osc: string; protected _params: number[]; protected _collect: string; // handler lookup containers protected _printHandler: (data: string, start: number, end: number) => void; protected _executeHandlers: any; protected _csiHandlers: any; protected _escHandlers: any; protected _oscHandlers: any; protected _dcsHandlers: any; protected _activeDcsHandler: IDcsHandler | null; protected _errorHandler: (state: IParsingState) => IParsingState; // fallback handlers protected _printHandlerFb: (data: string, start: number, end: number) => void; protected _executeHandlerFb: (code: number) => void; protected _csiHandlerFb: (collect: string, params: number[], flag: number) => void; protected _escHandlerFb: (collect: string, flag: number) => void; protected _oscHandlerFb: (identifier: number, data: string) => void; protected _dcsHandlerFb: IDcsHandler; protected _errorHandlerFb: (state: IParsingState) => IParsingState; constructor(readonly TRANSITIONS: TransitionTable = VT500_TRANSITION_TABLE) { super(); this.initialState = ParserState.GROUND; this.currentState = this.initialState; this._osc = ''; this._params = [0]; this._collect = ''; // set default fallback handlers and handler lookup containers this._printHandlerFb = (data, start, end): void => { }; this._executeHandlerFb = (code: number): void => { }; this._csiHandlerFb = (collect: string, params: number[], flag: number): void => { }; this._escHandlerFb = (collect: string, flag: number): void => { }; this._oscHandlerFb = (identifier: number, data: string): void => { }; this._dcsHandlerFb = new DcsDummy(); this._errorHandlerFb = (state: IParsingState): IParsingState => state; this._printHandler = this._printHandlerFb; this._executeHandlers = Object.create(null); this._csiHandlers = Object.create(null); this._escHandlers = Object.create(null); this._oscHandlers = Object.create(null); this._dcsHandlers = Object.create(null); this._activeDcsHandler = null; this._errorHandler = this._errorHandlerFb; } public dispose(): void { this._printHandlerFb = null; this._executeHandlerFb = null; this._csiHandlerFb = null; this._escHandlerFb = null; this._oscHandlerFb = null; this._dcsHandlerFb = null; this._errorHandlerFb = null; this._printHandler = null; this._executeHandlers = null; this._csiHandlers = null; this._escHandlers = null; this._oscHandlers = null; this._dcsHandlers = null; this._activeDcsHandler = null; this._errorHandler = null; } setPrintHandler(callback: (data: string, start: number, end: number) => void): void { this._printHandler = callback; } clearPrintHandler(): void { this._printHandler = this._printHandlerFb; } setExecuteHandler(flag: string, callback: () => void): void { this._executeHandlers[flag.charCodeAt(0)] = callback; } clearExecuteHandler(flag: string): void { if (this._executeHandlers[flag.charCodeAt(0)]) delete this._executeHandlers[flag.charCodeAt(0)]; } setExecuteHandlerFallback(callback: (code: number) => void): void { this._executeHandlerFb = callback; } setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void { this._csiHandlers[flag.charCodeAt(0)] = callback; } clearCsiHandler(flag: string): void { if (this._csiHandlers[flag.charCodeAt(0)]) delete this._csiHandlers[flag.charCodeAt(0)]; } setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void { this._csiHandlerFb = callback; } setEscHandler(collectAndFlag: string, callback: () => void): void { this._escHandlers[collectAndFlag] = callback; } clearEscHandler(collectAndFlag: string): void { if (this._escHandlers[collectAndFlag]) delete this._escHandlers[collectAndFlag]; } setEscHandlerFallback(callback: (collect: string, flag: number) => void): void { this._escHandlerFb = callback; } setOscHandler(ident: number, callback: (data: string) => void): void { this._oscHandlers[ident] = callback; } clearOscHandler(ident: number): void { if (this._oscHandlers[ident]) delete this._oscHandlers[ident]; } setOscHandlerFallback(callback: (identifier: number, data: string) => void): void { this._oscHandlerFb = callback; } setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void { this._dcsHandlers[collectAndFlag] = handler; } clearDcsHandler(collectAndFlag: string): void { if (this._dcsHandlers[collectAndFlag]) delete this._dcsHandlers[collectAndFlag]; } setDcsHandlerFallback(handler: IDcsHandler): void { this._dcsHandlerFb = handler; } setErrorHandler(callback: (state: IParsingState) => IParsingState): void { this._errorHandler = callback; } clearErrorHandler(): void { this._errorHandler = this._errorHandlerFb; } reset(): void { this.currentState = this.initialState; this._osc = ''; this._params = [0]; this._collect = ''; this._activeDcsHandler = null; } parse(data: string): void { let code = 0; let transition = 0; let error = false; let currentState = this.currentState; let print = -1; let dcs = -1; let osc = this._osc; let collect = this._collect; let params = this._params; const table: Uint8Array | number[] = this.TRANSITIONS.table; let dcsHandler: IDcsHandler | null = this._activeDcsHandler; let callback: Function | null = null; // process input string const l = data.length; for (let i = 0; i < l; ++i) { code = data.charCodeAt(i); // shortcut for most chars (print action) if (currentState === ParserState.GROUND && code > 0x1f && code < 0x80) { print = (~print) ? print : i; do i++; while (i < l && data.charCodeAt(i) > 0x1f && data.charCodeAt(i) < 0x80); i--; continue; } // shortcut for CSI params if (currentState === ParserState.CSI_PARAM && (code > 0x2f && code < 0x39)) { params[params.length - 1] = params[params.length - 1] * 10 + code - 48; continue; } // normal transition & action lookup transition = (code < 0xa0) ? (table[currentState << 8 | code]) : DEFAULT_TRANSITION; switch (transition >> 4) { case ParserAction.PRINT: print = (~print) ? print : i; break; case ParserAction.EXECUTE: if (~print) { this._printHandler(data, print, i); print = -1; } callback = this._executeHandlers[code]; if (callback) callback(); else this._executeHandlerFb(code); break; case ParserAction.IGNORE: // handle leftover print or dcs chars if (~print) { this._printHandler(data, print, i); print = -1; } else if (~dcs) { dcsHandler.put(data, dcs, i); dcs = -1; } break; case ParserAction.ERROR: // chars higher than 0x9f are handled by this action // to keep the transition table small if (code > 0x9f) { switch (currentState) { case ParserState.GROUND: print = (~print) ? print : i; break; case ParserState.OSC_STRING: osc += String.fromCharCode(code); transition |= ParserState.OSC_STRING; break; case ParserState.CSI_IGNORE: transition |= ParserState.CSI_IGNORE; break; case ParserState.DCS_IGNORE: transition |= ParserState.DCS_IGNORE; break; case ParserState.DCS_PASSTHROUGH: dcs = (~dcs) ? dcs : i; transition |= ParserState.DCS_PASSTHROUGH; break; default: error = true; } } else { error = true; } // if we end up here a real error happened if (error) { const inject: IParsingState = this._errorHandler( { position: i, code, currentState, print, dcs, osc, collect, params, abort: false }); if (inject.abort) return; // TODO: inject return values error = false; } break; case ParserAction.CSI_DISPATCH: callback = this._csiHandlers[code]; if (callback) callback(params, collect); else this._csiHandlerFb(collect, params, code); break; case ParserAction.PARAM: if (code === 0x3b) params.push(0); else params[params.length - 1] = params[params.length - 1] * 10 + code - 48; break; case ParserAction.COLLECT: collect += String.fromCharCode(code); break; case ParserAction.ESC_DISPATCH: callback = this._escHandlers[collect + String.fromCharCode(code)]; if (callback) callback(collect, code); else this._escHandlerFb(collect, code); break; case ParserAction.CLEAR: if (~print) { this._printHandler(data, print, i); print = -1; } osc = ''; params = [0]; collect = ''; dcs = -1; break; case ParserAction.DCS_HOOK: dcsHandler = this._dcsHandlers[collect + String.fromCharCode(code)]; if (!dcsHandler) dcsHandler = this._dcsHandlerFb; dcsHandler.hook(collect, params, code); break; case ParserAction.DCS_PUT: dcs = (~dcs) ? dcs : i; break; case ParserAction.DCS_UNHOOK: if (dcsHandler) { if (~dcs) dcsHandler.put(data, dcs, i); dcsHandler.unhook(); dcsHandler = null; } if (code === 0x1b) transition |= ParserState.ESCAPE; osc = ''; params = [0]; collect = ''; dcs = -1; break; case ParserAction.OSC_START: if (~print) { this._printHandler(data, print, i); print = -1; } osc = ''; break; case ParserAction.OSC_PUT: osc += data.charAt(i); break; case ParserAction.OSC_END: if (osc && code !== 0x18 && code !== 0x1a) { // NOTE: OSC subparsing is not part of the original parser // we do basic identifier parsing here to offer a jump table for OSC as well const idx = osc.indexOf(';'); if (idx === -1) { this._oscHandlerFb(-1, osc); // this is an error (malformed OSC) } else { // Note: NaN is not handled here // either catch it with the fallback handler // or with an explicit NaN OSC handler const identifier = parseInt(osc.substring(0, idx)); const content = osc.substring(idx + 1); callback = this._oscHandlers[identifier]; if (callback) callback(content); else this._oscHandlerFb(identifier, content); } } if (code === 0x1b) transition |= ParserState.ESCAPE; osc = ''; params = [0]; collect = ''; dcs = -1; break; } currentState = transition & 15; } // push leftover pushable buffers to terminal if (currentState === ParserState.GROUND && ~print) { this._printHandler(data, print, data.length); } else if (currentState === ParserState.DCS_PASSTHROUGH && ~dcs && dcsHandler) { dcsHandler.put(data, dcs, data.length); } // save non pushable buffers this._osc = osc; this._collect = collect; this._params = params; // save active dcs handler reference this._activeDcsHandler = dcsHandler; // save state this.currentState = currentState; } } xterm.js-3.8.1/src/InputHandler.test.ts000066400000000000000000000436011341514612000200100ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import { InputHandler } from './InputHandler'; import { MockInputHandlingTerminal } from './utils/TestUtils.test'; import { NULL_CELL_CHAR, NULL_CELL_CODE, NULL_CELL_WIDTH, CHAR_DATA_CHAR_INDEX } from './Buffer'; import { Terminal } from './Terminal'; import { IBufferLine } from './Types'; // TODO: This and the sections related to this object in associated tests can be // removed safely after InputHandler refactors are finished class OldInputHandler extends InputHandler { public eraseInLine(params: number[]): void { switch (params[0]) { case 0: this.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); break; case 1: this.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); break; case 2: this.eraseLine(this._terminal.buffer.y); break; } } public eraseInDisplay(params: number[]): void { let j; switch (params[0]) { case 0: this.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); j = this._terminal.buffer.y + 1; for (; j < this._terminal.rows; j++) { this.eraseLine(j); } break; case 1: this.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); j = this._terminal.buffer.y; while (j--) { this.eraseLine(j); } break; case 2: j = this._terminal.rows; while (j--) this.eraseLine(j); break; case 3: // Clear scrollback (everything not in viewport) const scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows; if (scrollBackSize > 0) { this._terminal.buffer.lines.trimStart(scrollBackSize); this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0); this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0); // Force a scroll event to refresh viewport this._terminal.emit('scroll', 0); } break; } } /** * Erase in the identified line everything from "x" to the end of the line (right). * @param x The column from which to start erasing to the end of the line. * @param y The line in which to operate. */ public eraseRight(x: number, y: number): void { const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y); if (!line) { return; } line.replaceCells(x, this._terminal.cols, [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); this._terminal.updateRange(y); } /** * Erase in the identified line everything from "x" to the start of the line (left). * @param x The column from which to start erasing to the start of the line. * @param y The line in which to operate. */ public eraseLeft(x: number, y: number): void { const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y); if (!line) { return; } line.replaceCells(0, x + 1, [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); this._terminal.updateRange(y); } /** * Erase all content in the given line * @param y The line to erase all of its contents. */ public eraseLine(y: number): void { this.eraseRight(0, y); } } describe('InputHandler', () => { describe('save and restore cursor', () => { const terminal = new MockInputHandlingTerminal(); terminal.buffer.x = 1; terminal.buffer.y = 2; terminal.curAttr = 3; const inputHandler = new InputHandler(terminal); // Save cursor position inputHandler.saveCursor([]); assert.equal(terminal.buffer.x, 1); assert.equal(terminal.buffer.y, 2); assert.equal(terminal.curAttr, 3); // Change cursor position terminal.buffer.x = 10; terminal.buffer.y = 20; terminal.curAttr = 30; // Restore cursor position inputHandler.restoreCursor([]); assert.equal(terminal.buffer.x, 1); assert.equal(terminal.buffer.y, 2); assert.equal(terminal.curAttr, 3); }); describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const terminal = new MockInputHandlingTerminal(); const inputHandler = new InputHandler(terminal); const collect = ' '; inputHandler.setCursorStyle([0], collect); assert.equal(terminal.options['cursorStyle'], 'block'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; inputHandler.setCursorStyle([1], collect); assert.equal(terminal.options['cursorStyle'], 'block'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; inputHandler.setCursorStyle([2], collect); assert.equal(terminal.options['cursorStyle'], 'block'); assert.equal(terminal.options['cursorBlink'], false); terminal.options = {}; inputHandler.setCursorStyle([3], collect); assert.equal(terminal.options['cursorStyle'], 'underline'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; inputHandler.setCursorStyle([4], collect); assert.equal(terminal.options['cursorStyle'], 'underline'); assert.equal(terminal.options['cursorBlink'], false); terminal.options = {}; inputHandler.setCursorStyle([5], collect); assert.equal(terminal.options['cursorStyle'], 'bar'); assert.equal(terminal.options['cursorBlink'], true); terminal.options = {}; inputHandler.setCursorStyle([6], collect); assert.equal(terminal.options['cursorStyle'], 'bar'); assert.equal(terminal.options['cursorBlink'], false); }); }); describe('setMode', () => { it('should toggle Terminal.bracketedPasteMode', () => { const terminal = new MockInputHandlingTerminal(); const collect = '?'; terminal.bracketedPasteMode = false; const inputHandler = new InputHandler(terminal); // Set bracketed paste mode inputHandler.setMode([2004], collect); assert.equal(terminal.bracketedPasteMode, true); // Reset bracketed paste mode inputHandler.resetMode([2004], collect); assert.equal(terminal.bracketedPasteMode, false); }); }); describe('regression tests', function(): void { type CharData = [number, string, number, number]; function lineContent(line: IBufferLine): string { let content = ''; for (let i = 0; i < line.length; ++i) content += line.get(i)[CHAR_DATA_CHAR_INDEX]; return content; } function termContent(term: Terminal): string[] { const result = []; for (let i = 0; i < term.rows; ++i) result.push(lineContent(term.buffer.lines.get(i))); return result; } it('insertChars', function(): void { const term = new Terminal(); const inputHandler = new InputHandler(term); // old variant of the method function insertChars(params: number[]): void { let param = params[0]; if (param < 1) param = 1; // make buffer local for faster access const buffer = term.buffer; const row = buffer.y + buffer.ybase; let j = buffer.x; const ch: CharData = [term.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // xterm while (param-- && j < term.cols) { buffer.lines.get(row).splice(j++, 0, ch); buffer.lines.get(row).pop(); } } // insert some data in first and second line inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); const line1: IBufferLine = term.buffer.lines.get(0); // line for old variant const line2: IBufferLine = term.buffer.lines.get(1); // line for new variant expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + '1234567890'); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + '1234567890'); // insert one char from params = [0] term.buffer.y = 0; term.buffer.x = 70; insertChars([0]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + ' 123456789'); term.buffer.y = 1; term.buffer.x = 70; inputHandler.insertChars([0]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + ' 123456789'); expect(lineContent(line2)).equals(lineContent(line1)); // insert one char from params = [1] term.buffer.y = 0; term.buffer.x = 70; insertChars([1]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + ' 12345678'); term.buffer.y = 1; term.buffer.x = 70; inputHandler.insertChars([1]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + ' 12345678'); expect(lineContent(line2)).equals(lineContent(line1)); // insert two chars from params = [2] term.buffer.y = 0; term.buffer.x = 70; insertChars([2]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + ' 123456'); term.buffer.y = 1; term.buffer.x = 70; inputHandler.insertChars([2]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + ' 123456'); expect(lineContent(line2)).equals(lineContent(line1)); // insert 10 chars from params = [10] term.buffer.y = 0; term.buffer.x = 70; insertChars([10]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + ' '); term.buffer.y = 1; term.buffer.x = 70; inputHandler.insertChars([10]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + ' '); expect(lineContent(line2)).equals(lineContent(line1)); }); it('deleteChars', function(): void { const term = new Terminal(); const inputHandler = new InputHandler(term); // old variant of the method function deleteChars(params: number[]): void { let param: number = params[0]; if (param < 1) { param = 1; } // make buffer local for faster access const buffer = term.buffer; const row = buffer.y + buffer.ybase; const ch: CharData = [term.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // xterm while (param--) { buffer.lines.get(row).splice(buffer.x, 1); buffer.lines.get(row).push(ch); } term.updateRange(buffer.y); } // insert some data in first and second line inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); inputHandler.parse(Array(term.cols - 9).join('a')); inputHandler.parse('1234567890'); const line1: IBufferLine = term.buffer.lines.get(0); // line for old variant const line2: IBufferLine = term.buffer.lines.get(1); // line for new variant expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + '1234567890'); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + '1234567890'); // delete one char from params = [0] term.buffer.y = 0; term.buffer.x = 70; deleteChars([0]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + '234567890 '); term.buffer.y = 1; term.buffer.x = 70; inputHandler.deleteChars([0]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + '234567890 '); expect(lineContent(line2)).equals(lineContent(line1)); // insert one char from params = [1] term.buffer.y = 0; term.buffer.x = 70; deleteChars([1]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + '34567890 '); term.buffer.y = 1; term.buffer.x = 70; inputHandler.deleteChars([1]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + '34567890 '); expect(lineContent(line2)).equals(lineContent(line1)); // insert two chars from params = [2] term.buffer.y = 0; term.buffer.x = 70; deleteChars([2]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + '567890 '); term.buffer.y = 1; term.buffer.x = 70; inputHandler.deleteChars([2]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + '567890 '); expect(lineContent(line2)).equals(lineContent(line1)); // insert 10 chars from params = [10] term.buffer.y = 0; term.buffer.x = 70; deleteChars([10]); expect(lineContent(line1)).equals(Array(term.cols - 9).join('a') + ' '); term.buffer.y = 1; term.buffer.x = 70; inputHandler.deleteChars([10]); expect(lineContent(line2)).equals(Array(term.cols - 9).join('a') + ' '); expect(lineContent(line2)).equals(lineContent(line1)); }); it('eraseInLine', function(): void { const term = new Terminal(); const inputHandler = new InputHandler(term); const oldInputHandler = new OldInputHandler(term); // fill 6 lines to test 3 different states inputHandler.parse(Array(term.cols + 1).join('a')); inputHandler.parse(Array(term.cols + 1).join('a')); inputHandler.parse(Array(term.cols + 1).join('a')); inputHandler.parse(Array(term.cols + 1).join('a')); inputHandler.parse(Array(term.cols + 1).join('a')); inputHandler.parse(Array(term.cols + 1).join('a')); // params[0] - right erase term.buffer.y = 0; term.buffer.x = 70; oldInputHandler.eraseInLine([0]); expect(lineContent(term.buffer.lines.get(0))).equals(Array(71).join('a') + ' '); term.buffer.y = 1; term.buffer.x = 70; inputHandler.eraseInLine([0]); expect(lineContent(term.buffer.lines.get(1))).equals(Array(71).join('a') + ' '); // params[1] - left erase term.buffer.y = 2; term.buffer.x = 70; oldInputHandler.eraseInLine([1]); expect(lineContent(term.buffer.lines.get(2))).equals(Array(71).join(' ') + ' aaaaaaaaa'); term.buffer.y = 3; term.buffer.x = 70; inputHandler.eraseInLine([1]); expect(lineContent(term.buffer.lines.get(3))).equals(Array(71).join(' ') + ' aaaaaaaaa'); // params[1] - left erase term.buffer.y = 4; term.buffer.x = 70; oldInputHandler.eraseInLine([2]); expect(lineContent(term.buffer.lines.get(4))).equals(Array(term.cols + 1).join(' ')); term.buffer.y = 5; term.buffer.x = 70; inputHandler.eraseInLine([2]); expect(lineContent(term.buffer.lines.get(5))).equals(Array(term.cols + 1).join(' ')); }); it('eraseInDisplay', function(): void { const termOld = new Terminal(); const inputHandlerOld = new OldInputHandler(termOld); const termNew = new Terminal(); const inputHandlerNew = new InputHandler(termNew); // fill display with a's for (let i = 0; i < termOld.rows; ++i) inputHandlerOld.parse(Array(termOld.cols + 1).join('a')); for (let i = 0; i < termNew.rows; ++i) inputHandlerNew.parse(Array(termOld.cols + 1).join('a')); const data = []; for (let i = 0; i < termOld.rows; ++i) data.push(Array(termOld.cols + 1).join('a')); expect(termContent(termOld)).eql(data); expect(termContent(termOld)).eql(termContent(termNew)); // params [0] - right and below erase termOld.buffer.y = 5; termOld.buffer.x = 40; inputHandlerOld.eraseInDisplay([0]); termNew.buffer.y = 5; termNew.buffer.x = 40; inputHandlerNew.eraseInDisplay([0]); expect(termContent(termNew)).eql(termContent(termOld)); // reset termOld.buffer.y = 0; termOld.buffer.x = 0; termNew.buffer.y = 0; termNew.buffer.x = 0; for (let i = 0; i < termOld.rows; ++i) inputHandlerOld.parse(Array(termOld.cols + 1).join('a')); for (let i = 0; i < termNew.rows; ++i) inputHandlerNew.parse(Array(termOld.cols + 1).join('a')); // params [1] - left and above termOld.buffer.y = 5; termOld.buffer.x = 40; inputHandlerOld.eraseInDisplay([1]); termNew.buffer.y = 5; termNew.buffer.x = 40; inputHandlerNew.eraseInDisplay([1]); expect(termContent(termNew)).eql(termContent(termOld)); // reset termOld.buffer.y = 0; termOld.buffer.x = 0; termNew.buffer.y = 0; termNew.buffer.x = 0; for (let i = 0; i < termOld.rows; ++i) inputHandlerOld.parse(Array(termOld.cols + 1).join('a')); for (let i = 0; i < termNew.rows; ++i) inputHandlerNew.parse(Array(termOld.cols + 1).join('a')); // params [2] - whole screen termOld.buffer.y = 5; termOld.buffer.x = 40; inputHandlerOld.eraseInDisplay([2]); termNew.buffer.y = 5; termNew.buffer.x = 40; inputHandlerNew.eraseInDisplay([2]); expect(termContent(termNew)).eql(termContent(termOld)); }); }); it('convertEol setting', function(): void { // not converting let s = ''; const termNotConverting = new Terminal({cols: 15, rows: 10}); (termNotConverting as any)._inputHandler.parse('Hello\nWorld'); for (let i = 0; i < termNotConverting.cols; ++i) { s += termNotConverting.buffer.lines.get(0).get(i)[CHAR_DATA_CHAR_INDEX]; } expect(s).equals('Hello '); s = ''; for (let i = 0; i < termNotConverting.cols; ++i) { s += termNotConverting.buffer.lines.get(1).get(i)[CHAR_DATA_CHAR_INDEX]; } expect(s).equals(' World '); // converting s = ''; const termConverting = new Terminal({cols: 15, rows: 10, convertEol: true}); (termConverting as any)._inputHandler.parse('Hello\nWorld'); for (let i = 0; i < termConverting.cols; ++i) { s += termConverting.buffer.lines.get(0).get(i)[CHAR_DATA_CHAR_INDEX]; } expect(s).equals('Hello '); s = ''; for (let i = 0; i < termConverting.cols; ++i) { s += termConverting.buffer.lines.get(1).get(i)[CHAR_DATA_CHAR_INDEX]; } expect(s).equals('World '); }); }); xterm.js-3.8.1/src/InputHandler.ts000066400000000000000000002072101341514612000170300ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) * @license MIT */ import { IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer, IInputHandlingTerminal } from './Types'; import { C0, C1 } from './common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Buffer'; import { FLAGS } from './renderer/Types'; import { wcwidth } from './CharWidth'; import { EscapeSequenceParser } from './EscapeSequenceParser'; import { ICharset } from './core/Types'; import { Disposable } from './common/Lifecycle'; import { BufferLine } from './BufferLine'; /** * Map collect to glevel. Used in `selectCharset`. */ const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2}; /** * DCS subparser implementations */ /** * DCS $ q Pt ST * DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html) * Request Status String (DECRQSS), VT420 and up. * Response: DECRPSS (https://vt100.net/docs/vt510-rm/DECRPSS.html) */ class DECRQSS implements IDcsHandler { private _data: string; constructor(private _terminal: any) { } hook(collect: string, params: number[], flag: number): void { // reset data this._data = ''; } put(data: string, start: number, end: number): void { this._data += data.substring(start, end); } unhook(): void { switch (this._data) { // valid: DCS 1 $ r Pt ST (xterm) case '"q': // DECSCA return this._terminal.handler(`${C0.ESC}P1$r0"q${C0.ESC}\\`); case '"p': // DECSCL return this._terminal.handler(`${C0.ESC}P1$r61"p${C0.ESC}\\`); case 'r': // DECSTBM const pt = '' + (this._terminal.buffer.scrollTop + 1) + ';' + (this._terminal.buffer.scrollBottom + 1) + 'r'; return this._terminal.handler(`${C0.ESC}P1$r${pt}${C0.ESC}\\`); case 'm': // SGR // TODO: report real settings instead of 0m return this._terminal.handler(`${C0.ESC}P1$r0m${C0.ESC}\\`); case ' q': // DECSCUSR const STYLES: {[key: string]: number} = {'block': 2, 'underline': 4, 'bar': 6}; let style = STYLES[this._terminal.getOption('cursorStyle')]; style -= this._terminal.getOption('cursorBlink'); return this._terminal.handler(`${C0.ESC}P1$r${style} q${C0.ESC}\\`); default: // invalid: DCS 0 $ r Pt ST (xterm) this._terminal.error('Unknown DCS $q %s', this._data); this._terminal.handler(`${C0.ESC}P0$r${C0.ESC}\\`); } } } /** * DCS Ps; Ps| Pt ST * DECUDK (https://vt100.net/docs/vt510-rm/DECUDK.html) * not supported */ /** * DCS + p Pt ST (xterm) * Set Terminfo Data * not supported */ /** * The terminal's standard implementation of IInputHandler, this handles all * input from the Parser. * * Refer to http://invisible-island.net/xterm/ctlseqs/ctlseqs.html to understand * each function's header comment. */ export class InputHandler extends Disposable implements IInputHandler { private _surrogateHigh: string; constructor( protected _terminal: IInputHandlingTerminal, private _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { super(); this.register(this._parser); this._surrogateHigh = ''; /** * custom fallback handlers */ this._parser.setCsiHandlerFallback((collect: string, params: number[], flag: number) => { this._terminal.error('Unknown CSI code: ', { collect, params, flag: String.fromCharCode(flag) }); }); this._parser.setEscHandlerFallback((collect: string, flag: number) => { this._terminal.error('Unknown ESC code: ', { collect, flag: String.fromCharCode(flag) }); }); this._parser.setExecuteHandlerFallback((code: number) => { this._terminal.error('Unknown EXECUTE code: ', { code }); }); this._parser.setOscHandlerFallback((identifier: number, data: string) => { this._terminal.error('Unknown OSC code: ', { identifier, data }); }); /** * print handler */ this._parser.setPrintHandler((data, start, end): void => this.print(data, start, end)); /** * CSI handler */ this._parser.setCsiHandler('@', (params, collect) => this.insertChars(params)); this._parser.setCsiHandler('A', (params, collect) => this.cursorUp(params)); this._parser.setCsiHandler('B', (params, collect) => this.cursorDown(params)); this._parser.setCsiHandler('C', (params, collect) => this.cursorForward(params)); this._parser.setCsiHandler('D', (params, collect) => this.cursorBackward(params)); this._parser.setCsiHandler('E', (params, collect) => this.cursorNextLine(params)); this._parser.setCsiHandler('F', (params, collect) => this.cursorPrecedingLine(params)); this._parser.setCsiHandler('G', (params, collect) => this.cursorCharAbsolute(params)); this._parser.setCsiHandler('H', (params, collect) => this.cursorPosition(params)); this._parser.setCsiHandler('I', (params, collect) => this.cursorForwardTab(params)); this._parser.setCsiHandler('J', (params, collect) => this.eraseInDisplay(params)); this._parser.setCsiHandler('K', (params, collect) => this.eraseInLine(params)); this._parser.setCsiHandler('L', (params, collect) => this.insertLines(params)); this._parser.setCsiHandler('M', (params, collect) => this.deleteLines(params)); this._parser.setCsiHandler('P', (params, collect) => this.deleteChars(params)); this._parser.setCsiHandler('S', (params, collect) => this.scrollUp(params)); this._parser.setCsiHandler('T', (params, collect) => this.scrollDown(params, collect)); this._parser.setCsiHandler('X', (params, collect) => this.eraseChars(params)); this._parser.setCsiHandler('Z', (params, collect) => this.cursorBackwardTab(params)); this._parser.setCsiHandler('`', (params, collect) => this.charPosAbsolute(params)); this._parser.setCsiHandler('a', (params, collect) => this.hPositionRelative(params)); this._parser.setCsiHandler('b', (params, collect) => this.repeatPrecedingCharacter(params)); this._parser.setCsiHandler('c', (params, collect) => this.sendDeviceAttributes(params, collect)); this._parser.setCsiHandler('d', (params, collect) => this.linePosAbsolute(params)); this._parser.setCsiHandler('e', (params, collect) => this.vPositionRelative(params)); this._parser.setCsiHandler('f', (params, collect) => this.hVPosition(params)); this._parser.setCsiHandler('g', (params, collect) => this.tabClear(params)); this._parser.setCsiHandler('h', (params, collect) => this.setMode(params, collect)); this._parser.setCsiHandler('l', (params, collect) => this.resetMode(params, collect)); this._parser.setCsiHandler('m', (params, collect) => this.charAttributes(params)); this._parser.setCsiHandler('n', (params, collect) => this.deviceStatus(params, collect)); this._parser.setCsiHandler('p', (params, collect) => this.softReset(params, collect)); this._parser.setCsiHandler('q', (params, collect) => this.setCursorStyle(params, collect)); this._parser.setCsiHandler('r', (params, collect) => this.setScrollRegion(params, collect)); this._parser.setCsiHandler('s', (params, collect) => this.saveCursor(params)); this._parser.setCsiHandler('u', (params, collect) => this.restoreCursor(params)); /** * execute handler */ this._parser.setExecuteHandler(C0.BEL, () => this.bell()); this._parser.setExecuteHandler(C0.LF, () => this.lineFeed()); this._parser.setExecuteHandler(C0.VT, () => this.lineFeed()); this._parser.setExecuteHandler(C0.FF, () => this.lineFeed()); this._parser.setExecuteHandler(C0.CR, () => this.carriageReturn()); this._parser.setExecuteHandler(C0.BS, () => this.backspace()); this._parser.setExecuteHandler(C0.HT, () => this.tab()); this._parser.setExecuteHandler(C0.SO, () => this.shiftOut()); this._parser.setExecuteHandler(C0.SI, () => this.shiftIn()); // FIXME: What do to with missing? Old code just added those to print. // some C1 control codes - FIXME: should those be enabled by default? this._parser.setExecuteHandler(C1.IND, () => this.index()); this._parser.setExecuteHandler(C1.NEL, () => this.nextLine()); this._parser.setExecuteHandler(C1.HTS, () => this.tabSet()); /** * OSC handler */ // 0 - icon name + title this._parser.setOscHandler(0, (data) => this.setTitle(data)); // 1 - icon name // 2 - title this._parser.setOscHandler(2, (data) => this.setTitle(data)); // 3 - set property X in the form "prop=value" // 4 - Change Color Number // 5 - Change Special Color Number // 6 - Enable/disable Special Color Number c // 7 - current directory? (not in xterm spec, see https://gitlab.com/gnachman/iterm2/issues/3939) // 10 - Change VT100 text foreground color to Pt. // 11 - Change VT100 text background color to Pt. // 12 - Change text cursor color to Pt. // 13 - Change mouse foreground color to Pt. // 14 - Change mouse background color to Pt. // 15 - Change Tektronix foreground color to Pt. // 16 - Change Tektronix background color to Pt. // 17 - Change highlight background color to Pt. // 18 - Change Tektronix cursor color to Pt. // 19 - Change highlight foreground color to Pt. // 46 - Change Log File to Pt. // 50 - Set Font to Pt. // 51 - reserved for Emacs shell. // 52 - Manipulate Selection Data. // 104 ; c - Reset Color Number c. // 105 ; c - Reset Special Color Number c. // 106 ; c; f - Enable/disable Special Color Number c. // 110 - Reset VT100 text foreground color. // 111 - Reset VT100 text background color. // 112 - Reset text cursor color. // 113 - Reset mouse foreground color. // 114 - Reset mouse background color. // 115 - Reset Tektronix foreground color. // 116 - Reset Tektronix background color. // 117 - Reset highlight color. // 118 - Reset Tektronix cursor color. // 119 - Reset highlight foreground color. /** * ESC handlers */ this._parser.setEscHandler('7', () => this.saveCursor([])); this._parser.setEscHandler('8', () => this.restoreCursor([])); this._parser.setEscHandler('D', () => this.index()); this._parser.setEscHandler('E', () => this.nextLine()); this._parser.setEscHandler('H', () => this.tabSet()); this._parser.setEscHandler('M', () => this.reverseIndex()); this._parser.setEscHandler('=', () => this.keypadApplicationMode()); this._parser.setEscHandler('>', () => this.keypadNumericMode()); this._parser.setEscHandler('c', () => this.reset()); this._parser.setEscHandler('n', () => this.setgLevel(2)); this._parser.setEscHandler('o', () => this.setgLevel(3)); this._parser.setEscHandler('|', () => this.setgLevel(3)); this._parser.setEscHandler('}', () => this.setgLevel(2)); this._parser.setEscHandler('~', () => this.setgLevel(1)); this._parser.setEscHandler('%@', () => this.selectDefaultCharset()); this._parser.setEscHandler('%G', () => this.selectDefaultCharset()); for (const flag in CHARSETS) { this._parser.setEscHandler('(' + flag, () => this.selectCharset('(' + flag)); this._parser.setEscHandler(')' + flag, () => this.selectCharset(')' + flag)); this._parser.setEscHandler('*' + flag, () => this.selectCharset('*' + flag)); this._parser.setEscHandler('+' + flag, () => this.selectCharset('+' + flag)); this._parser.setEscHandler('-' + flag, () => this.selectCharset('-' + flag)); this._parser.setEscHandler('.' + flag, () => this.selectCharset('.' + flag)); this._parser.setEscHandler('/' + flag, () => this.selectCharset('/' + flag)); // TODO: supported? } /** * error handler */ this._parser.setErrorHandler((state) => { this._terminal.error('Parsing error: ', state); return state; }); /** * DCS handler */ this._parser.setDcsHandler('$q', new DECRQSS(this._terminal)); } public dispose(): void { super.dispose(); this._terminal = null; } public parse(data: string): void { // Ensure the terminal is not disposed if (!this._terminal) { return; } let buffer = this._terminal.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; // TODO: Consolidate debug/logging #1560 if ((this._terminal).debug) { this._terminal.log('data: ' + data); } // apply leftover surrogate high from last write if (this._surrogateHigh) { data = this._surrogateHigh + data; this._surrogateHigh = ''; } this._parser.parse(data); buffer = this._terminal.buffer; if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) { this._terminal.emit('cursormove'); } } public print(data: string, start: number, end: number): void { let char: string; let code: number; let low: number; let chWidth: number; const buffer: IBuffer = this._terminal.buffer; const charset: ICharset = this._terminal.charset; const screenReaderMode: boolean = this._terminal.options.screenReaderMode; const cols: number = this._terminal.cols; const wraparoundMode: boolean = this._terminal.wraparoundMode; const insertMode: boolean = this._terminal.insertMode; const curAttr: number = this._terminal.curAttr; let bufferRow = buffer.lines.get(buffer.y + buffer.ybase); this._terminal.updateRange(buffer.y); for (let stringPosition = start; stringPosition < end; ++stringPosition) { char = data.charAt(stringPosition); code = data.charCodeAt(stringPosition); // surrogate pair handling if (0xD800 <= code && code <= 0xDBFF) { // we got a surrogate high // get surrogate low (next 2 bytes) low = data.charCodeAt(stringPosition + 1); if (isNaN(low)) { // end of data stream, save surrogate high this._surrogateHigh = char; continue; } code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; char += data.charAt(stringPosition + 1); } // surrogate low - already handled above if (0xDC00 <= code && code <= 0xDFFF) { continue; } // calculate print space // expensive call, therefore we save width in line buffer chWidth = wcwidth(code); // get charset replacement character if (charset) { char = charset[char] || char; code = char.charCodeAt(0); } if (screenReaderMode) { this._terminal.emit('a11y.char', char); } // insert combining char at last cursor position // FIXME: needs handling after cursor jumps // buffer.x should never be 0 for a combining char // since they always follow a cell consuming char // therefore we can test for buffer.x to avoid overflow left if (!chWidth && buffer.x) { const chMinusOne = bufferRow.get(buffer.x - 1); if (chMinusOne) { if (!chMinusOne[CHAR_DATA_WIDTH_INDEX]) { // found empty cell after fullwidth, need to go 2 cells back // it is save to step 2 cells back here // since an empty cell is only set by fullwidth chars const chMinusTwo = bufferRow.get(buffer.x - 2); if (chMinusTwo) { chMinusTwo[CHAR_DATA_CHAR_INDEX] += char; chMinusTwo[CHAR_DATA_CODE_INDEX] = code; } } else { chMinusOne[CHAR_DATA_CHAR_INDEX] += char; chMinusOne[CHAR_DATA_CODE_INDEX] = code; } } continue; } // goto next line if ch would overflow // TODO: needs a global min terminal width of 2 if (buffer.x + chWidth - 1 >= cols) { // autowrap - DECAWM // automatically wraps to the beginning of the next line if (wraparoundMode) { buffer.x = 0; buffer.y++; if (buffer.y > buffer.scrollBottom) { buffer.y--; this._terminal.scroll(true); } else { // The line already exists (eg. the initial viewport), mark it as a // wrapped line buffer.lines.get(buffer.y).isWrapped = true; } // row changed, get it again bufferRow = buffer.lines.get(buffer.y + buffer.ybase); } else { if (chWidth === 2) { // FIXME: check for xterm behavior // What to do here? We got a wide char that does not fit into last cell continue; } // FIXME: Do we have to set buffer.x to cols - 1, if not wrapping? } } // insert mode: move characters to right // To achieve insert, we remove cells from the right // and insert empty ones at cursor position if (insertMode) { // do this twice for a fullwidth char for (let moves = 0; moves < chWidth; ++moves) { // remove last cell // if it's width is 0, we have to adjust the second last cell as well const removed = bufferRow.pop(); const chMinusTwo = bufferRow.get(buffer.x - 2); if (removed[CHAR_DATA_WIDTH_INDEX] === 0 && chMinusTwo && chMinusTwo[CHAR_DATA_WIDTH_INDEX] === 2) { bufferRow.set(this._terminal.cols - 2, [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); } // insert empty cell at cursor bufferRow.splice(buffer.x, 0, [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); } } // write current char to buffer and advance cursor bufferRow.set(buffer.x++, [curAttr, char, chWidth, code]); // fullwidth char - also set next cell to placeholder stub and advance cursor if (chWidth === 2) { bufferRow.set(buffer.x++, [curAttr, '', 0, undefined]); } } this._terminal.updateRange(buffer.y); } /** * BEL * Bell (Ctrl-G). */ public bell(): void { this._terminal.bell(); } /** * LF * Line Feed or New Line (NL). (LF is Ctrl-J). */ public lineFeed(): void { // make buffer local for faster access const buffer = this._terminal.buffer; if (this._terminal.options.convertEol) { buffer.x = 0; } buffer.y++; if (buffer.y > buffer.scrollBottom) { buffer.y--; this._terminal.scroll(); } // If the end of the line is hit, prevent this action from wrapping around to the next line. if (buffer.x >= this._terminal.cols) { buffer.x--; } /** * This event is emitted whenever the terminal outputs a LF or NL. * * @event linefeed */ this._terminal.emit('linefeed'); } /** * CR * Carriage Return (Ctrl-M). */ public carriageReturn(): void { this._terminal.buffer.x = 0; } /** * BS * Backspace (Ctrl-H). */ public backspace(): void { if (this._terminal.buffer.x > 0) { this._terminal.buffer.x--; } } /** * TAB * Horizontal Tab (HT) (Ctrl-I). */ public tab(): void { const originalX = this._terminal.buffer.x; this._terminal.buffer.x = this._terminal.buffer.nextStop(); if (this._terminal.options.screenReaderMode) { this._terminal.emit('a11y.tab', this._terminal.buffer.x - originalX); } } /** * SO * Shift Out (Ctrl-N) -> Switch to Alternate Character Set. This invokes the * G1 character set. */ public shiftOut(): void { this._terminal.setgLevel(1); } /** * SI * Shift In (Ctrl-O) -> Switch to Standard Character Set. This invokes the G0 * character set (the default). */ public shiftIn(): void { this._terminal.setgLevel(0); } /** * CSI Ps @ * Insert Ps (Blank) Character(s) (default = 1) (ICH). */ public insertChars(params: number[]): void { this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).insertCells( this._terminal.buffer.x, params[0] || 1, [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE] ); this._terminal.updateRange(this._terminal.buffer.y); } /** * CSI Ps A * Cursor Up Ps Times (default = 1) (CUU). */ public cursorUp(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.y -= param; if (this._terminal.buffer.y < 0) { this._terminal.buffer.y = 0; } } /** * CSI Ps B * Cursor Down Ps Times (default = 1) (CUD). */ public cursorDown(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.y += param; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; } // If the end of the line is hit, prevent this action from wrapping around to the next line. if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x--; } } /** * CSI Ps C * Cursor Forward Ps Times (default = 1) (CUF). */ public cursorForward(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.x += param; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; } } /** * CSI Ps D * Cursor Backward Ps Times (default = 1) (CUB). */ public cursorBackward(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } // If the end of the line is hit, prevent this action from wrapping around to the next line. if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x--; } this._terminal.buffer.x -= param; if (this._terminal.buffer.x < 0) { this._terminal.buffer.x = 0; } } /** * CSI Ps E * Cursor Next Line Ps Times (default = 1) (CNL). * same as CSI Ps B ? */ public cursorNextLine(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.y += param; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; } this._terminal.buffer.x = 0; } /** * CSI Ps F * Cursor Preceding Line Ps Times (default = 1) (CNL). * reuse CSI Ps A ? */ public cursorPrecedingLine(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.y -= param; if (this._terminal.buffer.y < 0) { this._terminal.buffer.y = 0; } this._terminal.buffer.x = 0; } /** * CSI Ps G * Cursor Character Absolute [column] (default = [row,1]) (CHA). */ public cursorCharAbsolute(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.x = param - 1; } /** * CSI Ps ; Ps H * Cursor Position [row;column] (default = [1,1]) (CUP). */ public cursorPosition(params: number[]): void { let col: number; let row: number = params[0] - 1; if (params.length >= 2) { col = params[1] - 1; } else { col = 0; } if (row < 0) { row = 0; } else if (row >= this._terminal.rows) { row = this._terminal.rows - 1; } if (col < 0) { col = 0; } else if (col >= this._terminal.cols) { col = this._terminal.cols - 1; } this._terminal.buffer.x = col; this._terminal.buffer.y = row; } /** * CSI Ps I * Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). */ public cursorForwardTab(params: number[]): void { let param = params[0] || 1; while (param--) { this._terminal.buffer.x = this._terminal.buffer.nextStop(); } } /** * Helper method to erase cells in a terminal row. * The cell gets replaced with the eraseChar of the terminal. * @param y row index * @param start first cell index to be erased * @param end end - 1 is last erased cell */ private _eraseInBufferLine(y: number, start: number, end: number): void { this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y).replaceCells( start, end, [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE] ); } /** * CSI Ps J Erase in Display (ED). * Ps = 0 -> Erase Below (default). * Ps = 1 -> Erase Above. * Ps = 2 -> Erase All. * Ps = 3 -> Erase Saved Lines (xterm). * CSI ? Ps J * Erase in Display (DECSED). * Ps = 0 -> Selective Erase Below (default). * Ps = 1 -> Selective Erase Above. * Ps = 2 -> Selective Erase All. */ public eraseInDisplay(params: number[]): void { let j; switch (params[0]) { case 0: j = this._terminal.buffer.y; this._terminal.updateRange(j); this._eraseInBufferLine(j++, this._terminal.buffer.x, this._terminal.cols); for (; j < this._terminal.rows; j++) { this._eraseInBufferLine(j, 0, this._terminal.cols); } this._terminal.updateRange(j); break; case 1: j = this._terminal.buffer.y; this._terminal.updateRange(j); this._eraseInBufferLine(j, 0, this._terminal.buffer.x + 1); while (j--) { this._eraseInBufferLine(j, 0, this._terminal.cols); } this._terminal.updateRange(0); break; case 2: j = this._terminal.rows; this._terminal.updateRange(j - 1); while (j--) { this._eraseInBufferLine(j, 0, this._terminal.cols); } this._terminal.updateRange(0); break; case 3: // Clear scrollback (everything not in viewport) const scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows; if (scrollBackSize > 0) { this._terminal.buffer.lines.trimStart(scrollBackSize); this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0); this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0); // Force a scroll event to refresh viewport this._terminal.emit('scroll', 0); } break; } } /** * CSI Ps K Erase in Line (EL). * Ps = 0 -> Erase to Right (default). * Ps = 1 -> Erase to Left. * Ps = 2 -> Erase All. * CSI ? Ps K * Erase in Line (DECSEL). * Ps = 0 -> Selective Erase to Right (default). * Ps = 1 -> Selective Erase to Left. * Ps = 2 -> Selective Erase All. */ public eraseInLine(params: number[]): void { switch (params[0]) { case 0: this._eraseInBufferLine(this._terminal.buffer.y, this._terminal.buffer.x, this._terminal.cols); break; case 1: this._eraseInBufferLine(this._terminal.buffer.y, 0, this._terminal.buffer.x + 1); break; case 2: this._eraseInBufferLine(this._terminal.buffer.y, 0, this._terminal.cols); break; } this._terminal.updateRange(this._terminal.buffer.y); } /** * CSI Ps L * Insert Ps Line(s) (default = 1) (IL). */ public insertLines(params: number[]): void { let param: number = params[0]; if (param < 1) { param = 1; } // make buffer local for faster access const buffer = this._terminal.buffer; const row: number = buffer.y + buffer.ybase; const scrollBottomRowsOffset = this._terminal.rows - 1 - buffer.scrollBottom; const scrollBottomAbsolute = this._terminal.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1; while (param--) { // test: echo -e '\e[44m\e[1L\e[0m' // blankLine(true) - xterm/linux behavior buffer.lines.splice(scrollBottomAbsolute - 1, 1); buffer.lines.splice(row, 0, BufferLine.blankLine(this._terminal.cols, this._terminal.eraseAttr())); } // this.maxRange(); this._terminal.updateRange(buffer.y); this._terminal.updateRange(buffer.scrollBottom); } /** * CSI Ps M * Delete Ps Line(s) (default = 1) (DL). */ public deleteLines(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } // make buffer local for faster access const buffer = this._terminal.buffer; const row: number = buffer.y + buffer.ybase; let j: number; j = this._terminal.rows - 1 - buffer.scrollBottom; j = this._terminal.rows - 1 + buffer.ybase - j; while (param--) { // test: echo -e '\e[44m\e[1M\e[0m' // blankLine(true) - xterm/linux behavior buffer.lines.splice(row, 1); buffer.lines.splice(j, 0, BufferLine.blankLine(this._terminal.cols, this._terminal.eraseAttr())); } // this.maxRange(); this._terminal.updateRange(buffer.y); this._terminal.updateRange(buffer.scrollBottom); } /** * CSI Ps P * Delete Ps Character(s) (default = 1) (DCH). */ public deleteChars(params: number[]): void { this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).deleteCells( this._terminal.buffer.x, params[0] || 1, [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE] ); this._terminal.updateRange(this._terminal.buffer.y); } /** * CSI Ps S Scroll up Ps lines (default = 1) (SU). */ public scrollUp(params: number[]): void { let param = params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; while (param--) { buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1); buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, BufferLine.blankLine(this._terminal.cols, DEFAULT_ATTR)); } // this.maxRange(); this._terminal.updateRange(buffer.scrollTop); this._terminal.updateRange(buffer.scrollBottom); } /** * CSI Ps T Scroll down Ps lines (default = 1) (SD). */ public scrollDown(params: number[], collect?: string): void { if (params.length < 2 && !collect) { let param = params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; while (param--) { buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1); buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, BufferLine.blankLine(this._terminal.cols, DEFAULT_ATTR)); } // this.maxRange(); this._terminal.updateRange(buffer.scrollTop); this._terminal.updateRange(buffer.scrollBottom); } } /** * CSI Ps X * Erase Ps Character(s) (default = 1) (ECH). */ public eraseChars(params: number[]): void { this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).replaceCells( this._terminal.buffer.x, this._terminal.buffer.x + (params[0] || 1), [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE] ); } /** * CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT). */ public cursorBackwardTab(params: number[]): void { let param = params[0] || 1; // make buffer local for faster access const buffer = this._terminal.buffer; while (param--) { buffer.x = buffer.prevStop(); } } /** * CSI Pm ` Character Position Absolute * [column] (default = [row,1]) (HPA). */ public charPosAbsolute(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.x = param - 1; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; } } /** * CSI Pm a Character Position Relative * [columns] (default = [row,col+1]) (HPR) * reuse CSI Ps C ? */ public hPositionRelative(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.x += param; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; } } /** * CSI Ps b Repeat the preceding graphic character Ps times (REP). */ public repeatPrecedingCharacter(params: number[]): void { // make buffer local for faster access const buffer = this._terminal.buffer; const line = buffer.lines.get(buffer.ybase + buffer.y); line.replaceCells(buffer.x, buffer.x + (params[0] || 1), line.get(buffer.x - 1) || [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE] ); // FIXME: no updateRange here? } /** * CSI Ps c Send Device Attributes (Primary DA). * Ps = 0 or omitted -> request attributes from terminal. The * response depends on the decTerminalID resource setting. * -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'') * -> CSI ? 1 ; 0 c (``VT101 with No Options'') * -> CSI ? 6 c (``VT102'') * -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'') * The VT100-style response parameters do not mean anything by * themselves. VT220 parameters do, telling the host what fea- * tures the terminal supports: * Ps = 1 -> 132-columns. * Ps = 2 -> Printer. * Ps = 6 -> Selective erase. * Ps = 8 -> User-defined keys. * Ps = 9 -> National replacement character sets. * Ps = 1 5 -> Technical characters. * Ps = 2 2 -> ANSI color, e.g., VT525. * Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode). * CSI > Ps c * Send Device Attributes (Secondary DA). * Ps = 0 or omitted -> request the terminal's identification * code. The response depends on the decTerminalID resource set- * ting. It should apply only to VT220 and up, but xterm extends * this to VT100. * -> CSI > Pp ; Pv ; Pc c * where Pp denotes the terminal type * Pp = 0 -> ``VT100''. * Pp = 1 -> ``VT220''. * and Pv is the firmware version (for xterm, this was originally * the XFree86 patch number, starting with 95). In a DEC termi- * nal, Pc indicates the ROM cartridge registration number and is * always zero. * More information: * xterm/charproc.c - line 2012, for more information. * vim responds with ^[[?0c or ^[[?1c after the terminal's response (?) */ public sendDeviceAttributes(params: number[], collect?: string): void { if (params[0] > 0) { return; } if (!collect) { if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) { this._terminal.handler(C0.ESC + '[?1;2c'); } else if (this._terminal.is('linux')) { this._terminal.handler(C0.ESC + '[?6c'); } } else if (collect === '>') { // xterm and urxvt // seem to spit this // out around ~370 times (?). if (this._terminal.is('xterm')) { this._terminal.handler(C0.ESC + '[>0;276;0c'); } else if (this._terminal.is('rxvt-unicode')) { this._terminal.handler(C0.ESC + '[>85;95;0c'); } else if (this._terminal.is('linux')) { // not supported by linux console. // linux console echoes parameters. this._terminal.handler(params[0] + 'c'); } else if (this._terminal.is('screen')) { this._terminal.handler(C0.ESC + '[>83;40003;0c'); } } } /** * CSI Pm d Vertical Position Absolute (VPA) * [row] (default = [1,column]) */ public linePosAbsolute(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.y = param - 1; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; } } /** * CSI Pm e Vertical Position Relative (VPR) * [rows] (default = [row+1,column]) * reuse CSI Ps B ? */ public vPositionRelative(params: number[]): void { let param = params[0]; if (param < 1) { param = 1; } this._terminal.buffer.y += param; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; } // If the end of the line is hit, prevent this action from wrapping around to the next line. if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x--; } } /** * CSI Ps ; Ps f * Horizontal and Vertical Position [row;column] (default = * [1,1]) (HVP). */ public hVPosition(params: number[]): void { if (params[0] < 1) params[0] = 1; if (params[1] < 1) params[1] = 1; this._terminal.buffer.y = params[0] - 1; if (this._terminal.buffer.y >= this._terminal.rows) { this._terminal.buffer.y = this._terminal.rows - 1; } this._terminal.buffer.x = params[1] - 1; if (this._terminal.buffer.x >= this._terminal.cols) { this._terminal.buffer.x = this._terminal.cols - 1; } } /** * CSI Ps g Tab Clear (TBC). * Ps = 0 -> Clear Current Column (default). * Ps = 3 -> Clear All. * Potentially: * Ps = 2 -> Clear Stops on Line. * http://vt100.net/annarbor/aaa-ug/section6.html */ public tabClear(params: number[]): void { const param = params[0]; if (param <= 0) { delete this._terminal.buffer.tabs[this._terminal.buffer.x]; } else if (param === 3) { this._terminal.buffer.tabs = {}; } } /** * CSI Pm h Set Mode (SM). * Ps = 2 -> Keyboard Action Mode (AM). * Ps = 4 -> Insert Mode (IRM). * Ps = 1 2 -> Send/receive (SRM). * Ps = 2 0 -> Automatic Newline (LNM). * CSI ? Pm h * DEC Private Mode Set (DECSET). * Ps = 1 -> Application Cursor Keys (DECCKM). * Ps = 2 -> Designate USASCII for character sets G0-G3 * (DECANM), and set VT100 mode. * Ps = 3 -> 132 Column Mode (DECCOLM). * Ps = 4 -> Smooth (Slow) Scroll (DECSCLM). * Ps = 5 -> Reverse Video (DECSCNM). * Ps = 6 -> Origin Mode (DECOM). * Ps = 7 -> Wraparound Mode (DECAWM). * Ps = 8 -> Auto-repeat Keys (DECARM). * Ps = 9 -> Send Mouse X & Y on button press. See the sec- * tion Mouse Tracking. * Ps = 1 0 -> Show toolbar (rxvt). * Ps = 1 2 -> Start Blinking Cursor (att610). * Ps = 1 8 -> Print form feed (DECPFF). * Ps = 1 9 -> Set print extent to full screen (DECPEX). * Ps = 2 5 -> Show Cursor (DECTCEM). * Ps = 3 0 -> Show scrollbar (rxvt). * Ps = 3 5 -> Enable font-shifting functions (rxvt). * Ps = 3 8 -> Enter Tektronix Mode (DECTEK). * Ps = 4 0 -> Allow 80 -> 132 Mode. * Ps = 4 1 -> more(1) fix (see curses resource). * Ps = 4 2 -> Enable Nation Replacement Character sets (DECN- * RCM). * Ps = 4 4 -> Turn On Margin Bell. * Ps = 4 5 -> Reverse-wraparound Mode. * Ps = 4 6 -> Start Logging. This is normally disabled by a * compile-time option. * Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis- * abled by the titeInhibit resource). * Ps = 6 6 -> Application keypad (DECNKM). * Ps = 6 7 -> Backarrow key sends backspace (DECBKM). * Ps = 1 0 0 0 -> Send Mouse X & Y on button press and * release. See the section Mouse Tracking. * Ps = 1 0 0 1 -> Use Hilite Mouse Tracking. * Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking. * Ps = 1 0 0 3 -> Use All Motion Mouse Tracking. * Ps = 1 0 0 4 -> Send FocusIn/FocusOut events. * Ps = 1 0 0 5 -> Enable Extended Mouse Mode. * Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt). * Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt). * Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit. * (enables the eightBitInput resource). * Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num- * Lock keys. (This enables the numLock resource). * Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This * enables the metaSendsEscape resource). * Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete * key. * Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This * enables the altSendsEscape resource). * Ps = 1 0 4 0 -> Keep selection even if not highlighted. * (This enables the keepSelection resource). * Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables * the selectToClipboard resource). * Ps = 1 0 4 2 -> Enable Urgency window manager hint when * Control-G is received. (This enables the bellIsUrgent * resource). * Ps = 1 0 4 3 -> Enable raising of the window when Control-G * is received. (enables the popOnBell resource). * Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be * disabled by the titeInhibit resource). * Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis- * abled by the titeInhibit resource). * Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate * Screen Buffer, clearing it first. (This may be disabled by * the titeInhibit resource). This combines the effects of the 1 * 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based * applications rather than the 4 7 mode. * Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode. * Ps = 1 0 5 1 -> Set Sun function-key mode. * Ps = 1 0 5 2 -> Set HP function-key mode. * Ps = 1 0 5 3 -> Set SCO function-key mode. * Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6). * Ps = 1 0 6 1 -> Set VT220 keyboard emulation. * Ps = 2 0 0 4 -> Set bracketed paste mode. * Modes: * http: *vt100.net/docs/vt220-rm/chapter4.html */ public setMode(params: number[], collect?: string): void { if (params.length > 1) { for (let i = 0; i < params.length; i++) { this.setMode([params[i]]); } return; } if (!collect) { switch (params[0]) { case 4: this._terminal.insertMode = true; break; case 20: // this._t.convertEol = true; break; } } else if (collect === '?') { switch (params[0]) { case 1: this._terminal.applicationCursor = true; break; case 2: this._terminal.setgCharset(0, DEFAULT_CHARSET); this._terminal.setgCharset(1, DEFAULT_CHARSET); this._terminal.setgCharset(2, DEFAULT_CHARSET); this._terminal.setgCharset(3, DEFAULT_CHARSET); // set VT100 mode here break; case 3: // 132 col mode this._terminal.savedCols = this._terminal.cols; this._terminal.resize(132, this._terminal.rows); break; case 6: this._terminal.originMode = true; break; case 7: this._terminal.wraparoundMode = true; break; case 12: // this.cursorBlink = true; break; case 66: this._terminal.log('Serial port requested application keypad.'); this._terminal.applicationKeypad = true; this._terminal.viewport.syncScrollArea(); break; case 9: // X10 Mouse // no release, no motion, no wheel, no modifiers. case 1000: // vt200 mouse // no motion. // no modifiers, except control on the wheel. case 1002: // button event mouse case 1003: // any event mouse // any event - sends motion events, // even if there is no button held down. // TODO: Why are params[0] compares nested within a switch for params[0]? this._terminal.x10Mouse = params[0] === 9; this._terminal.vt200Mouse = params[0] === 1000; this._terminal.normalMouse = params[0] > 1000; this._terminal.mouseEvents = true; this._terminal.element.classList.add('enable-mouse-events'); this._terminal.selectionManager.disable(); this._terminal.log('Binding to mouse events.'); break; case 1004: // send focusin/focusout events // focusin: ^[[I // focusout: ^[[O this._terminal.sendFocus = true; break; case 1005: // utf8 ext mode mouse this._terminal.utfMouse = true; // for wide terminals // simply encodes large values as utf8 characters break; case 1006: // sgr ext mode mouse this._terminal.sgrMouse = true; // for wide terminals // does not add 32 to fields // press: ^[[ Keyboard Action Mode (AM). * Ps = 4 -> Replace Mode (IRM). * Ps = 1 2 -> Send/receive (SRM). * Ps = 2 0 -> Normal Linefeed (LNM). * CSI ? Pm l * DEC Private Mode Reset (DECRST). * Ps = 1 -> Normal Cursor Keys (DECCKM). * Ps = 2 -> Designate VT52 mode (DECANM). * Ps = 3 -> 80 Column Mode (DECCOLM). * Ps = 4 -> Jump (Fast) Scroll (DECSCLM). * Ps = 5 -> Normal Video (DECSCNM). * Ps = 6 -> Normal Cursor Mode (DECOM). * Ps = 7 -> No Wraparound Mode (DECAWM). * Ps = 8 -> No Auto-repeat Keys (DECARM). * Ps = 9 -> Don't send Mouse X & Y on button press. * Ps = 1 0 -> Hide toolbar (rxvt). * Ps = 1 2 -> Stop Blinking Cursor (att610). * Ps = 1 8 -> Don't print form feed (DECPFF). * Ps = 1 9 -> Limit print to scrolling region (DECPEX). * Ps = 2 5 -> Hide Cursor (DECTCEM). * Ps = 3 0 -> Don't show scrollbar (rxvt). * Ps = 3 5 -> Disable font-shifting functions (rxvt). * Ps = 4 0 -> Disallow 80 -> 132 Mode. * Ps = 4 1 -> No more(1) fix (see curses resource). * Ps = 4 2 -> Disable Nation Replacement Character sets (DEC- * NRCM). * Ps = 4 4 -> Turn Off Margin Bell. * Ps = 4 5 -> No Reverse-wraparound Mode. * Ps = 4 6 -> Stop Logging. (This is normally disabled by a * compile-time option). * Ps = 4 7 -> Use Normal Screen Buffer. * Ps = 6 6 -> Numeric keypad (DECNKM). * Ps = 6 7 -> Backarrow key sends delete (DECBKM). * Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and * release. See the section Mouse Tracking. * Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking. * Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking. * Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking. * Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events. * Ps = 1 0 0 5 -> Disable Extended Mouse Mode. * Ps = 1 0 1 0 -> Don't scroll to bottom on tty output * (rxvt). * Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt). * Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables * the eightBitInput resource). * Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num- * Lock keys. (This disables the numLock resource). * Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key. * (This disables the metaSendsEscape resource). * Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad * Delete key. * Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key. * (This disables the altSendsEscape resource). * Ps = 1 0 4 0 -> Do not keep selection when not highlighted. * (This disables the keepSelection resource). * Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables * the selectToClipboard resource). * Ps = 1 0 4 2 -> Disable Urgency window manager hint when * Control-G is received. (This disables the bellIsUrgent * resource). * Ps = 1 0 4 3 -> Disable raising of the window when Control- * G is received. (This disables the popOnBell resource). * Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen * first if in the Alternate Screen. (This may be disabled by * the titeInhibit resource). * Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be * disabled by the titeInhibit resource). * Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor * as in DECRC. (This may be disabled by the titeInhibit * resource). This combines the effects of the 1 0 4 7 and 1 0 * 4 8 modes. Use this with terminfo-based applications rather * than the 4 7 mode. * Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode. * Ps = 1 0 5 1 -> Reset Sun function-key mode. * Ps = 1 0 5 2 -> Reset HP function-key mode. * Ps = 1 0 5 3 -> Reset SCO function-key mode. * Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6). * Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style. * Ps = 2 0 0 4 -> Reset bracketed paste mode. */ public resetMode(params: number[], collect?: string): void { if (params.length > 1) { for (let i = 0; i < params.length; i++) { this.resetMode([params[i]]); } return; } if (!collect) { switch (params[0]) { case 4: this._terminal.insertMode = false; break; case 20: // this._t.convertEol = false; break; } } else if (collect === '?') { switch (params[0]) { case 1: this._terminal.applicationCursor = false; break; case 3: if (this._terminal.cols === 132 && this._terminal.savedCols) { this._terminal.resize(this._terminal.savedCols, this._terminal.rows); } delete this._terminal.savedCols; break; case 6: this._terminal.originMode = false; break; case 7: this._terminal.wraparoundMode = false; break; case 12: // this.cursorBlink = false; break; case 66: this._terminal.log('Switching back to normal keypad.'); this._terminal.applicationKeypad = false; this._terminal.viewport.syncScrollArea(); break; case 9: // X10 Mouse case 1000: // vt200 mouse case 1002: // button event mouse case 1003: // any event mouse this._terminal.x10Mouse = false; this._terminal.vt200Mouse = false; this._terminal.normalMouse = false; this._terminal.mouseEvents = false; this._terminal.element.classList.remove('enable-mouse-events'); this._terminal.selectionManager.enable(); break; case 1004: // send focusin/focusout events this._terminal.sendFocus = false; break; case 1005: // utf8 ext mode mouse this._terminal.utfMouse = false; break; case 1006: // sgr ext mode mouse this._terminal.sgrMouse = false; break; case 1015: // urxvt ext mode mouse this._terminal.urxvtMouse = false; break; case 25: // hide cursor this._terminal.cursorHidden = true; break; case 1049: // alt screen buffer cursor // FALL-THROUGH case 47: // normal screen buffer case 1047: // normal screen buffer - clearing it first // Ensure the selection manager has the correct buffer this._terminal.buffers.activateNormalBuffer(); // TODO: Not sure if we need to save/restore after switching the buffer // if (params[0] === 1049) { // this.restoreCursor(params); // } this._terminal.refresh(0, this._terminal.rows - 1); this._terminal.viewport.syncScrollArea(); this._terminal.showCursor(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) this._terminal.bracketedPasteMode = false; break; } } } /** * CSI Pm m Character Attributes (SGR). * Ps = 0 -> Normal (default). * Ps = 1 -> Bold. * Ps = 2 -> Faint, decreased intensity (ISO 6429). * Ps = 4 -> Underlined. * Ps = 5 -> Blink (appears as Bold). * Ps = 7 -> Inverse. * Ps = 8 -> Invisible, i.e., hidden (VT300). * Ps = 2 2 -> Normal (neither bold nor faint). * Ps = 2 4 -> Not underlined. * Ps = 2 5 -> Steady (not blinking). * Ps = 2 7 -> Positive (not inverse). * Ps = 2 8 -> Visible, i.e., not hidden (VT300). * Ps = 3 0 -> Set foreground color to Black. * Ps = 3 1 -> Set foreground color to Red. * Ps = 3 2 -> Set foreground color to Green. * Ps = 3 3 -> Set foreground color to Yellow. * Ps = 3 4 -> Set foreground color to Blue. * Ps = 3 5 -> Set foreground color to Magenta. * Ps = 3 6 -> Set foreground color to Cyan. * Ps = 3 7 -> Set foreground color to White. * Ps = 3 9 -> Set foreground color to default (original). * Ps = 4 0 -> Set background color to Black. * Ps = 4 1 -> Set background color to Red. * Ps = 4 2 -> Set background color to Green. * Ps = 4 3 -> Set background color to Yellow. * Ps = 4 4 -> Set background color to Blue. * Ps = 4 5 -> Set background color to Magenta. * Ps = 4 6 -> Set background color to Cyan. * Ps = 4 7 -> Set background color to White. * Ps = 4 9 -> Set background color to default (original). * * If 16-color support is compiled, the following apply. Assume * that xterm's resources are set so that the ISO color codes are * the first 8 of a set of 16. Then the aixterm colors are the * bright versions of the ISO colors: * Ps = 9 0 -> Set foreground color to Black. * Ps = 9 1 -> Set foreground color to Red. * Ps = 9 2 -> Set foreground color to Green. * Ps = 9 3 -> Set foreground color to Yellow. * Ps = 9 4 -> Set foreground color to Blue. * Ps = 9 5 -> Set foreground color to Magenta. * Ps = 9 6 -> Set foreground color to Cyan. * Ps = 9 7 -> Set foreground color to White. * Ps = 1 0 0 -> Set background color to Black. * Ps = 1 0 1 -> Set background color to Red. * Ps = 1 0 2 -> Set background color to Green. * Ps = 1 0 3 -> Set background color to Yellow. * Ps = 1 0 4 -> Set background color to Blue. * Ps = 1 0 5 -> Set background color to Magenta. * Ps = 1 0 6 -> Set background color to Cyan. * Ps = 1 0 7 -> Set background color to White. * * If xterm is compiled with the 16-color support disabled, it * supports the following, from rxvt: * Ps = 1 0 0 -> Set foreground and background color to * default. * * If 88- or 256-color support is compiled, the following apply. * Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second * Ps. * Ps = 4 8 ; 5 ; Ps -> Set background color to the second * Ps. */ public charAttributes(params: number[]): void { // Optimize a single SGR0. if (params.length === 1 && params[0] === 0) { this._terminal.curAttr = DEFAULT_ATTR; return; } const l = params.length; let flags = this._terminal.curAttr >> 18; let fg = (this._terminal.curAttr >> 9) & 0x1ff; let bg = this._terminal.curAttr & 0x1ff; let p; for (let i = 0; i < l; i++) { p = params[i]; if (p >= 30 && p <= 37) { // fg color 8 fg = p - 30; } else if (p >= 40 && p <= 47) { // bg color 8 bg = p - 40; } else if (p >= 90 && p <= 97) { // fg color 16 p += 8; fg = p - 90; } else if (p >= 100 && p <= 107) { // bg color 16 p += 8; bg = p - 100; } else if (p === 0) { // default flags = DEFAULT_ATTR >> 18; fg = (DEFAULT_ATTR >> 9) & 0x1ff; bg = DEFAULT_ATTR & 0x1ff; // flags = 0; // fg = 0x1ff; // bg = 0x1ff; } else if (p === 1) { // bold text flags |= FLAGS.BOLD; } else if (p === 3) { // italic text flags |= FLAGS.ITALIC; } else if (p === 4) { // underlined text flags |= FLAGS.UNDERLINE; } else if (p === 5) { // blink flags |= FLAGS.BLINK; } else if (p === 7) { // inverse and positive // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' flags |= FLAGS.INVERSE; } else if (p === 8) { // invisible flags |= FLAGS.INVISIBLE; } else if (p === 2) { // dimmed text flags |= FLAGS.DIM; } else if (p === 22) { // not bold nor faint flags &= ~FLAGS.BOLD; flags &= ~FLAGS.DIM; } else if (p === 23) { // not italic flags &= ~FLAGS.ITALIC; } else if (p === 24) { // not underlined flags &= ~FLAGS.UNDERLINE; } else if (p === 25) { // not blink flags &= ~FLAGS.BLINK; } else if (p === 27) { // not inverse flags &= ~FLAGS.INVERSE; } else if (p === 28) { // not invisible flags &= ~FLAGS.INVISIBLE; } else if (p === 39) { // reset fg fg = (DEFAULT_ATTR >> 9) & 0x1ff; } else if (p === 49) { // reset bg bg = DEFAULT_ATTR & 0x1ff; } else if (p === 38) { // fg color 256 if (params[i + 1] === 2) { i += 2; fg = this._terminal.matchColor( params[i] & 0xff, params[i + 1] & 0xff, params[i + 2] & 0xff); if (fg === -1) fg = 0x1ff; i += 2; } else if (params[i + 1] === 5) { i += 2; p = params[i] & 0xff; fg = p; } } else if (p === 48) { // bg color 256 if (params[i + 1] === 2) { i += 2; bg = this._terminal.matchColor( params[i] & 0xff, params[i + 1] & 0xff, params[i + 2] & 0xff); if (bg === -1) bg = 0x1ff; i += 2; } else if (params[i + 1] === 5) { i += 2; p = params[i] & 0xff; bg = p; } } else if (p === 100) { // reset fg/bg fg = (DEFAULT_ATTR >> 9) & 0x1ff; bg = DEFAULT_ATTR & 0x1ff; } else { this._terminal.error('Unknown SGR attribute: %d.', p); } } this._terminal.curAttr = (flags << 18) | (fg << 9) | bg; } /** * CSI Ps n Device Status Report (DSR). * Ps = 5 -> Status Report. Result (``OK'') is * CSI 0 n * Ps = 6 -> Report Cursor Position (CPR) [row;column]. * Result is * CSI r ; c R * CSI ? Ps n * Device Status Report (DSR, DEC-specific). * Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI * ? r ; c R (assumes page is zero). * Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready). * or CSI ? 1 1 n (not ready). * Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked) * or CSI ? 2 1 n (locked). * Ps = 2 6 -> Report Keyboard status as * CSI ? 2 7 ; 1 ; 0 ; 0 n (North American). * The last two parameters apply to VT400 & up, and denote key- * board ready and LK01 respectively. * Ps = 5 3 -> Report Locator status as * CSI ? 5 3 n Locator available, if compiled-in, or * CSI ? 5 0 n No Locator, if not. */ public deviceStatus(params: number[], collect?: string): void { if (!collect) { switch (params[0]) { case 5: // status report this._terminal.emit('data', `${C0.ESC}[0n`); break; case 6: // cursor position const y = this._terminal.buffer.y + 1; const x = this._terminal.buffer.x + 1; this._terminal.emit('data', `${C0.ESC}[${y};${x}R`); break; } } else if (collect === '?') { // modern xterm doesnt seem to // respond to any of these except ?6, 6, and 5 switch (params[0]) { case 6: // cursor position const y = this._terminal.buffer.y + 1; const x = this._terminal.buffer.x + 1; this._terminal.emit('data', `${C0.ESC}[?${y};${x}R`); break; case 15: // no printer // this.handler(C0.ESC + '[?11n'); break; case 25: // dont support user defined keys // this.handler(C0.ESC + '[?21n'); break; case 26: // north american keyboard // this.handler(C0.ESC + '[?27;1;0;0n'); break; case 53: // no dec locator/mouse // this.handler(C0.ESC + '[?50n'); break; } } } /** * CSI ! p Soft terminal reset (DECSTR). * http://vt100.net/docs/vt220-rm/table4-10.html */ public softReset(params: number[], collect?: string): void { if (collect === '!') { this._terminal.cursorHidden = false; this._terminal.insertMode = false; this._terminal.originMode = false; this._terminal.wraparoundMode = true; // defaults: xterm - true, vt100 - false this._terminal.applicationKeypad = false; // ? this._terminal.viewport.syncScrollArea(); this._terminal.applicationCursor = false; this._terminal.buffer.scrollTop = 0; this._terminal.buffer.scrollBottom = this._terminal.rows - 1; this._terminal.curAttr = DEFAULT_ATTR; this._terminal.buffer.x = this._terminal.buffer.y = 0; // ? this._terminal.charset = null; this._terminal.glevel = 0; // ?? this._terminal.charsets = [null]; // ?? } } /** * CSI Ps SP q Set cursor style (DECSCUSR, VT520). * Ps = 0 -> blinking block. * Ps = 1 -> blinking block (default). * Ps = 2 -> steady block. * Ps = 3 -> blinking underline. * Ps = 4 -> steady underline. * Ps = 5 -> blinking bar (xterm). * Ps = 6 -> steady bar (xterm). */ public setCursorStyle(params?: number[], collect?: string): void { if (collect === ' ') { const param = params[0] < 1 ? 1 : params[0]; switch (param) { case 1: case 2: this._terminal.setOption('cursorStyle', 'block'); break; case 3: case 4: this._terminal.setOption('cursorStyle', 'underline'); break; case 5: case 6: this._terminal.setOption('cursorStyle', 'bar'); break; } const isBlinking = param % 2 === 1; this._terminal.setOption('cursorBlink', isBlinking); } } /** * CSI Ps ; Ps r * Set Scrolling Region [top;bottom] (default = full size of win- * dow) (DECSTBM). * CSI ? Pm r */ public setScrollRegion(params: number[], collect?: string): void { if (collect) return; this._terminal.buffer.scrollTop = (params[0] || 1) - 1; this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1; this._terminal.buffer.x = 0; this._terminal.buffer.y = 0; } /** * CSI s * ESC 7 * Save cursor (ANSI.SYS). */ public saveCursor(params: number[]): void { this._terminal.buffer.savedX = this._terminal.buffer.x; this._terminal.buffer.savedY = this._terminal.buffer.y; this._terminal.savedCurAttr = this._terminal.curAttr; } /** * CSI u * ESC 8 * Restore cursor (ANSI.SYS). */ public restoreCursor(params: number[]): void { this._terminal.buffer.x = this._terminal.buffer.savedX || 0; this._terminal.buffer.y = this._terminal.buffer.savedY || 0; this._terminal.curAttr = this._terminal.savedCurAttr || DEFAULT_ATTR; } /** * OSC 0; ST (set icon name + window title) * OSC 2; ST (set window title) * Proxy to set window title. Icon name is not supported. */ public setTitle(data: string): void { this._terminal.handleTitle(data); } /** * ESC E * C1.NEL * DEC mnemonic: NEL (https://vt100.net/docs/vt510-rm/NEL) * Moves cursor to first position on next line. */ public nextLine(): void { this._terminal.buffer.x = 0; this.index(); } /** * ESC = * DEC mnemonic: DECKPAM (https://vt100.net/docs/vt510-rm/DECKPAM.html) * Enables the numeric keypad to send application sequences to the host. */ public keypadApplicationMode(): void { this._terminal.log('Serial port requested application keypad.'); this._terminal.applicationKeypad = true; if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); } } /** * ESC > * DEC mnemonic: DECKPNM (https://vt100.net/docs/vt510-rm/DECKPNM.html) * Enables the keypad to send numeric characters to the host. */ public keypadNumericMode(): void { this._terminal.log('Switching back to normal keypad.'); this._terminal.applicationKeypad = false; if (this._terminal.viewport) { this._terminal.viewport.syncScrollArea(); } } /** * ESC % @ * ESC % G * Select default character set. UTF-8 is not supported (string are unicode anyways) * therefore ESC % G does the same. */ public selectDefaultCharset(): void { this._terminal.setgLevel(0); this._terminal.setgCharset(0, DEFAULT_CHARSET); // US (default) } /** * ESC ( C * Designate G0 Character Set, VT100, ISO 2022. * ESC ) C * Designate G1 Character Set (ISO 2022, VT100). * ESC * C * Designate G2 Character Set (ISO 2022, VT220). * ESC + C * Designate G3 Character Set (ISO 2022, VT220). * ESC - C * Designate G1 Character Set (VT300). * ESC . C * Designate G2 Character Set (VT300). * ESC / C * Designate G3 Character Set (VT300). C = A -> ISO Latin-1 Supplemental. - Supported? */ public selectCharset(collectAndFlag: string): void { if (collectAndFlag.length !== 2) return this.selectDefaultCharset(); if (collectAndFlag[0] === '/') return; // TODO: Is this supported? this._terminal.setgCharset(GLEVEL[collectAndFlag[0]], CHARSETS[collectAndFlag[1]] || DEFAULT_CHARSET); } /** * ESC D * C1.IND * DEC mnemonic: IND (https://vt100.net/docs/vt510-rm/IND.html) * Moves the cursor down one line in the same column. */ public index(): void { this._terminal.index(); // TODO: save to move from terminal? } /** * ESC H * C1.HTS * DEC mnemonic: HTS (https://vt100.net/docs/vt510-rm/HTS.html) * Sets a horizontal tab stop at the column position indicated by * the value of the active column when the terminal receives an HTS. */ public tabSet(): void { this._terminal.tabSet(); // TODO: save to move from terminal? } /** * ESC M * C1.RI * DEC mnemonic: HTS * Moves the cursor up one line in the same column. If the cursor is at the top margin, * the page scrolls down. */ public reverseIndex(): void { this._terminal.reverseIndex(); // TODO: save to move from terminal? } /** * ESC c * DEC mnemonic: RIS (https://vt100.net/docs/vt510-rm/RIS.html) * Reset to initial state. */ public reset(): void { this._parser.reset(); this._terminal.reset(); // TODO: save to move from terminal? } /** * ESC n * ESC o * ESC | * ESC } * ESC ~ * DEC mnemonic: LS (https://vt100.net/docs/vt510-rm/LS.html) * When you use a locking shift, the character set remains in GL or GR until * you use another locking shift. (partly supported) */ public setgLevel(level: number): void { this._terminal.setgLevel(level); // TODO: save to move from terminal? } } xterm.js-3.8.1/src/Linkifier.test.ts000066400000000000000000000351711341514612000173320ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { IMouseZoneManager, IMouseZone } from './ui/Types'; import { ILinkMatcher, ITerminal, IBufferLine } from './Types'; import { Linkifier } from './Linkifier'; import { MockBuffer, MockTerminal, TestTerminal } from './utils/TestUtils.test'; import { CircularList } from './common/CircularList'; import { BufferLine } from './BufferLine'; class TestLinkifier extends Linkifier { constructor(terminal: ITerminal) { super(terminal); (Linkifier).TIME_BEFORE_LINKIFY = 0; } public get linkMatchers(): ILinkMatcher[] { return this._linkMatchers; } public linkifyRows(): void { super.linkifyRows(0, this._terminal.buffer.lines.length - 1); } } class TestMouseZoneManager implements IMouseZoneManager { dispose(): void { } public clears: number = 0; public zones: IMouseZone[] = []; add(zone: IMouseZone): void { this.zones.push(zone); } clearAll(): void { this.clears++; } } describe('Linkifier', () => { let terminal: ITerminal; let linkifier: TestLinkifier; let mouseZoneManager: TestMouseZoneManager; beforeEach(() => { terminal = new MockTerminal(); terminal.cols = 100; terminal.rows = 10; terminal.buffer = new MockBuffer(); (terminal.buffer).setLines(new CircularList(20)); terminal.buffer.ydisp = 0; linkifier = new TestLinkifier(terminal); mouseZoneManager = new TestMouseZoneManager(); }); function stringToRow(text: string): IBufferLine { const result = new BufferLine(); for (let i = 0; i < text.length; i++) { result.push([0, text.charAt(i), 1, text.charCodeAt(i)]); } return result; } function addRow(text: string): void { terminal.buffer.lines.push(stringToRow(text)); } function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, links: {x: number, length: number}[], done: MochaDone): void { addRow(rowText); linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); terminal.rows = terminal.buffer.lines.length - 1; linkifier.linkifyRows(); // Allow linkify to happen setTimeout(() => { assert.equal(mouseZoneManager.zones.length, links.length); links.forEach((l, i) => { assert.equal(mouseZoneManager.zones[i].x1, l.x + 1); assert.equal(mouseZoneManager.zones[i].x2, l.x + l.length + 1); assert.equal(mouseZoneManager.zones[i].y1, terminal.buffer.lines.length); assert.equal(mouseZoneManager.zones[i].y2, terminal.buffer.lines.length); }); done(); }, 0); } function assertLinkifiesMultiLineLink(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { addRow(rowText); linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); linkifier.linkifyRows(); // Allow linkify to happen setTimeout(() => { assert.equal(mouseZoneManager.zones.length, links.length); links.forEach((l, i) => { assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1); assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1); assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1); assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1); }); done(); }, 0); } describe('before attachToDom', () => { it('should allow link matcher registration', done => { assert.doesNotThrow(() => { const linkMatcherId = linkifier.registerLinkMatcher(/foo/, () => {}); assert.isTrue(linkifier.deregisterLinkMatcher(linkMatcherId)); done(); }); }); }); describe('after attachToDom', () => { beforeEach(() => { linkifier.attachToDom(mouseZoneManager); }); describe('link matcher', () => { it('should match a single link', done => { assertLinkifiesRow('foo', /foo/, [{x: 0, length: 3}], done); }); it('should match a single link at the start of a text node', done => { assertLinkifiesRow('foo bar', /foo/, [{x: 0, length: 3}], done); }); it('should match a single link in the middle of a text node', done => { assertLinkifiesRow('foo bar baz', /bar/, [{x: 4, length: 3}], done); }); it('should match a single link at the end of a text node', done => { assertLinkifiesRow('foo bar', /bar/, [{x: 4, length: 3}], done); }); it('should match a link after a link at the start of a text node', done => { assertLinkifiesRow('foo bar', /foo|bar/, [{x: 0, length: 3}, {x: 4, length: 3}], done); }); it('should match a link after a link in the middle of a text node', done => { assertLinkifiesRow('foo bar baz', /bar|baz/, [{x: 4, length: 3}, {x: 8, length: 3}], done); }); it('should match a link immediately after a link at the end of a text node', done => { assertLinkifiesRow('foo barbaz', /bar|baz/, [{x: 4, length: 3}, {x: 7, length: 3}], done); }); it('should not duplicate text after a unicode character (wrapped in a span)', done => { // This is a regression test for an issue that came about when using // an oh-my-zsh theme that added the large blue diamond unicode // character (U+1F537) which caused the path to be duplicated. See #642. assertLinkifiesRow('echo \'🔷foo\'', /foo/, [{x: 8, length: 3}], done); }); describe('multi-line links', () => { it('should match links that start on line 1/2 of a wrapped line and end on the last character of line 1/2', done => { terminal.cols = 4; assertLinkifiesMultiLineLink('12345', /1234/, [{x1: 0, x2: 4, y1: 0, y2: 0}], done); }); it('should match links that start on line 1/2 of a wrapped line and wrap to line 2/2', done => { terminal.cols = 4; assertLinkifiesMultiLineLink('12345', /12345/, [{x1: 0, x2: 1, y1: 0, y2: 1}], done); }); it('should match links that start and end on line 2/2 of a wrapped line', done => { terminal.cols = 4; assertLinkifiesMultiLineLink('12345678', /5678/, [{x1: 0, x2: 4, y1: 1, y2: 1}], done); }); it('should match links that start on line 2/3 of a wrapped line and wrap to line 3/3', done => { terminal.cols = 4; assertLinkifiesMultiLineLink('123456789', /56789/, [{x1: 0, x2: 1, y1: 1, y2: 2}], done); }); }); }); describe('validationCallback', () => { it('should enable link if true', done => { addRow('test'); linkifier.registerLinkMatcher(/test/, () => done(), { validationCallback: (url, cb) => { assert.equal(mouseZoneManager.zones.length, 0); cb(true); assert.equal(mouseZoneManager.zones.length, 1); assert.equal(mouseZoneManager.zones[0].x1, 1); assert.equal(mouseZoneManager.zones[0].x2, 5); assert.equal(mouseZoneManager.zones[0].y1, 1); assert.equal(mouseZoneManager.zones[0].y2, 1); // Fires done() mouseZoneManager.zones[0].clickCallback({}); } }); linkifier.linkifyRows(); }); it('should validate the uri, not the row', done => { addRow('abc test abc'); linkifier.registerLinkMatcher(/test/, () => done(), { validationCallback: (uri, cb) => { assert.equal(uri, 'test'); done(); } }); linkifier.linkifyRows(); }); it('should disable link if false', done => { addRow('test'); linkifier.registerLinkMatcher(/test/, () => assert.fail(), { validationCallback: (url, cb) => { assert.equal(mouseZoneManager.zones.length, 0); cb(false); assert.equal(mouseZoneManager.zones.length, 0); } }); linkifier.linkifyRows(); // Allow time for the validation callback to be performed setTimeout(() => done(), 10); }); it('should trigger for multiple link matches on one row', done => { addRow('test test'); let count = 0; linkifier.registerLinkMatcher(/test/, () => assert.fail(), { validationCallback: (url, cb) => { count += 1; if (count === 2) { done(); } cb(false); } }); linkifier.linkifyRows(); }); }); describe('priority', () => { it('should order the list from highest priority to lowest #1', () => { const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 }); const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 }); assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, bId]); }); it('should order the list from highest priority to lowest #2', () => { const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 }); const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 }); assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, aId]); }); it('should order items of equal priority in the order they are added', () => { const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 }); const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 }); assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, bId]); }); }); }); describe('unicode handling', () => { let terminal: TestTerminal; // other than the tests above unicode testing needs the full terminal instance // to get the special handling of fullwidth, surrogate and combining chars in the input handler beforeEach(() => { terminal = new TestTerminal({cols: 10, rows: 5}); linkifier = new TestLinkifier(terminal); mouseZoneManager = new TestMouseZoneManager(); linkifier.attachToDom(mouseZoneManager); }); function assertLinkifiesInTerminal(rowText: string, linkMatcherRegex: RegExp, links: {x1: number, y1: number, x2: number, y2: number}[], done: MochaDone): void { terminal.writeSync(rowText); linkifier.registerLinkMatcher(linkMatcherRegex, () => {}); linkifier.linkifyRows(); // Allow linkify to happen setTimeout(() => { assert.equal(mouseZoneManager.zones.length, links.length); links.forEach((l, i) => { assert.equal(mouseZoneManager.zones[i].x1, l.x1 + 1); assert.equal(mouseZoneManager.zones[i].x2, l.x2 + 1); assert.equal(mouseZoneManager.zones[i].y1, l.y1 + 1); assert.equal(mouseZoneManager.zones[i].y2, l.y2 + 1); }); done(); }, 0); } describe('unicode before the match', () => { it('combining - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); }); it('combining - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('e\u0301e\u0301e\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); }); it('surrogate - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); }); it('surrogate - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('𝄞𝄞𝄞 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); }); it('combining surrogate - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 4, x2: 7, y1: 0, y2: 0}], done); }); it('combining surrogate - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('𓂀\u0301𓂀\u0301𓂀\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); }); it('fullwidth - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); }); it('fullwidth - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('12 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); }); it('combining fullwidth - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); }); it('combining fullwidth - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('¥\u0301¥\u0301 foo', /foo/, [{x1: 8, x2: 1, y1: 0, y2: 1}], done); }); }); describe('unicode within the match', () => { it('combining - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('test cafe\u0301', /cafe\u0301/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); }); it('combining - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('testtest cafe\u0301', /cafe\u0301/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); }); it('surrogate - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('test a𝄞b', /a𝄞b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); }); it('surrogate - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('testtest a𝄞b', /a𝄞b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); }); it('combining surrogate - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('test a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 5, x2: 8, y1: 0, y2: 0}], done); }); it('combining surrogate - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('testtest a𓂀\u0301b', /a𓂀\u0301b/, [{x1: 9, x2: 2, y1: 0, y2: 1}], done); }); it('fullwidth - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('test a1b', /a1b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); }); it('fullwidth - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('testtest a1b', /a1b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); }); it('combining fullwidth - match within one line', function(done: () => void): void { assertLinkifiesInTerminal('test a¥\u0301b', /a¥\u0301b/, [{x1: 5, x2: 9, y1: 0, y2: 0}], done); }); it('combining fullwidth - match over two lines', function(done: () => void): void { assertLinkifiesInTerminal('testtest a¥\u0301b', /a¥\u0301b/, [{x1: 9, x2: 3, y1: 0, y2: 1}], done); }); }); }); }); xterm.js-3.8.1/src/Linkifier.ts000066400000000000000000000256621341514612000163600ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IMouseZoneManager } from './ui/Types'; import { ILinkHoverEvent, ILinkMatcher, LinkMatcherHandler, LinkHoverEventTypes, ILinkMatcherOptions, ILinkifier, ITerminal, IBufferStringIteratorResult } from './Types'; import { MouseZone } from './ui/MouseZoneManager'; import { EventEmitter } from './common/EventEmitter'; import { CHAR_DATA_ATTR_INDEX } from './Buffer'; import { getStringCellWidth } from './CharWidth'; /** * The Linkifier applies links to rows shortly after they have been refreshed. */ export class Linkifier extends EventEmitter implements ILinkifier { /** * The time to wait after a row is changed before it is linkified. This prevents * the costly operation of searching every row multiple times, potentially a * huge amount of times. */ protected static readonly TIME_BEFORE_LINKIFY = 200; /** * Limit of the unwrapping line expansion (overscan) at the top and bottom * of the actual viewport in ASCII characters. * A limit of 2000 should match most sane urls. */ protected static readonly OVERSCAN_CHAR_LIMIT = 2000; protected _linkMatchers: ILinkMatcher[] = []; private _mouseZoneManager: IMouseZoneManager; private _rowsTimeoutId: number; private _nextLinkMatcherId = 0; private _rowsToLinkify: { start: number, end: number }; constructor( protected _terminal: ITerminal ) { super(); this._rowsToLinkify = { start: null, end: null }; } /** * Attaches the linkifier to the DOM, enabling linkification. * @param mouseZoneManager The mouse zone manager to register link zones with. */ public attachToDom(mouseZoneManager: IMouseZoneManager): void { this._mouseZoneManager = mouseZoneManager; } /** * Queue linkification on a set of rows. * @param start The row to linkify from (inclusive). * @param end The row to linkify to (inclusive). */ public linkifyRows(start: number, end: number): void { // Don't attempt linkify if not yet attached to DOM if (!this._mouseZoneManager) { return; } // Increase range to linkify if (this._rowsToLinkify.start === null) { this._rowsToLinkify.start = start; this._rowsToLinkify.end = end; } else { this._rowsToLinkify.start = Math.min(this._rowsToLinkify.start, start); this._rowsToLinkify.end = Math.max(this._rowsToLinkify.end, end); } // Clear out any existing links on this row range this._mouseZoneManager.clearAll(start, end); // Restart timer if (this._rowsTimeoutId) { clearTimeout(this._rowsTimeoutId); } this._rowsTimeoutId = setTimeout(() => this._linkifyRows(), Linkifier.TIME_BEFORE_LINKIFY); } /** * Linkifies the rows requested. */ private _linkifyRows(): void { this._rowsTimeoutId = null; const buffer = this._terminal.buffer; // Ensure the start row exists const absoluteRowIndexStart = buffer.ydisp + this._rowsToLinkify.start; if (absoluteRowIndexStart >= buffer.lines.length) { return; } // Invalidate bad end row values (if a resize happened) const absoluteRowIndexEnd = buffer.ydisp + Math.min(this._rowsToLinkify.end, this._terminal.rows) + 1; // Iterate over the range of unwrapped content strings within start..end // (excluding). // _doLinkifyRow gets full unwrapped lines with the start row as buffer offset // for every matcher. // The unwrapping is needed to also match content that got wrapped across // several buffer lines. To avoid a worst case scenario where the whole buffer // contains just a single unwrapped string we limit this line expansion beyond // the viewport to +OVERSCAN_CHAR_LIMIT chars (overscan) at top and bottom. // This comes with the tradeoff that matches longer than OVERSCAN_CHAR_LIMIT // chars will not match anymore at the viewport borders. const overscanLineLimit = Math.ceil(Linkifier.OVERSCAN_CHAR_LIMIT / this._terminal.cols); const iterator = this._terminal.buffer.iterator( false, absoluteRowIndexStart, absoluteRowIndexEnd, overscanLineLimit, overscanLineLimit); while (iterator.hasNext()) { const lineData: IBufferStringIteratorResult = iterator.next(); for (let i = 0; i < this._linkMatchers.length; i++) { this._doLinkifyRow(lineData.range.first, lineData.content, this._linkMatchers[i]); } } this._rowsToLinkify.start = null; this._rowsToLinkify.end = null; } /** * Registers a link matcher, allowing custom link patterns to be matched and * handled. * @param regex The regular expression to search for. Specifically, this * searches the textContent of the rows. You will want to use \s to match a * space ' ' character for example. * @param handler The callback when the link is called. * @param options Options for the link matcher. * @return The ID of the new matcher, this can be used to deregister. */ public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options: ILinkMatcherOptions = {}): number { if (!handler) { throw new Error('handler must be defined'); } const matcher: ILinkMatcher = { id: this._nextLinkMatcherId++, regex, handler, matchIndex: options.matchIndex, validationCallback: options.validationCallback, hoverTooltipCallback: options.tooltipCallback, hoverLeaveCallback: options.leaveCallback, willLinkActivate: options.willLinkActivate, priority: options.priority || 0 }; this._addLinkMatcherToList(matcher); return matcher.id; } /** * Inserts a link matcher to the list in the correct position based on the * priority of each link matcher. New link matchers of equal priority are * considered after older link matchers. * @param matcher The link matcher to be added. */ private _addLinkMatcherToList(matcher: ILinkMatcher): void { if (this._linkMatchers.length === 0) { this._linkMatchers.push(matcher); return; } for (let i = this._linkMatchers.length - 1; i >= 0; i--) { if (matcher.priority <= this._linkMatchers[i].priority) { this._linkMatchers.splice(i + 1, 0, matcher); return; } } this._linkMatchers.splice(0, 0, matcher); } /** * Deregisters a link matcher if it has been registered. * @param matcherId The link matcher's ID (returned after register) * @return Whether a link matcher was found and deregistered. */ public deregisterLinkMatcher(matcherId: number): boolean { for (let i = 0; i < this._linkMatchers.length; i++) { if (this._linkMatchers[i].id === matcherId) { this._linkMatchers.splice(i, 1); return true; } } return false; } /** * Linkifies a row given a specific handler. * @param rowIndex The row index to linkify (absolute index). * @param text string content of the unwrapped row. * @param matcher The link matcher for this line. */ private _doLinkifyRow(rowIndex: number, text: string, matcher: ILinkMatcher): void { // clone regex to do a global search on text const rex = new RegExp(matcher.regex.source, matcher.regex.flags + 'g'); let match; let stringIndex = -1; while ((match = rex.exec(text)) !== null) { const uri = match[typeof matcher.matchIndex !== 'number' ? 0 : matcher.matchIndex]; if (!uri) { // something matched but does not comply with the given matchIndex // since this is most likely a bug the regex itself we simply do nothing here // DEBUG: print match and throw if ((this._terminal).debug) { console.log({match, matcher}); throw new Error('match found without corresponding matchIndex'); } break; } // Get index, match.index is for the outer match which includes negated chars // therefore we cannot use match.index directly, instead we search the position // of the match group in text again // also correct regex and string search offsets for the next loop run stringIndex = text.indexOf(uri, stringIndex + 1); rex.lastIndex = stringIndex + uri.length; // get the buffer index as [absolute row, col] for the match const bufferIndex = this._terminal.buffer.stringIndexToBufferIndex(rowIndex, stringIndex); const line = this._terminal.buffer.lines.get(bufferIndex[0]); const char = line.get(bufferIndex[1]); let fg: number | undefined; if (char) { const attr: number = char[CHAR_DATA_ATTR_INDEX]; fg = (attr >> 9) & 0x1ff; } if (matcher.validationCallback) { matcher.validationCallback(uri, isValid => { // Discard link if the line has already changed if (this._rowsTimeoutId) { return; } if (isValid) { this._addLink(bufferIndex[1], bufferIndex[0] - this._terminal.buffer.ydisp, uri, matcher, fg); } }); } else { this._addLink(bufferIndex[1], bufferIndex[0] - this._terminal.buffer.ydisp, uri, matcher, fg); } } } /** * Registers a link to the mouse zone manager. * @param x The column the link starts. * @param y The row the link is on. * @param uri The URI of the link. * @param matcher The link matcher for the link. * @param fg The link color for hover event. */ private _addLink(x: number, y: number, uri: string, matcher: ILinkMatcher, fg: number): void { const width = getStringCellWidth(uri); const x1 = x % this._terminal.cols; const y1 = y + Math.floor(x / this._terminal.cols); let x2 = (x1 + width) % this._terminal.cols; let y2 = y1 + Math.floor((x1 + width) / this._terminal.cols); if (x2 === 0) { x2 = this._terminal.cols; y2--; } this._mouseZoneManager.add(new MouseZone( x1 + 1, y1 + 1, x2 + 1, y2 + 1, e => { if (matcher.handler) { return matcher.handler(e, uri); } window.open(uri, '_blank'); }, e => { this.emit(LinkHoverEventTypes.HOVER, this._createLinkHoverEvent(x1, y1, x2, y2, fg)); this._terminal.element.classList.add('xterm-cursor-pointer'); }, e => { this.emit(LinkHoverEventTypes.TOOLTIP, this._createLinkHoverEvent(x1, y1, x2, y2, fg)); if (matcher.hoverTooltipCallback) { matcher.hoverTooltipCallback(e, uri); } }, () => { this.emit(LinkHoverEventTypes.LEAVE, this._createLinkHoverEvent(x1, y1, x2, y2, fg)); this._terminal.element.classList.remove('xterm-cursor-pointer'); if (matcher.hoverLeaveCallback) { matcher.hoverLeaveCallback(); } }, e => { if (matcher.willLinkActivate) { return matcher.willLinkActivate(e, uri); } return true; } )); } private _createLinkHoverEvent(x1: number, y1: number, x2: number, y2: number, fg: number): ILinkHoverEvent { return { x1, y1, x2, y2, cols: this._terminal.cols, fg }; } } xterm.js-3.8.1/src/SelectionManager.test.ts000066400000000000000000000560251341514612000206370ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { CharMeasure } from './ui/CharMeasure'; import { SelectionManager, SelectionMode } from './SelectionManager'; import { SelectionModel } from './SelectionModel'; import { BufferSet } from './BufferSet'; import { ITerminal, IBuffer, IBufferLine } from './Types'; import { MockTerminal } from './utils/TestUtils.test'; import { BufferLine } from './BufferLine'; class TestMockTerminal extends MockTerminal { emit(event: string, data: any): void {} } class TestSelectionManager extends SelectionManager { constructor( terminal: ITerminal, charMeasure: CharMeasure ) { super(terminal, charMeasure); } public get model(): SelectionModel { return this._model; } public set selectionMode(mode: SelectionMode) { this._activeSelectionMode = mode; } public selectLineAt(line: number): void { this._selectLineAt(line); } public selectWordAt(coords: [number, number]): void { this._selectWordAt(coords, true); } // Disable DOM interaction public enable(): void {} public disable(): void {} public refresh(): void {} } describe('SelectionManager', () => { let terminal: ITerminal; let buffer: IBuffer; let selectionManager: TestSelectionManager; beforeEach(() => { terminal = new TestMockTerminal(); terminal.cols = 80; terminal.rows = 2; terminal.options.scrollback = 100; terminal.buffers = new BufferSet(terminal); terminal.buffer = terminal.buffers.active; buffer = terminal.buffer; selectionManager = new TestSelectionManager(terminal, null); }); function stringToRow(text: string): IBufferLine { const result = new BufferLine(); for (let i = 0; i < text.length; i++) { result.push([0, text.charAt(i), 1, text.charCodeAt(i)]); } return result; } function stringArrayToRow(chars: string[]): IBufferLine { const line = new BufferLine(); chars.map(c => line.push([0, c, 1, c.charCodeAt(0)])); return line; } describe('_selectWordAt', () => { it('should expand selection for normal width chars', () => { buffer.lines.set(0, stringToRow('foo bar')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, 'foo'); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, 'foo'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, 'foo'); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([4, 0]); assert.equal(selectionManager.selectionText, 'bar'); selectionManager.selectWordAt([5, 0]); assert.equal(selectionManager.selectionText, 'bar'); selectionManager.selectWordAt([6, 0]); assert.equal(selectionManager.selectionText, 'bar'); }); it('should expand selection for whitespace', () => { buffer.lines.set(0, stringToRow('a b')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, 'a'); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([4, 0]); assert.equal(selectionManager.selectionText, 'b'); }); it('should expand selection for wide characters', () => { // Wide characters use a special format const line = new BufferLine(); const data: [number, string, number, number][] = [ [null, '中', 2, '中'.charCodeAt(0)], [null, '', 0, null], [null, '文', 2, '文'.charCodeAt(0)], [null, '', 0, null], [null, ' ', 1, ' '.charCodeAt(0)], [null, 'a', 1, 'a'.charCodeAt(0)], [null, '中', 2, '中'.charCodeAt(0)], [null, '', 0, null], [null, '文', 2, '文'.charCodeAt(0)], [null, '', 0, ''.charCodeAt(0)], [null, 'b', 1, 'b'.charCodeAt(0)], [null, ' ', 1, ' '.charCodeAt(0)], [null, 'f', 1, 'f'.charCodeAt(0)], [null, 'o', 1, 'o'.charCodeAt(0)], [null, 'o', 1, 'o'.charCodeAt(0)] ]; for (let i = 0; i < data.length; ++i) line.push(data[i]); buffer.lines.set(0, line); // Ensure wide characters take up 2 columns selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, '中文'); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, '中文'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, '中文'); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, '中文'); selectionManager.selectWordAt([4, 0]); assert.equal(selectionManager.selectionText, ' '); // Ensure wide characters work when wrapped in normal width characters selectionManager.selectWordAt([5, 0]); assert.equal(selectionManager.selectionText, 'a中文b'); selectionManager.selectWordAt([6, 0]); assert.equal(selectionManager.selectionText, 'a中文b'); selectionManager.selectWordAt([7, 0]); assert.equal(selectionManager.selectionText, 'a中文b'); selectionManager.selectWordAt([8, 0]); assert.equal(selectionManager.selectionText, 'a中文b'); selectionManager.selectWordAt([9, 0]); assert.equal(selectionManager.selectionText, 'a中文b'); selectionManager.selectWordAt([10, 0]); assert.equal(selectionManager.selectionText, 'a中文b'); selectionManager.selectWordAt([11, 0]); assert.equal(selectionManager.selectionText, ' '); // Ensure normal width characters work fine in a line containing wide characters selectionManager.selectWordAt([12, 0]); assert.equal(selectionManager.selectionText, 'foo'); selectionManager.selectWordAt([13, 0]); assert.equal(selectionManager.selectionText, 'foo'); selectionManager.selectWordAt([14, 0]); assert.equal(selectionManager.selectionText, 'foo'); }); it('should select up to non-path characters that are commonly adjacent to paths', () => { buffer.lines.set(0, stringToRow('(cd)[ef]{gh}\'ij"')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, '(cd'); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, 'cd'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, 'cd'); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, 'cd)'); selectionManager.selectWordAt([4, 0]); assert.equal(selectionManager.selectionText, '[ef'); selectionManager.selectWordAt([5, 0]); assert.equal(selectionManager.selectionText, 'ef'); selectionManager.selectWordAt([6, 0]); assert.equal(selectionManager.selectionText, 'ef'); selectionManager.selectWordAt([7, 0]); assert.equal(selectionManager.selectionText, 'ef]'); selectionManager.selectWordAt([8, 0]); assert.equal(selectionManager.selectionText, '{gh'); selectionManager.selectWordAt([9, 0]); assert.equal(selectionManager.selectionText, 'gh'); selectionManager.selectWordAt([10, 0]); assert.equal(selectionManager.selectionText, 'gh'); selectionManager.selectWordAt([11, 0]); assert.equal(selectionManager.selectionText, 'gh}'); selectionManager.selectWordAt([12, 0]); assert.equal(selectionManager.selectionText, '\'ij'); selectionManager.selectWordAt([13, 0]); assert.equal(selectionManager.selectionText, 'ij'); selectionManager.selectWordAt([14, 0]); assert.equal(selectionManager.selectionText, 'ij'); selectionManager.selectWordAt([15, 0]); assert.equal(selectionManager.selectionText, 'ij"'); }); it('should expand upwards or downards for wrapped lines', () => { buffer.lines.set(0, stringToRow(' foo')); buffer.lines.set(1, stringToRow('bar ')); buffer.lines.get(1).isWrapped = true; selectionManager.selectWordAt([1, 1]); assert.equal(selectionManager.selectionText, 'foobar'); selectionManager.model.clearSelection(); selectionManager.selectWordAt([78, 0]); assert.equal(selectionManager.selectionText, 'foobar'); }); it('should expand both upwards and downwards for word wrapped over many lines', () => { const expectedText = 'fooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbar'; buffer.lines.set(0, stringToRow(' foo')); buffer.lines.set(1, stringToRow('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')); buffer.lines.set(2, stringToRow('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')); buffer.lines.set(3, stringToRow('cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc')); buffer.lines.set(4, stringToRow('bar ')); buffer.lines.get(1).isWrapped = true; buffer.lines.get(2).isWrapped = true; buffer.lines.get(3).isWrapped = true; buffer.lines.get(4).isWrapped = true; selectionManager.selectWordAt([78, 0]); assert.equal(selectionManager.selectionText, expectedText); selectionManager.model.clearSelection(); selectionManager.selectWordAt([40, 1]); assert.equal(selectionManager.selectionText, expectedText); selectionManager.model.clearSelection(); selectionManager.selectWordAt([40, 2]); assert.equal(selectionManager.selectionText, expectedText); selectionManager.model.clearSelection(); selectionManager.selectWordAt([40, 3]); assert.equal(selectionManager.selectionText, expectedText); selectionManager.model.clearSelection(); selectionManager.selectWordAt([1, 4]); assert.equal(selectionManager.selectionText, expectedText); }); describe('emoji', () => { it('should treat a single emoji as a word when wrapped in spaces', () => { buffer.lines.set(0, stringToRow(' ⚽ a')); // The a is here to prevent the space being trimmed in selectionText selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, '⚽'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, ' '); }); it('should treat multiple emojis as a word when wrapped in spaces', () => { buffer.lines.set(0, stringToRow(' ⚽⚽ a')); // The a is here to prevent the space being trimmed in selectionText selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, '⚽⚽'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, '⚽⚽'); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, ' '); }); it('should treat emojis using the zero-width-joiner as a single word', () => { // Note that the first 3 emojis include the invisible ZWJ char buffer.lines.set(0, stringArrayToRow([ ' ', '👨‍', '👩‍', '👧‍', '👦', ' ', 'a' ])); // The a is here to prevent the space being trimmed in selectionText selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, ' '); // ZWJ emojis do not combine in the terminal so the family emoji used here consumed 4 cells // The selection text should retain ZWJ chars despite not combining on the terminal selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, '👨‍👩‍👧‍👦'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, '👨‍👩‍👧‍👦'); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, '👨‍👩‍👧‍👦'); selectionManager.selectWordAt([4, 0]); assert.equal(selectionManager.selectionText, '👨‍👩‍👧‍👦'); selectionManager.selectWordAt([5, 0]); assert.equal(selectionManager.selectionText, ' '); }); it('should treat emojis and characters joined together as a word', () => { buffer.lines.set(0, stringToRow(' ⚽ab cd⚽ ef⚽gh')); // The a is here to prevent the space being trimmed in selectionText selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, '⚽ab'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, '⚽ab'); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, '⚽ab'); selectionManager.selectWordAt([4, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([5, 0]); assert.equal(selectionManager.selectionText, 'cd⚽'); selectionManager.selectWordAt([6, 0]); assert.equal(selectionManager.selectionText, 'cd⚽'); selectionManager.selectWordAt([7, 0]); assert.equal(selectionManager.selectionText, 'cd⚽'); selectionManager.selectWordAt([8, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([9, 0]); assert.equal(selectionManager.selectionText, 'ef⚽gh'); selectionManager.selectWordAt([10, 0]); assert.equal(selectionManager.selectionText, 'ef⚽gh'); selectionManager.selectWordAt([11, 0]); assert.equal(selectionManager.selectionText, 'ef⚽gh'); selectionManager.selectWordAt([12, 0]); assert.equal(selectionManager.selectionText, 'ef⚽gh'); selectionManager.selectWordAt([13, 0]); assert.equal(selectionManager.selectionText, 'ef⚽gh'); }); it('should treat complex emojis and characters joined together as a word', () => { // This emoji is the flag for England and is made up of: 1F3F4 E0067 E0062 E0065 E006E E0067 E007F buffer.lines.set(0, stringArrayToRow([ ' ', '🏴󠁧󠁢󠁥󠁮󠁧󠁿', 'a', 'b', ' ', 'c', 'd', '🏴󠁧󠁢󠁥󠁮󠁧󠁿', ' ', 'e', 'f', '🏴󠁧󠁢󠁥󠁮󠁧󠁿', 'g', 'h', ' ', 'a' ])); // The a is here to prevent the space being trimmed in selectionText selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([1, 0]); assert.equal(selectionManager.selectionText, '🏴󠁧󠁢󠁥󠁮󠁧󠁿ab'); selectionManager.selectWordAt([2, 0]); assert.equal(selectionManager.selectionText, '🏴󠁧󠁢󠁥󠁮󠁧󠁿ab'); selectionManager.selectWordAt([3, 0]); assert.equal(selectionManager.selectionText, '🏴󠁧󠁢󠁥󠁮󠁧󠁿ab'); selectionManager.selectWordAt([4, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([5, 0]); assert.equal(selectionManager.selectionText, 'cd🏴󠁧󠁢󠁥󠁮󠁧󠁿'); selectionManager.selectWordAt([6, 0]); assert.equal(selectionManager.selectionText, 'cd🏴󠁧󠁢󠁥󠁮󠁧󠁿'); selectionManager.selectWordAt([7, 0]); assert.equal(selectionManager.selectionText, 'cd🏴󠁧󠁢󠁥󠁮󠁧󠁿'); selectionManager.selectWordAt([8, 0]); assert.equal(selectionManager.selectionText, ' '); selectionManager.selectWordAt([9, 0]); assert.equal(selectionManager.selectionText, 'ef🏴󠁧󠁢󠁥󠁮󠁧󠁿gh'); selectionManager.selectWordAt([10, 0]); assert.equal(selectionManager.selectionText, 'ef🏴󠁧󠁢󠁥󠁮󠁧󠁿gh'); selectionManager.selectWordAt([11, 0]); assert.equal(selectionManager.selectionText, 'ef🏴󠁧󠁢󠁥󠁮󠁧󠁿gh'); selectionManager.selectWordAt([12, 0]); assert.equal(selectionManager.selectionText, 'ef🏴󠁧󠁢󠁥󠁮󠁧󠁿gh'); selectionManager.selectWordAt([13, 0]); assert.equal(selectionManager.selectionText, 'ef🏴󠁧󠁢󠁥󠁮󠁧󠁿gh'); }); }); }); describe('_selectLineAt', () => { it('should select the entire line', () => { buffer.lines.set(0, stringToRow('foo bar')); selectionManager.selectLineAt(0); assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct'); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]); assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 0], 'The actual selection spans the entire column'); }); it('should select the entire wrapped line', () => { buffer.lines.set(0, stringToRow('foo')); const line2 = stringToRow('bar'); line2.isWrapped = true; buffer.lines.set(1, line2); selectionManager.selectLineAt(0); assert.equal(selectionManager.selectionText, 'foobar', 'The selected text is correct'); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]); assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1], 'The actual selection spans the entire column'); }); }); describe('selectAll', () => { it('should select the entire buffer, beyond the viewport', () => { buffer.lines.length = 5; buffer.lines.set(0, stringToRow('1')); buffer.lines.set(1, stringToRow('2')); buffer.lines.set(2, stringToRow('3')); buffer.lines.set(3, stringToRow('4')); buffer.lines.set(4, stringToRow('5')); selectionManager.selectAll(); terminal.buffer.ybase = buffer.lines.length - terminal.rows; assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5'); }); }); describe('selectLines', () => { it('should select a single line', () => { buffer.lines.length = 3; buffer.lines.set(0, stringToRow('1')); buffer.lines.set(1, stringToRow('2')); buffer.lines.set(2, stringToRow('3')); selectionManager.selectLines(1, 1); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]); assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1]); }); it('should select multiple lines', () => { buffer.lines.length = 5; buffer.lines.set(0, stringToRow('1')); buffer.lines.set(1, stringToRow('2')); buffer.lines.set(2, stringToRow('3')); buffer.lines.set(3, stringToRow('4')); buffer.lines.set(4, stringToRow('5')); selectionManager.selectLines(1, 3); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]); assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 3]); }); it('should select the to the start when requesting a negative row', () => { buffer.lines.length = 2; buffer.lines.set(0, stringToRow('1')); buffer.lines.set(1, stringToRow('2')); selectionManager.selectLines(-1, 0); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]); assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 0]); }); it('should select the to the end when requesting beyond the final row', () => { buffer.lines.length = 2; buffer.lines.set(0, stringToRow('1')); buffer.lines.set(1, stringToRow('2')); selectionManager.selectLines(1, 2); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 1]); assert.deepEqual(selectionManager.model.finalSelectionEnd, [terminal.cols, 1]); }); }); describe('hasSelection', () => { it('should return whether there is a selection', () => { selectionManager.model.selectionStart = [0, 0]; selectionManager.model.selectionStartLength = 0; assert.equal(selectionManager.hasSelection, false); selectionManager.model.selectionEnd = [0, 0]; assert.equal(selectionManager.hasSelection, false); selectionManager.model.selectionEnd = [1, 0]; assert.equal(selectionManager.hasSelection, true); selectionManager.model.selectionEnd = [0, 1]; assert.equal(selectionManager.hasSelection, true); selectionManager.model.selectionEnd = [1, 1]; assert.equal(selectionManager.hasSelection, true); }); }); describe('column selection', () => { it('should select a column of text', () => { buffer.lines.length = 3; buffer.lines.set(0, stringToRow('abcdefghij')); buffer.lines.set(1, stringToRow('klmnopqrst')); buffer.lines.set(2, stringToRow('uvwxyz')); selectionManager.selectionMode = SelectionMode.COLUMN; selectionManager.model.selectionStart = [2, 0]; selectionManager.model.selectionEnd = [4, 2]; assert.equal(selectionManager.selectionText, 'cd\nmn\nwx'); }); it('should select a column of text without chopping up double width characters', () => { buffer.lines.length = 3; buffer.lines.set(0, stringToRow('a')); buffer.lines.set(1, stringToRow('語')); buffer.lines.set(2, stringToRow('b')); selectionManager.selectionMode = SelectionMode.COLUMN; selectionManager.model.selectionStart = [0, 0]; selectionManager.model.selectionEnd = [1, 2]; assert.equal(selectionManager.selectionText, 'a\n語\nb'); }); it('should select a column of text with single character emojis', () => { buffer.lines.length = 3; buffer.lines.set(0, stringToRow('a')); buffer.lines.set(1, stringToRow('☃')); buffer.lines.set(2, stringToRow('c')); selectionManager.selectionMode = SelectionMode.COLUMN; selectionManager.model.selectionStart = [0, 0]; selectionManager.model.selectionEnd = [1, 2]; assert.equal(selectionManager.selectionText, 'a\n☃\nc'); }); it('should select a column of text with double character emojis', () => { // TODO the case this is testing works for me in the demo webapp, // but doing it programmatically fails. buffer.lines.length = 3; buffer.lines.set(0, stringToRow('a ')); buffer.lines.set(1, stringArrayToRow(['😁', ' '])); buffer.lines.set(2, stringToRow('c ')); selectionManager.selectionMode = SelectionMode.COLUMN; selectionManager.model.selectionStart = [0, 0]; selectionManager.model.selectionEnd = [1, 2]; assert.equal(selectionManager.selectionText, 'a\n😁\nc'); }); }); }); xterm.js-3.8.1/src/SelectionManager.ts000066400000000000000000000762241341514612000176640ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal, ISelectionManager, IBuffer, CharData, IBufferLine } from './Types'; import { XtermListener } from './common/Types'; import { MouseHelper } from './utils/MouseHelper'; import * as Browser from './shared/utils/Browser'; import { CharMeasure } from './ui/CharMeasure'; import { EventEmitter } from './common/EventEmitter'; import { SelectionModel } from './SelectionModel'; import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX } from './Buffer'; import { AltClickHandler } from './handlers/AltClickHandler'; /** * The number of pixels the mouse needs to be above or below the viewport in * order to scroll at the maximum speed. */ const DRAG_SCROLL_MAX_THRESHOLD = 50; /** * The maximum scrolling speed */ const DRAG_SCROLL_MAX_SPEED = 15; /** * The number of milliseconds between drag scroll updates. */ const DRAG_SCROLL_INTERVAL = 50; /** * The maximum amount of time that can have elapsed for an alt click to move the * cursor. */ const ALT_CLICK_MOVE_CURSOR_TIME = 500; /** * A string containing all characters that are considered word separated by the * double click to select work logic. */ const WORD_SEPARATORS = ' ()[]{}\'"'; const NON_BREAKING_SPACE_CHAR = String.fromCharCode(160); const ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g'); /** * Represents a position of a word on a line. */ interface IWordPosition { start: number; length: number; } /** * A selection mode, this drives how the selection behaves on mouse move. */ export const enum SelectionMode { NORMAL, WORD, LINE, COLUMN } /** * A class that manages the selection of the terminal. With help from * SelectionModel, SelectionManager handles with all logic associated with * dealing with the selection, including handling mouse interaction, wide * characters and fetching the actual text within the selection. Rendering is * not handled by the SelectionManager but a 'refresh' event is fired when the * selection is ready to be redrawn. */ export class SelectionManager extends EventEmitter implements ISelectionManager { protected _model: SelectionModel; /** * The amount to scroll every drag scroll update (depends on how far the mouse * drag is above or below the terminal). */ private _dragScrollAmount: number; /** * The current selection mode. */ protected _activeSelectionMode: SelectionMode; /** * A setInterval timer that is active while the mouse is down whose callback * scrolls the viewport when necessary. */ private _dragScrollIntervalTimer: NodeJS.Timer; /** * The animation frame ID used for refreshing the selection. */ private _refreshAnimationFrame: number; /** * Whether selection is enabled. */ private _enabled = true; private _mouseMoveListener: EventListener; private _mouseUpListener: EventListener; private _trimListener: XtermListener; private _mouseDownTimeStamp: number; constructor( private _terminal: ITerminal, private _charMeasure: CharMeasure ) { super(); this._initListeners(); this.enable(); this._model = new SelectionModel(_terminal); this._activeSelectionMode = SelectionMode.NORMAL; } public dispose(): void { super.dispose(); this._removeMouseDownListeners(); } private get _buffer(): IBuffer { return this._terminal.buffers.active; } /** * Initializes listener variables. */ private _initListeners(): void { this._mouseMoveListener = event => this._onMouseMove(event); this._mouseUpListener = event => this._onMouseUp(event); this._trimListener = (amount: number) => this._onTrim(amount); this.initBuffersListeners(); } public initBuffersListeners(): void { this._terminal.buffer.lines.on('trim', this._trimListener); this._terminal.buffers.on('activate', e => this._onBufferActivate(e)); } /** * Disables the selection manager. This is useful for when terminal mouse * are enabled. */ public disable(): void { this.clearSelection(); this._enabled = false; } /** * Enable the selection manager. */ public enable(): void { this._enabled = true; } public get selectionStart(): [number, number] { return this._model.finalSelectionStart; } public get selectionEnd(): [number, number] { return this._model.finalSelectionEnd; } /** * Gets whether there is an active text selection. */ public get hasSelection(): boolean { const start = this._model.finalSelectionStart; const end = this._model.finalSelectionEnd; if (!start || !end) { return false; } return start[0] !== end[0] || start[1] !== end[1]; } /** * Gets the text currently selected. */ public get selectionText(): string { const start = this._model.finalSelectionStart; const end = this._model.finalSelectionEnd; if (!start || !end) { return ''; } const result: string[] = []; if (this._activeSelectionMode === SelectionMode.COLUMN) { // Ignore zero width selections if (start[0] === end[0]) { return ''; } for (let i = start[1]; i <= end[1]; i++) { const lineText = this._buffer.translateBufferLineToString(i, true, start[0], end[0]); result.push(lineText); } } else { // Get first row const startRowEndCol = start[1] === end[1] ? end[0] : null; result.push(this._buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol)); // Get middle rows for (let i = start[1] + 1; i <= end[1] - 1; i++) { const bufferLine = this._buffer.lines.get(i); const lineText = this._buffer.translateBufferLineToString(i, true); if (bufferLine.isWrapped) { result[result.length - 1] += lineText; } else { result.push(lineText); } } // Get final row if (start[1] !== end[1]) { const bufferLine = this._buffer.lines.get(end[1]); const lineText = this._buffer.translateBufferLineToString(end[1], true, 0, end[0]); if (bufferLine.isWrapped) { result[result.length - 1] += lineText; } else { result.push(lineText); } } } // Format string by replacing non-breaking space chars with regular spaces // and joining the array into a multi-line string. const formattedResult = result.map(line => { return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' '); }).join(Browser.isMSWindows ? '\r\n' : '\n'); return formattedResult; } /** * Clears the current terminal selection. */ public clearSelection(): void { this._model.clearSelection(); this._removeMouseDownListeners(); this.refresh(); } /** * Queues a refresh, redrawing the selection on the next opportunity. * @param isNewSelection Whether the selection should be registered as a new * selection on Linux. */ public refresh(isNewSelection?: boolean): void { // Queue the refresh for the renderer if (!this._refreshAnimationFrame) { this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh()); } // If the platform is Linux and the refresh call comes from a mouse event, // we need to update the selection for middle click to paste selection. if (Browser.isLinux && isNewSelection) { const selectionText = this.selectionText; if (selectionText.length) { this.emit('newselection', this.selectionText); } } } /** * Fires the refresh event, causing consumers to pick it up and redraw the * selection state. */ private _refresh(): void { this._refreshAnimationFrame = null; this.emit('refresh', { start: this._model.finalSelectionStart, end: this._model.finalSelectionEnd, columnSelectMode: this._activeSelectionMode === SelectionMode.COLUMN }); } /** * Checks if the current click was inside the current selection * @param event The mouse event */ public isClickInSelection(event: MouseEvent): boolean { const coords = this._getMouseBufferCoords(event); const start = this._model.finalSelectionStart; const end = this._model.finalSelectionEnd; if (!start || !end) { return false; } return (coords[1] > start[1] && coords[1] < end[1]) || (start[1] === end[1] && coords[1] === start[1] && coords[0] > start[0] && coords[0] < end[0]) || (start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]); } /** * Selects word at the current mouse event coordinates. * @param event The mouse event. */ public selectWordAtCursor(event: MouseEvent): void { const coords = this._getMouseBufferCoords(event); if (coords) { this._selectWordAt(coords, false); this._model.selectionEnd = null; this.refresh(true); } } /** * Selects all text within the terminal. */ public selectAll(): void { this._model.isSelectAllActive = true; this.refresh(); this._terminal.emit('selection'); } public selectLines(start: number, end: number): void { this._model.clearSelection(); start = Math.max(start, 0); end = Math.min(end, this._terminal.buffer.lines.length - 1); this._model.selectionStart = [0, start]; this._model.selectionEnd = [this._terminal.cols, end]; this.refresh(); this._terminal.emit('selection'); } /** * Handle the buffer being trimmed, adjust the selection position. * @param amount The amount the buffer is being trimmed. */ private _onTrim(amount: number): void { const needsRefresh = this._model.onTrim(amount); if (needsRefresh) { this.refresh(); } } /** * Gets the 0-based [x, y] buffer coordinates of the current mouse event. * @param event The mouse event. */ private _getMouseBufferCoords(event: MouseEvent): [number, number] { const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, true); if (!coords) { return null; } // Convert to 0-based coords[0]--; coords[1]--; // Convert viewport coords to buffer coords coords[1] += this._terminal.buffer.ydisp; return coords; } /** * Gets the amount the viewport should be scrolled based on how far out of the * terminal the mouse is. * @param event The mouse event. */ private _getMouseEventScrollAmount(event: MouseEvent): number { let offset = MouseHelper.getCoordsRelativeToElement(event, this._terminal.screenElement)[1]; const terminalHeight = this._terminal.rows * Math.ceil(this._charMeasure.height * this._terminal.options.lineHeight); if (offset >= 0 && offset <= terminalHeight) { return 0; } if (offset > terminalHeight) { offset -= terminalHeight; } offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD); offset /= DRAG_SCROLL_MAX_THRESHOLD; return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1)); } /** * Returns whether the selection manager should force selection, regardless of * whether the terminal is in mouse events mode. * @param event The mouse event. */ public shouldForceSelection(event: MouseEvent): boolean { if (Browser.isMac) { return event.altKey && this._terminal.options.macOptionClickForcesSelection; } return event.shiftKey; } /** * Handles te mousedown event, setting up for a new selection. * @param event The mousedown event. */ public onMouseDown(event: MouseEvent): void { this._mouseDownTimeStamp = event.timeStamp; // If we have selection, we want the context menu on right click even if the // terminal is in mouse mode. if (event.button === 2 && this.hasSelection) { return; } // Only action the primary button if (event.button !== 0) { return; } // Allow selection when using a specific modifier key, even when disabled if (!this._enabled) { if (!this.shouldForceSelection(event)) { return; } // Don't send the mouse down event to the current process, we want to select event.stopPropagation(); } // Tell the browser not to start a regular selection event.preventDefault(); // Reset drag scroll state this._dragScrollAmount = 0; if (this._enabled && event.shiftKey) { this._onIncrementalClick(event); } else { if (event.detail === 1) { this._onSingleClick(event); } else if (event.detail === 2) { this._onDoubleClick(event); } else if (event.detail === 3) { this._onTripleClick(event); } } this._addMouseDownListeners(); this.refresh(true); } /** * Adds listeners when mousedown is triggered. */ private _addMouseDownListeners(): void { // Listen on the document so that dragging outside of viewport works this._terminal.element.ownerDocument.addEventListener('mousemove', this._mouseMoveListener); this._terminal.element.ownerDocument.addEventListener('mouseup', this._mouseUpListener); this._dragScrollIntervalTimer = setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL); } /** * Removes the listeners that are registered when mousedown is triggered. */ private _removeMouseDownListeners(): void { if (this._terminal.element.ownerDocument) { this._terminal.element.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener); this._terminal.element.ownerDocument.removeEventListener('mouseup', this._mouseUpListener); } clearInterval(this._dragScrollIntervalTimer); this._dragScrollIntervalTimer = null; } /** * Performs an incremental click, setting the selection end position to the mouse * position. * @param event The mouse event. */ private _onIncrementalClick(event: MouseEvent): void { if (this._model.selectionStart) { this._model.selectionEnd = this._getMouseBufferCoords(event); } } /** * Performs a single click, resetting relevant state and setting the selection * start position. * @param event The mouse event. */ private _onSingleClick(event: MouseEvent): void { this._model.selectionStartLength = 0; this._model.isSelectAllActive = false; this._activeSelectionMode = this.shouldColumnSelect(event) ? SelectionMode.COLUMN : SelectionMode.NORMAL; // Initialize the new selection this._model.selectionStart = this._getMouseBufferCoords(event); if (!this._model.selectionStart) { return; } this._model.selectionEnd = null; // Ensure the line exists const line = this._buffer.lines.get(this._model.selectionStart[1]); if (!line) { return; } // Return early if the click event is not in the buffer (eg. in scroll bar) if (line.length >= this._model.selectionStart[0]) { return; } // If the mouse is over the second half of a wide character, adjust the // selection to cover the whole character const char = line.get(this._model.selectionStart[0]); if (char[CHAR_DATA_WIDTH_INDEX] === 0) { this._model.selectionStart[0]++; } } /** * Performs a double click, selecting the current work. * @param event The mouse event. */ private _onDoubleClick(event: MouseEvent): void { const coords = this._getMouseBufferCoords(event); if (coords) { this._activeSelectionMode = SelectionMode.WORD; this._selectWordAt(coords, true); } } /** * Performs a triple click, selecting the current line and activating line * select mode. * @param event The mouse event. */ private _onTripleClick(event: MouseEvent): void { const coords = this._getMouseBufferCoords(event); if (coords) { this._activeSelectionMode = SelectionMode.LINE; this._selectLineAt(coords[1]); } } /** * Returns whether the selection manager should operate in column select mode * @param event the mouse or keyboard event */ public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean { return event.altKey && !(Browser.isMac && this._terminal.options.macOptionClickForcesSelection); } /** * Handles the mousemove event when the mouse button is down, recording the * end of the selection and refreshing the selection. * @param event The mousemove event. */ private _onMouseMove(event: MouseEvent): void { // If the mousemove listener is active it means that a selection is // currently being made, we should stop propogation to prevent mouse events // to be sent to the pty. event.stopImmediatePropagation(); // Record the previous position so we know whether to redraw the selection // at the end. const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null; // Set the initial selection end based on the mouse coordinates this._model.selectionEnd = this._getMouseBufferCoords(event); if (!this._model.selectionEnd) { this.refresh(true); return; } // Select the entire line if line select mode is active. if (this._activeSelectionMode === SelectionMode.LINE) { if (this._model.selectionEnd[1] < this._model.selectionStart[1]) { this._model.selectionEnd[0] = 0; } else { this._model.selectionEnd[0] = this._terminal.cols; } } else if (this._activeSelectionMode === SelectionMode.WORD) { this._selectToWordAt(this._model.selectionEnd); } // Determine the amount of scrolling that will happen. this._dragScrollAmount = this._getMouseEventScrollAmount(event); // If the cursor was above or below the viewport, make sure it's at the // start or end of the viewport respectively. This should only happen when // NOT in column select mode. if (this._activeSelectionMode !== SelectionMode.COLUMN) { if (this._dragScrollAmount > 0) { this._model.selectionEnd[0] = this._terminal.cols; } else if (this._dragScrollAmount < 0) { this._model.selectionEnd[0] = 0; } } // If the character is a wide character include the cell to the right in the // selection. Note that selections at the very end of the line will never // have a character. if (this._model.selectionEnd[1] < this._buffer.lines.length) { const char = this._buffer.lines.get(this._model.selectionEnd[1]).get(this._model.selectionEnd[0]); if (char && char[CHAR_DATA_WIDTH_INDEX] === 0) { this._model.selectionEnd[0]++; } } // Only draw here if the selection changes. if (!previousSelectionEnd || previousSelectionEnd[0] !== this._model.selectionEnd[0] || previousSelectionEnd[1] !== this._model.selectionEnd[1]) { this.refresh(true); } } /** * The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the * scrolling of the viewport. */ private _dragScroll(): void { if (this._dragScrollAmount) { this._terminal.scrollLines(this._dragScrollAmount, false); // Re-evaluate selection // If the cursor was above or below the viewport, make sure it's at the // start or end of the viewport respectively. This should only happen when // NOT in column select mode. if (this._dragScrollAmount > 0) { if (this._activeSelectionMode !== SelectionMode.COLUMN) { this._model.selectionEnd[0] = this._terminal.cols; } this._model.selectionEnd[1] = Math.min(this._terminal.buffer.ydisp + this._terminal.rows, this._terminal.buffer.lines.length - 1); } else { if (this._activeSelectionMode !== SelectionMode.COLUMN) { this._model.selectionEnd[0] = 0; } this._model.selectionEnd[1] = this._terminal.buffer.ydisp; } this.refresh(); } } /** * Handles the mouseup event, removing the mousedown listeners. * @param event The mouseup event. */ private _onMouseUp(event: MouseEvent): void { const timeElapsed = event.timeStamp - this._mouseDownTimeStamp; this._removeMouseDownListeners(); if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME) { (new AltClickHandler(event, this._terminal)).move(); } else if (this.hasSelection) { this._terminal.emit('selection'); } } private _onBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void { this.clearSelection(); // Only adjust the selection on trim, shiftElements is rarely used (only in // reverseIndex) and delete in a splice is only ever used when the same // number of elements was just added. Given this is could actually be // beneficial to leave the selection as is for these cases. e.inactiveBuffer.lines.off('trim', this._trimListener); e.activeBuffer.lines.on('trim', this._trimListener); } /** * Converts a viewport column to the character index on the buffer line, the * latter takes into account wide characters. * @param coords The coordinates to find the 2 index for. */ private _convertViewportColToCharacterIndex(bufferLine: IBufferLine, coords: [number, number]): number { let charIndex = coords[0]; for (let i = 0; coords[0] >= i; i++) { const char = bufferLine.get(i); if (char[CHAR_DATA_WIDTH_INDEX] === 0) { // Wide characters aren't included in the line string so decrement the // index so the index is back on the wide character. charIndex--; } else if (char[CHAR_DATA_CHAR_INDEX].length > 1 && coords[0] !== i) { // Emojis take up multiple characters, so adjust accordingly. For these // we don't want ot include the character at the column as we're // returning the start index in the string, not the end index. charIndex += char[CHAR_DATA_CHAR_INDEX].length - 1; } } return charIndex; } public setSelection(col: number, row: number, length: number): void { this._model.clearSelection(); this._removeMouseDownListeners(); this._model.selectionStart = [col, row]; this._model.selectionStartLength = length; this.refresh(); } /** * Gets positional information for the word at the coordinated specified. * @param coords The coordinates to get the word at. */ private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean, followWrappedLinesAbove: boolean = true, followWrappedLinesBelow: boolean = true): IWordPosition { // Ensure coords are within viewport (eg. not within scroll bar) if (coords[0] >= this._terminal.cols) { return null; } const bufferLine = this._buffer.lines.get(coords[1]); if (!bufferLine) { return null; } const line = this._buffer.translateBufferLineToString(coords[1], false); // Get actual index, taking into consideration wide characters let startIndex = this._convertViewportColToCharacterIndex(bufferLine, coords); let endIndex = startIndex; // Record offset to be used later const charOffset = coords[0] - startIndex; let leftWideCharCount = 0; let rightWideCharCount = 0; let leftLongCharOffset = 0; let rightLongCharOffset = 0; if (line.charAt(startIndex) === ' ') { // Expand until non-whitespace is hit while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') { startIndex--; } while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') { endIndex++; } } else { // Expand until whitespace is hit. This algorithm works by scanning left // and right from the starting position, keeping both the index format // (line) and the column format (bufferLine) in sync. When a wide // character is hit, it is recorded and the column index is adjusted. let startCol = coords[0]; let endCol = coords[0]; // Consider the initial position, skip it and increment the wide char // variable if (bufferLine.get(startCol)[CHAR_DATA_WIDTH_INDEX] === 0) { leftWideCharCount++; startCol--; } if (bufferLine.get(endCol)[CHAR_DATA_WIDTH_INDEX] === 2) { rightWideCharCount++; endCol++; } // Adjust the end index for characters whose length are > 1 (emojis) if (bufferLine.get(endCol)[CHAR_DATA_CHAR_INDEX].length > 1) { rightLongCharOffset += bufferLine.get(endCol)[CHAR_DATA_CHAR_INDEX].length - 1; endIndex += bufferLine.get(endCol)[CHAR_DATA_CHAR_INDEX].length - 1; } // Expand the string in both directions until a space is hit while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine.get(startCol - 1))) { const char = bufferLine.get(startCol - 1); if (char[CHAR_DATA_WIDTH_INDEX] === 0) { // If the next character is a wide char, record it and skip the column leftWideCharCount++; startCol--; } else if (char[CHAR_DATA_CHAR_INDEX].length > 1) { // If the next character's string is longer than 1 char (eg. emoji), // adjust the index leftLongCharOffset += char[CHAR_DATA_CHAR_INDEX].length - 1; startIndex -= char[CHAR_DATA_CHAR_INDEX].length - 1; } startIndex--; startCol--; } while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine.get(endCol + 1))) { const char = bufferLine.get(endCol + 1); if (char[CHAR_DATA_WIDTH_INDEX] === 2) { // If the next character is a wide char, record it and skip the column rightWideCharCount++; endCol++; } else if (char[CHAR_DATA_CHAR_INDEX].length > 1) { // If the next character's string is longer than 1 char (eg. emoji), // adjust the index rightLongCharOffset += char[CHAR_DATA_CHAR_INDEX].length - 1; endIndex += char[CHAR_DATA_CHAR_INDEX].length - 1; } endIndex++; endCol++; } } // Incremenet the end index so it is at the start of the next character endIndex++; // Calculate the start _column_, converting the the string indexes back to // column coordinates. let start = startIndex // The index of the selection's start char in the line string + charOffset // The difference between the initial char's column and index - leftWideCharCount // The number of wide chars left of the initial char + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) // Calculate the length in _columns_, converting the the string indexes back // to column coordinates. let length = Math.min(this._terminal.cols, // Disallow lengths larger than the terminal cols endIndex // The index of the selection's end char in the line string - startIndex // The index of the selection's start char in the line string + leftWideCharCount // The number of wide chars left of the initial char + rightWideCharCount // The number of wide chars right of the initial char (inclusive) - leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis) - rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis) if (!allowWhitespaceOnlySelection && line.slice(startIndex, endIndex).trim() === '') { return null; } // Recurse upwards if the line is wrapped and the word wraps to the above line if (followWrappedLinesAbove) { if (start === 0 && bufferLine.get(0)[CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) { const previousBufferLine = this._buffer.lines.get(coords[1] - 1); if (previousBufferLine && bufferLine.isWrapped && previousBufferLine.get(this._terminal.cols - 1)[CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) { const previousLineWordPosition = this._getWordAt([this._terminal.cols - 1, coords[1] - 1], false, true, false); if (previousLineWordPosition) { const offset = this._terminal.cols - previousLineWordPosition.start; start -= offset; length += offset; } } } } // Recurse downwards if the line is wrapped and the word wraps to the next line if (followWrappedLinesBelow) { if (start + length === this._terminal.cols && bufferLine.get(this._terminal.cols - 1)[CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) { const nextBufferLine = this._buffer.lines.get(coords[1] + 1); if (nextBufferLine && nextBufferLine.isWrapped && nextBufferLine.get(0)[CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) { const nextLineWordPosition = this._getWordAt([0, coords[1] + 1], false, false, true); if (nextLineWordPosition) { length += nextLineWordPosition.length; } } } } return { start, length }; } /** * Selects the word at the coordinates specified. * @param coords The coordinates to get the word at. * @param allowWhitespaceOnlySelection If whitespace should be selected */ protected _selectWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean): void { const wordPosition = this._getWordAt(coords, allowWhitespaceOnlySelection); if (wordPosition) { // Adjust negative start value while (wordPosition.start < 0) { wordPosition.start += this._terminal.cols; coords[1]--; } this._model.selectionStart = [wordPosition.start, coords[1]]; this._model.selectionStartLength = wordPosition.length; } } /** * Sets the selection end to the word at the coordinated specified. * @param coords The coordinates to get the word at. */ private _selectToWordAt(coords: [number, number]): void { const wordPosition = this._getWordAt(coords, true); if (wordPosition) { let endRow = coords[1]; // Adjust negative start value while (wordPosition.start < 0) { wordPosition.start += this._terminal.cols; endRow--; } // Adjust wrapped length value, this only needs to happen when values are reversed as in that // case we're interested in the start of the word, not the end if (!this._model.areSelectionValuesReversed()) { while (wordPosition.start + wordPosition.length > this._terminal.cols) { wordPosition.length -= this._terminal.cols; endRow++; } } this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : wordPosition.start + wordPosition.length, endRow]; } } /** * Gets whether the character is considered a word separator by the select * word logic. * @param char The character to check. */ private _isCharWordSeparator(charData: CharData): boolean { // Zero width characters are never separators as they are always to the // right of wide characters if (charData[CHAR_DATA_WIDTH_INDEX] === 0) { return false; } return WORD_SEPARATORS.indexOf(charData[CHAR_DATA_CHAR_INDEX]) >= 0; } /** * Selects the line specified. * @param line The line index. */ protected _selectLineAt(line: number): void { const wrappedRange = this._buffer.getWrappedRangeForLine(line); this._model.selectionStart = [0, wrappedRange.first]; this._model.selectionEnd = [this._terminal.cols, wrappedRange.last]; this._model.selectionStartLength = 0; } } xterm.js-3.8.1/src/SelectionModel.test.ts000066400000000000000000000122101341514612000203110ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { ITerminal } from './Types'; import { SelectionModel } from './SelectionModel'; import { BufferSet } from './BufferSet'; import { MockTerminal } from './utils/TestUtils.test'; class TestSelectionModel extends SelectionModel { constructor( terminal: ITerminal ) { super(terminal); } } describe('SelectionManager', () => { let terminal: ITerminal; let model: TestSelectionModel; beforeEach(() => { terminal = new MockTerminal(); terminal.cols = 80; terminal.rows = 2; terminal.options.scrollback = 10; terminal.buffers = new BufferSet(terminal); terminal.buffer = terminal.buffers.active; model = new TestSelectionModel(terminal); }); describe('clearSelection', () => { it('should clear the final selection', () => { model.selectionStart = [0, 0]; model.selectionEnd = [10, 2]; assert.deepEqual(model.finalSelectionStart, [0, 0]); assert.deepEqual(model.finalSelectionEnd, [10, 2]); model.clearSelection(); assert.deepEqual(model.finalSelectionStart, null); assert.deepEqual(model.finalSelectionEnd, null); }); }); describe('areSelectionValuesReversed', () => { it('should return true when the selection end is before selection start', () => { model.selectionStart = [1, 0]; model.selectionEnd = [0, 0]; assert.equal(model.areSelectionValuesReversed(), true); model.selectionStart = [10, 2]; model.selectionEnd = [0, 0]; assert.equal(model.areSelectionValuesReversed(), true); }); it('should return false when the selection end is after selection start', () => { model.selectionStart = [0, 0]; model.selectionEnd = [1, 0]; assert.equal(model.areSelectionValuesReversed(), false); model.selectionStart = [0, 0]; model.selectionEnd = [10, 2]; assert.equal(model.areSelectionValuesReversed(), false); }); }); describe('onTrim', () => { it('should trim a portion of the selection when a part of it is trimmed', () => { model.selectionStart = [0, 0]; model.selectionEnd = [10, 2]; model.onTrim(1); assert.deepEqual(model.finalSelectionStart, [0, 0]); assert.deepEqual(model.finalSelectionEnd, [10, 1]); model.onTrim(1); assert.deepEqual(model.finalSelectionStart, [0, 0]); assert.deepEqual(model.finalSelectionEnd, [10, 0]); }); it('should clear selection when it is trimmed in its entirety', () => { model.selectionStart = [0, 0]; model.selectionEnd = [10, 0]; model.onTrim(1); assert.deepEqual(model.finalSelectionStart, null); assert.deepEqual(model.finalSelectionEnd, null); }); }); describe('finalSelectionStart', () => { it('should return the start of the buffer if select all is active', () => { model.isSelectAllActive = true; assert.deepEqual(model.finalSelectionStart, [0, 0]); }); it('should return selection start if there is no selection end', () => { model.selectionStart = [2, 2]; assert.deepEqual(model.finalSelectionStart, [2, 2]); }); it('should return selection end if values are reversed', () => { model.selectionStart = [2, 2]; model.selectionEnd = [3, 2]; assert.deepEqual(model.finalSelectionStart, [2, 2]); model.selectionEnd = [1, 2]; assert.deepEqual(model.finalSelectionStart, [1, 2]); }); }); describe('finalSelectionEnd', () => { it('should return the end of the buffer if select all is active', () => { model.isSelectAllActive = true; assert.deepEqual(model.finalSelectionEnd, [80, 1]); }); it('should return null if there is no selection start', () => { assert.equal(model.finalSelectionEnd, null); model.selectionEnd = [1, 2]; assert.equal(model.finalSelectionEnd, null); }); it('should return selection start + length if there is no selection end', () => { model.selectionStart = [2, 2]; model.selectionStartLength = 2; assert.deepEqual(model.finalSelectionEnd, [4, 2]); }); it('should return selection start + length if values are reversed', () => { model.selectionStart = [2, 2]; model.selectionStartLength = 2; model.selectionEnd = [2, 1]; assert.deepEqual(model.finalSelectionEnd, [4, 2]); }); it('should return selection start + length if selection end is inside the start selection', () => { model.selectionStart = [2, 2]; model.selectionStartLength = 2; model.selectionEnd = [3, 2]; assert.deepEqual(model.finalSelectionEnd, [4, 2]); }); it('should return the end on a different row when start + length overflows onto a following row', () => { model.selectionStart = [78, 2]; model.selectionStartLength = 4; assert.deepEqual(model.finalSelectionEnd, [2, 3]); }); it('should return selection end if selection end is after selection start + length', () => { model.selectionStart = [2, 2]; model.selectionStartLength = 2; model.selectionEnd = [5, 2]; assert.deepEqual(model.finalSelectionEnd, [5, 2]); }); }); }); xterm.js-3.8.1/src/SelectionModel.ts000066400000000000000000000076441341514612000173520ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal } from './Types'; /** * Represents a selection within the buffer. This model only cares about column * and row coordinates, not wide characters. */ export class SelectionModel { /** * Whether select all is currently active. */ public isSelectAllActive: boolean; /** * The [x, y] position the selection starts at. */ public selectionStart: [number, number]; /** * The minimal length of the selection from the start position. When double * clicking on a word, the word will be selected which makes the selection * start at the start of the word and makes this variable the length. */ public selectionStartLength: number; /** * The [x, y] position the selection ends at. */ public selectionEnd: [number, number]; constructor( private _terminal: ITerminal ) { this.clearSelection(); } /** * Clears the current selection. */ public clearSelection(): void { this.selectionStart = null; this.selectionEnd = null; this.isSelectAllActive = false; this.selectionStartLength = 0; } /** * The final selection start, taking into consideration select all. */ public get finalSelectionStart(): [number, number] { if (this.isSelectAllActive) { return [0, 0]; } if (!this.selectionEnd || !this.selectionStart) { return this.selectionStart; } return this.areSelectionValuesReversed() ? this.selectionEnd : this.selectionStart; } /** * The final selection end, taking into consideration select all, double click * word selection and triple click line selection. */ public get finalSelectionEnd(): [number, number] { if (this.isSelectAllActive) { return [this._terminal.cols, this._terminal.buffer.ybase + this._terminal.rows - 1]; } if (!this.selectionStart) { return null; } // Use the selection start + length if the end doesn't exist or they're reversed if (!this.selectionEnd || this.areSelectionValuesReversed()) { const startPlusLength = this.selectionStart[0] + this.selectionStartLength; if (startPlusLength > this._terminal.cols) { return [startPlusLength % this._terminal.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._terminal.cols)]; } return [startPlusLength, this.selectionStart[1]]; } // Ensure the the word/line is selected after a double/triple click if (this.selectionStartLength) { // Select the larger of the two when start and end are on the same line if (this.selectionEnd[1] === this.selectionStart[1]) { return [Math.max(this.selectionStart[0] + this.selectionStartLength, this.selectionEnd[0]), this.selectionEnd[1]]; } } return this.selectionEnd; } /** * Returns whether the selection start and end are reversed. */ public areSelectionValuesReversed(): boolean { const start = this.selectionStart; const end = this.selectionEnd; if (!start || !end) { return false; } return start[1] > end[1] || (start[1] === end[1] && start[0] > end[0]); } /** * Handle the buffer being trimmed, adjust the selection position. * @param amount The amount the buffer is being trimmed. * @return Whether a refresh is necessary. */ public onTrim(amount: number): boolean { // Adjust the selection position based on the trimmed amount. if (this.selectionStart) { this.selectionStart[1] -= amount; } if (this.selectionEnd) { this.selectionEnd[1] -= amount; } // The selection has moved off the buffer, clear it. if (this.selectionEnd && this.selectionEnd[1] < 0) { this.clearSelection(); return true; } // If the selection start is trimmed, ensure the start column is 0. if (this.selectionStart && this.selectionStart[1] < 0) { this.selectionStart[1] = 0; } return false; } } xterm.js-3.8.1/src/SoundManager.ts000066400000000000000000000045231341514612000170200ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal, ISoundManager } from './Types'; // Source: https://freesound.org/people/altemark/sounds/45759/ // This sound is released under the Creative Commons Attribution 3.0 Unported // (CC BY 3.0) license. It was created by 'altemark'. No modifications have been // made, apart from the conversion to base64. export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg=='; export class SoundManager implements ISoundManager { private _audioContext: AudioContext; constructor( private _terminal: ITerminal ) { } public playBellSound(): void { const audioContextCtor: typeof AudioContext = (window).AudioContext || (window).webkitAudioContext; if (!this._audioContext && audioContextCtor) { this._audioContext = new audioContextCtor(); } if (this._audioContext) { const bellAudioSource = this._audioContext.createBufferSource(); const context = this._audioContext; this._audioContext.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => { bellAudioSource.buffer = buffer; bellAudioSource.connect(context.destination); bellAudioSource.start(0); }); } else { console.warn('Sorry, but the Web Audio API is not supported by your browser. Please, consider upgrading to the latest version'); } } private _base64ToArrayBuffer(base64: string): ArrayBuffer { const binaryString = window.atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; } private _removeMimeType(dataURI: string): string { // Split the input to get the mime-type and the data itself const splitUri = dataURI.split(','); // Return only the data return splitUri[1]; } } xterm.js-3.8.1/src/Strings.ts000066400000000000000000000004071341514612000160630ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ export let blankLine = 'Blank line'; export let promptLabel = 'Terminal input'; export let tooMuchOutput = 'Too much output to announce, navigate to rows manually to read'; xterm.js-3.8.1/src/Terminal.integration.ts000066400000000000000000000132341341514612000205310ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT * * This file contains integration tests for xterm.js. */ import * as cp from 'child_process'; import * as glob from 'glob'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as pty from 'node-pty'; import { assert } from 'chai'; import { Terminal } from './Terminal'; import { CHAR_DATA_CHAR_INDEX } from './Buffer'; import { IViewport } from './Types'; class TestTerminal extends Terminal { innerWrite(): void { this._innerWrite(); } } let primitivePty: any; // fake sychronous pty write - read // we just pipe the data from slave to master as a child program would do // pty.js opens pipe fds with O_NONBLOCK // just wait 10ms instead of setting fds to blocking mode function ptyWriteRead(data: string, cb: (result: string) => void): void { fs.writeSync(primitivePty.slave, data); setTimeout(() => { const b = new Buffer(64000); const bytes = fs.readSync(primitivePty.master, b, 0, 64000, null); cb(b.toString('utf8', 0, bytes)); }); } // make sure raw pty is at x=0 and has no pending data function ptyReset(cb: (result: string) => void): void { ptyWriteRead('\r\n', cb); } /* debug helpers */ // generate colorful noisy output to compare xterm and emulator cell states function formatError(input: string, output: string, expected: string): string { function addLineNumber(start: number, color: string): (s: string) => string { let counter = start || 0; return function(s: string): string { counter += 1; return '\x1b[33m' + (' ' + counter).slice(-2) + color + s; }; } const line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890'; let s = ''; s += '\n\x1b[34m' + JSON.stringify(input); s += '\n\x1b[33m ' + line80 + '\n'; s += output.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n'); s += '\n\x1b[33m ' + line80 + '\n'; s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n'); return s; } // simple debug output of terminal cells function terminalToString(term: Terminal): string { let result = ''; let lineText = ''; for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) { lineText = ''; for (let cell = 0; cell < term.cols; ++cell) { lineText += term.buffer.lines.get(line).get(cell)[CHAR_DATA_CHAR_INDEX]; } // rtrim empty cells as xterm does lineText = lineText.replace(/\s+$/, ''); result += lineText; result += '\n'; } return result; } // Skip tests on Windows since pty.open isn't supported if (os.platform() !== 'win32') { const consoleLog = console.log; // expect files need terminal at 80x25! const cols = 80; const rows = 25; /** some helpers for pty interaction */ // we need a pty in between to get the termios decorations // for the basic test cases a raw pty device is enough primitivePty = (pty).native.open(cols, rows); /** tests */ describe('xterm output comparison', () => { let xterm: TestTerminal; beforeEach(() => { xterm = new TestTerminal({ cols: cols, rows: rows }); xterm.refresh = () => {}; xterm.viewport = { syncScrollArea: () => {} }; }); // omit stack trace for escape sequence files Error.stackTraceLimit = 0; const files = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')}); // for (let i = 0; i < files.length; ++i) console.debug(i, files[i]); // only successful tests for now const skip = [ 10, 16, 17, 19, 32, 33, 34, 35, 36, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 63, 68 ]; if (os.platform() === 'darwin') { // These are failing on macOS only skip.push(3, 7, 11, 67); } for (let i = 0; i < files.length; i++) { if (skip.indexOf(i) >= 0) { continue; } ((filename: string) => { it(filename.split('/').slice(-1)[0], done => { ptyReset(() => { const inFile = fs.readFileSync(filename, 'utf8'); ptyWriteRead(inFile, fromPty => { // uncomment this to get log from terminal // console.log = function(){}; // Perform a synchronous .write(data) xterm.writeBuffer.push(fromPty); xterm.innerWrite(); const fromEmulator = terminalToString(xterm); console.log = consoleLog; const expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8'); // Some of the tests have whitespace on the right of lines, we trim all the linex // from xterm.js so ignore this for now at least. const expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n'); if (fromEmulator !== expectedRightTrimmed) { // uncomment to get noisy output throw new Error(formatError(inFile, fromEmulator, expected)); // throw new Error('mismatch'); } done(); }); }); }); })(files[i]); } }); } describe('typings', () => { it('should throw no compile errors', function (): void { this.timeout(20000); let tsc = path.join(__dirname, '..', 'node_modules', '.bin', 'tsc'); if (process.platform === 'win32') { tsc += '.cmd'; } const fixtureDir = path.join(__dirname, '..', 'fixtures', 'typings-test'); const result = cp.spawnSync(tsc, { cwd: fixtureDir }); assert.equal(result.status, 0, `build did not succeed:\nstdout: ${result.stdout.toString()}\nstderr: ${result.stderr.toString()}\n`); }); }); xterm.js-3.8.1/src/Terminal.test.ts000066400000000000000000001205761341514612000171750ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import { Terminal } from './Terminal'; import { MockViewport, MockCompositionHelper, MockRenderer } from './utils/TestUtils.test'; import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, DEFAULT_ATTR } from './Buffer'; import { BufferLine } from './BufferLine'; const INIT_COLS = 80; const INIT_ROWS = 24; class TestTerminal extends Terminal { public keyDown(ev: any): boolean { return this._keyDown(ev); } public keyPress(ev: any): boolean { return this._keyPress(ev); } } describe('term.js addons', () => { let term: TestTerminal; const termOptions = { cols: INIT_COLS, rows: INIT_ROWS }; beforeEach(() => { term = new TestTerminal(termOptions); term.refresh = () => { }; (term).renderer = new MockRenderer(); term.viewport = new MockViewport(); (term)._compositionHelper = new MockCompositionHelper(); // Force synchronous writes term.write = (data) => { term.writeBuffer.push(data); (term)._innerWrite(); }; (term).element = { classList: { toggle: () => { }, remove: () => { } } }; }); it('should not mutate the options parameter', () => { term.setOption('cols', 1000); assert.deepEqual(termOptions, { cols: INIT_COLS, rows: INIT_ROWS }); }); describe('getOption', () => { it('should retrieve the option correctly', () => { // In the `options` namespace. term.options.cursorBlink = true; assert.equal(term.getOption('cursorBlink'), true); // On the Terminal instance delete term.options.cursorBlink; term.options.cursorBlink = false; assert.equal(term.getOption('cursorBlink'), false); }); it('should throw when retrieving a non-existant option', () => { assert.throws(term.getOption.bind(term, 'fake', true)); }); }); describe('on', () => { beforeEach(() => { term.on('key', () => { }); term.on('keypress', () => { }); term.on('keydown', () => { }); }); describe('data', () => { it('should emit a data event', (done) => { term.on('data', () => { done(); }); term.handler('fake'); }); }); describe(`keypress (including 'key' event)`, () => { it('should receive a string and event object', (done) => { let steps = 0; const finish = () => { if ((++steps) === 2) { done(); } }; const evKeyPress = { preventDefault: () => { }, stopPropagation: () => { }, type: 'keypress', keyCode: 13 }; term.on('keypress', (key, event) => { assert.equal(typeof key, 'string'); expect(event).to.be.an.instanceof(Object); finish(); }); term.on('key', (key, event) => { assert.equal(typeof key, 'string'); expect(event).to.be.an.instanceof(Object); finish(); }); term.keyPress(evKeyPress); }); }); describe(`keydown (including 'key' event)`, () => { it(`should receive an event object for 'keydown' and a string and event object for 'key'`, (done) => { let steps = 0; const finish = () => { if ((++steps) === 2) { done(); } }; const evKeyDown = { preventDefault: () => { }, stopPropagation: () => { }, type: 'keydown', keyCode: 13 }; term.on('keydown', (event) => { expect(event).to.be.an.instanceof(Object); finish(); }); term.on('key', (key, event) => { assert.equal(typeof key, 'string'); expect(event).to.be.an.instanceof(Object); finish(); }); term.keyDown(evKeyDown); }); }); describe('resize', () => { it('should receive an object: {cols: number, rows: number}', (done) => { term.on('resize', (data) => { expect(data).to.have.keys(['cols', 'rows']); assert.equal(typeof data.cols, 'number'); assert.equal(typeof data.rows, 'number'); done(); }); term.resize(1, 1); }); }); describe('scroll', () => { it('should receive a number', (done) => { term.on('scroll', (ydisp) => { assert.equal(typeof ydisp, 'number'); done(); }); term.scroll(); }); }); describe('title', () => { it('should receive a string', (done) => { term.on('title', (title) => { assert.equal(typeof title, 'string'); done(); }); term.handleTitle('title'); }); }); }); describe('attachCustomKeyEventHandler', () => { const evKeyDown = { preventDefault: () => { }, stopPropagation: () => { }, type: 'keydown', keyCode: 77 }; const evKeyPress = { preventDefault: () => { }, stopPropagation: () => { }, type: 'keypress', keyCode: 77 }; beforeEach(() => { term.handler = () => { }; term.showCursor = () => { }; term.clearSelection = () => { }; }); it('should process the keydown/keypress event based on what the handler returns', () => { assert.equal(term.keyDown(evKeyDown), true); assert.equal(term.keyPress(evKeyPress), true); term.attachCustomKeyEventHandler(ev => ev.keyCode === 77); assert.equal(term.keyDown(evKeyDown), true); assert.equal(term.keyPress(evKeyPress), true); term.attachCustomKeyEventHandler(ev => ev.keyCode !== 77); assert.equal(term.keyDown(evKeyDown), false); assert.equal(term.keyPress(evKeyPress), false); }); it('should alive after reset(ESC c Full Reset (RIS))', () => { term.attachCustomKeyEventHandler(ev => ev.keyCode !== 77); assert.equal(term.keyDown(evKeyDown), false); assert.equal(term.keyPress(evKeyPress), false); term.reset(); assert.equal(term.keyDown(evKeyDown), false); assert.equal(term.keyPress(evKeyPress), false); }); }); describe('setOption', () => { it('should set the option correctly', () => { term.setOption('cursorBlink', true); assert.equal(term.options.cursorBlink, true); term.setOption('cursorBlink', false); assert.equal(term.options.cursorBlink, false); }); it('should throw when setting a non-existant option', () => { assert.throws(term.setOption.bind(term, 'fake', true)); }); }); describe('reset', () => { it('should not affect cursorState', () => { term.cursorState = 1; term.reset(); assert.equal(term.cursorState, 1); term.cursorState = 0; term.reset(); assert.equal(term.cursorState, 0); }); }); describe('clear', () => { it('should clear a buffer equal to rows', () => { const promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y); term.clear(); assert.equal(term.buffer.y, 0); assert.equal(term.buffer.ybase, 0); assert.equal(term.buffer.ydisp, 0); assert.equal(term.buffer.lines.length, term.rows); assert.deepEqual(term.buffer.lines.get(0), promptLine); for (let i = 1; i < term.rows; i++) { assert.deepEqual(term.buffer.lines.get(i), BufferLine.blankLine(term.cols, DEFAULT_ATTR)); } }); it('should clear a buffer larger than rows', () => { // Fill the buffer with dummy rows for (let i = 0; i < term.rows * 2; i++) { term.write('test\n'); } const promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y); term.clear(); assert.equal(term.buffer.y, 0); assert.equal(term.buffer.ybase, 0); assert.equal(term.buffer.ydisp, 0); assert.equal(term.buffer.lines.length, term.rows); assert.deepEqual(term.buffer.lines.get(0), promptLine); for (let i = 1; i < term.rows; i++) { assert.deepEqual(term.buffer.lines.get(i), BufferLine.blankLine(term.cols, DEFAULT_ATTR)); } }); it('should not break the prompt when cleared twice', () => { const promptLine = term.buffer.lines.get(term.buffer.ybase + term.buffer.y); term.clear(); term.clear(); assert.equal(term.buffer.y, 0); assert.equal(term.buffer.ybase, 0); assert.equal(term.buffer.ydisp, 0); assert.equal(term.buffer.lines.length, term.rows); assert.deepEqual(term.buffer.lines.get(0), promptLine); for (let i = 1; i < term.rows; i++) { assert.deepEqual(term.buffer.lines.get(i), BufferLine.blankLine(term.cols, DEFAULT_ATTR)); } }); }); describe('scroll', () => { describe('scrollLines', () => { let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 2; i++) { term.writeln('test'); } startYDisp = term.rows + 1; }); it('should scroll a single line', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollLines(-1); assert.equal(term.buffer.ydisp, startYDisp - 1); term.scrollLines(1); assert.equal(term.buffer.ydisp, startYDisp); }); it('should scroll multiple lines', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollLines(-5); assert.equal(term.buffer.ydisp, startYDisp - 5); term.scrollLines(5); assert.equal(term.buffer.ydisp, startYDisp); }); it('should not scroll beyond the bounds of the buffer', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollLines(1); assert.equal(term.buffer.ydisp, startYDisp); for (let i = 0; i < startYDisp; i++) { term.scrollLines(-1); } assert.equal(term.buffer.ydisp, 0); term.scrollLines(-1); assert.equal(term.buffer.ydisp, 0); }); }); describe('scrollPages', () => { let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); } startYDisp = (term.rows * 2) + 1; }); it('should scroll a single page', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollPages(-1); assert.equal(term.buffer.ydisp, startYDisp - (term.rows - 1)); term.scrollPages(1); assert.equal(term.buffer.ydisp, startYDisp); }); it('should scroll a multiple pages', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollPages(-2); assert.equal(term.buffer.ydisp, startYDisp - (term.rows - 1) * 2); term.scrollPages(2); assert.equal(term.buffer.ydisp, startYDisp); }); }); describe('scrollToTop', () => { beforeEach(() => { for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); } }); it('should scroll to the top', () => { assert.notEqual(term.buffer.ydisp, 0); term.scrollToTop(); assert.equal(term.buffer.ydisp, 0); }); }); describe('scrollToBottom', () => { let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); } startYDisp = (term.rows * 2) + 1; }); it('should scroll to the bottom', () => { term.scrollLines(-1); term.scrollToBottom(); assert.equal(term.buffer.ydisp, startYDisp); term.scrollPages(-1); term.scrollToBottom(); assert.equal(term.buffer.ydisp, startYDisp); term.scrollToTop(); term.scrollToBottom(); assert.equal(term.buffer.ydisp, startYDisp); }); }); describe('scrollToLine', () => { let startYDisp: number; beforeEach(() => { for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); } startYDisp = (term.rows * 2) + 1; }); it('should scroll to requested line', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollToLine(0); assert.equal(term.buffer.ydisp, 0); term.scrollToLine(10); assert.equal(term.buffer.ydisp, 10); term.scrollToLine(startYDisp); assert.equal(term.buffer.ydisp, startYDisp); term.scrollToLine(20); assert.equal(term.buffer.ydisp, 20); }); it('should not scroll beyond boundary lines', () => { assert.equal(term.buffer.ydisp, startYDisp); term.scrollToLine(-1); assert.equal(term.buffer.ydisp, 0); term.scrollToLine(startYDisp + 1); assert.equal(term.buffer.ydisp, startYDisp); }); }); describe('keyPress', () => { it('should scroll down, when a key is pressed and terminal is scrolled up', () => { const event = { type: 'keydown', key: 'a', keyCode: 65, preventDefault: () => { }, stopPropagation: () => { } }; term.buffer.ydisp = 0; term.buffer.ybase = 40; term.keyPress(event); // Ensure that now the terminal is scrolled to bottom assert.equal(term.buffer.ydisp, term.buffer.ybase); }); it('should not scroll down, when a custom keydown handler prevents the event', () => { // Add some output to the terminal for (let i = 0; i < term.rows * 3; i++) { term.writeln('test'); } const startYDisp = (term.rows * 2) + 1; term.attachCustomKeyEventHandler(() => { return false; }); assert.equal(term.buffer.ydisp, startYDisp); term.scrollLines(-1); assert.equal(term.buffer.ydisp, startYDisp - 1); term.keyPress({ keyCode: 0 }); assert.equal(term.buffer.ydisp, startYDisp - 1); }); }); describe('scroll() function', () => { describe('when scrollback > 0', () => { it('should create a new line and scroll', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(INIT_ROWS - 1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.y = INIT_ROWS - 1; // Move cursor to last line term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS + 1); assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'a'); assert.equal(term.buffer.lines.get(INIT_ROWS - 1).get(0)[CHAR_DATA_CHAR_INDEX], 'b'); assert.equal(term.buffer.lines.get(INIT_ROWS).get(0)[CHAR_DATA_CHAR_INDEX], ' '); }); it('should properly scroll inside a scroll region (scrollTop set)', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX] = 'c'; term.buffer.y = INIT_ROWS - 1; // Move cursor to last line term.buffer.scrollTop = 1; term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS); assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'a'); assert.equal(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX], 'c'); }); it('should properly scroll inside a scroll region (scrollBottom set)', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX] = 'c'; term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX] = 'd'; term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX] = 'e'; term.buffer.y = 3; term.buffer.scrollBottom = 3; term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS + 1); assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'a', '\'a\' should be pushed to the scrollback'); assert.equal(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX], 'b'); assert.equal(term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX], 'c'); assert.equal(term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX], 'd'); assert.equal(term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX], ' ', 'a blank line should be added at scrollBottom\'s index'); assert.equal(term.buffer.lines.get(5).get(0)[CHAR_DATA_CHAR_INDEX], 'e'); }); it('should properly scroll inside a scroll region (scrollTop and scrollBottom set)', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX] = 'c'; term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX] = 'd'; term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX] = 'e'; term.buffer.y = INIT_ROWS - 1; // Move cursor to last line term.buffer.scrollTop = 1; term.buffer.scrollBottom = 3; term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS); assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'a'); assert.equal(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX], 'c', '\'b\' should be removed from the buffer'); assert.equal(term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX], 'd'); assert.equal(term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX], ' ', 'a blank line should be added at scrollBottom\'s index'); assert.equal(term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX], 'e'); }); }); describe('when scrollback === 0', () => { beforeEach(() => { term.setOption('scrollback', 0); assert.equal(term.buffer.lines.maxLength, INIT_ROWS); }); it('should create a new line and shift everything up', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.lines.get(INIT_ROWS - 1).get(0)[CHAR_DATA_CHAR_INDEX] = 'c'; term.buffer.y = INIT_ROWS - 1; // Move cursor to last line assert.equal(term.buffer.lines.length, INIT_ROWS); term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS); // 'a' gets pushed out of buffer assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'b'); assert.equal(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX], ' '); assert.equal(term.buffer.lines.get(INIT_ROWS - 2).get(0)[CHAR_DATA_CHAR_INDEX], 'c'); assert.equal(term.buffer.lines.get(INIT_ROWS - 1).get(0)[CHAR_DATA_CHAR_INDEX], ' '); }); it('should properly scroll inside a scroll region (scrollTop set)', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX] = 'c'; term.buffer.y = INIT_ROWS - 1; // Move cursor to last line term.buffer.scrollTop = 1; term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS); assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'a'); assert.equal(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX], 'c'); }); it('should properly scroll inside a scroll region (scrollBottom set)', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX] = 'c'; term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX] = 'd'; term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX] = 'e'; term.buffer.y = 3; term.buffer.scrollBottom = 3; term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS); assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'b'); assert.equal(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX], 'c'); assert.equal(term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX], 'd'); assert.equal(term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX], ' ', 'a blank line should be added at scrollBottom\'s index'); assert.equal(term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX], 'e'); }); it('should properly scroll inside a scroll region (scrollTop and scrollBottom set)', () => { term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX] = 'a'; term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX] = 'b'; term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX] = 'c'; term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX] = 'd'; term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX] = 'e'; term.buffer.y = INIT_ROWS - 1; // Move cursor to last line term.buffer.scrollTop = 1; term.buffer.scrollBottom = 3; term.scroll(); assert.equal(term.buffer.lines.length, INIT_ROWS); assert.equal(term.buffer.lines.get(0).get(0)[CHAR_DATA_CHAR_INDEX], 'a'); assert.equal(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX], 'c', '\'b\' should be removed from the buffer'); assert.equal(term.buffer.lines.get(2).get(0)[CHAR_DATA_CHAR_INDEX], 'd'); assert.equal(term.buffer.lines.get(3).get(0)[CHAR_DATA_CHAR_INDEX], ' ', 'a blank line should be added at scrollBottom\'s index'); assert.equal(term.buffer.lines.get(4).get(0)[CHAR_DATA_CHAR_INDEX], 'e'); }); }); }); }); describe('Third level shift', () => { let evKeyDown: any; let evKeyPress: any; beforeEach(() => { term.handler = () => { }; term.showCursor = () => { }; term.clearSelection = () => { }; // term.compositionHelper = { // isComposing: false, // keydown: { // bind: () => { // return () => { return true; }; // } // } // }; evKeyDown = { preventDefault: () => { }, stopPropagation: () => { }, type: 'keydown', altKey: null, keyCode: null }; evKeyPress = { preventDefault: () => { }, stopPropagation: () => { }, type: 'keypress', altKey: null, charCode: null, keyCode: null }; }); describe('with macOptionIsMeta', () => { beforeEach(() => { term.browser.isMac = true; term.setOption('macOptionIsMeta', true); }); it('should interfere with the alt key on keyDown', () => { evKeyDown.altKey = true; evKeyDown.keyCode = 81; assert.equal(term.keyDown(evKeyDown), false); evKeyDown.altKey = true; evKeyDown.keyCode = 192; assert.equal(term.keyDown(evKeyDown), false); }); }); describe('On Mac OS', () => { beforeEach(() => { term.browser.isMac = true; }); it('should not interfere with the alt key on keyDown', () => { evKeyDown.altKey = true; evKeyDown.keyCode = 81; assert.equal(term.keyDown(evKeyDown), true); evKeyDown.altKey = true; evKeyDown.keyCode = 192; assert.equal(term.keyDown(evKeyDown), true); }); it('should interefere with the alt + arrow keys', () => { evKeyDown.altKey = true; evKeyDown.keyCode = 37; assert.equal(term.keyDown(evKeyDown), false); evKeyDown.altKey = true; evKeyDown.keyCode = 39; assert.equal(term.keyDown(evKeyDown), false); }); it('should emit key with alt + key on keyPress', (done) => { const keys = ['@', '@', '\\', '\\', '|', '|']; term.on('keypress', (key) => { if (key) { const index = keys.indexOf(key); assert(index !== -1, 'Emitted wrong key: ' + key); keys.splice(index, 1); } if (keys.length === 0) done(); }); evKeyPress.altKey = true; // @ evKeyPress.charCode = null; evKeyPress.keyCode = 64; term.keyPress(evKeyPress); // Firefox @ evKeyPress.charCode = 64; evKeyPress.keyCode = 0; term.keyPress(evKeyPress); // \ evKeyPress.charCode = null; evKeyPress.keyCode = 92; term.keyPress(evKeyPress); // Firefox \ evKeyPress.charCode = 92; evKeyPress.keyCode = 0; term.keyPress(evKeyPress); // | evKeyPress.charCode = null; evKeyPress.keyCode = 124; term.keyPress(evKeyPress); // Firefox | evKeyPress.charCode = 124; evKeyPress.keyCode = 0; term.keyPress(evKeyPress); }); }); describe('On MS Windows', () => { beforeEach(() => { term.browser.isMSWindows = true; }); it('should not interfere with the alt + ctrl key on keyDown', () => { evKeyPress.altKey = true; evKeyPress.ctrlKey = true; evKeyPress.keyCode = 81; assert.equal(term.keyDown(evKeyPress), true); evKeyDown.altKey = true; evKeyDown.ctrlKey = true; evKeyDown.keyCode = 81; assert.equal(term.keyDown(evKeyDown), true); }); it('should interefere with the alt + ctrl + arrow keys', () => { evKeyDown.altKey = true; evKeyDown.ctrlKey = true; evKeyDown.keyCode = 37; assert.equal(term.keyDown(evKeyDown), false); evKeyDown.keyCode = 39; assert.equal(term.keyDown(evKeyDown), false); }); it('should emit key with alt + ctrl + key on keyPress', (done) => { const keys = ['@', '@', '\\', '\\', '|', '|']; term.on('keypress', (key) => { if (key) { const index = keys.indexOf(key); assert(index !== -1, 'Emitted wrong key: ' + key); keys.splice(index, 1); } if (keys.length === 0) done(); }); evKeyPress.altKey = true; evKeyPress.ctrlKey = true; // @ evKeyPress.charCode = null; evKeyPress.keyCode = 64; term.keyPress(evKeyPress); // Firefox @ evKeyPress.charCode = 64; evKeyPress.keyCode = 0; term.keyPress(evKeyPress); // \ evKeyPress.charCode = null; evKeyPress.keyCode = 92; term.keyPress(evKeyPress); // Firefox \ evKeyPress.charCode = 92; evKeyPress.keyCode = 0; term.keyPress(evKeyPress); // | evKeyPress.charCode = null; evKeyPress.keyCode = 124; term.keyPress(evKeyPress); // Firefox | evKeyPress.charCode = 124; evKeyPress.keyCode = 0; term.keyPress(evKeyPress); }); }); }); describe('unicode - surrogates', () => { it('2 characters per cell', function (): void { this.timeout(10000); // This is needed because istanbul patches code and slows it down const high = String.fromCharCode(0xD800); for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.write(high + String.fromCharCode(i)); const tchar = term.buffer.lines.get(0).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); expect(term.buffer.lines.get(0).get(1)[CHAR_DATA_CHAR_INDEX]).eql(' '); term.reset(); } }); it('2 characters at last cell', () => { const high = String.fromCharCode(0xD800); for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.buffer.x = term.cols - 1; term.write(high + String.fromCharCode(i)); expect(term.buffer.lines.get(0).get(term.buffer.x - 1)[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); expect(term.buffer.lines.get(0).get(term.buffer.x - 1)[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX]).eql(' '); term.reset(); } }); it('2 characters per cell over line end with autowrap', () => { const high = String.fromCharCode(0xD800); for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.buffer.x = term.cols - 1; term.wraparoundMode = true; term.write('a' + high + String.fromCharCode(i)); expect(term.buffer.lines.get(0).get(term.cols - 1)[CHAR_DATA_CHAR_INDEX]).eql('a'); expect(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); expect(term.buffer.lines.get(1).get(0)[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(term.buffer.lines.get(1).get(1)[CHAR_DATA_CHAR_INDEX]).eql(' '); term.reset(); } }); it('2 characters per cell over line end without autowrap', () => { const high = String.fromCharCode(0xD800); for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.buffer.x = term.cols - 1; term.wraparoundMode = false; term.write('a' + high + String.fromCharCode(i)); // auto wraparound mode should cut off the rest of the line expect(term.buffer.lines.get(0).get(term.cols - 1)[CHAR_DATA_CHAR_INDEX]).eql('a'); expect(term.buffer.lines.get(0).get(term.cols - 1)[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(term.buffer.lines.get(1).get(1)[CHAR_DATA_CHAR_INDEX]).eql(' '); term.reset(); } }); it('splitted surrogates', () => { const high = String.fromCharCode(0xD800); for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.write(high); term.write(String.fromCharCode(i)); const tchar = term.buffer.lines.get(0).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(high + String.fromCharCode(i)); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); expect(term.buffer.lines.get(0).get(1)[CHAR_DATA_CHAR_INDEX]).eql(' '); term.reset(); } }); }); describe('unicode - combining characters', () => { it('café', () => { term.write('cafe\u0301'); expect(term.buffer.lines.get(0).get(3)[CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); expect(term.buffer.lines.get(0).get(3)[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(term.buffer.lines.get(0).get(3)[CHAR_DATA_WIDTH_INDEX]).eql(1); }); it('café - end of line', () => { term.buffer.x = term.cols - 1 - 3; term.write('cafe\u0301'); expect(term.buffer.lines.get(0).get(term.cols - 1)[CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); expect(term.buffer.lines.get(0).get(term.cols - 1)[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(term.buffer.lines.get(0).get(term.cols - 1)[CHAR_DATA_WIDTH_INDEX]).eql(1); expect(term.buffer.lines.get(0).get(1)[CHAR_DATA_CHAR_INDEX]).eql(' '); expect(term.buffer.lines.get(0).get(1)[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(term.buffer.lines.get(0).get(1)[CHAR_DATA_WIDTH_INDEX]).eql(1); }); it('multiple combined é', () => { term.wraparoundMode = true; term.write(Array(100).join('e\u0301')); for (let i = 0; i < term.cols; ++i) { const tchar = term.buffer.lines.get(0).get(i); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); } const tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('e\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); }); it('multiple surrogate with combined', () => { term.wraparoundMode = true; term.write(Array(100).join('\uD800\uDC00\u0301')); for (let i = 0; i < term.cols; ++i) { const tchar = term.buffer.lines.get(0).get(i); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\uD800\uDC00\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); } const tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\uD800\uDC00\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); }); }); describe('unicode - fullwidth characters', () => { it('cursor movement even', () => { expect(term.buffer.x).eql(0); term.write('¥'); expect(term.buffer.x).eql(2); }); it('cursor movement odd', () => { term.buffer.x = 1; expect(term.buffer.x).eql(1); term.write('¥'); expect(term.buffer.x).eql(3); }); it('line of ¥ even', () => { term.wraparoundMode = true; term.write(Array(50).join('¥')); for (let i = 0; i < term.cols; ++i) { const tchar = term.buffer.lines.get(0).get(i); if (i % 2) { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); } else { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); } } const tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); }); it('line of ¥ odd', () => { term.wraparoundMode = true; term.buffer.x = 1; term.write(Array(50).join('¥')); for (let i = 1; i < term.cols - 1; ++i) { const tchar = term.buffer.lines.get(0).get(i); if (!(i % 2)) { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); } else { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); } } let tchar = term.buffer.lines.get(0).get(term.cols - 1); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' '); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); }); it('line of ¥ with combining odd', () => { term.wraparoundMode = true; term.buffer.x = 1; term.write(Array(50).join('¥\u0301')); for (let i = 1; i < term.cols - 1; ++i) { const tchar = term.buffer.lines.get(0).get(i); if (!(i % 2)) { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); } else { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); } } let tchar = term.buffer.lines.get(0).get(term.cols - 1); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' '); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); }); it('line of ¥ with combining even', () => { term.wraparoundMode = true; term.write(Array(50).join('¥\u0301')); for (let i = 0; i < term.cols; ++i) { const tchar = term.buffer.lines.get(0).get(i); if (i % 2) { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); } else { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); } } const tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('¥\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(2); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); }); it('line of surrogate fullwidth with combining odd', () => { term.wraparoundMode = true; term.buffer.x = 1; term.write(Array(50).join('\ud843\ude6d\u0301')); for (let i = 1; i < term.cols - 1; ++i) { const tchar = term.buffer.lines.get(0).get(i); if (!(i % 2)) { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); } else { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); } } let tchar = term.buffer.lines.get(0).get(term.cols - 1); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(' '); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(1); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(1); tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); }); it('line of surrogate fullwidth with combining even', () => { term.wraparoundMode = true; term.write(Array(50).join('\ud843\ude6d\u0301')); for (let i = 0; i < term.cols; ++i) { const tchar = term.buffer.lines.get(0).get(i); if (i % 2) { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql(''); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(0); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(0); } else { expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); } } const tchar = term.buffer.lines.get(1).get(0); expect(tchar[CHAR_DATA_CHAR_INDEX]).eql('\ud843\ude6d\u0301'); expect(tchar[CHAR_DATA_CHAR_INDEX].length).eql(3); expect(tchar[CHAR_DATA_WIDTH_INDEX]).eql(2); }); }); describe('insert mode', () => { it('halfwidth - all', () => { term.write(Array(9).join('0123456789').slice(-80)); term.buffer.x = 10; term.buffer.y = 0; term.insertMode = true; term.write('abcde'); expect(term.buffer.lines.get(0).length).eql(term.cols); expect(term.buffer.lines.get(0).get(10)[CHAR_DATA_CHAR_INDEX]).eql('a'); expect(term.buffer.lines.get(0).get(14)[CHAR_DATA_CHAR_INDEX]).eql('e'); expect(term.buffer.lines.get(0).get(15)[CHAR_DATA_CHAR_INDEX]).eql('0'); expect(term.buffer.lines.get(0).get(79)[CHAR_DATA_CHAR_INDEX]).eql('4'); }); it('fullwidth - insert', () => { term.write(Array(9).join('0123456789').slice(-80)); term.buffer.x = 10; term.buffer.y = 0; term.insertMode = true; term.write('¥¥¥'); expect(term.buffer.lines.get(0).length).eql(term.cols); expect(term.buffer.lines.get(0).get(10)[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(term.buffer.lines.get(0).get(11)[CHAR_DATA_CHAR_INDEX]).eql(''); expect(term.buffer.lines.get(0).get(14)[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(term.buffer.lines.get(0).get(15)[CHAR_DATA_CHAR_INDEX]).eql(''); expect(term.buffer.lines.get(0).get(79)[CHAR_DATA_CHAR_INDEX]).eql('3'); }); it('fullwidth - right border', () => { term.write(Array(41).join('¥')); term.buffer.x = 10; term.buffer.y = 0; term.insertMode = true; term.write('a'); expect(term.buffer.lines.get(0).length).eql(term.cols); expect(term.buffer.lines.get(0).get(10)[CHAR_DATA_CHAR_INDEX]).eql('a'); expect(term.buffer.lines.get(0).get(11)[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(term.buffer.lines.get(0).get(79)[CHAR_DATA_CHAR_INDEX]).eql(' '); // fullwidth char got replaced term.write('b'); expect(term.buffer.lines.get(0).length).eql(term.cols); expect(term.buffer.lines.get(0).get(11)[CHAR_DATA_CHAR_INDEX]).eql('b'); expect(term.buffer.lines.get(0).get(12)[CHAR_DATA_CHAR_INDEX]).eql('¥'); expect(term.buffer.lines.get(0).get(79)[CHAR_DATA_CHAR_INDEX]).eql(''); // empty cell after fullwidth }); }); }); xterm.js-3.8.1/src/Terminal.ts000066400000000000000000001700521341514612000162110ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) * @license MIT * * 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. * * 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 */ import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharData, CharacterJoinerHandler } from './Types'; import { IMouseZoneManager } from './ui/Types'; import { IRenderer } from './renderer/Types'; import { BufferSet } from './BufferSet'; import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from './Buffer'; import { CompositionHelper } from './CompositionHelper'; import { EventEmitter } from './common/EventEmitter'; import { Viewport } from './Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard'; import { C0 } from './common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; import { Renderer } from './renderer/Renderer'; import { Linkifier } from './Linkifier'; import { SelectionManager } from './SelectionManager'; import { CharMeasure } from './ui/CharMeasure'; import * as Browser from './shared/utils/Browser'; import { addDisposableDomListener } from './ui/Lifecycle'; import * as Strings from './Strings'; import { MouseHelper } from './utils/MouseHelper'; import { clone } from './utils/Clone'; import { DEFAULT_BELL_SOUND, SoundManager } from './SoundManager'; import { DEFAULT_ANSI_COLORS } from './renderer/ColorManager'; import { MouseZoneManager } from './ui/MouseZoneManager'; import { AccessibilityManager } from './AccessibilityManager'; import { ScreenDprMonitor } from './ui/ScreenDprMonitor'; import { ITheme, IMarker, IDisposable } from 'xterm'; import { removeTerminalFromCache } from './renderer/atlas/CharAtlasCache'; import { DomRenderer } from './renderer/dom/DomRenderer'; import { IKeyboardEvent } from './common/Types'; import { evaluateKeyboardEvent } from './core/input/Keyboard'; import { KeyboardResultType, ICharset } from './core/Types'; import { BufferLine } from './BufferLine'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; /** * The amount of write requests to queue before sending an XOFF signal to the * pty process. This number must be small in order for ^C and similar sequences * to be responsive. */ const WRITE_BUFFER_PAUSE_THRESHOLD = 5; /** * The number of writes to perform in a single batch before allowing the * renderer to catch up with a 0ms setTimeout. */ const WRITE_BATCH_SIZE = 300; /** * The set of options that only have an effect when set in the Terminal constructor. */ const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; const DEFAULT_OPTIONS: ITerminalOptions = { cols: 80, rows: 24, convertEol: false, termName: 'xterm', cursorBlink: false, cursorStyle: 'block', bellSound: DEFAULT_BELL_SOUND, bellStyle: 'none', drawBoldTextInBrightColors: true, enableBold: true, experimentalCharAtlas: 'static', fontFamily: 'courier-new, courier, monospace', fontSize: 15, fontWeight: 'normal', fontWeightBold: 'bold', lineHeight: 1.0, letterSpacing: 0, scrollback: 1000, screenKeys: false, screenReaderMode: false, debug: false, macOptionIsMeta: false, macOptionClickForcesSelection: false, cancelEvents: false, disableStdin: false, useFlowControl: false, allowTransparency: false, tabStopWidth: 8, theme: null, rightClickSelectsWord: Browser.isMac, rendererType: 'canvas' }; export class Terminal extends EventEmitter implements ITerminal, IDisposable, IInputHandlingTerminal { public textarea: HTMLTextAreaElement; public element: HTMLElement; public screenElement: HTMLElement; /** * The HTMLElement that the terminal is created in, set by Terminal.open. */ private _parent: HTMLElement; private _context: Window; private _document: Document; private _viewportScrollArea: HTMLElement; private _viewportElement: HTMLElement; private _helperContainer: HTMLElement; private _compositionView: HTMLElement; private _visualBellTimer: number; public browser: IBrowser = Browser; public options: ITerminalOptions; // TODO: This can be changed to an enum or boolean, 0 and 1 seem to be the only options public cursorState: number; public cursorHidden: boolean; private _customKeyEventHandler: CustomKeyEventHandler; // modes public applicationKeypad: boolean; public applicationCursor: boolean; public originMode: boolean; public insertMode: boolean; public wraparoundMode: boolean; // defaults: xterm - true, vt100 - false public bracketedPasteMode: boolean; // charset // The current charset public charset: ICharset; public gcharset: number; public glevel: number; public charsets: ICharset[]; // mouse properties private _decLocator: boolean; // This is unstable and never set public x10Mouse: boolean; public vt200Mouse: boolean; private _vt300Mouse: boolean; // This is unstable and never set public normalMouse: boolean; public mouseEvents: boolean; public sendFocus: boolean; public utfMouse: boolean; public sgrMouse: boolean; public urxvtMouse: boolean; // misc private _refreshStart: number; private _refreshEnd: number; public savedCols: number; public curAttr: number; public savedCurAttr: number; public params: (string | number)[]; public currentParam: string | number; // user input states public writeBuffer: string[]; private _writeInProgress: boolean; /** * Whether _xterm.js_ sent XOFF in order to catch up with the pty process. * This is a distinct state from writeStopped so that if the user requested * XOFF via ^S that it will not automatically resume when the writeBuffer goes * below threshold. */ private _xoffSentToCatchUp: boolean; /** Whether writing has been stopped as a result of XOFF */ // private _writeStopped: boolean; // Store if user went browsing history in scrollback private _userScrolling: boolean; private _inputHandler: InputHandler; public soundManager: SoundManager; public renderer: IRenderer; public selectionManager: SelectionManager; public linkifier: ILinkifier; public buffers: BufferSet; public viewport: IViewport; private _compositionHelper: ICompositionHelper; public charMeasure: CharMeasure; private _mouseZoneManager: IMouseZoneManager; public mouseHelper: MouseHelper; private _accessibilityManager: AccessibilityManager; private _screenDprMonitor: ScreenDprMonitor; private _theme: ITheme; public cols: number; public rows: number; /** * Creates a new `Terminal` object. * * @param options An object containing a set of options, the available options are: * - `cursorBlink` (boolean): Whether the terminal cursor blinks * - `cols` (number): The number of columns of the terminal (horizontal size) * - `rows` (number): The number of rows of the terminal (vertical size) * * @public * @class Xterm Xterm * @alias module:xterm/src/xterm */ constructor( options: ITerminalOptions = {} ) { super(); this.options = clone(options); this._setup(); } public dispose(): void { super.dispose(); this._customKeyEventHandler = null; removeTerminalFromCache(this); this.handler = () => {}; this.write = () => {}; if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } } /** * @deprecated Use dispose instead. */ public destroy(): void { this.dispose(); } private _setup(): void { Object.keys(DEFAULT_OPTIONS).forEach((key) => { if (this.options[key] === null || this.options[key] === undefined) { this.options[key] = DEFAULT_OPTIONS[key]; } }); // this.context = options.context || window; // this.document = options.document || document; // TODO: WHy not document.body? this._parent = document ? document.body : null; this.cols = this.options.cols; this.rows = this.options.rows; if (this.options.handler) { this.on('data', this.options.handler); } this.cursorState = 0; this.cursorHidden = false; this._customKeyEventHandler = null; // modes this.applicationKeypad = false; this.applicationCursor = false; this.originMode = false; this.insertMode = false; this.wraparoundMode = true; // defaults: xterm - true, vt100 - false this.bracketedPasteMode = false; // charset this.charset = null; this.gcharset = null; this.glevel = 0; // TODO: Can this be just []? this.charsets = [null]; this.curAttr = DEFAULT_ATTR; this.params = []; this.currentParam = 0; // user input states this.writeBuffer = []; this._writeInProgress = false; this._xoffSentToCatchUp = false; // this._writeStopped = false; this._userScrolling = false; this._inputHandler = new InputHandler(this); this.register(this._inputHandler); // Reuse renderer if the Terminal is being recreated via a reset call. this.renderer = this.renderer || null; this.selectionManager = this.selectionManager || null; this.linkifier = this.linkifier || new Linkifier(this); this._mouseZoneManager = this._mouseZoneManager || null; this.soundManager = this.soundManager || new SoundManager(this); // Create the terminal's buffers and set the current buffer this.buffers = new BufferSet(this); if (this.selectionManager) { this.selectionManager.clearSelection(); this.selectionManager.initBuffersListeners(); } } /** * Convenience property to active buffer. */ public get buffer(): Buffer { return this.buffers.active; } /** * back_color_erase feature for xterm. */ public eraseAttr(): number { // if (this.is('screen')) return DEFAULT_ATTR; return (DEFAULT_ATTR & ~0x1ff) | (this.curAttr & 0x1ff); } /** * Focus the terminal. Delegates focus handling to the terminal's DOM element. */ public focus(): void { if (this.textarea) { this.textarea.focus(); } } public get isFocused(): boolean { return document.activeElement === this.textarea; } /** * Retrieves an option's value from the terminal. * @param key The option key. */ public getOption(key: string): any { if (!(key in DEFAULT_OPTIONS)) { throw new Error('No option with key "' + key + '"'); } return this.options[key]; } /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ public setOption(key: string, value: any): void { if (!(key in DEFAULT_OPTIONS)) { throw new Error('No option with key "' + key + '"'); } if (CONSTRUCTOR_ONLY_OPTIONS.indexOf(key) !== -1) { console.error(`Option "${key}" can only be set in the constructor`); } if (this.options[key] === value) { return; } switch (key) { case 'bellStyle': if (!value) { value = 'none'; } break; case 'cursorStyle': if (!value) { value = 'block'; } break; case 'fontWeight': if (!value) { value = 'normal'; } break; case 'fontWeightBold': if (!value) { value = 'bold'; } break; case 'lineHeight': if (value < 1) { console.warn(`${key} cannot be less than 1, value: ${value}`); return; } case 'rendererType': if (!value) { value = 'canvas'; } break; case 'tabStopWidth': if (value < 1) { console.warn(`${key} cannot be less than 1, value: ${value}`); return; } break; case 'theme': // If open has been called we do not want to set options.theme as the // source of truth is owned by the renderer. if (this.renderer) { this._setTheme(value); return; } break; case 'scrollback': value = Math.min(value, MAX_BUFFER_SIZE); if (value < 0) { console.warn(`${key} cannot be less than 0, value: ${value}`); return; } if (this.options[key] !== value) { const newBufferLength = this.rows + value; if (this.buffer.lines.length > newBufferLength) { const amountToTrim = this.buffer.lines.length - newBufferLength; const needsRefresh = (this.buffer.ydisp - amountToTrim < 0); this.buffer.lines.trimStart(amountToTrim); this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0); this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0); if (needsRefresh) { this.refresh(0, this.rows - 1); } } } break; } this.options[key] = value; switch (key) { case 'fontFamily': case 'fontSize': // When the font changes the size of the cells may change which requires a renderer clear if (this.renderer) { this.renderer.clear(); this.charMeasure.measure(this.options); } break; case 'drawBoldTextInBrightColors': case 'experimentalCharAtlas': case 'enableBold': case 'letterSpacing': case 'lineHeight': case 'fontWeight': case 'fontWeightBold': // When the font changes the size of the cells may change which requires a renderer clear if (this.renderer) { this.renderer.clear(); this.renderer.onResize(this.cols, this.rows); this.refresh(0, this.rows - 1); } case 'rendererType': if (this.renderer) { this.unregister(this.renderer); this.renderer.dispose(); this.renderer = null; } this._setupRenderer(); this.renderer.onCharSizeChanged(); if (this._theme) { this.renderer.setTheme(this._theme); } break; case 'scrollback': this.buffers.resize(this.cols, this.rows); if (this.viewport) { this.viewport.syncScrollArea(); } break; case 'screenReaderMode': if (value) { if (!this._accessibilityManager) { this._accessibilityManager = new AccessibilityManager(this); } } else { if (this._accessibilityManager) { this._accessibilityManager.dispose(); this._accessibilityManager = null; } } break; case 'tabStopWidth': this.buffers.setupTabStops(); break; } // Inform renderer of changes if (this.renderer) { this.renderer.onOptionsChanged(); } } /** * Binds the desired focus behavior on a given terminal object. */ private _onTextAreaFocus(ev: KeyboardEvent): void { if (this.sendFocus) { this.handler(C0.ESC + '[I'); } this.updateCursorStyle(ev); this.element.classList.add('focus'); this.showCursor(); this.emit('focus'); } /** * Blur the terminal, calling the blur function on the terminal's underlying * textarea. */ public blur(): void { return this.textarea.blur(); } /** * Binds the desired blur behavior on a given terminal object. */ private _onTextAreaBlur(): void { // Text can safely be removed on blur. Doing it earlier could interfere with // screen readers reading it out. this.textarea.value = ''; this.refresh(this.buffer.y, this.buffer.y); if (this.sendFocus) { this.handler(C0.ESC + '[O'); } this.element.classList.remove('focus'); this.emit('blur'); } /** * Initialize default behavior */ private _initGlobal(): void { this._bindKeys(); // Bind clipboard functionality this.register(addDisposableDomListener(this.element, 'copy', (event: ClipboardEvent) => { // If mouse events are active it means the selection manager is disabled and // copy should be handled by the host program. if (!this.hasSelection()) { return; } copyHandler(event, this, this.selectionManager); })); const pasteHandlerWrapper = (event: ClipboardEvent) => pasteHandler(event, this); this.register(addDisposableDomListener(this.textarea, 'paste', pasteHandlerWrapper)); this.register(addDisposableDomListener(this.element, 'paste', pasteHandlerWrapper)); // Handle right click context menus if (Browser.isFirefox) { // Firefox doesn't appear to fire the contextmenu event on right click this.register(addDisposableDomListener(this.element, 'mousedown', (event: MouseEvent) => { if (event.button === 2) { rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord); } })); } else { this.register(addDisposableDomListener(this.element, 'contextmenu', (event: MouseEvent) => { rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord); })); } // Move the textarea under the cursor when middle clicking on Linux to ensure // middle click to paste selection works. This only appears to work in Chrome // at the time is writing. if (Browser.isLinux) { // Use auxclick event over mousedown the latter doesn't seem to work. Note // that the regular click event doesn't fire for the middle mouse button. this.register(addDisposableDomListener(this.element, 'auxclick', (event: MouseEvent) => { if (event.button === 1) { moveTextAreaUnderMouseCursor(event, this.textarea); } })); } } /** * Apply key handling to the terminal */ private _bindKeys(): void { const self = this; this.register(addDisposableDomListener(this.element, 'keydown', function (ev: KeyboardEvent): void { if (document.activeElement !== this) { return; } self._keyDown(ev); }, true)); this.register(addDisposableDomListener(this.element, 'keypress', function (ev: KeyboardEvent): void { if (document.activeElement !== this) { return; } self._keyPress(ev); }, true)); this.register(addDisposableDomListener(this.element, 'keyup', (ev: KeyboardEvent) => { if (!wasModifierKeyOnlyEvent(ev)) { this.focus(); } self._keyUp(ev); }, true)); this.register(addDisposableDomListener(this.textarea, 'keydown', (ev: KeyboardEvent) => this._keyDown(ev), true)); this.register(addDisposableDomListener(this.textarea, 'keypress', (ev: KeyboardEvent) => this._keyPress(ev), true)); this.register(addDisposableDomListener(this.textarea, 'compositionstart', () => this._compositionHelper.compositionstart())); this.register(addDisposableDomListener(this.textarea, 'compositionupdate', (e: CompositionEvent) => this._compositionHelper.compositionupdate(e))); this.register(addDisposableDomListener(this.textarea, 'compositionend', () => this._compositionHelper.compositionend())); this.register(this.addDisposableListener('refresh', () => this._compositionHelper.updateCompositionElements())); this.register(this.addDisposableListener('refresh', (data) => this._queueLinkification(data.start, data.end))); } /** * Opens the terminal within an element. * * @param parent The element to create the terminal within. */ public open(parent: HTMLElement): void { 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._screenDprMonitor = new ScreenDprMonitor(); this._screenDprMonitor.setListener(() => this.emit('dprchange', window.devicePixelRatio)); this.register(this._screenDprMonitor); // Create main element container this.element = this._document.createElement('div'); this.element.dir = 'ltr'; // xterm.css assumes LTR this.element.classList.add('terminal'); this.element.classList.add('xterm'); this.element.setAttribute('tabindex', '0'); this._parent.appendChild(this.element); // Performance: Use a document fragment to build the terminal // viewport and helper elements detached from the DOM const fragment = document.createDocumentFragment(); this._viewportElement = document.createElement('div'); this._viewportElement.classList.add('xterm-viewport'); fragment.appendChild(this._viewportElement); this._viewportScrollArea = document.createElement('div'); this._viewportScrollArea.classList.add('xterm-scroll-area'); this._viewportElement.appendChild(this._viewportScrollArea); this.screenElement = document.createElement('div'); this.screenElement.classList.add('xterm-screen'); // 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'); this.screenElement.appendChild(this._helperContainer); fragment.appendChild(this.screenElement); this._mouseZoneManager = new MouseZoneManager(this); this.register(this._mouseZoneManager); this.register(this.addDisposableListener('scroll', () => this._mouseZoneManager.clearAll())); this.linkifier.attachToDom(this._mouseZoneManager); this.textarea = document.createElement('textarea'); this.textarea.classList.add('xterm-helper-textarea'); // TODO: New API to set title? This could say "Terminal bash input", etc. this.textarea.setAttribute('aria-label', Strings.promptLabel); this.textarea.setAttribute('aria-multiline', 'false'); this.textarea.setAttribute('autocorrect', 'off'); this.textarea.setAttribute('autocapitalize', 'off'); this.textarea.setAttribute('spellcheck', 'false'); this.textarea.tabIndex = 0; this.register(addDisposableDomListener(this.textarea, 'focus', (ev: KeyboardEvent) => this._onTextAreaFocus(ev))); this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur())); 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.charMeasure = new CharMeasure(document, this._helperContainer); // Performance: Add viewport and helper elements from the fragment this.element.appendChild(fragment); this._setupRenderer(); this._theme = this.options.theme; this.options.theme = null; this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this.charMeasure); this.viewport.onThemeChanged(this.renderer.colorManager.colors); this.register(this.viewport); this.register(this.addDisposableListener('cursormove', () => this.renderer.onCursorMove())); this.register(this.addDisposableListener('resize', () => this.renderer.onResize(this.cols, this.rows))); this.register(this.addDisposableListener('blur', () => this.renderer.onBlur())); this.register(this.addDisposableListener('focus', () => this.renderer.onFocus())); this.register(this.addDisposableListener('dprchange', () => this.renderer.onWindowResize(window.devicePixelRatio))); // dprchange should handle this case, we need this as well for browsers that don't support the // matchMedia query. this.register(addDisposableDomListener(window, 'resize', () => this.renderer.onWindowResize(window.devicePixelRatio))); this.register(this.charMeasure.addDisposableListener('charsizechanged', () => this.renderer.onCharSizeChanged())); this.register(this.renderer.addDisposableListener('resize', (dimensions) => this.viewport.syncScrollArea())); this.selectionManager = new SelectionManager(this, this.charMeasure); this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this.selectionManager.onMouseDown(e))); this.register(this.selectionManager.addDisposableListener('refresh', data => this.renderer.onSelectionChanged(data.start, data.end, data.columnSelectMode))); this.register(this.selectionManager.addDisposableListener('newselection', text => { // If there's a new selection, put it into the textarea, focus and select it // in order to register it as a selection on the OS. This event is fired // only on Linux to enable middle click to paste selection. this.textarea.value = text; this.textarea.focus(); this.textarea.select(); })); this.register(this.addDisposableListener('scroll', () => { this.viewport.syncScrollArea(); this.selectionManager.refresh(); })); this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this.selectionManager.refresh())); this.mouseHelper = new MouseHelper(this.renderer); if (this.options.screenReaderMode) { // Note that this must be done *after* the renderer is created in order to // ensure the correct order of the dprchange event this._accessibilityManager = new AccessibilityManager(this); } // Measure the character size this.charMeasure.measure(this.options); // Setup loop that draws to screen this.refresh(0, this.rows - 1); // Initialize global actions that need to be taken on the document. this._initGlobal(); // Listen for mouse events and translate // them into terminal mouse protocols. this.bindMouse(); } private _setupRenderer(): void { switch (this.options.rendererType) { case 'canvas': this.renderer = new Renderer(this, this.options.theme); break; case 'dom': this.renderer = new DomRenderer(this, this.options.theme); break; default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } this.register(this.renderer); } /** * Sets the theme on the renderer. The renderer must have been initialized. * @param theme The theme to set. */ private _setTheme(theme: ITheme): void { this._theme = theme; const colors = this.renderer.setTheme(theme); if (this.viewport) { this.viewport.onThemeChanged(colors); } } /** * 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 */ public bindMouse(): void { const el = this.element; const self = this; let pressed = 32; // mouseup, mousedown, wheel // left click: ^[[M 3<^[[M#3< // wheel up: ^[[M`3> function sendButton(ev: MouseEvent | WheelEvent): void { let button; let pos; // get the xterm-style button button = getButton(ev); // get mouse coordinates pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows); 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: MouseEvent): void { let button = pressed; const pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.options.lineHeight, self.cols, self.rows); 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: number[], ch: number): void { if (!self.utfMouse) { if (ch === 255) { data.push(0); return; } if (ch > 127) ch = 127; data.push(ch); } else { if (ch === 2047) { data.push(0); return; } 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: number, pos: {x: number, y: number}): void { // 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; let data = C0.ESC + '[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.handler(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.handler(C0.ESC + '[' + button + ';' + (button === 3 ? 4 : 0) + ';' + pos.y + ';' + pos.x + ';' // Not sure what page is meant to be + (pos).page || 0 + '&w'); return; } if (self.urxvtMouse) { pos.x -= 32; pos.y -= 32; pos.x++; pos.y++; self.handler(C0.ESC + '[' + button + ';' + pos.x + ';' + pos.y + 'M'); return; } if (self.sgrMouse) { pos.x -= 32; pos.y -= 32; self.handler(C0.ESC + '[<' + (((button & 3) === 3 ? button & ~3 : button) - 32) + ';' + pos.x + ';' + pos.y + ((button & 3) === 3 ? 'm' : 'M')); return; } const data: number[] = []; encode(data, button); encode(data, pos.x); encode(data, pos.y); self.handler(C0.ESC + '[M' + String.fromCharCode.apply(String, data)); } function getButton(ev: MouseEvent): number { let button; let shift; let meta; let ctrl; let mod; // two low bits: // 0 = left // 1 = middle // 2 = right // 3 = release // wheel up/down: // 1, and 2 - with 64 added switch ((ev).overrideType || ev.type) { case 'mousedown': button = ev.button !== null && ev.button !== undefined ? +ev.button : ev.which !== null && ev.which !== undefined ? ev.which - 1 : null; if (Browser.isMSIE) { button = button === 1 ? 0 : button === 4 ? 1 : button; } break; case 'mouseup': button = 3; break; case 'DOMMouseScroll': button = ev.detail < 0 ? 64 : 65; break; case 'wheel': button = (ev).wheelDeltaY > 0 ? 64 : 65; break; } // next three bits are the modifiers: // 4 = shift, 8 = meta, 16 = control shift = ev.shiftKey ? 4 : 0; meta = ev.metaKey ? 8 : 0; ctrl = ev.ctrlKey ? 16 : 0; mod = shift | meta | ctrl; // no mods if (self.vt200Mouse) { // ctrl only mod &= ctrl; } else if (!self.normalMouse) { mod = 0; } // increment to SP button = (32 + (mod << 2)) + button; return button; } this.register(addDisposableDomListener(el, 'mousedown', (ev: MouseEvent) => { // Prevent the focus on the textarea from getting lost // and make sure we get focused on mousedown ev.preventDefault(); this.focus(); // Don't send the mouse button to the pty if mouse events are disabled or // if the selection manager is having selection forced (ie. a modifier is // held). if (!this.mouseEvents || this.selectionManager.shouldForceSelection(ev)) { return; } // send the button sendButton(ev); // fix for odd bug // if (this.vt200Mouse && !this.normalMouse) { if (this.vt200Mouse) { (ev).overrideType = 'mouseup'; sendButton(ev); return this.cancel(ev); } // TODO: All mouse handling should be pulled into its own file. // bind events let moveHandler: (event: MouseEvent) => void; if (this.normalMouse) { moveHandler = (event: MouseEvent) => { // Do nothing if normal mouse mode is on. This can happen if the mouse is held down when the // terminal exits normalMouse mode. if (!this.normalMouse) { return; } sendMove(event); }; // TODO: these event listeners should be managed by the disposable, the Terminal reference may // be kept aroud if Terminal.dispose is fired when the mouse is down this._document.addEventListener('mousemove', moveHandler); } // x10 compatibility mode can't send button releases const handler = (ev: MouseEvent) => { if (this.normalMouse && !this.x10Mouse) { sendButton(ev); } if (moveHandler) { // Even though this should only be attached when this.normalMouse is true, holding the // mouse button down when normalMouse changes can happen. Just always try to remove it. this._document.removeEventListener('mousemove', moveHandler); moveHandler = null; } this._document.removeEventListener('mouseup', handler); return this.cancel(ev); }; this._document.addEventListener('mouseup', handler); return this.cancel(ev); })); // if (this.normalMouse) { // on(this.document, 'mousemove', sendMove); // } this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => { if (!this.mouseEvents) { // Convert wheel events into up/down events when the buffer does not have scrollback, this // enables scrolling in apps hosted in the alt buffer such as vim or tmux. if (!this.buffer.hasScrollback) { const amount = this.viewport.getLinesScrolled(ev); // Do nothing if there's no vertical scroll if (amount === 0) { return; } // Construct and send sequences const sequence = C0.ESC + (this.applicationCursor ? 'O' : '[') + ( ev.deltaY < 0 ? 'A' : 'B'); let data = ''; for (let i = 0; i < Math.abs(amount); i++) { data += sequence; } this.handler(data); } return; } if (this.x10Mouse || this._vt300Mouse || this._decLocator) return; sendButton(ev); ev.preventDefault(); })); // allow wheel scrolling in // the shell for example this.register(addDisposableDomListener(el, 'wheel', (ev: WheelEvent) => { if (this.mouseEvents) return; this.viewport.onWheel(ev); return this.cancel(ev); })); this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => { if (this.mouseEvents) return; this.viewport.onTouchStart(ev); return this.cancel(ev); })); this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => { if (this.mouseEvents) return; this.viewport.onTouchMove(ev); return this.cancel(ev); })); } /** * Tells the renderer to refresh terminal content between two rows (inclusive) at the next * opportunity. * @param start The row to start from (between 0 and this.rows - 1). * @param end The row to end at (between start and this.rows - 1). */ public refresh(start: number, end: number): void { if (this.renderer) { this.renderer.refreshRows(start, end); } } /** * Queues linkification for the specified rows. * @param start The row to start from (between 0 and this.rows - 1). * @param end The row to end at (between start and this.rows - 1). */ private _queueLinkification(start: number, end: number): void { if (this.linkifier) { this.linkifier.linkifyRows(start, end); } } /** * Change the cursor style for different selection modes */ public updateCursorStyle(ev: KeyboardEvent): void { if (this.selectionManager && this.selectionManager.shouldColumnSelect(ev)) { this.element.classList.add('xterm-cursor-crosshair'); } else { this.element.classList.remove('xterm-cursor-crosshair'); } } /** * Display the cursor element */ public showCursor(): void { if (!this.cursorState) { this.cursorState = 1; this.refresh(this.buffer.y, this.buffer.y); } } /** * Scroll the terminal down 1 row, creating a blank line. * @param isWrapped Whether the new line is wrapped from the previous line. */ public scroll(isWrapped?: boolean): void { const newLine = BufferLine.blankLine(this.cols, DEFAULT_ATTR, isWrapped); const topRow = this.buffer.ybase + this.buffer.scrollTop; const bottomRow = this.buffer.ybase + this.buffer.scrollBottom; if (this.buffer.scrollTop === 0) { // Determine whether the buffer is going to be trimmed after insertion. const willBufferBeTrimmed = this.buffer.lines.length === this.buffer.lines.maxLength; // Insert the line using the fastest method if (bottomRow === this.buffer.lines.length - 1) { this.buffer.lines.push(newLine); } else { this.buffer.lines.splice(bottomRow + 1, 0, newLine); } // Only adjust ybase and ydisp when the buffer is not trimmed if (!willBufferBeTrimmed) { this.buffer.ybase++; // Only scroll the ydisp with ybase if the user has not scrolled up if (!this._userScrolling) { this.buffer.ydisp++; } } else { // When the buffer is full and the user has scrolled up, keep the text // stable unless ydisp is right at the top if (this._userScrolling) { this.buffer.ydisp = Math.max(this.buffer.ydisp - 1, 0); } } } else { // scrollTop is non-zero which means no line will be going to the // scrollback, instead we can just shift them in-place. const scrollRegionHeight = bottomRow - topRow + 1/*as it's zero-based*/; this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); this.buffer.lines.set(bottomRow, newLine); } // Move the viewport to the bottom of the buffer unless the user is // scrolling. if (!this._userScrolling) { this.buffer.ydisp = this.buffer.ybase; } // Flag rows that need updating this.updateRange(this.buffer.scrollTop); this.updateRange(this.buffer.scrollBottom); /** * This event is emitted whenever the terminal is scrolled. * The one parameter passed is the new y display position. * * @event scroll */ this.emit('scroll', this.buffer.ydisp); } /** * Scroll the display of the terminal * @param disp The number of lines to scroll down (negative scroll up). * @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used * to avoid unwanted events being handled by the viewport when the event was triggered from the * viewport originally. */ public scrollLines(disp: number, suppressScrollEvent?: boolean): void { if (disp < 0) { if (this.buffer.ydisp === 0) { return; } this._userScrolling = true; } else if (disp + this.buffer.ydisp >= this.buffer.ybase) { this._userScrolling = false; } const oldYdisp = this.buffer.ydisp; this.buffer.ydisp = Math.max(Math.min(this.buffer.ydisp + disp, this.buffer.ybase), 0); // No change occurred, don't trigger scroll/refresh if (oldYdisp === this.buffer.ydisp) { return; } if (!suppressScrollEvent) { this.emit('scroll', this.buffer.ydisp); } this.refresh(0, this.rows - 1); } /** * Scroll the display of the terminal by a number of pages. * @param pageCount The number of pages to scroll (negative scrolls up). */ public scrollPages(pageCount: number): void { this.scrollLines(pageCount * (this.rows - 1)); } /** * Scrolls the display of the terminal to the top. */ public scrollToTop(): void { this.scrollLines(-this.buffer.ydisp); } /** * Scrolls the display of the terminal to the bottom. */ public scrollToBottom(): void { this.scrollLines(this.buffer.ybase - this.buffer.ydisp); } public scrollToLine(line: number): void { const scrollAmount = line - this.buffer.ydisp; if (scrollAmount !== 0) { this.scrollLines(scrollAmount); } } /** * Writes text to the terminal. * @param data The text to write to the terminal. */ public write(data: string): void { // Ensure the terminal isn't disposed if (this._isDisposed) { return; } // Ignore falsy data values (including the empty string) if (!data) { return; } this.writeBuffer.push(data); // Send XOFF to pause the pty process if the write buffer becomes too large so // xterm.js can catch up before more data is sent. This is necessary in order // to keep signals such as ^C responsive. if (this.options.useFlowControl && !this._xoffSentToCatchUp && this.writeBuffer.length >= WRITE_BUFFER_PAUSE_THRESHOLD) { // XOFF - stop pty pipe // XON will be triggered by emulator before processing data chunk this.handler(C0.DC3); this._xoffSentToCatchUp = true; } if (!this._writeInProgress && this.writeBuffer.length > 0) { // Kick off a write which will write all data in sequence recursively this._writeInProgress = true; // Kick off an async innerWrite so more writes can come in while processing data setTimeout(() => { this._innerWrite(); }); } } protected _innerWrite(): void { // Ensure the terminal isn't disposed if (this._isDisposed) { this.writeBuffer = []; } const writeBatch = this.writeBuffer.splice(0, WRITE_BATCH_SIZE); while (writeBatch.length > 0) { const data = writeBatch.shift(); // If XOFF was sent in order to catch up with the pty process, resume it if // the writeBuffer is empty to allow more data to come in. if (this._xoffSentToCatchUp && writeBatch.length === 0 && this.writeBuffer.length === 0) { this.handler(C0.DC1); this._xoffSentToCatchUp = false; } this._refreshStart = this.buffer.y; this._refreshEnd = this.buffer.y; // HACK: Set the parser state based on it's state at the time of return. // This works around the bug #662 which saw the parser state reset in the // middle of parsing escape sequence in two chunks. For some reason the // state of the parser resets to 0 after exiting parser.parse. This change // just sets the state back based on the correct return statement. this._inputHandler.parse(data); this.updateRange(this.buffer.y); this.refresh(this._refreshStart, this._refreshEnd); } if (this.writeBuffer.length > 0) { // Allow renderer to catch up before processing the next batch setTimeout(() => this._innerWrite(), 0); } else { this._writeInProgress = false; } } /** * Writes text to the terminal, followed by a break line character (\n). * @param data The text to write to the terminal. */ public writeln(data: string): void { this.write(data + '\r\n'); } /** * Attaches a custom key event handler which is run before keys are processed, * giving consumers of xterm.js ultimate control as to what keys should be * processed by the terminal and what keys should not. * @param customKeyEventHandler The custom KeyboardEvent handler to attach. * This is a function that takes a KeyboardEvent, allowing consumers to stop * propogation and/or prevent the default action. The function returns whether * the event should be processed by xterm.js. */ public attachCustomKeyEventHandler(customKeyEventHandler: CustomKeyEventHandler): void { this._customKeyEventHandler = customKeyEventHandler; } /** * Registers a link matcher, allowing custom link patterns to be matched and * handled. * @param regex The regular expression to search for, specifically * this searches the textContent of the rows. You will want to use \s to match * a space ' ' character for example. * @param handler The callback when the link is called. * @param options Options for the link matcher. * @return The ID of the new matcher, this can be used to deregister. */ public registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number { const matcherId = this.linkifier.registerLinkMatcher(regex, handler, options); this.refresh(0, this.rows - 1); return matcherId; } /** * Deregisters a link matcher if it has been registered. * @param matcherId The link matcher's ID (returned after register) */ public deregisterLinkMatcher(matcherId: number): void { if (this.linkifier.deregisterLinkMatcher(matcherId)) { this.refresh(0, this.rows - 1); } } public registerCharacterJoiner(handler: CharacterJoinerHandler): number { const joinerId = this.renderer.registerCharacterJoiner(handler); this.refresh(0, this.rows - 1); return joinerId; } public deregisterCharacterJoiner(joinerId: number): void { if (this.renderer.deregisterCharacterJoiner(joinerId)) { this.refresh(0, this.rows - 1); } } public get markers(): IMarker[] { return this.buffer.markers; } public addMarker(cursorYOffset: number): IMarker { // Disallow markers on the alt buffer if (this.buffer !== this.buffers.normal) { return; } return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset); } /** * Gets whether the terminal has an active selection. */ public hasSelection(): boolean { return this.selectionManager ? this.selectionManager.hasSelection : false; } /** * Gets the terminal's current selection, this is useful for implementing copy * behavior outside of xterm.js. */ public getSelection(): string { return this.selectionManager ? this.selectionManager.selectionText : ''; } /** * Clears the current terminal selection. */ public clearSelection(): void { if (this.selectionManager) { this.selectionManager.clearSelection(); } } /** * Selects all text within the terminal. */ public selectAll(): void { if (this.selectionManager) { this.selectionManager.selectAll(); } } public selectLines(start: number, end: number): void { if (this.selectionManager) { this.selectionManager.selectLines(start, end); } } /** * Handle a keydown event * Key Resources: * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent * @param ev The keydown event to be handled. */ protected _keyDown(event: KeyboardEvent): boolean { if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) { return false; } if (!this._compositionHelper.keydown(event)) { if (this.buffer.ybase !== this.buffer.ydisp) { this.scrollToBottom(); } return false; } const result = evaluateKeyboardEvent(event, this.applicationCursor, this.browser.isMac, this.options.macOptionIsMeta); this.updateCursorStyle(event); // if (result.key === C0.DC3) { // XOFF // this._writeStopped = true; // } else if (result.key === C0.DC1) { // XON // this._writeStopped = false; // } if (result.type === KeyboardResultType.PAGE_DOWN || result.type === KeyboardResultType.PAGE_UP) { const scrollCount = this.rows - 1; this.scrollLines(result.type === KeyboardResultType.PAGE_UP ? -scrollCount : scrollCount); return this.cancel(event, true); } if (result.type === KeyboardResultType.SELECT_ALL) { this.selectAll(); } if (this._isThirdLevelShift(this.browser, event)) { return true; } if (result.cancel) { // The event is canceled at the end already, is this necessary? this.cancel(event, true); } if (!result.key) { return true; } this.emit('keydown', event); this.emit('key', result.key, event); this.showCursor(); this.handler(result.key); return this.cancel(event, true); } private _isThirdLevelShift(browser: IBrowser, ev: IKeyboardEvent): boolean { const thirdLevelKey = (browser.isMac && !this.options.macOptionIsMeta && ev.altKey && !ev.ctrlKey && !ev.metaKey) || (browser.isMSWindows && ev.altKey && ev.ctrlKey && !ev.metaKey); if (ev.type === 'keypress') { return thirdLevelKey; } // Don't invoke for arrows, pageDown, home, backspace, etc. (on non-keypress events) return thirdLevelKey && (!ev.keyCode || ev.keyCode > 47); } /** * Set the G level of the terminal * @param g */ public setgLevel(g: number): void { this.glevel = g; this.charset = this.charsets[g]; } /** * Set the charset for the given G level of the terminal * @param g * @param charset */ public setgCharset(g: number, charset: ICharset): void { this.charsets[g] = charset; if (this.glevel === g) { this.charset = charset; } } protected _keyUp(ev: KeyboardEvent): void { this.updateCursorStyle(ev); } /** * Handle a keypress event. * Key Resources: * - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent * @param ev The keypress event to be handled. */ protected _keyPress(ev: KeyboardEvent): boolean { let key; if (this._customKeyEventHandler && this._customKeyEventHandler(ev) === false) { return false; } this.cancel(ev); if (ev.charCode) { key = ev.charCode; } else if (ev.which === null || ev.which === undefined) { key = ev.keyCode; } else if (ev.which !== 0 && ev.charCode !== 0) { key = ev.which; } else { return false; } if (!key || ( (ev.altKey || ev.ctrlKey || ev.metaKey) && !this._isThirdLevelShift(this.browser, ev) )) { return false; } key = String.fromCharCode(key); this.emit('keypress', key, ev); this.emit('key', key, ev); this.showCursor(); this.handler(key); return true; } /** * Ring the bell. * Note: We could do sweet things with webaudio here */ public bell(): void { this.emit('bell'); if (this._soundBell()) { this.soundManager.playBellSound(); } if (this._visualBell()) { this.element.classList.add('visual-bell-active'); clearTimeout(this._visualBellTimer); this._visualBellTimer = window.setTimeout(() => { this.element.classList.remove('visual-bell-active'); }, 200); } } /** * Log the current state to the console. */ public log(text: string, data?: any): void { if (!this.options.debug) return; if (!this._context.console || !this._context.console.log) return; this._context.console.log(text, data); } /** * Log the current state as error to the console. */ public error(text: string, data?: any): void { if (!this.options.debug) return; if (!this._context.console || !this._context.console.error) return; this._context.console.error(text, data); } /** * Resizes the terminal. * * @param x The number of columns to resize to. * @param y The number of rows to resize to. */ public resize(x: number, y: number): void { if (isNaN(x) || isNaN(y)) { return; } if (x === this.cols && y === this.rows) { // Check if we still need to measure the char size (fixes #785). if (this.charMeasure && (!this.charMeasure.width || !this.charMeasure.height)) { this.charMeasure.measure(this.options); } return; } if (x < 1) x = 1; if (y < 1) y = 1; this.buffers.resize(x, y); this.cols = x; this.rows = y; this.buffers.setupTabStops(this.cols); if (this.charMeasure) { this.charMeasure.measure(this.options); } this.refresh(0, this.rows - 1); this.emit('resize', {cols: x, rows: y}); } /** * Updates the range of rows to refresh * @param y The number of rows to refresh next. */ public updateRange(y: number): void { if (y < this._refreshStart) this._refreshStart = y; if (y > this._refreshEnd) this._refreshEnd = y; // if (y > this.refreshEnd) { // this.refreshEnd = y; // if (y > this.rows - 1) { // this.refreshEnd = this.rows - 1; // } // } } /** * Set the range of refreshing to the maximum value */ public maxRange(): void { this._refreshStart = 0; this._refreshEnd = this.rows - 1; } /** * Clear the entire buffer, making the prompt line the new first line. */ public clear(): void { if (this.buffer.ybase === 0 && this.buffer.y === 0) { // Don't clear if it's already clear return; } this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)); this.buffer.lines.length = 1; this.buffer.ydisp = 0; this.buffer.ybase = 0; this.buffer.y = 0; for (let i = 1; i < this.rows; i++) { this.buffer.lines.push(BufferLine.blankLine(this.cols, DEFAULT_ATTR)); } this.refresh(0, this.rows - 1); this.emit('scroll', this.buffer.ydisp); } /** * If cur return the back color xterm feature attribute. Else return default attribute. * @param cur */ public ch(cur?: boolean): CharData { if (cur) { return [this.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; } return [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; } /** * Evaluate if the current terminal is the given argument. * @param term The terminal name to evaluate */ public is(term: string): boolean { return (this.options.termName + '').indexOf(term) === 0; } /** * Emit the 'data' event and populate the given data. * @param data The data to populate in the event. */ public handler(data: string): void { // Prevents all events to pty process if stdin is disabled if (this.options.disableStdin) { return; } // Clear the selection if the selection manager is available and has an active selection if (this.selectionManager && this.selectionManager.hasSelection) { this.selectionManager.clearSelection(); } // Input is being sent to the terminal, the terminal should focus the prompt. if (this.buffer.ybase !== this.buffer.ydisp) { this.scrollToBottom(); } this.emit('data', data); } /** * Emit the 'title' event and populate the given title. * @param title The title to populate in the event. */ public handleTitle(title: string): void { /** * This event is emitted when the title of the terminal is changed * from inside the terminal. The parameter is the new title. * * @event title */ this.emit('title', title); } /** * ESC */ /** * ESC D Index (IND is 0x84). */ public index(): void { this.buffer.y++; if (this.buffer.y > this.buffer.scrollBottom) { this.buffer.y--; this.scroll(); } // If the end of the line is hit, prevent this action from wrapping around to the next line. if (this.buffer.x >= this.cols) { this.buffer.x--; } } /** * ESC M Reverse Index (RI is 0x8d). * * Move the cursor up one row, inserting a new blank line if necessary. */ public reverseIndex(): void { if (this.buffer.y === this.buffer.scrollTop) { // possibly move the code below to term.reverseScroll(); // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' // blankLine(true) is xterm/linux behavior const scrollRegionHeight = this.buffer.scrollBottom - this.buffer.scrollTop; this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, scrollRegionHeight, 1); this.buffer.lines.set(this.buffer.y + this.buffer.ybase, BufferLine.blankLine(this.cols, this.eraseAttr())); this.updateRange(this.buffer.scrollTop); this.updateRange(this.buffer.scrollBottom); } else { this.buffer.y--; } } /** * ESC c Full Reset (RIS). */ public reset(): void { this.options.rows = this.rows; this.options.cols = this.cols; const customKeyEventHandler = this._customKeyEventHandler; const inputHandler = this._inputHandler; const cursorState = this.cursorState; this._setup(); this._customKeyEventHandler = customKeyEventHandler; this._inputHandler = inputHandler; this.cursorState = cursorState; this.refresh(0, this.rows - 1); if (this.viewport) { this.viewport.syncScrollArea(); } } /** * ESC H Tab Set (HTS is 0x88). */ public tabSet(): void { this.buffer.tabs[this.buffer.x] = true; } // TODO: Remove cancel function and cancelEvents option public cancel(ev: Event, force?: boolean): boolean { if (!this.options.cancelEvents && !force) { return; } ev.preventDefault(); ev.stopPropagation(); return false; } // TODO: Remove when true color is implemented public matchColor(r1: number, g1: number, b1: number): number { const hash = (r1 << 16) | (g1 << 8) | b1; if (matchColorCache[hash] !== null && matchColorCache[hash] !== undefined) { return matchColorCache[hash]; } let ldiff = Infinity; let li = -1; let i = 0; let c: number; let r2: number; let g2: number; let b2: number; let diff: number; for (; i < DEFAULT_ANSI_COLORS.length; i++) { c = DEFAULT_ANSI_COLORS[i].rgba; r2 = c >>> 24; g2 = c >>> 16 & 0xFF; b2 = c >>> 8 & 0xFF; // assume that alpha is 0xFF diff = matchColorDistance(r1, g1, b1, r2, g2, b2); if (diff === 0) { li = i; break; } if (diff < ldiff) { ldiff = diff; li = i; } } return matchColorCache[hash] = li; } private _visualBell(): boolean { return false; // return this.options.bellStyle === 'visual' || // this.options.bellStyle === 'both'; } private _soundBell(): boolean { return this.options.bellStyle === 'sound'; // return this.options.bellStyle === 'sound' || // this.options.bellStyle === 'both'; } } /** * Helpers */ function wasModifierKeyOnlyEvent(ev: KeyboardEvent): boolean { return ev.keyCode === 16 || // Shift ev.keyCode === 17 || // Ctrl ev.keyCode === 18; // Alt } /** * TODO: * The below color-related code can be removed when true color is implemented. * It's only purpose is to match true color requests with the closest matching * ANSI color code. */ const matchColorCache: {[colorRGBHash: number]: number} = {}; // http://stackoverflow.com/questions/1633828 function matchColorDistance(r1: number, g1: number, b1: number, r2: number, g2: number, b2: number): number { return Math.pow(30 * (r1 - r2), 2) + Math.pow(59 * (g1 - g2), 2) + Math.pow(11 * (b1 - b2), 2); } xterm.js-3.8.1/src/Types.ts000066400000000000000000000376601341514612000155510ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal as PublicTerminal, ITerminalOptions as IPublicTerminalOptions, IEventEmitter, IDisposable } from 'xterm'; import { IColorSet, IRenderer } from './renderer/Types'; import { IMouseZoneManager } from './ui/Types'; import { ICharset } from './core/Types'; import { ICircularList } from './common/Types'; export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean; export type CharData = [number, string, number, number]; export type LineData = CharData[]; export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void; export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void; export type CharacterJoinerHandler = (text: string) => [number, number][]; // BufferIndex denotes a position in the buffer: [rowIndex, colIndex] export type BufferIndex = [number, number]; export const enum LinkHoverEventTypes { HOVER = 'linkhover', TOOLTIP = 'linktooltip', LEAVE = 'linkleave' } /** * This interface encapsulates everything needed from the Terminal by the * InputHandler. This cleanly separates the large amount of methods needed by * InputHandler cleanly from the ITerminal interface. */ export interface IInputHandlingTerminal extends IEventEmitter { element: HTMLElement; options: ITerminalOptions; cols: number; rows: number; charset: ICharset; gcharset: number; glevel: number; charsets: ICharset[]; applicationKeypad: boolean; applicationCursor: boolean; originMode: boolean; insertMode: boolean; wraparoundMode: boolean; bracketedPasteMode: boolean; curAttr: number; savedCurAttr: number; savedCols: number; x10Mouse: boolean; vt200Mouse: boolean; normalMouse: boolean; mouseEvents: boolean; sendFocus: boolean; utfMouse: boolean; sgrMouse: boolean; urxvtMouse: boolean; cursorHidden: boolean; buffers: IBufferSet; buffer: IBuffer; viewport: IViewport; selectionManager: ISelectionManager; bell(): void; focus(): void; updateRange(y: number): void; scroll(isWrapped?: boolean): void; setgLevel(g: number): void; eraseAttr(): number; is(term: string): boolean; setgCharset(g: number, charset: ICharset): void; resize(x: number, y: number): void; log(text: string, data?: any): void; reset(): void; showCursor(): void; refresh(start: number, end: number): void; matchColor(r1: number, g1: number, b1: number): number; error(text: string, data?: any): void; setOption(key: string, value: any): void; tabSet(): void; handler(data: string): void; handleTitle(title: string): void; index(): void; reverseIndex(): void; } export interface IViewport extends IDisposable { scrollBarWidth: number; syncScrollArea(): void; getLinesScrolled(ev: WheelEvent): number; onWheel(ev: WheelEvent): void; onTouchStart(ev: TouchEvent): void; onTouchMove(ev: TouchEvent): void; onThemeChanged(colors: IColorSet): void; } export interface ICompositionHelper { compositionstart(): void; compositionupdate(ev: CompositionEvent): void; compositionend(): void; updateCompositionElements(dontRecurse?: boolean): void; keydown(ev: KeyboardEvent): boolean; } /** * Calls the parser and handles actions generated by the parser. */ export interface IInputHandler { parse(data: string): void; print(data: string, start: number, end: number): void; /** C0 BEL */ bell(): void; /** C0 LF */ lineFeed(): void; /** C0 CR */ carriageReturn(): void; /** C0 BS */ backspace(): void; /** C0 HT */ tab(): void; /** C0 SO */ shiftOut(): void; /** C0 SI */ shiftIn(): void; /** CSI @ */ insertChars(params?: number[]): void; /** CSI A */ cursorUp(params?: number[]): void; /** CSI B */ cursorDown(params?: number[]): void; /** CSI C */ cursorForward(params?: number[]): void; /** CSI D */ cursorBackward(params?: number[]): void; /** CSI E */ cursorNextLine(params?: number[]): void; /** CSI F */ cursorPrecedingLine(params?: number[]): void; /** CSI G */ cursorCharAbsolute(params?: number[]): void; /** CSI H */ cursorPosition(params?: number[]): void; /** CSI I */ cursorForwardTab(params?: number[]): void; /** CSI J */ eraseInDisplay(params?: number[]): void; /** CSI K */ eraseInLine(params?: number[]): void; /** CSI L */ insertLines(params?: number[]): void; /** CSI M */ deleteLines(params?: number[]): void; /** CSI P */ deleteChars(params?: number[]): void; /** CSI S */ scrollUp(params?: number[]): void; /** CSI T */ scrollDown(params?: number[], collect?: string): void; /** CSI X */ eraseChars(params?: number[]): void; /** CSI Z */ cursorBackwardTab(params?: number[]): void; /** CSI ` */ charPosAbsolute(params?: number[]): void; /** CSI a */ hPositionRelative(params?: number[]): void; /** CSI b */ repeatPrecedingCharacter(params?: number[]): void; /** CSI c */ sendDeviceAttributes(params?: number[], collect?: string): void; /** CSI d */ linePosAbsolute(params?: number[]): void; /** CSI e */ vPositionRelative(params?: number[]): void; /** CSI f */ hVPosition(params?: number[]): void; /** CSI g */ tabClear(params?: number[]): void; /** CSI h */ setMode(params?: number[], collect?: string): void; /** CSI l */ resetMode(params?: number[], collect?: string): void; /** CSI m */ charAttributes(params?: number[]): void; /** CSI n */ deviceStatus(params?: number[], collect?: string): void; /** CSI p */ softReset(params?: number[], collect?: string): void; /** CSI q */ setCursorStyle(params?: number[], collect?: string): void; /** CSI r */ setScrollRegion(params?: number[], collect?: string): void; /** CSI s */ saveCursor(params?: number[]): void; /** CSI u */ restoreCursor(params?: number[]): void; /** OSC 0 OSC 2 */ setTitle(data: string): void; /** ESC E */ nextLine(): void; /** ESC = */ keypadApplicationMode(): void; /** ESC > */ keypadNumericMode(): void; /** ESC % G ESC % @ */ selectDefaultCharset(): void; /** ESC ( C ESC ) C ESC * C ESC + C ESC - C ESC . C ESC / C */ selectCharset(collectAndFlag: string): void; /** ESC D */ index(): void; /** ESC H */ tabSet(): void; /** ESC M */ reverseIndex(): void; /** ESC c */ reset(): void; /** ESC n ESC o ESC | ESC } ESC ~ */ setgLevel(level: number): void; } export interface ILinkMatcher { id: number; regex: RegExp; handler: LinkMatcherHandler; hoverTooltipCallback?: LinkMatcherHandler; hoverLeaveCallback?: () => void; matchIndex?: number; validationCallback?: LinkMatcherValidationCallback; priority?: number; willLinkActivate?: (event: MouseEvent, uri: string) => boolean; } export interface ILinkHoverEvent { x1: number; y1: number; x2: number; y2: number; cols: number; fg: number; } export interface ITerminal extends PublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor { screenElement: HTMLElement; selectionManager: ISelectionManager; charMeasure: ICharMeasure; renderer: IRenderer; browser: IBrowser; writeBuffer: string[]; cursorHidden: boolean; cursorState: number; options: ITerminalOptions; buffer: IBuffer; buffers: IBufferSet; isFocused: boolean; mouseHelper: IMouseHelper; viewport: IViewport; bracketedPasteMode: boolean; applicationCursor: boolean; /** * Emit the 'data' event and populate the given data. * @param data The data to populate in the event. */ handler(data: string): void; scrollLines(disp: number, suppressScrollEvent?: boolean): void; cancel(ev: Event, force?: boolean): boolean | void; log(text: string): void; showCursor(): void; } export interface IBufferAccessor { buffer: IBuffer; } export interface IElementAccessor { element: HTMLElement; } export interface ILinkifierAccessor { linkifier: ILinkifier; } export interface IMouseHelper { getCoords(event: { pageX: number, pageY: number }, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number]; getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number }; } export interface ICharMeasure { width: number; height: number; measure(options: ITerminalOptions): void; } // TODO: The options that are not in the public API should be reviewed export interface ITerminalOptions extends IPublicTerminalOptions { [key: string]: any; cancelEvents?: boolean; convertEol?: boolean; debug?: boolean; handler?: (data: string) => void; screenKeys?: boolean; termName?: string; useFlowControl?: boolean; } export interface IBufferStringIteratorResult { range: {first: number, last: number}; content: string; } export interface IBufferStringIterator { hasNext(): boolean; next(): IBufferStringIteratorResult; } export interface IBuffer { readonly lines: ICircularList; ydisp: number; ybase: number; y: number; x: number; tabs: any; scrollBottom: number; scrollTop: number; hasScrollback: boolean; savedY: number; savedX: number; isCursorInViewport: boolean; translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; getWrappedRangeForLine(y: number): { first: number, last: number }; nextStop(x?: number): number; prevStop(x?: number): number; stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[]; iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator; } export interface IBufferSet extends IEventEmitter { alt: IBuffer; normal: IBuffer; active: IBuffer; activateNormalBuffer(): void; activateAltBuffer(): void; } export interface ISelectionManager { selectionText: string; selectionStart: [number, number]; selectionEnd: [number, number]; disable(): void; enable(): void; setSelection(row: number, col: number, length: number): void; isClickInSelection(event: MouseEvent): boolean; selectWordAtCursor(event: MouseEvent): void; } export interface ILinkifier extends IEventEmitter { attachToDom(mouseZoneManager: IMouseZoneManager): void; linkifyRows(start: number, end: number): void; registerLinkMatcher(regex: RegExp, handler: LinkMatcherHandler, options?: ILinkMatcherOptions): number; deregisterLinkMatcher(matcherId: number): boolean; } export interface ILinkMatcherOptions { /** * The index of the link from the regex.match(text) call. This defaults to 0 * (for regular expressions without capture groups). */ matchIndex?: number; /** * A callback that validates an individual link, returning true if valid and * false if invalid. */ validationCallback?: LinkMatcherValidationCallback; /** * A callback that fires when the mouse hovers over a link. */ tooltipCallback?: LinkMatcherHandler; /** * A callback that fires when the mouse leaves a link that was hovered. */ leaveCallback?: () => void; /** * The priority of the link matcher, this defines the order in which the link * matcher is evaluated relative to others, from highest to lowest. The * default value is 0. */ priority?: number; /** * A callback that fires when the mousedown and click events occur that * determines whether a link will be activated upon click. This enables * only activating a link when a certain modifier is held down, if not the * mouse event will continue propagation (eg. double click to select word). */ willLinkActivate?: (event: MouseEvent, uri: string) => boolean; } export interface IBrowser { isNode: boolean; userAgent: string; platform: string; isFirefox: boolean; isMSIE: boolean; isMac: boolean; isIpad: boolean; isIphone: boolean; isMSWindows: boolean; } export interface ISoundManager { playBellSound(): void; } /** * Internal states of EscapeSequenceParser. */ export const enum ParserState { GROUND = 0, ESCAPE = 1, ESCAPE_INTERMEDIATE = 2, CSI_ENTRY = 3, CSI_PARAM = 4, CSI_INTERMEDIATE = 5, CSI_IGNORE = 6, SOS_PM_APC_STRING = 7, OSC_STRING = 8, DCS_ENTRY = 9, DCS_PARAM = 10, DCS_IGNORE = 11, DCS_INTERMEDIATE = 12, DCS_PASSTHROUGH = 13 } /** * Internal actions of EscapeSequenceParser. */ export const enum ParserAction { IGNORE = 0, ERROR = 1, PRINT = 2, EXECUTE = 3, OSC_START = 4, OSC_PUT = 5, OSC_END = 6, CSI_DISPATCH = 7, PARAM = 8, COLLECT = 9, ESC_DISPATCH = 10, CLEAR = 11, DCS_HOOK = 12, DCS_PUT = 13, DCS_UNHOOK = 14 } /** * Internal state of EscapeSequenceParser. * Used as argument of the error handler to allow * introspection at runtime on parse errors. * Return it with altered values to recover from * faulty states (not yet supported). * Set `abort` to `true` to abort the current parsing. */ export interface IParsingState { // position in parse string position: number; // actual character code code: number; // current parser state currentState: ParserState; // print buffer start index (-1 for not set) print: number; // dcs buffer start index (-1 for not set) dcs: number; // osc string buffer osc: string; // collect buffer with intermediate characters collect: string; // params buffer params: number[]; // should abort (default: false) abort: boolean; } /** * DCS handler signature for EscapeSequenceParser. * EscapeSequenceParser handles DCS commands via separate * subparsers that get hook/unhooked and can handle * arbitrary amount of print data. * On entering a DSC sequence `hook` is called by * `EscapeSequenceParser`. Use it to initialize or reset * states needed to handle the current DCS sequence. * EscapeSequenceParser will call `put` several times if the * parsed string got splitted, therefore you might have to collect * `data` until `unhook` is called. `unhook` marks the end * of the current DCS sequence. */ export interface IDcsHandler { hook(collect: string, params: number[], flag: number): void; put(data: string, start: number, end: number): void; unhook(): void; } /** * EscapeSequenceParser interface. */ export interface IEscapeSequenceParser extends IDisposable { /** * Reset the parser to its initial state (handlers are kept). */ reset(): void; /** * Parse string `data`. * @param data The data to parse. */ parse(data: string): void; setPrintHandler(callback: (data: string, start: number, end: number) => void): void; clearPrintHandler(): void; setExecuteHandler(flag: string, callback: () => void): void; clearExecuteHandler(flag: string): void; setExecuteHandlerFallback(callback: (code: number) => void): void; setCsiHandler(flag: string, callback: (params: number[], collect: string) => void): void; clearCsiHandler(flag: string): void; setCsiHandlerFallback(callback: (collect: string, params: number[], flag: number) => void): void; setEscHandler(collectAndFlag: string, callback: () => void): void; clearEscHandler(collectAndFlag: string): void; setEscHandlerFallback(callback: (collect: string, flag: number) => void): void; setOscHandler(ident: number, callback: (data: string) => void): void; clearOscHandler(ident: number): void; setOscHandlerFallback(callback: (identifier: number, data: string) => void): void; setDcsHandler(collectAndFlag: string, handler: IDcsHandler): void; clearDcsHandler(collectAndFlag: string): void; setDcsHandlerFallback(handler: IDcsHandler): void; setErrorHandler(callback: (state: IParsingState) => IParsingState): void; clearErrorHandler(): void; } /** * Interface for a line in the terminal buffer. */ export interface IBufferLine { length: number; isWrapped: boolean; get(index: number): CharData; set(index: number, value: CharData): void; pop(): CharData | undefined; push(data: CharData): void; splice(start: number, deleteCount: number, ...items: CharData[]): CharData[]; insertCells(pos: number, n: number, ch: CharData): void; deleteCells(pos: number, n: number, fill: CharData): void; replaceCells(start: number, end: number, fill: CharData): void; } xterm.js-3.8.1/src/Viewport.ts000066400000000000000000000205241341514612000162530ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { IColorSet } from './renderer/Types'; import { ITerminal, IViewport } from './Types'; import { CharMeasure } from './ui/CharMeasure'; import { Disposable } from './common/Lifecycle'; import { addDisposableDomListener } from './ui/Lifecycle'; const FALLBACK_SCROLL_BAR_WIDTH = 15; /** * 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. */ export class Viewport extends Disposable implements IViewport { public scrollBarWidth: number = 0; private _currentRowHeight: number = 0; private _lastRecordedBufferLength: number = 0; private _lastRecordedViewportHeight: number = 0; private _lastRecordedBufferHeight: number = 0; private _lastTouchY: number; private _lastScrollTop: number = 0; // Stores a partial line amount when scrolling, this is used to keep track of how much of a line // is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a // quick fix and could have a more robust solution in place that reset the value when needed. private _wheelPartialScroll: number = 0; private _refreshAnimationFrame: number | null = null; private _ignoreNextScrollEvent: boolean = false; /** * Creates a new Viewport. * @param _terminal The terminal this viewport belongs to. * @param _viewportElement The DOM element acting as the viewport. * @param _scrollArea The DOM element acting as the scroll area. * @param _charMeasure A DOM element used to measure the character size of. the terminal. */ constructor( private _terminal: ITerminal, private _viewportElement: HTMLElement, private _scrollArea: HTMLElement, private _charMeasure: CharMeasure ) { super(); // Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar. // Unfortunately the overlay scrollbar would be hidden underneath the screen element in that case, // therefore we account for a standard amount to make it visible this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH; this.register(addDisposableDomListener(this._viewportElement, 'scroll', this._onScroll.bind(this))); // Perform this async to ensure the CharMeasure is ready. setTimeout(() => this.syncScrollArea(), 0); } public onThemeChanged(colors: IColorSet): void { this._viewportElement.style.backgroundColor = colors.background.css; } /** * Refreshes row height, setting line-height, viewport height and scroll area height if * necessary. */ private _refresh(): void { if (this._refreshAnimationFrame === null) { this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh()); } } private _innerRefresh(): void { if (this._charMeasure.height > 0) { this._currentRowHeight = this._terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio; this._lastRecordedViewportHeight = this._viewportElement.offsetHeight; const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._terminal.renderer.dimensions.canvasHeight); if (this._lastRecordedBufferHeight !== newBufferHeight) { this._lastRecordedBufferHeight = newBufferHeight; this._scrollArea.style.height = this._lastRecordedBufferHeight + 'px'; } } // Sync scrollTop const scrollTop = this._terminal.buffer.ydisp * this._currentRowHeight; if (this._viewportElement.scrollTop !== scrollTop) { // Ignore the next scroll event which will be triggered by setting the scrollTop as we do not // want this event to scroll the terminal this._ignoreNextScrollEvent = true; this._viewportElement.scrollTop = scrollTop; } this._refreshAnimationFrame = null; } /** * Updates dimensions and synchronizes the scroll area if necessary. */ public syncScrollArea(): void { // If buffer height changed if (this._lastRecordedBufferLength !== this._terminal.buffer.lines.length) { this._lastRecordedBufferLength = this._terminal.buffer.lines.length; this._refresh(); return; } // If viewport height changed if (this._lastRecordedViewportHeight !== (this._terminal).renderer.dimensions.canvasHeight) { this._refresh(); return; } // If the buffer position doesn't match last scroll top const newScrollTop = this._terminal.buffer.ydisp * this._currentRowHeight; if (this._lastScrollTop !== newScrollTop) { this._refresh(); return; } // If element's scroll top changed, this can happen when hiding the element if (this._lastScrollTop !== this._viewportElement.scrollTop) { this._refresh(); return; } // If row height changed if (this._terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) { this._refresh(); return; } } /** * 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): void { // Record current scroll top position this._lastScrollTop = this._viewportElement.scrollTop; // Don't attempt to scroll if the element is not visible, otherwise scrollTop will be corrupt // which causes the terminal to scroll the buffer to the top if (!this._viewportElement.offsetParent) { return; } // Ignore the event if it was flagged to ignore (when the source of the event is from Viewport) if (this._ignoreNextScrollEvent) { this._ignoreNextScrollEvent = false; return; } const newRow = Math.round(this._lastScrollTop / this._currentRowHeight); const diff = newRow - this._terminal.buffer.ydisp; this._terminal.scrollLines(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): void { const amount = this._getPixelsScrolled(ev); if (amount === 0) { return; } this._viewportElement.scrollTop += amount; // Prevent the page from scrolling when the terminal scrolls ev.preventDefault(); } private _getPixelsScrolled(ev: WheelEvent): number { // Do nothing if it's not a vertical scroll event if (ev.deltaY === 0) { return 0; } // Fallback to WheelEvent.DOM_DELTA_PIXEL let amount = ev.deltaY; if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) { amount *= this._currentRowHeight; } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { amount *= this._currentRowHeight * this._terminal.rows; } return amount; } /** * Gets the number of pixels scrolled by the mouse event taking into account what type of delta * is being used. * @param ev The mouse wheel event. */ public getLinesScrolled(ev: WheelEvent): number { // Do nothing if it's not a vertical scroll event if (ev.deltaY === 0) { return 0; } // Fallback to WheelEvent.DOM_DELTA_LINE let amount = ev.deltaY; if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) { amount /= this._currentRowHeight + 0.0; // Prevent integer division this._wheelPartialScroll += amount; amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1); this._wheelPartialScroll %= 1; } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) { amount *= this._terminal.rows; } return amount; } /** * Handles the touchstart event, recording the touch occurred. * @param ev The touch event. */ public onTouchStart(ev: TouchEvent): void { this._lastTouchY = ev.touches[0].pageY; } /** * Handles the touchmove event, scrolling the viewport if the position shifted. * @param ev The touch event. */ public onTouchMove(ev: TouchEvent): void { const deltaY = this._lastTouchY - ev.touches[0].pageY; this._lastTouchY = ev.touches[0].pageY; if (deltaY === 0) { return; } this._viewportElement.scrollTop += deltaY; ev.preventDefault(); } } xterm.js-3.8.1/src/addons/000077500000000000000000000000001341514612000153315ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/attach/000077500000000000000000000000001341514612000165755ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/attach/Interfaces.ts000066400000000000000000000010441341514612000212270ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT * * Implements the attach method, that attaches the terminal to a WebSocket stream. */ import { Terminal, IDisposable } from 'xterm'; export interface IAttachAddonTerminal extends Terminal { _core: { register(d: T): void; }; __socket?: WebSocket; __attachSocketBuffer?: string; __getMessage?(ev: MessageEvent): void; __flushBuffer?(): void; __pushToBuffer?(data: string): void; __sendData?(data: string): void; } xterm.js-3.8.1/src/addons/attach/attach.test.ts000066400000000000000000000010251341514612000213650ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as attach from './attach'; class MockTerminal {} describe('attach addon', () => { describe('apply', () => { it('should do register the `attach` and `detach` methods', () => { attach.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.attach, 'function'); assert.equal(typeof (MockTerminal).prototype.detach, 'function'); }); }); }); xterm.js-3.8.1/src/addons/attach/attach.ts000066400000000000000000000117021341514612000204120ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * @license MIT * * Implements the attach method, that attaches the terminal to a WebSocket stream. */ import { Terminal, IDisposable } from 'xterm'; import { IAttachAddonTerminal } from './Interfaces'; /** * Attaches the given terminal to the given socket. * * @param term The terminal to be attached to the given socket. * @param socket The socket to attach the current terminal. * @param bidirectional Whether the terminal should send data to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ export function attach(term: Terminal, socket: WebSocket, bidirectional: boolean, buffered: boolean): void { const addonTerminal = term; bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional; addonTerminal.__socket = socket; addonTerminal.__flushBuffer = () => { addonTerminal.write(addonTerminal.__attachSocketBuffer); addonTerminal.__attachSocketBuffer = null; }; addonTerminal.__pushToBuffer = (data: string) => { if (addonTerminal.__attachSocketBuffer) { addonTerminal.__attachSocketBuffer += data; } else { addonTerminal.__attachSocketBuffer = data; setTimeout(addonTerminal.__flushBuffer, 10); } }; // TODO: This should be typed but there seem to be issues importing the type let myTextDecoder: any; addonTerminal.__getMessage = function(ev: MessageEvent): void { let str: string; if (typeof ev.data === 'object') { if (!myTextDecoder) { myTextDecoder = new TextDecoder(); } if (ev.data instanceof ArrayBuffer) { str = myTextDecoder.decode(ev.data); displayData(str); } else { const fileReader = new FileReader(); fileReader.addEventListener('load', () => { str = myTextDecoder.decode(fileReader.result); displayData(str); }); fileReader.readAsArrayBuffer(ev.data); } } else if (typeof ev.data === 'string') { displayData(ev.data); } else { throw Error(`Cannot handle "${typeof ev.data}" websocket message.`); } }; /** * Push data to buffer or write it in the terminal. * This is used as a callback for FileReader.onload. * * @param str String decoded by FileReader. * @param data The data of the EventMessage. */ function displayData(str?: string, data?: string): void { if (buffered) { addonTerminal.__pushToBuffer(str || data); } else { addonTerminal.write(str || data); } } addonTerminal.__sendData = (data: string) => { if (socket.readyState !== 1) { return; } socket.send(data); }; addonTerminal._core.register(addSocketListener(socket, 'message', addonTerminal.__getMessage)); if (bidirectional) { addonTerminal._core.register(addonTerminal.addDisposableListener('data', addonTerminal.__sendData)); } addonTerminal._core.register(addSocketListener(socket, 'close', () => detach(addonTerminal, socket))); addonTerminal._core.register(addSocketListener(socket, 'error', () => detach(addonTerminal, socket))); } function addSocketListener(socket: WebSocket, type: string, handler: (this: WebSocket, ev: Event) => any): IDisposable { socket.addEventListener(type, handler); return { dispose: () => { if (!handler) { // Already disposed return; } socket.removeEventListener(type, handler); handler = null; } }; } /** * Detaches the given terminal from the given socket * * @param term The terminal to be detached from the given socket. * @param socket The socket from which to detach the current terminal. */ export function detach(term: Terminal, socket: WebSocket): void { const addonTerminal = term; addonTerminal.off('data', addonTerminal.__sendData); socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket; if (socket) { socket.removeEventListener('message', addonTerminal.__getMessage); } delete addonTerminal.__socket; } export function apply(terminalConstructor: typeof Terminal): void { /** * Attaches the current terminal to the given socket * * @param socket The socket to attach the current terminal. * @param bidirectional Whether the terminal should send data to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ (terminalConstructor.prototype).attach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void { attach(this, socket, bidirectional, buffered); }; /** * Detaches the current terminal from the given socket. * * @param socket The socket from which to detach the current terminal. */ (terminalConstructor.prototype).detach = function (socket: WebSocket): void { detach(this, socket); }; } xterm.js-3.8.1/src/addons/attach/index.html000066400000000000000000000043761341514612000206040ustar00rootroot00000000000000

xterm.js: socket attach

Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your Docker containers.

Socket information

xterm.js-3.8.1/src/addons/attach/package.json000066400000000000000000000001071341514612000210610ustar00rootroot00000000000000{ "name": "xterm.attach", "main": "attach.js", "private": true } xterm.js-3.8.1/src/addons/attach/tsconfig.json000066400000000000000000000005561341514612000213120ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/attach/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/fit/000077500000000000000000000000001341514612000161135ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/fit/README.md000066400000000000000000000002451341514612000173730ustar00rootroot00000000000000## fit addon The fit addon adjusts the dimensions of the terminal to match best fit its parent element container. `fit` will only work when the element is visible. xterm.js-3.8.1/src/addons/fit/fit.test.ts000066400000000000000000000010251341514612000202210ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as fit from './fit'; class MockTerminal {} describe('fit addon', () => { describe('apply', () => { it('should do register the `proposeGeometry` and `fit` methods', () => { fit.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.proposeGeometry, 'function'); assert.equal(typeof (MockTerminal).prototype.fit, 'function'); }); }); }); xterm.js-3.8.1/src/addons/fit/fit.ts000066400000000000000000000047241341514612000172540ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * @license MIT * * Fit terminal columns and rows to the dimensions of its DOM element. * * ## Approach * * Rows: Truncate the division of the terminal parent element height by the * terminal row height. * Columns: Truncate the division of the terminal parent element width by the * terminal character width (apply display: inline at the terminal * row and truncate its width with the current number of columns). */ import { Terminal } from 'xterm'; export interface IGeometry { rows: number; cols: number; } export function proposeGeometry(term: Terminal): IGeometry { if (!term.element.parentElement) { return null; } const parentElementStyle = window.getComputedStyle(term.element.parentElement); const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'))); const elementStyle = window.getComputedStyle(term.element); const elementPadding = { top: parseInt(elementStyle.getPropertyValue('padding-top')), bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')), right: parseInt(elementStyle.getPropertyValue('padding-right')), left: parseInt(elementStyle.getPropertyValue('padding-left')) }; const elementPaddingVer = elementPadding.top + elementPadding.bottom; const elementPaddingHor = elementPadding.right + elementPadding.left; const availableHeight = parentElementHeight - elementPaddingVer; const availableWidth = parentElementWidth - elementPaddingHor - (term)._core.viewport.scrollBarWidth; const geometry = { cols: Math.floor(availableWidth / (term)._core.renderer.dimensions.actualCellWidth), rows: Math.floor(availableHeight / (term)._core.renderer.dimensions.actualCellHeight) }; return geometry; } export function fit(term: Terminal): void { const geometry = proposeGeometry(term); if (geometry) { // Force a full render if (term.rows !== geometry.rows || term.cols !== geometry.cols) { (term)._core.renderer.clear(); term.resize(geometry.cols, geometry.rows); } } } export function apply(terminalConstructor: typeof Terminal): void { (terminalConstructor.prototype).proposeGeometry = function (): IGeometry { return proposeGeometry(this); }; (terminalConstructor.prototype).fit = function (): void { fit(this); }; } xterm.js-3.8.1/src/addons/fit/package.json000066400000000000000000000001011341514612000203710ustar00rootroot00000000000000{ "name": "xterm.fit", "main": "fit.js", "private": true } xterm.js-3.8.1/src/addons/fit/tsconfig.json000066400000000000000000000005531341514612000206250ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/fit/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/fullscreen/000077500000000000000000000000001341514612000174735ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/fullscreen/fullscreen.css000066400000000000000000000002261341514612000223470ustar00rootroot00000000000000.xterm.fullscreen { position: fixed; top: 0; bottom: 0; left: 0; right: 0; width: auto; height: auto; z-index: 255; } xterm.js-3.8.1/src/addons/fullscreen/fullscreen.test.ts000066400000000000000000000007361341514612000231710ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as fullscreen from './fullscreen'; class MockTerminal {} describe('fullscreen addon', () => { describe('apply', () => { it('should do register the `toggleFullscreen` method', () => { fullscreen.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.toggleFullScreen, 'function'); }); }); }); xterm.js-3.8.1/src/addons/fullscreen/fullscreen.ts000066400000000000000000000015271341514612000222120ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal } from 'xterm'; /** * Toggle the given terminal's fullscreen mode. * @param term The terminal to toggle full screen mode * @param fullscreen Toggle fullscreen on (true) or off (false) */ export function toggleFullScreen(term: Terminal, fullscreen: boolean): void { let fn: string; if (typeof fullscreen === 'undefined') { fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add'; } else if (!fullscreen) { fn = 'remove'; } else { fn = 'add'; } term.element.classList[fn]('fullscreen'); } export function apply(terminalConstructor: typeof Terminal): void { (terminalConstructor.prototype).toggleFullScreen = function (fullscreen: boolean): void { toggleFullScreen(this, fullscreen); }; } xterm.js-3.8.1/src/addons/fullscreen/package.json000066400000000000000000000001171341514612000217600ustar00rootroot00000000000000{ "name": "xterm.fullscreen", "main": "fullscreen.js", "private": true } xterm.js-3.8.1/src/addons/fullscreen/tsconfig.json000066400000000000000000000005621341514612000222050ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/fullscreen/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/search/000077500000000000000000000000001341514612000165765ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/search/Interfaces.ts000066400000000000000000000013161341514612000212320ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal } from 'xterm'; // TODO: Don't rely on this private API export interface ITerminalCore { buffer: any; selectionManager: any; } export interface ISearchAddonTerminal extends Terminal { __searchHelper?: ISearchHelper; _core: ITerminalCore; } export interface ISearchHelper { findNext(term: string, searchOptions: ISearchOptions): boolean; findPrevious(term: string, searchOptions: ISearchOptions): boolean; } export interface ISearchOptions { regex?: boolean; wholeWord?: boolean; caseSensitive?: boolean; } export interface ISearchResult { term: string; col: number; row: number; } xterm.js-3.8.1/src/addons/search/SearchHelper.ts000066400000000000000000000167601341514612000215250ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ISearchHelper, ISearchAddonTerminal, ISearchOptions, ISearchResult } from './Interfaces'; const nonWordCharacters = ' ~!@#$%^&*()_+`-=[]{}|\;:"\',./<>?'; /** * A class that knows how to search the terminal and how to display the results. */ export class SearchHelper implements ISearchHelper { constructor(private _terminal: ISearchAddonTerminal) { // TODO: Search for multiple instances on 1 line // TODO: Don't use the actual selection, instead use a "find selection" so multiple instances can be highlighted // TODO: Highlight other instances in the viewport } /** * Find the next instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. * @param term Tne search term. * @param searchOptions Search options. * @return Whether a result was found. */ public findNext(term: string, searchOptions?: ISearchOptions): boolean { if (!term || term.length === 0) { return false; } let result: ISearchResult; let startRow = this._terminal._core.buffer.ydisp; if (this._terminal._core.selectionManager.selectionEnd) { // Start from the selection end if there is a selection startRow = this._terminal._core.selectionManager.selectionEnd[1]; } // Search from ydisp + 1 to end for (let y = startRow + 1; y < this._terminal._core.buffer.ybase + this._terminal.rows; y++) { result = this._findInLine(term, y, searchOptions); if (result) { break; } } // Search from the top to the current ydisp if (!result) { for (let y = 0; y < startRow; y++) { result = this._findInLine(term, y, searchOptions); if (result) { break; } } } // Set selection and scroll if a result was found return this._selectResult(result); } /** * Find the previous instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. * @param term Tne search term. * @param searchOptions Search options. * @return Whether a result was found. */ public findPrevious(term: string, searchOptions?: ISearchOptions): boolean { if (!term || term.length === 0) { return false; } let result: ISearchResult; let startRow = this._terminal._core.buffer.ydisp; if (this._terminal._core.selectionManager.selectionStart) { // Start from the selection end if there is a selection startRow = this._terminal._core.selectionManager.selectionStart[1]; } // Search from ydisp + 1 to end for (let y = startRow - 1; y >= 0; y--) { result = this._findInLine(term, y, searchOptions); if (result) { break; } } // Search from the top to the current ydisp if (!result) { for (let y = this._terminal._core.buffer.ybase + this._terminal.rows - 1; y > startRow; y--) { result = this._findInLine(term, y, searchOptions); if (result) { break; } } } // Set selection and scroll if a result was found return this._selectResult(result); } /** * A found substring is a whole word if it doesn't have an alphanumeric character directly adjacent to it. * @param searchIndex starting indext of the potential whole word substring * @param line entire string in which the potential whole word was found * @param term the substring that starts at searchIndex */ private _isWholeWord(searchIndex: number, line: string, term: string): boolean { return (((searchIndex === 0) || (nonWordCharacters.indexOf(line[searchIndex - 1]) !== -1)) && (((searchIndex + term.length) === line.length) || (nonWordCharacters.indexOf(line[searchIndex + term.length]) !== -1))); } /** * Searches a line for a search term. Takes the provided terminal line and searches the text line, which may contain * subsequent terminal lines if the text is wrapped. If the provided line number is part of a wrapped text line that * started on an earlier line then it is skipped since it will be properly searched when the terminal line that the * text starts on is searched. * @param term Tne search term. * @param y The line to search. * @param searchOptions Search options. * @return The search result if it was found. */ protected _findInLine(term: string, y: number, searchOptions: ISearchOptions = {}): ISearchResult { if (this._terminal._core.buffer.lines.get(y).isWrapped) { return; } const stringLine = this.translateBufferLineToStringWithWrap(y, true); const searchStringLine = searchOptions.caseSensitive ? stringLine : stringLine.toLowerCase(); const searchTerm = searchOptions.caseSensitive ? term : term.toLowerCase(); let searchIndex = -1; if (searchOptions.regex) { const searchRegex = RegExp(searchTerm, 'g'); const foundTerm = searchRegex.exec(searchStringLine); if (foundTerm && foundTerm[0].length > 0) { searchIndex = searchRegex.lastIndex - foundTerm[0].length; term = foundTerm[0]; } } else { searchIndex = searchStringLine.indexOf(searchTerm); } if (searchIndex >= 0) { // Adjust the row number and search index if needed since a "line" of text can span multiple rows if (searchIndex >= this._terminal.cols) { y += Math.floor(searchIndex / this._terminal.cols); searchIndex = searchIndex % this._terminal.cols; } if (searchOptions.wholeWord && !this._isWholeWord(searchIndex, searchStringLine, term)) { return; } const line = this._terminal._core.buffer.lines.get(y); for (let i = 0; i < searchIndex; i++) { const charData = line.get(i); // Adjust the searchIndex to normalize emoji into single chars const char = charData[1/*CHAR_DATA_CHAR_INDEX*/]; if (char.length > 1) { searchIndex -= char.length - 1; } // Adjust the searchIndex for empty characters following wide unicode // chars (eg. CJK) const charWidth = charData[2/*CHAR_DATA_WIDTH_INDEX*/]; if (charWidth === 0) { searchIndex++; } } return { term, col: searchIndex, row: y }; } } /** * Translates a buffer line to a string, including subsequent lines if they are wraps. * Wide characters will count as two columns in the resulting string. This * function is useful for getting the actual text underneath the raw selection * position. * @param line The line being translated. * @param trimRight Whether to trim whitespace to the right. */ public translateBufferLineToStringWithWrap(lineIndex: number, trimRight: boolean): string { let lineString = ''; let lineWrapsToNext: boolean; do { const nextLine = this._terminal._core.buffer.lines.get(lineIndex + 1); lineWrapsToNext = nextLine ? nextLine.isWrapped : false; lineString += this._terminal._core.buffer.translateBufferLineToString(lineIndex, !lineWrapsToNext && trimRight); lineIndex++; } while (lineWrapsToNext); return lineString; } /** * Selects and scrolls to a result. * @param result The result to select. * @return Whethera result was selected. */ private _selectResult(result: ISearchResult): boolean { if (!result) { return false; } this._terminal._core.selectionManager.setSelection(result.col, result.row, result.term.length); this._terminal.scrollLines(result.row - this._terminal._core.buffer.ydisp); return true; } } xterm.js-3.8.1/src/addons/search/package.json000066400000000000000000000001071341514612000210620ustar00rootroot00000000000000{ "name": "xterm.search", "main": "search.js", "private": true } xterm.js-3.8.1/src/addons/search/search.test.ts000066400000000000000000000237161341514612000214020ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as search from './search'; import { SearchHelper } from './SearchHelper'; import { ISearchOptions, ISearchResult } from './Interfaces'; class MockTerminalPlain {} class MockTerminal { private _core: any; public searchHelper: TestSearchHelper; public cols: number; constructor(options: any) { this._core = new (require('../../../lib/Terminal').Terminal)(options); this.searchHelper = new TestSearchHelper(this as any); this.cols = options.cols; } get core(): any { return this._core; } pushWriteData(): void { this._core._innerWrite(); } } class TestSearchHelper extends SearchHelper { public findInLine(term: string, y: number, searchOptions?: ISearchOptions): ISearchResult { return this._findInLine(term, y, searchOptions); } } describe('search addon', () => { describe('apply', () => { it('should register findNext and findPrevious', () => { search.apply(MockTerminalPlain); assert.equal(typeof (MockTerminalPlain).prototype.findNext, 'function'); assert.equal(typeof (MockTerminalPlain).prototype.findPrevious, 'function'); }); }); describe('find', () => { it('Searchhelper - should find correct position', () => { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 3}); term.core.write('Hello World\r\ntest\n123....hello'); term.pushWriteData(); const hello0 = term.searchHelper.findInLine('Hello', 0); const hello1 = term.searchHelper.findInLine('Hello', 1); const hello2 = term.searchHelper.findInLine('Hello', 2); expect(hello0).eql({col: 0, row: 0, term: 'Hello'}); expect(hello1).eql(undefined); expect(hello2).eql({col: 11, row: 2, term: 'Hello'}); }); it('should find search term accross line wrap', () => { search.apply(MockTerminal); const term = new MockTerminal({cols: 10, rows: 5}); term.core.write('texttextHellotext\r\n'); term.core.write('texttexttextHellotext goodbye'); term.pushWriteData(); /* texttextHe llotext texttextte xtHellotex t (these spaces included intentionally) goodbye */ const hello0 = term.searchHelper.findInLine('Hello', 0); const hello1 = term.searchHelper.findInLine('Hello', 1); const hello2 = term.searchHelper.findInLine('Hello', 2); const hello3 = term.searchHelper.findInLine('Hello', 3); const llo = term.searchHelper.findInLine('llo', 1); const goodbye = term.searchHelper.findInLine('goodbye', 2); expect(hello0).eql({col: 8, row: 0, term: 'Hello'}); expect(hello1).eql(undefined); expect(hello2).eql({col: 2, row: 3, term: 'Hello'}); expect(hello3).eql(undefined); expect(llo).eql(undefined); expect(goodbye).eql({col: 0, row: 5, term: 'goodbye'}); }); it('should respect search regex', () => { search.apply(MockTerminal); const term = new MockTerminal({cols: 10, rows: 4}); term.core.write('abcdefghijklmnopqrstuvwxyz\r\n~/dev '); /* abcdefghij klmnopqrst uvwxyz ~/dev */ term.pushWriteData(); const searchOptions = { regex: true, wholeWord: false, caseSensitive: false }; const hello0 = term.searchHelper.findInLine('dee*', 0, searchOptions); const hello1 = term.searchHelper.findInLine('jkk*', 0, searchOptions); const hello2 = term.searchHelper.findInLine('mnn*', 1, searchOptions); const tilda0 = term.searchHelper.findInLine('^~', 3, searchOptions); const tilda1 = term.searchHelper.findInLine('^[~]', 3, searchOptions); const tilda2 = term.searchHelper.findInLine('^\\~', 3, searchOptions); expect(hello0).eql({col: 3, row: 0, term: 'de'}); expect(hello1).eql({col: 9, row: 0, term: 'jk'}); expect(hello2).eql(undefined); expect(tilda0).eql({col: 0, row: 3, term: '~'}); expect(tilda1).eql({col: 0, row: 3, term: '~'}); expect(tilda2).eql({col: 0, row: 3, term: '~'}); }); it('should not select empty lines', () => { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 3}); term.core.write(' '); term.pushWriteData(); const line = term.searchHelper.findInLine('^.*$', 0, { regex: true }); expect(line).eql(undefined); }); it('should respect case sensitive', function(): void { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 4}); term.core.write('Hello World\r\n123....hello\r\nmoreTestHello'); term.pushWriteData(); const searchOptions = { regex: false, wholeWord: false, caseSensitive: true }; const hello0 = term.searchHelper.findInLine('Hello', 0, searchOptions); const hello1 = term.searchHelper.findInLine('Hello', 1, searchOptions); const hello2 = term.searchHelper.findInLine('Hello', 2, searchOptions); expect(hello0).eql({col: 0, row: 0, term: 'Hello'}); expect(hello1).eql(undefined); expect(hello2).eql({col: 8, row: 2, term: 'Hello'}); }); it('should respect case sensitive + regex', function(): void { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 4}); term.core.write('hellohello\r\nHelloHello'); term.pushWriteData(); /** * hellohello * HelloHello */ const searchOptions = { regex: true, wholeWord: false, caseSensitive: true }; const hello0 = term.searchHelper.findInLine('Hello', 0, searchOptions); const hello1 = term.searchHelper.findInLine('Hello$', 0, searchOptions); const hello2 = term.searchHelper.findInLine('Hello', 1, searchOptions); const hello3 = term.searchHelper.findInLine('Hello$', 1, searchOptions); expect(hello0).eql(undefined); expect(hello1).eql(undefined); expect(hello2).eql({col: 0, row: 1, term: 'Hello'}); expect(hello3).eql({col: 5, row: 1, term: 'Hello'}); }); it('should respect whole-word search option', function(): void { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 5}); term.core.write('Hello World\r\nWorld Hello\r\nWorldHelloWorld\r\nHelloWorld\r\nWorldHello'); term.pushWriteData(); const searchOptions = { regex: false, wholeWord: true, caseSensitive: false }; const hello0 = term.searchHelper.findInLine('Hello', 0, searchOptions); const hello1 = term.searchHelper.findInLine('Hello', 1, searchOptions); const hello2 = term.searchHelper.findInLine('Hello', 2, searchOptions); const hello3 = term.searchHelper.findInLine('Hello', 3, searchOptions); const hello4 = term.searchHelper.findInLine('Hello', 4, searchOptions); expect(hello0).eql({col: 0, row: 0, term: 'Hello'}); expect(hello1).eql({col: 6, row: 1, term: 'Hello'}); expect(hello2).eql(undefined); expect(hello3).eql(undefined); expect(hello4).eql(undefined); }); it('should respect whole-word + case sensitive search options', function(): void { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 5}); term.core.write('Hello World\r\nHelloWorld'); term.pushWriteData(); const searchOptions = { regex: false, wholeWord: true, caseSensitive: true }; const hello0 = term.searchHelper.findInLine('Hello', 0, searchOptions); const hello1 = term.searchHelper.findInLine('hello', 0, searchOptions); const hello2 = term.searchHelper.findInLine('Hello', 1, searchOptions); const hello3 = term.searchHelper.findInLine('hello', 1, searchOptions); expect(hello0).eql({col: 0, row: 0, term: 'Hello'}); expect(hello1).eql(undefined); expect(hello2).eql(undefined); expect(hello3).eql(undefined); }); it('should respect whole-word + regex search options', function(): void { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 5}); term.core.write('Hello World Hello\r\nHelloWorldHello'); term.pushWriteData(); const searchOptions = { regex: true, wholeWord: true, caseSensitive: false }; const hello0 = term.searchHelper.findInLine('Hello', 0, searchOptions); const hello1 = term.searchHelper.findInLine('Hello$', 0, searchOptions); const hello2 = term.searchHelper.findInLine('Hello', 1, searchOptions); const hello3 = term.searchHelper.findInLine('Hello$', 1, searchOptions); expect(hello0).eql({col: 0, row: 0, term: 'hello'}); expect(hello1).eql({col: 12, row: 0, term: 'hello'}); expect(hello2).eql(undefined); expect(hello3).eql(undefined); }); it('should respect all search options', function(): void { search.apply(MockTerminal); const term = new MockTerminal({cols: 20, rows: 5}); term.core.write('Hello World Hello\r\nHelloWorldHello'); term.pushWriteData(); const searchOptions = { regex: true, wholeWord: true, caseSensitive: true }; const hello0 = term.searchHelper.findInLine('Hello', 0, searchOptions); const hello1 = term.searchHelper.findInLine('Hello$', 0, searchOptions); const hello2 = term.searchHelper.findInLine('hello', 0, searchOptions); const hello3 = term.searchHelper.findInLine('hello$', 0, searchOptions); const hello4 = term.searchHelper.findInLine('hello', 1, searchOptions); const hello5 = term.searchHelper.findInLine('hello$', 1, searchOptions); expect(hello0).eql({col: 0, row: 0, term: 'Hello'}); expect(hello1).eql({col: 12, row: 0, term: 'Hello'}); expect(hello2).eql(undefined); expect(hello3).eql(undefined); expect(hello4).eql(undefined); expect(hello5).eql(undefined); }); }); }); xterm.js-3.8.1/src/addons/search/search.ts000066400000000000000000000034071341514612000204170ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { SearchHelper } from './SearchHelper'; import { Terminal } from 'xterm'; import { ISearchAddonTerminal, ISearchOptions } from './Interfaces'; /** * Find the next instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. * @param term Tne search term. * @param searchOptions Search options * @return Whether a result was found. */ export function findNext(terminal: Terminal, term: string, searchOptions: ISearchOptions = {}): boolean { const addonTerminal = terminal; if (!addonTerminal.__searchHelper) { addonTerminal.__searchHelper = new SearchHelper(addonTerminal); } return addonTerminal.__searchHelper.findNext(term, searchOptions); } /** * Find the previous instance of the term, then scroll to and select it. If it * doesn't exist, do nothing. * @param term Tne search term. * @param searchOptions Search options * @return Whether a result was found. */ export function findPrevious(terminal: Terminal, term: string, searchOptions: ISearchOptions): boolean { const addonTerminal = terminal; if (!addonTerminal.__searchHelper) { addonTerminal.__searchHelper = new SearchHelper(addonTerminal); } return addonTerminal.__searchHelper.findPrevious(term, searchOptions); } export function apply(terminalConstructor: typeof Terminal): void { (terminalConstructor.prototype).findNext = function(term: string, searchOptions: ISearchOptions): boolean { return findNext(this, term, searchOptions); }; (terminalConstructor.prototype).findPrevious = function(term: string, searchOptions: ISearchOptions): boolean { return findPrevious(this, term, searchOptions); }; } xterm.js-3.8.1/src/addons/search/tsconfig.json000066400000000000000000000005561341514612000213130ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/search/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/terminado/000077500000000000000000000000001341514612000173135ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/terminado/Interfaces.ts000066400000000000000000000010201341514612000217370ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT * * Implements the attach method, that attaches the terminal to a WebSocket stream. */ import { Terminal } from 'xterm'; export interface ITerminadoAddonTerminal extends Terminal { __socket?: WebSocket; __attachSocketBuffer?: string; __getMessage?(ev: MessageEvent): void; __flushBuffer?(): void; __pushToBuffer?(data: string): void; __sendData?(data: string): void; __setSize?(size: {rows: number, cols: number}): void; } xterm.js-3.8.1/src/addons/terminado/package.json000066400000000000000000000001151341514612000215760ustar00rootroot00000000000000{ "name": "xterm.terminado", "main": "terminado.js", "private": true } xterm.js-3.8.1/src/addons/terminado/terminado.test.ts000066400000000000000000000011051341514612000226200ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as terminado from './terminado'; class MockTerminal {} describe('terminado addon', () => { describe('apply', () => { it('should do register the `terminadoAttach` and `terminadoDetach` methods', () => { terminado.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.terminadoAttach, 'function'); assert.equal(typeof (MockTerminal).prototype.terminadoDetach, 'function'); }); }); }); xterm.js-3.8.1/src/addons/terminado/terminado.ts000066400000000000000000000073571341514612000216610ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT * * This module provides methods for attaching a terminal to a terminado * WebSocket stream. */ import { Terminal } from 'xterm'; import { ITerminadoAddonTerminal } from './Interfaces'; /** * Attaches the given terminal to the given socket. * * @param term The terminal to be attached to the given socket. * @param socket The socket to attach the current terminal. * @param bidirectional Whether the terminal should send data to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ export function terminadoAttach(term: Terminal, socket: WebSocket, bidirectional: boolean, buffered: boolean): void { const addonTerminal = term; bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional; addonTerminal.__socket = socket; addonTerminal.__flushBuffer = () => { addonTerminal.write(addonTerminal.__attachSocketBuffer); addonTerminal.__attachSocketBuffer = null; }; addonTerminal.__pushToBuffer = (data: string) => { if (addonTerminal.__attachSocketBuffer) { addonTerminal.__attachSocketBuffer += data; } else { addonTerminal.__attachSocketBuffer = data; setTimeout(addonTerminal.__flushBuffer, 10); } }; addonTerminal.__getMessage = (ev: MessageEvent) => { const data = JSON.parse(ev.data); if (data[0] === 'stdout') { if (buffered) { addonTerminal.__pushToBuffer(data[1]); } else { addonTerminal.write(data[1]); } } }; addonTerminal.__sendData = (data: string) => { socket.send(JSON.stringify(['stdin', data])); }; addonTerminal.__setSize = (size: {rows: number, cols: number}) => { socket.send(JSON.stringify(['set_size', size.rows, size.cols])); }; socket.addEventListener('message', addonTerminal.__getMessage); if (bidirectional) { addonTerminal.on('data', addonTerminal.__sendData); } addonTerminal.on('resize', addonTerminal.__setSize); socket.addEventListener('close', () => terminadoDetach(addonTerminal, socket)); socket.addEventListener('error', () => terminadoDetach(addonTerminal, socket)); } /** * Detaches the given terminal from the given socket * * @param term The terminal to be detached from the given socket. * @param socket The socket from which to detach the current terminal. */ export function terminadoDetach(term: Terminal, socket: WebSocket): void { const addonTerminal = term; addonTerminal.off('data', addonTerminal.__sendData); socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket; if (socket) { socket.removeEventListener('message', addonTerminal.__getMessage); } delete addonTerminal.__socket; } export function apply(terminalConstructor: typeof Terminal): void { /** * Attaches the current terminal to the given socket * * @param socket - The socket to attach the current terminal. * @param bidirectional - Whether the terminal should send data to the socket as well. * @param buffered - Whether the rendering of incoming data should happen instantly or at a * maximum frequency of 1 rendering per 10ms. */ (terminalConstructor.prototype).terminadoAttach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void { return terminadoAttach(this, socket, bidirectional, buffered); }; /** * Detaches the current terminal from the given socket. * * @param socket The socket from which to detach the current terminal. */ (terminalConstructor.prototype).terminadoDetach = function (socket: WebSocket): void { return terminadoDetach(this, socket); }; } xterm.js-3.8.1/src/addons/terminado/tsconfig.json000066400000000000000000000005611341514612000220240ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/terminado/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/webLinks/000077500000000000000000000000001341514612000171075ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/webLinks/package.json000066400000000000000000000001131341514612000213700ustar00rootroot00000000000000{ "name": "xterm.weblinks", "main": "weblinks.js", "private": true } xterm.js-3.8.1/src/addons/webLinks/tsconfig.json000066400000000000000000000005601341514612000216170ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/webLinks/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/webLinks/webLinks.test.ts000066400000000000000000000021401341514612000222100ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as webLinks from './webLinks'; class MockTerminal { public regex: RegExp; public handler: (event: MouseEvent, uri: string) => void; public options?: any; public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: any): number { this.regex = regex; this.handler = handler; this.options = options; return 0; } } describe('webLinks addon', () => { describe('apply', () => { it('should do register the `webLinksInit` method', () => { webLinks.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.webLinksInit, 'function'); }); }); it('should allow ~ character in URI path', () => { const term = new MockTerminal(); webLinks.webLinksInit(term); const row = ' http://foo.com/a~b#c~d?e~f '; const match = row.match(term.regex); const uri = match[term.options.matchIndex]; assert.equal(uri, 'http://foo.com/a~b#c~d?e~f'); }); }); xterm.js-3.8.1/src/addons/webLinks/webLinks.ts000066400000000000000000000041001341514612000212300ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ILinkMatcherOptions } from 'xterm'; const protocolClause = '(https?:\\/\\/)'; const domainCharacterSet = '[\\da-z\\.-]+'; const negatedDomainCharacterSet = '[^\\da-z\\.-]+'; const domainBodyClause = '(' + domainCharacterSet + ')'; const tldClause = '([a-z\\.]{2,6})'; const ipClause = '((\\d{1,3}\\.){3}\\d{1,3})'; const localHostClause = '(localhost)'; const portClause = '(:\\d{1,5})'; const hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + '|' + localHostClause + ')' + portClause + '?'; const pathClause = '(\\/[\\/\\w\\.\\-%~]*)*'; const queryStringHashFragmentCharacterSet = '[0-9\\w\\[\\]\\(\\)\\/\\?\\!#@$%&\'*+,:;~\\=\\.\\-]*'; const queryStringClause = '(\\?' + queryStringHashFragmentCharacterSet + ')?'; const hashFragmentClause = '(#' + queryStringHashFragmentCharacterSet + ')?'; const negatedPathCharacterSet = '[^\\/\\w\\.\\-%]+'; const bodyClause = hostClause + pathClause + queryStringClause + hashFragmentClause; const start = '(?:^|' + negatedDomainCharacterSet + ')('; const end = ')($|' + negatedPathCharacterSet + ')'; const strictUrlRegex = new RegExp(start + protocolClause + bodyClause + end); function handleLink(event: MouseEvent, uri: string): void { window.open(uri, '_blank'); } /** * Initialize the web links addon, registering the link matcher. * @param term The terminal to use web links within. * @param handler A custom handler to use. * @param options Custom options to use, matchIndex will always be ignored. */ export function webLinksInit(term: Terminal, handler: (event: MouseEvent, uri: string) => void = handleLink, options: ILinkMatcherOptions = {}): void { options.matchIndex = 1; term.registerLinkMatcher(strictUrlRegex, handler, options); } export function apply(terminalConstructor: typeof Terminal): void { (terminalConstructor.prototype).webLinksInit = function (handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): void { webLinksInit(this, handler, options); }; } xterm.js-3.8.1/src/addons/winptyCompat/000077500000000000000000000000001341514612000200275ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/winptyCompat/Interfaces.ts000066400000000000000000000004111341514612000224560ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal } from 'xterm'; export interface ITerminalCore { buffer: any; } export interface IWinptyCompatAddonTerminal extends Terminal { _core: ITerminalCore; } xterm.js-3.8.1/src/addons/winptyCompat/package.json000066400000000000000000000001231341514612000223110ustar00rootroot00000000000000{ "name": "xterm.winptycompat", "main": "winptyCompat.js", "private": true } xterm.js-3.8.1/src/addons/winptyCompat/tsconfig.json000066400000000000000000000005641341514612000225430ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/winptyCompat/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/winptyCompat/winptyCompat.test.ts000066400000000000000000000007461341514612000240620ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as winptyCompat from './winptyCompat'; class MockTerminal {} describe('winptyCompat addon', () => { describe('apply', () => { it('should do register the `winptyCompatInit` method', () => { winptyCompat.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.winptyCompatInit, 'function'); }); }); }); xterm.js-3.8.1/src/addons/winptyCompat/winptyCompat.ts000066400000000000000000000033611341514612000231000ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal } from 'xterm'; import { IWinptyCompatAddonTerminal } from './Interfaces'; const CHAR_DATA_CODE_INDEX = 3; const NULL_CELL_CODE = 32; export function winptyCompatInit(terminal: Terminal): void { const addonTerminal = terminal; // Don't do anything when the platform is not Windows const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; if (!isWindows) { return; } // Winpty does not support wraparound mode which means that lines will never // be marked as wrapped. This causes issues for things like copying a line // retaining the wrapped new line characters or if consumers are listening // in on the data stream. // // The workaround for this is to listen to every incoming line feed and mark // the line as wrapped if the last character in the previous line is not a // space. This is certainly not without its problems, but generally on // Windows when text reaches the end of the terminal it's likely going to be // wrapped. addonTerminal.on('linefeed', () => { const line = addonTerminal._core.buffer.lines.get(addonTerminal._core.buffer.ybase + addonTerminal._core.buffer.y - 1); const lastChar = line.get(addonTerminal.cols - 1); if (lastChar[CHAR_DATA_CODE_INDEX] !== NULL_CELL_CODE) { const nextLine = addonTerminal._core.buffer.lines.get(addonTerminal._core.buffer.ybase + addonTerminal._core.buffer.y); nextLine.isWrapped = true; } }); } export function apply(terminalConstructor: typeof Terminal): void { (terminalConstructor.prototype).winptyCompatInit = function (): void { winptyCompatInit(this); }; } xterm.js-3.8.1/src/addons/zmodem/000077500000000000000000000000001341514612000166245ustar00rootroot00000000000000xterm.js-3.8.1/src/addons/zmodem/package.json000066400000000000000000000001071341514612000211100ustar00rootroot00000000000000{ "name": "xterm.zmodem", "main": "zmodem.js", "private": true } xterm.js-3.8.1/src/addons/zmodem/tsconfig.json000066400000000000000000000005561341514612000213410ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es6", ], "rootDir": ".", "outDir": "../../../lib/addons/zmodem/", "sourceMap": true, "removeComments": true, "declaration": true, "preserveWatchOutput": true }, "include": [ "**/*.ts", "../../../typings/xterm.d.ts" ] } xterm.js-3.8.1/src/addons/zmodem/zmodem.test.ts000066400000000000000000000010661341514612000214500ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import * as zmodem from './zmodem'; class MockTerminal {} describe('zmodem addon', () => { describe('apply', () => { it('should do register the `zmodemAttach` method and `zmodemBrowser` attribute', () => { zmodem.apply(MockTerminal); assert.equal(typeof (MockTerminal).prototype.zmodemAttach, 'function'); assert.equal(typeof (MockTerminal).prototype.zmodemBrowser, 'object'); }); }); }); xterm.js-3.8.1/src/addons/zmodem/zmodem.ts000066400000000000000000000056341341514612000204770ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal } from 'xterm'; /** * * Allow xterm.js to handle ZMODEM uploads and downloads. * * This addon is a wrapper around zmodem.js. It adds the following to the * Terminal class: * * - function `zmodemAttach(, )` - creates a Zmodem.Sentry * on the passed WebSocket object. The Object passed is optional and * can contain: * - noTerminalWriteOutsideSession: Suppress writes from the Sentry * object to the Terminal while there is no active Session. This * is necessary for compatibility with, for example, the * `attach.js` addon. * * - event `zmodemDetect` - fired on Zmodem.Sentry’s `on_detect` callback. * Passes the zmodem.js Detection object. * * - event `zmodemRetract` - fired on Zmodem.Sentry’s `on_retract` callback. * * You’ll need to provide logic to handle uploads and downloads. * See zmodem.js’s documentation for more details. * * **IMPORTANT:** After you confirm() a zmodem.js Detection, if you have * used the `attach` or `terminado` addons, you’ll need to suspend their * operation for the duration of the ZMODEM session. (The demo does this * via `detach()` and a re-`attach()`.) */ let zmodem; export interface IZmodemOptions { noTerminalWriteOutsideSession?: boolean; } function zmodemAttach(ws: WebSocket, opts: IZmodemOptions = {}): void { const term = this; const senderFunc = (octets: ArrayLike) => ws.send(new Uint8Array(octets)); let zsentry; function shouldWrite(): boolean { return !!zsentry.get_confirmed_session() || !opts.noTerminalWriteOutsideSession; } zsentry = new zmodem.Sentry({ to_terminal: (octets: ArrayLike) => { if (shouldWrite()) { term.write( String.fromCharCode.apply(String, octets) ); } }, sender: senderFunc, on_retract: () => (term).emit('zmodemRetract'), on_detect: (detection: any) => (term).emit('zmodemDetect', detection) }); function handleWSMessage(evt: MessageEvent): void { // In testing with xterm.js’s demo the first message was // always text even if the rest were binary. While that // may be specific to xterm.js’s demo, ultimately we // should reject anything that isn’t binary. if (typeof evt.data === 'string') { if (shouldWrite()) { term.write(evt.data); } } else { zsentry.consume(evt.data); } } ws.binaryType = 'arraybuffer'; ws.addEventListener('message', handleWSMessage); } export function apply(terminalConstructor: typeof Terminal): void { zmodem = (typeof window === 'object') ? (window).Zmodem : {Browser: null}; // Nullify browser for tests (terminalConstructor.prototype).zmodemAttach = zmodemAttach; (terminalConstructor.prototype).zmodemBrowser = zmodem.Browser; } xterm.js-3.8.1/src/common/000077500000000000000000000000001341514612000153515ustar00rootroot00000000000000xterm.js-3.8.1/src/common/CircularList.test.ts000066400000000000000000000174521341514612000213100ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { CircularList } from './CircularList'; describe('CircularList', () => { describe('push', () => { it('should push values onto the array', () => { const list = new CircularList(5); list.push('1'); list.push('2'); list.push('3'); list.push('4'); list.push('5'); assert.equal(list.get(0), '1'); assert.equal(list.get(1), '2'); assert.equal(list.get(2), '3'); assert.equal(list.get(3), '4'); assert.equal(list.get(4), '5'); }); it('should push old values from the start out of the array when max length is reached', () => { const list = new CircularList(2); list.push('1'); list.push('2'); assert.equal(list.get(0), '1'); assert.equal(list.get(1), '2'); list.push('3'); assert.equal(list.get(0), '2'); assert.equal(list.get(1), '3'); list.push('4'); assert.equal(list.get(0), '3'); assert.equal(list.get(1), '4'); }); }); describe('maxLength', () => { it('should increase the size of the list', () => { const list = new CircularList(2); list.push('1'); list.push('2'); assert.equal(list.get(0), '1'); assert.equal(list.get(1), '2'); list.maxLength = 4; list.push('3'); list.push('4'); assert.equal(list.get(0), '1'); assert.equal(list.get(1), '2'); assert.equal(list.get(2), '3'); assert.equal(list.get(3), '4'); list.push('wrapped'); assert.equal(list.get(0), '2'); assert.equal(list.get(1), '3'); assert.equal(list.get(2), '4'); assert.equal(list.get(3), 'wrapped'); }); it('should return the maximum length of the list', () => { const list = new CircularList(2); assert.equal(list.maxLength, 2); list.push('1'); list.push('2'); assert.equal(list.maxLength, 2); list.push('3'); assert.equal(list.maxLength, 2); list.maxLength = 4; assert.equal(list.maxLength, 4); }); }); describe('length', () => { it('should return the current length of the list, capped at the maximum length', () => { const list = new CircularList(2); assert.equal(list.length, 0); list.push('1'); assert.equal(list.length, 1); list.push('2'); assert.equal(list.length, 2); list.push('3'); assert.equal(list.length, 2); }); }); describe('splice', () => { it('should delete items', () => { const list = new CircularList(2); list.push('1'); list.push('2'); list.splice(0, 1); assert.equal(list.length, 1); assert.equal(list.get(0), '2'); list.push('3'); list.splice(1, 1); assert.equal(list.length, 1); assert.equal(list.get(0), '2'); }); it('should insert items', () => { const list = new CircularList(2); list.push('1'); list.splice(0, 0, '2'); assert.equal(list.length, 2); assert.equal(list.get(0), '2'); assert.equal(list.get(1), '1'); list.splice(1, 0, '3'); assert.equal(list.length, 2); assert.equal(list.get(0), '3'); assert.equal(list.get(1), '1'); }); it('should delete items then insert items', () => { const list = new CircularList(3); list.push('1'); list.push('2'); list.splice(0, 1, '3', '4'); assert.equal(list.length, 3); assert.equal(list.get(0), '3'); assert.equal(list.get(1), '4'); assert.equal(list.get(2), '2'); }); it('should wrap the array correctly when more items are inserted than deleted', () => { const list = new CircularList(3); list.push('1'); list.push('2'); list.splice(1, 0, '3', '4'); assert.equal(list.length, 3); assert.equal(list.get(0), '3'); assert.equal(list.get(1), '4'); assert.equal(list.get(2), '2'); }); }); describe('trimStart', () => { it('should remove items from the beginning of the list', () => { const list = new CircularList(5); list.push('1'); list.push('2'); list.push('3'); list.push('4'); list.push('5'); list.trimStart(1); assert.equal(list.length, 4); assert.deepEqual(list.get(0), '2'); assert.deepEqual(list.get(1), '3'); assert.deepEqual(list.get(2), '4'); assert.deepEqual(list.get(3), '5'); list.trimStart(2); assert.equal(list.length, 2); assert.deepEqual(list.get(0), '4'); assert.deepEqual(list.get(1), '5'); }); it('should remove all items if the requested trim amount is larger than the list\'s length', () => { const list = new CircularList(5); list.push('1'); list.trimStart(2); assert.equal(list.length, 0); }); }); describe('shiftElements', () => { it('should not mutate the list when count is 0', () => { const list = new CircularList(5); list.push(1); list.push(2); list.shiftElements(0, 0, 1); assert.equal(list.length, 2); assert.equal(list.get(0), 1); assert.equal(list.get(1), 2); }); it('should throw for invalid args', () => { const list = new CircularList(5); list.push(1); assert.throws(() => list.shiftElements(-1, 1, 1), 'start argument out of range'); assert.throws(() => list.shiftElements(1, 1, 1), 'start argument out of range'); assert.throws(() => list.shiftElements(0, 1, -1), 'Cannot shift elements in list beyond index 0'); }); it('should shift an element forward', () => { const list = new CircularList(5); list.push(1); list.push(2); list.shiftElements(0, 1, 1); assert.equal(list.length, 2); assert.equal(list.get(0), 1); assert.equal(list.get(1), 1); }); it('should shift elements forward', () => { const list = new CircularList(5); list.push(1); list.push(2); list.push(3); list.push(4); list.shiftElements(0, 2, 2); assert.equal(list.length, 4); assert.equal(list.get(0), 1); assert.equal(list.get(1), 2); assert.equal(list.get(2), 1); assert.equal(list.get(3), 2); }); it('should shift elements forward, expanding the list if needed', () => { const list = new CircularList(5); list.push(1); list.push(2); list.shiftElements(0, 2, 2); assert.equal(list.length, 4); assert.equal(list.get(0), 1); assert.equal(list.get(1), 2); assert.equal(list.get(2), 1); assert.equal(list.get(3), 2); }); it('should shift elements forward, wrapping the list if needed', () => { const list = new CircularList(5); list.push(1); list.push(2); list.push(3); list.push(4); list.push(5); list.shiftElements(2, 2, 3); assert.equal(list.length, 5); assert.equal(list.get(0), 3); assert.equal(list.get(1), 4); assert.equal(list.get(2), 5); assert.equal(list.get(3), 3); assert.equal(list.get(4), 4); }); it('should shift an element backwards', () => { const list = new CircularList(5); list.push(1); list.push(2); list.shiftElements(1, 1, -1); assert.equal(list.length, 2); assert.equal(list.get(0), 2); assert.equal(list.get(1), 2); }); it('should shift elements backwards', () => { const list = new CircularList(5); list.push(1); list.push(2); list.push(3); list.push(4); list.shiftElements(2, 2, -2); assert.equal(list.length, 4); assert.equal(list.get(0), 3); assert.equal(list.get(1), 4); assert.equal(list.get(2), 3); assert.equal(list.get(3), 4); }); }); }); xterm.js-3.8.1/src/common/CircularList.ts000066400000000000000000000140621341514612000203240ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { EventEmitter } from './EventEmitter'; import { ICircularList } from './Types'; /** * Represents a circular list; a list with a maximum size that wraps around when push is called, * overriding values at the start of the list. */ export class CircularList extends EventEmitter implements ICircularList { protected _array: (T | undefined)[]; private _startIndex: number; private _length: number; constructor( private _maxLength: number ) { super(); this._array = new Array(this._maxLength); this._startIndex = 0; this._length = 0; } public get maxLength(): number { return this._maxLength; } public set maxLength(newMaxLength: number) { // There was no change in maxLength, return early. if (this._maxLength === newMaxLength) { return; } // Reconstruct array, starting at index 0. Only transfer values from the // indexes 0 to length. const newArray = new Array(newMaxLength); for (let i = 0; i < Math.min(newMaxLength, this.length); i++) { newArray[i] = this._array[this._getCyclicIndex(i)]; } this._array = newArray; this._maxLength = newMaxLength; this._startIndex = 0; } public get length(): number { return this._length; } public set length(newLength: number) { if (newLength > this._length) { for (let i = this._length; i < newLength; i++) { this._array[i] = undefined; } } this._length = newLength; } /** * Gets the value at an index. * * Note that for performance reasons there is no bounds checking here, the index reference is * circular so this should always return a value and never throw. * @param index The index of the value to get. * @return The value corresponding to the index. */ public get(index: number): T | undefined { return this._array[this._getCyclicIndex(index)]; } /** * Sets the value at an index. * * Note that for performance reasons there is no bounds checking here, the index reference is * circular so this should always return a value and never throw. * @param index The index to set. * @param value The value to set. */ public set(index: number, value: T | undefined): void { this._array[this._getCyclicIndex(index)] = value; } /** * Pushes a new value onto the list, wrapping around to the start of the array, overriding index 0 * if the maximum length is reached. * @param value The value to push onto the list. */ public push(value: T): void { this._array[this._getCyclicIndex(this._length)] = value; if (this._length === this._maxLength) { this._startIndex++; if (this._startIndex === this._maxLength) { this._startIndex = 0; } this.emit('trim', 1); } else { this._length++; } } /** * Removes and returns the last value on the list. * @return The popped value. */ public pop(): T | undefined { return this._array[this._getCyclicIndex(this._length-- - 1)]; } /** * Deletes and/or inserts items at a particular index (in that order). Unlike * Array.prototype.splice, this operation does not return the deleted items as a new array in * order to save creating a new array. Note that this operation may shift all values in the list * in the worst case. * @param start The index to delete and/or insert. * @param deleteCount The number of elements to delete. * @param items The items to insert. */ public splice(start: number, deleteCount: number, ...items: T[]): void { // Delete items if (deleteCount) { for (let i = start; i < this._length - deleteCount; i++) { this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)]; } this._length -= deleteCount; } if (items && items.length) { // Add items for (let i = this._length - 1; i >= start; i--) { this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)]; } for (let i = 0; i < items.length; i++) { this._array[this._getCyclicIndex(start + i)] = items[i]; } // Adjust length as needed if (this._length + items.length > this.maxLength) { const countToTrim = (this._length + items.length) - this.maxLength; this._startIndex += countToTrim; this._length = this.maxLength; this.emit('trim', countToTrim); } else { this._length += items.length; } } } /** * Trims a number of items from the start of the list. * @param count The number of items to remove. */ public trimStart(count: number): void { if (count > this._length) { count = this._length; } this._startIndex += count; this._length -= count; this.emit('trim', count); } public shiftElements(start: number, count: number, offset: number): void { if (count <= 0) { return; } if (start < 0 || start >= this._length) { throw new Error('start argument out of range'); } if (start + offset < 0) { throw new Error('Cannot shift elements in list beyond index 0'); } if (offset > 0) { for (let i = count - 1; i >= 0; i--) { this.set(start + i + offset, this.get(start + i)); } const expandListBy = (start + count + offset) - this._length; if (expandListBy > 0) { this._length += expandListBy; while (this._length > this.maxLength) { this._length--; this._startIndex++; this.emit('trim', 1); } } } else { for (let i = 0; i < count; i++) { this.set(start + i + offset, this.get(start + i)); } } } /** * Gets the cyclic index for the specified regular index. The cyclic index can then be used on the * backing array to get the element associated with the regular index. * @param index The regular index. * @returns The cyclic index. */ private _getCyclicIndex(index: number): number { return (this._startIndex + index) % this.maxLength; } } xterm.js-3.8.1/src/common/EventEmitter.test.ts000066400000000000000000000043731341514612000213210ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { EventEmitter } from './EventEmitter'; describe('EventEmitter', () => { let eventEmitter: EventEmitter; beforeEach(() => { eventEmitter = new EventEmitter(); }); describe('emit', () => { it('should emit events to listeners', () => { let count1 = 0; let count2 = 0; const listener1 = () => count1++; const listener2 = () => count2++; eventEmitter.on('test', listener1); eventEmitter.on('test', listener2); eventEmitter.emit('test'); assert.equal(count1, 1); assert.equal(count2, 1); eventEmitter.emit('test'); assert.equal(count1, 2); assert.equal(count2, 2); }); it('should manage multiple listener types', () => { let count1 = 0; let count2 = 0; const listener1 = () => count1++; const listener2 = () => count2++; eventEmitter.on('test', listener1); eventEmitter.on('foo', listener2); eventEmitter.emit('test'); assert.equal(count1, 1); assert.equal(count2, 0); eventEmitter.emit('foo'); assert.equal(count1, 1); assert.equal(count2, 1); }); }); describe('listeners', () => { it('should return listeners for the type requested', () => { assert.equal(eventEmitter.listeners('test').length, 0); const listener = () => {}; eventEmitter.on('test', listener); assert.deepEqual(eventEmitter.listeners('test'), [listener]); }); }); describe('off', () => { it('should remove the specific listener', () => { const listener1 = () => {}; const listener2 = () => {}; eventEmitter.on('foo', listener1); eventEmitter.on('foo', listener2); assert.equal(eventEmitter.listeners('foo').length, 2); eventEmitter.off('foo', listener1); assert.deepEqual(eventEmitter.listeners('foo'), [listener2]); }); }); describe('removeAllListeners', () => { it('should clear all listeners', () => { eventEmitter.on('foo', () => {}); assert.equal(eventEmitter.listeners('foo').length, 1); eventEmitter.removeAllListeners('foo'); assert.equal(eventEmitter.listeners('foo').length, 0); }); }); }); xterm.js-3.8.1/src/common/EventEmitter.ts000066400000000000000000000041531341514612000203370ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { XtermListener } from './Types'; import { IEventEmitter, IDisposable } from 'xterm'; import { Disposable } from './Lifecycle'; export class EventEmitter extends Disposable implements IEventEmitter, IDisposable { private _events: {[type: string]: XtermListener[]}; constructor() { super(); // Restore the previous events if available, this will happen if the // constructor is called multiple times on the same object (terminal reset). this._events = this._events || {}; } public on(type: string, listener: XtermListener): void { this._events[type] = this._events[type] || []; this._events[type].push(listener); } /** * Adds a disposabe listener to the EventEmitter, returning the disposable. * @param type The event type. * @param handler The handler for the listener. */ public addDisposableListener(type: string, handler: XtermListener): IDisposable { // TODO: Rename addDisposableEventListener to more easily disambiguate from Dom listener this.on(type, handler); let disposed = false; return { dispose: () => { if (disposed) { // Already disposed return; } this.off(type, handler); disposed = true; } }; } public off(type: string, listener: XtermListener): void { if (!this._events[type]) { return; } const obj = this._events[type]; let i = obj.length; while (i--) { if (obj[i] === listener) { obj.splice(i, 1); return; } } } public removeAllListeners(type: string): void { if (this._events[type]) { delete this._events[type]; } } public emit(type: string, ...args: any[]): void { if (!this._events[type]) { return; } const obj = this._events[type]; for (let i = 0; i < obj.length; i++) { obj[i].apply(this, args); } } public listeners(type: string): XtermListener[] { return this._events[type] || []; } public dispose(): void { super.dispose(); this._events = {}; } } xterm.js-3.8.1/src/common/Lifecycle.test.ts000066400000000000000000000021101341514612000205700ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { Disposable } from './Lifecycle'; class TestDisposable extends Disposable { public get isDisposed(): boolean { return this._isDisposed; } } describe('Disposable', () => { describe('register', () => { it('should register disposables', () => { const d = new TestDisposable(); const d2 = { dispose: () => { throw new Error(); } }; d.register(d2); assert.throws(() => d.dispose()); }); }); describe('unregister', () => { it('should unregister disposables', () => { const d = new TestDisposable(); const d2 = { dispose: () => { throw new Error(); } }; d.register(d2); d.unregister(d2); assert.doesNotThrow(() => d.dispose()); }); }); describe('dispose', () => { it('should set is disposed flag', () => { const d = new TestDisposable(); assert.isFalse(d.isDisposed); d.dispose(); assert.isTrue(d.isDisposed); }); }); }); xterm.js-3.8.1/src/common/Lifecycle.ts000066400000000000000000000022471341514612000176250ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable } from 'xterm'; /** * A base class that can be extended to provide convenience methods for managing the lifecycle of an * object and its components. */ export abstract class Disposable implements IDisposable { protected _disposables: IDisposable[] = []; protected _isDisposed: boolean = false; constructor() { } /** * Disposes the object, triggering the `dispose` method on all registered IDisposables. */ public dispose(): void { this._isDisposed = true; this._disposables.forEach(d => d.dispose()); this._disposables.length = 0; } /** * Registers a disposable object. * @param d The disposable to register. */ public register(d: T): void { this._disposables.push(d); } /** * Unregisters a disposable object if it has been registered, if not do * nothing. * @param d The disposable to unregister. */ public unregister(d: T): void { const index = this._disposables.indexOf(d); if (index !== -1) { this._disposables.splice(index, 1); } } } xterm.js-3.8.1/src/common/Types.ts000066400000000000000000000015411341514612000170260ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { IEventEmitter } from 'xterm'; export type XtermListener = (...args: any[]) => void; /** * A keyboard event interface which does not depend on the DOM, KeyboardEvent implicitly extends * this event. */ export interface IKeyboardEvent { altKey: boolean; ctrlKey: boolean; shiftKey: boolean; metaKey: boolean; keyCode: number; key: string; type: string; } export interface ICircularList extends IEventEmitter { length: number; maxLength: number; get(index: number): T | undefined; set(index: number, value: T): void; push(value: T): void; pop(): T | undefined; splice(start: number, deleteCount: number, ...items: T[]): void; trimStart(count: number): void; shiftElements(start: number, count: number, offset: number): void; } xterm.js-3.8.1/src/common/data/000077500000000000000000000000001341514612000162625ustar00rootroot00000000000000xterm.js-3.8.1/src/common/data/EscapeSequences.ts000066400000000000000000000107141341514612000217110ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ /** * C0 control codes * See = https://en.wikipedia.org/wiki/C0_and_C1_control_codes */ export namespace C0 { /** Null (Caret = ^@, C = \0) */ export const NUL = '\x00'; /** Start of Heading (Caret = ^A) */ export const SOH = '\x01'; /** Start of Text (Caret = ^B) */ export const STX = '\x02'; /** End of Text (Caret = ^C) */ export const ETX = '\x03'; /** End of Transmission (Caret = ^D) */ export const EOT = '\x04'; /** Enquiry (Caret = ^E) */ export const ENQ = '\x05'; /** Acknowledge (Caret = ^F) */ export const ACK = '\x06'; /** Bell (Caret = ^G, C = \a) */ export const BEL = '\x07'; /** Backspace (Caret = ^H, C = \b) */ export const BS = '\x08'; /** Character Tabulation, Horizontal Tabulation (Caret = ^I, C = \t) */ export const HT = '\x09'; /** Line Feed (Caret = ^J, C = \n) */ export const LF = '\x0a'; /** Line Tabulation, Vertical Tabulation (Caret = ^K, C = \v) */ export const VT = '\x0b'; /** Form Feed (Caret = ^L, C = \f) */ export const FF = '\x0c'; /** Carriage Return (Caret = ^M, C = \r) */ export const CR = '\x0d'; /** Shift Out (Caret = ^N) */ export const SO = '\x0e'; /** Shift In (Caret = ^O) */ export const SI = '\x0f'; /** Data Link Escape (Caret = ^P) */ export const DLE = '\x10'; /** Device Control One (XON) (Caret = ^Q) */ export const DC1 = '\x11'; /** Device Control Two (Caret = ^R) */ export const DC2 = '\x12'; /** Device Control Three (XOFF) (Caret = ^S) */ export const DC3 = '\x13'; /** Device Control Four (Caret = ^T) */ export const DC4 = '\x14'; /** Negative Acknowledge (Caret = ^U) */ export const NAK = '\x15'; /** Synchronous Idle (Caret = ^V) */ export const SYN = '\x16'; /** End of Transmission Block (Caret = ^W) */ export const ETB = '\x17'; /** Cancel (Caret = ^X) */ export const CAN = '\x18'; /** End of Medium (Caret = ^Y) */ export const EM = '\x19'; /** Substitute (Caret = ^Z) */ export const SUB = '\x1a'; /** Escape (Caret = ^[, C = \e) */ export const ESC = '\x1b'; /** File Separator (Caret = ^\) */ export const FS = '\x1c'; /** Group Separator (Caret = ^]) */ export const GS = '\x1d'; /** Record Separator (Caret = ^^) */ export const RS = '\x1e'; /** Unit Separator (Caret = ^_) */ export const US = '\x1f'; /** Space */ export const SP = '\x20'; /** Delete (Caret = ^?) */ export const DEL = '\x7f'; } /** * C1 control codes * See = https://en.wikipedia.org/wiki/C0_and_C1_control_codes */ export namespace C1 { /** padding character */ export const PAD = '\x80'; /** High Octet Preset */ export const HOP = '\x81'; /** Break Permitted Here */ export const BPH = '\x82'; /** No Break Here */ export const NBH = '\x83'; /** Index */ export const IND = '\x84'; /** Next Line */ export const NEL = '\x85'; /** Start of Selected Area */ export const SSA = '\x86'; /** End of Selected Area */ export const ESA = '\x87'; /** Horizontal Tabulation Set */ export const HTS = '\x88'; /** Horizontal Tabulation With Justification */ export const HTJ = '\x89'; /** Vertical Tabulation Set */ export const VTS = '\x8a'; /** Partial Line Down */ export const PLD = '\x8b'; /** Partial Line Up */ export const PLU = '\x8c'; /** Reverse Index */ export const RI = '\x8d'; /** Single-Shift 2 */ export const SS2 = '\x8e'; /** Single-Shift 3 */ export const SS3 = '\x8f'; /** Device Control String */ export const DCS = '\x90'; /** Private Use 1 */ export const PU1 = '\x91'; /** Private Use 2 */ export const PU2 = '\x92'; /** Set Transmit State */ export const STS = '\x93'; /** Destructive backspace, intended to eliminate ambiguity about meaning of BS. */ export const CCH = '\x94'; /** Message Waiting */ export const MW = '\x95'; /** Start of Protected Area */ export const SPA = '\x96'; /** End of Protected Area */ export const EPA = '\x97'; /** Start of String */ export const SOS = '\x98'; /** Single Graphic Character Introducer */ export const SGCI = '\x99'; /** Single Character Introducer */ export const SCI = '\x9a'; /** Control Sequence Introducer */ export const CSI = '\x9b'; /** String Terminator */ export const ST = '\x9c'; /** Operating System Command */ export const OSC = '\x9d'; /** Privacy Message */ export const PM = '\x9e'; /** Application Program Command */ export const APC = '\x9f'; } xterm.js-3.8.1/src/core/000077500000000000000000000000001341514612000150115ustar00rootroot00000000000000xterm.js-3.8.1/src/core/Types.ts000066400000000000000000000005341341514612000164670ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ export const enum KeyboardResultType { SEND_KEY, SELECT_ALL, PAGE_UP, PAGE_DOWN } export interface IKeyboardResult { type: KeyboardResultType; cancel: boolean; key: string | undefined; } export interface ICharset { [key: string]: string; } xterm.js-3.8.1/src/core/data/000077500000000000000000000000001341514612000157225ustar00rootroot00000000000000xterm.js-3.8.1/src/core/data/Charsets.ts000066400000000000000000000106761341514612000200600ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { ICharset } from '../Types'; /** * The character sets supported by the terminal. These enable several languages * to be represented within the terminal with only 8-bit encoding. See ISO 2022 * for a discussion on character sets. Only VT100 character sets are supported. */ export const CHARSETS: { [key: string]: ICharset } = {}; /** * The default character set, US. */ export const DEFAULT_CHARSET: ICharset = CHARSETS['B']; /** * DEC Special Character and Line Drawing Set. * Reference: http://vt100.net/docs/vt102-ug/table5-13.html * A lot of curses apps use this if they see TERM=xterm. * testing: echo -e '\e(0a\e(B' * The xterm output sometimes seems to conflict with the * reference above. xterm seems in line with the reference * when running vttest however. * The table below now uses xterm's output from vttest. */ CHARSETS['0'] = { '`': '\u25c6', // '◆' 'a': '\u2592', // '▒' 'b': '\u0009', // '\t' 'c': '\u000c', // '\f' 'd': '\u000d', // '\r' 'e': '\u000a', // '\n' 'f': '\u00b0', // '°' 'g': '\u00b1', // '±' 'h': '\u2424', // '\u2424' (NL) 'i': '\u000b', // '\v' 'j': '\u2518', // '┘' 'k': '\u2510', // '┐' 'l': '\u250c', // '┌' 'm': '\u2514', // '└' 'n': '\u253c', // '┼' 'o': '\u23ba', // '⎺' 'p': '\u23bb', // '⎻' 'q': '\u2500', // '─' 'r': '\u23bc', // '⎼' 's': '\u23bd', // '⎽' 't': '\u251c', // '├' 'u': '\u2524', // '┤' 'v': '\u2534', // '┴' 'w': '\u252c', // '┬' 'x': '\u2502', // '│' 'y': '\u2264', // '≤' 'z': '\u2265', // '≥' '{': '\u03c0', // 'π' '|': '\u2260', // '≠' '}': '\u00a3', // '£' '~': '\u00b7' // '·' }; /** * British character set * ESC (A * Reference: http://vt100.net/docs/vt220-rm/table2-5.html */ CHARSETS['A'] = { '#': '£' }; /** * United States character set * ESC (B */ CHARSETS['B'] = null; /** * Dutch character set * ESC (4 * Reference: http://vt100.net/docs/vt220-rm/table2-6.html */ CHARSETS['4'] = { '#': '£', '@': '¾', '[': 'ij', '\\': '½', ']': '|', '{': '¨', '|': 'f', '}': '¼', '~': '´' }; /** * Finnish character set * ESC (C or ESC (5 * Reference: http://vt100.net/docs/vt220-rm/table2-7.html */ CHARSETS['C'] = CHARSETS['5'] = { '[': 'Ä', '\\': 'Ö', ']': 'Å', '^': 'Ü', '`': 'é', '{': 'ä', '|': 'ö', '}': 'å', '~': 'ü' }; /** * French character set * ESC (R * Reference: http://vt100.net/docs/vt220-rm/table2-8.html */ CHARSETS['R'] = { '#': '£', '@': 'à', '[': '°', '\\': 'ç', ']': '§', '{': 'é', '|': 'ù', '}': 'è', '~': '¨' }; /** * French Canadian character set * ESC (Q * Reference: http://vt100.net/docs/vt220-rm/table2-9.html */ CHARSETS['Q'] = { '@': 'à', '[': 'â', '\\': 'ç', ']': 'ê', '^': 'î', '`': 'ô', '{': 'é', '|': 'ù', '}': 'è', '~': 'û' }; /** * German character set * ESC (K * Reference: http://vt100.net/docs/vt220-rm/table2-10.html */ CHARSETS['K'] = { '@': '§', '[': 'Ä', '\\': 'Ö', ']': 'Ü', '{': 'ä', '|': 'ö', '}': 'ü', '~': 'ß' }; /** * Italian character set * ESC (Y * Reference: http://vt100.net/docs/vt220-rm/table2-11.html */ CHARSETS['Y'] = { '#': '£', '@': '§', '[': '°', '\\': 'ç', ']': 'é', '`': 'ù', '{': 'à', '|': 'ò', '}': 'è', '~': 'ì' }; /** * Norwegian/Danish character set * ESC (E or ESC (6 * Reference: http://vt100.net/docs/vt220-rm/table2-12.html */ CHARSETS['E'] = CHARSETS['6'] = { '@': 'Ä', '[': 'Æ', '\\': 'Ø', ']': 'Å', '^': 'Ü', '`': 'ä', '{': 'æ', '|': 'ø', '}': 'å', '~': 'ü' }; /** * Spanish character set * ESC (Z * Reference: http://vt100.net/docs/vt220-rm/table2-13.html */ CHARSETS['Z'] = { '#': '£', '@': '§', '[': '¡', '\\': 'Ñ', ']': '¿', '{': '°', '|': 'ñ', '}': 'ç' }; /** * Swedish character set * ESC (H or ESC (7 * Reference: http://vt100.net/docs/vt220-rm/table2-14.html */ CHARSETS['H'] = CHARSETS['7'] = { '@': 'É', '[': 'Ä', '\\': 'Ö', ']': 'Å', '^': 'Ü', '`': 'é', '{': 'ä', '|': 'ö', '}': 'å', '~': 'ü' }; /** * Swiss character set * ESC (= * Reference: http://vt100.net/docs/vt220-rm/table2-15.html */ CHARSETS['='] = { '#': 'ù', '@': 'à', '[': 'é', '\\': 'ç', ']': 'ê', '^': 'î', '_': 'è', '`': 'ô', '{': 'ä', '|': 'ö', '}': 'ü', '~': 'û' }; xterm.js-3.8.1/src/core/input/000077500000000000000000000000001341514612000161505ustar00rootroot00000000000000xterm.js-3.8.1/src/core/input/Keyboard.test.ts000066400000000000000000000430731341514612000212450ustar00rootroot00000000000000 import { assert } from 'chai'; import { evaluateKeyboardEvent } from './Keyboard'; import { IKeyboardResult } from '../Types'; /** * A helper function for testing which allows passing in a partial event and defaults will be filled * in on it. */ function testEvaluateKeyboardEvent(partialEvent: { altKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean; metaKey?: boolean; keyCode?: number; key?: string; type?: string; }, partialOptions: { applicationCursorMode?: boolean; isMac?: boolean; macOptionIsMeta?: boolean; } = {}): IKeyboardResult { const event = { altKey: partialEvent.altKey || false, ctrlKey: partialEvent.ctrlKey || false, shiftKey: partialEvent.shiftKey || false, metaKey: partialEvent.metaKey || false, keyCode: partialEvent.keyCode !== undefined ? partialEvent.keyCode : undefined, key: partialEvent.key || '', type: partialEvent.type || '' }; const options = { applicationCursorMode: partialOptions.applicationCursorMode || false, isMac: partialOptions.isMac || false, macOptionIsMeta: partialOptions.macOptionIsMeta || false }; return evaluateKeyboardEvent(event, options.applicationCursorMode, options.isMac, options.macOptionIsMeta); } describe('Keyboard', () => { describe('evaluateKeyEscapeSequence', () => { it('should return the correct escape sequence for unmodified keys', () => { // Backspace assert.equal(testEvaluateKeyboardEvent({ keyCode: 8 }).key, '\x7f'); // ^? // Tab assert.equal(testEvaluateKeyboardEvent({ keyCode: 9 }).key, '\t'); // Return/enter assert.equal(testEvaluateKeyboardEvent({ keyCode: 13 }).key, '\r'); // CR // Escape assert.equal(testEvaluateKeyboardEvent({ keyCode: 27 }).key, '\x1b'); // Page up, page down assert.equal(testEvaluateKeyboardEvent({ keyCode: 33 }).key, '\x1b[5~'); // CSI 5 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 34 }).key, '\x1b[6~'); // CSI 6 ~ // End, Home assert.equal(testEvaluateKeyboardEvent({ keyCode: 35 }).key, '\x1b[F'); // SS3 F assert.equal(testEvaluateKeyboardEvent({ keyCode: 36 }).key, '\x1b[H'); // SS3 H // Left, up, right, down arrows assert.equal(testEvaluateKeyboardEvent({ keyCode: 37 }).key, '\x1b[D'); // CSI D assert.equal(testEvaluateKeyboardEvent({ keyCode: 38 }).key, '\x1b[A'); // CSI A assert.equal(testEvaluateKeyboardEvent({ keyCode: 39 }).key, '\x1b[C'); // CSI C assert.equal(testEvaluateKeyboardEvent({ keyCode: 40 }).key, '\x1b[B'); // CSI B // Insert assert.equal(testEvaluateKeyboardEvent({ keyCode: 45 }).key, '\x1b[2~'); // CSI 2 ~ // Delete assert.equal(testEvaluateKeyboardEvent({ keyCode: 46 }).key, '\x1b[3~'); // CSI 3 ~ // F1-F12 assert.equal(testEvaluateKeyboardEvent({ keyCode: 112 }).key, '\x1bOP'); // SS3 P assert.equal(testEvaluateKeyboardEvent({ keyCode: 113 }).key, '\x1bOQ'); // SS3 Q assert.equal(testEvaluateKeyboardEvent({ keyCode: 114 }).key, '\x1bOR'); // SS3 R assert.equal(testEvaluateKeyboardEvent({ keyCode: 115 }).key, '\x1bOS'); // SS3 S assert.equal(testEvaluateKeyboardEvent({ keyCode: 116 }).key, '\x1b[15~'); // CSI 1 5 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 117 }).key, '\x1b[17~'); // CSI 1 7 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 118 }).key, '\x1b[18~'); // CSI 1 8 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 119 }).key, '\x1b[19~'); // CSI 1 9 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 120 }).key, '\x1b[20~'); // CSI 2 0 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 121 }).key, '\x1b[21~'); // CSI 2 1 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 122 }).key, '\x1b[23~'); // CSI 2 3 ~ assert.equal(testEvaluateKeyboardEvent({ keyCode: 123 }).key, '\x1b[24~'); // CSI 2 4 ~ }); it('should return \\x1b[3;5~ for ctrl+delete', () => { assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 46 }).key, '\x1b[3;5~'); }); it('should return \\x1b[3;2~ for shift+delete', () => { assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 46 }).key, '\x1b[3;2~'); }); it('should return \\x1b[3;3~ for alt+delete', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 46 }).key, '\x1b[3;3~'); }); it('should return \\x1b[5D for ctrl+left', () => { assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 37 }).key, '\x1b[1;5D'); // CSI 5 D }); it('should return \\x1b[5C for ctrl+right', () => { assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 39 }).key, '\x1b[1;5C'); // CSI 5 C }); it('should return \\x1b[5A for ctrl+up', () => { assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A }); it('should return \\x1b[5B for ctrl+down', () => { assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B }); describe('On non-macOS platforms', () => { // Evalueate alt + arrow key movement, which is a feature of terminal emulators but not VT100 // http://unix.stackexchange.com/a/108106 it('should return \\x1b[5D for alt+left', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: false }).key, '\x1b[1;5D'); // CSI 5 D }); it('should return \\x1b[5C for alt+right', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;5C'); // CSI 5 C }); it('should return \\x1ba for alt+a', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba'); }); }); describe('On macOS platforms', () => { it('should return \\x1bb for alt+left', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 37 }, { isMac: true }).key, '\x1bb'); // CSI 5 D }); it('should return \\x1bf for alt+right', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C }); it('should return undefined for alt+a', () => { assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true }; }); }); describe('with macOptionIsMeta', () => { it('should return \\x1ba for alt+a', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true, macOptionIsMeta: true }).key, '\x1ba'); }); }); it('should return \\x1b[5A for alt+up', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }).key, '\x1b[1;5A'); // CSI 5 A }); it('should return \\x1b[5B for alt+down', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }).key, '\x1b[1;5B'); // CSI 5 B }); it('should return the correct escape sequence for modified F1-F12 keys', () => { assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 112 }).key, '\x1b[1;2P'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 113 }).key, '\x1b[1;2Q'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 114 }).key, '\x1b[1;2R'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 115 }).key, '\x1b[1;2S'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 116 }).key, '\x1b[15;2~'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 117 }).key, '\x1b[17;2~'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 118 }).key, '\x1b[18;2~'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 119 }).key, '\x1b[19;2~'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 120 }).key, '\x1b[20;2~'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 121 }).key, '\x1b[21;2~'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 122 }).key, '\x1b[23;2~'); assert.equal(testEvaluateKeyboardEvent({ shiftKey: true, keyCode: 123 }).key, '\x1b[24;2~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 112 }).key, '\x1b[1;3P'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 113 }).key, '\x1b[1;3Q'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 114 }).key, '\x1b[1;3R'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 115 }).key, '\x1b[1;3S'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 116 }).key, '\x1b[15;3~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 117 }).key, '\x1b[17;3~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 118 }).key, '\x1b[18;3~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 119 }).key, '\x1b[19;3~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 120 }).key, '\x1b[20;3~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 121 }).key, '\x1b[21;3~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 122 }).key, '\x1b[23;3~'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 123 }).key, '\x1b[24;3~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 112 }).key, '\x1b[1;5P'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 113 }).key, '\x1b[1;5Q'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 114 }).key, '\x1b[1;5R'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 115 }).key, '\x1b[1;5S'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 116 }).key, '\x1b[15;5~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 117 }).key, '\x1b[17;5~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 118 }).key, '\x1b[18;5~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 119 }).key, '\x1b[19;5~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 120 }).key, '\x1b[20;5~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 121 }).key, '\x1b[21;5~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 122 }).key, '\x1b[23;5~'); assert.equal(testEvaluateKeyboardEvent({ ctrlKey: true, keyCode: 123 }).key, '\x1b[24;5~'); }); // Characters using ctrl+alt sequences it('should return proper sequence for ctrl+alt+a', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, ctrlKey: true, keyCode: 65 }).key, '\x1b\x01'); }); // Characters using alt sequences (numbers) it('should return proper sequences for alt+0', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 48 }).key, '\x1b0'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 48 }).key, '\x1b)'); }); it('should return proper sequences for alt+1', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 49 }).key, '\x1b1'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 49 }).key, '\x1b!'); }); it('should return proper sequences for alt+2', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 50 }).key, '\x1b2'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 50 }).key, '\x1b@'); }); it('should return proper sequences for alt+3', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 51 }).key, '\x1b3'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 51 }).key, '\x1b#'); }); it('should return proper sequences for alt+4', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 52 }).key, '\x1b4'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 52 }).key, '\x1b$'); }); it('should return proper sequences for alt+5', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 53 }).key, '\x1b5'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 53 }).key, '\x1b%'); }); it('should return proper sequences for alt+6', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 54 }).key, '\x1b6'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 54 }).key, '\x1b^'); }); it('should return proper sequences for alt+7', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 55 }).key, '\x1b7'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 55 }).key, '\x1b&'); }); it('should return proper sequences for alt+8', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 56 }).key, '\x1b8'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 56 }).key, '\x1b*'); }); it('should return proper sequences for alt+9', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 57 }).key, '\x1b9'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 57 }).key, '\x1b('); }); // Characters using alt sequences (special chars) it('should return proper sequences for alt+;', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 186 }).key, '\x1b;'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 186 }).key, '\x1b:'); }); it('should return proper sequences for alt+=', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 187 }).key, '\x1b='); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 187 }).key, '\x1b+'); }); it('should return proper sequences for alt+,', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 188 }).key, '\x1b,'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 188 }).key, '\x1b<'); }); it('should return proper sequences for alt+-', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 189 }).key, '\x1b-'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 189 }).key, '\x1b_'); }); it('should return proper sequences for alt+.', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 190 }).key, '\x1b.'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 190 }).key, '\x1b>'); }); it('should return proper sequences for alt+/', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 191 }).key, '\x1b/'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 191 }).key, '\x1b?'); }); it('should return proper sequences for alt+~', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 192 }).key, '\x1b`'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 192 }).key, '\x1b~'); }); it('should return proper sequences for alt+[', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 219 }).key, '\x1b['); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 219 }).key, '\x1b{'); }); it('should return proper sequences for alt+\\', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 220 }).key, '\x1b\\'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 220 }).key, '\x1b|'); }); it('should return proper sequences for alt+]', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 221 }).key, '\x1b]'); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 221 }).key, '\x1b}'); }); it('should return proper sequences for alt+\'', () => { assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: false, keyCode: 222 }).key, '\x1b\''); assert.equal(testEvaluateKeyboardEvent({ altKey: true, shiftKey: true, keyCode: 222 }).key, '\x1b"'); }); it('should handle mobile arrow events', () => { assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputUpArrow' }).key, '\x1b[A'); assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputUpArrow' }, { applicationCursorMode: true }).key, '\x1bOA'); assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputLeftArrow' }).key, '\x1b[D'); assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputLeftArrow' }, { applicationCursorMode: true }).key, '\x1bOD'); assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputRightArrow' }).key, '\x1b[C'); assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputRightArrow' }, { applicationCursorMode: true }).key, '\x1bOC'); assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputDownArrow' }).key, '\x1b[B'); assert.equal(testEvaluateKeyboardEvent({ keyCode: 0, key: 'UIKeyInputDownArrow' }, { applicationCursorMode: true }).key, '\x1bOB'); }); }); }); xterm.js-3.8.1/src/core/input/Keyboard.ts000066400000000000000000000237631341514612000202730ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) * @license MIT */ import { IKeyboardEvent } from '../../common/Types'; import { IKeyboardResult, KeyboardResultType } from '../Types'; import { C0 } from '../../common/data/EscapeSequences'; // reg + shift key mappings for digits and special chars const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = { // digits 0-9 48: ['0', ')'], 49: ['1', '!'], 50: ['2', '@'], 51: ['3', '#'], 52: ['4', '$'], 53: ['5', '%'], 54: ['6', '^'], 55: ['7', '&'], 56: ['8', '*'], 57: ['9', '('], // special chars 186: [';', ':'], 187: ['=', '+'], 188: [',', '<'], 189: ['-', '_'], 190: ['.', '>'], 191: ['/', '?'], 192: ['`', '~'], 219: ['[', '{'], 220: ['\\', '|'], 221: [']', '}'], 222: ['\'', '"'] }; export function evaluateKeyboardEvent( ev: IKeyboardEvent, applicationCursorMode: boolean, isMac: boolean, macOptionIsMeta: boolean ): IKeyboardResult { const result: IKeyboardResult = { type: KeyboardResultType.SEND_KEY, // Whether to cancel event propogation (NOTE: this may not be needed since the event is // canceled at the end of keyDown cancel: false, // The new key even to emit key: undefined }; const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0); switch (ev.keyCode) { case 0: if (ev.key === 'UIKeyInputUpArrow') { if (applicationCursorMode) { result.key = C0.ESC + 'OA'; } else { result.key = C0.ESC + '[A'; } } else if (ev.key === 'UIKeyInputLeftArrow') { if (applicationCursorMode) { result.key = C0.ESC + 'OD'; } else { result.key = C0.ESC + '[D'; } } else if (ev.key === 'UIKeyInputRightArrow') { if (applicationCursorMode) { result.key = C0.ESC + 'OC'; } else { result.key = C0.ESC + '[C'; } } else if (ev.key === 'UIKeyInputDownArrow') { if (applicationCursorMode) { result.key = C0.ESC + 'OB'; } else { result.key = C0.ESC + '[B'; } } break; case 8: // backspace if (ev.shiftKey) { result.key = C0.BS; // ^H break; } else if (ev.altKey) { result.key = C0.ESC + C0.DEL; // \e ^? break; } result.key = C0.DEL; // ^? break; case 9: // tab if (ev.shiftKey) { result.key = C0.ESC + '[Z'; break; } result.key = C0.HT; result.cancel = true; break; case 13: // return/enter result.key = C0.CR; result.cancel = true; break; case 27: // escape result.key = C0.ESC; result.cancel = true; break; case 37: // left-arrow if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D'; // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards // http://unix.stackexchange.com/a/108106 // macOS uses different escape sequences than linux if (result.key === C0.ESC + '[1;3D') { result.key = isMac ? C0.ESC + 'b' : C0.ESC + '[1;5D'; } } else if (applicationCursorMode) { result.key = C0.ESC + 'OD'; } else { result.key = C0.ESC + '[D'; } break; case 39: // right-arrow if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C'; // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward // http://unix.stackexchange.com/a/108106 // macOS uses different escape sequences than linux if (result.key === C0.ESC + '[1;3C') { result.key = isMac ? C0.ESC + 'f' : C0.ESC + '[1;5C'; } } else if (applicationCursorMode) { result.key = C0.ESC + 'OC'; } else { result.key = C0.ESC + '[C'; } break; case 38: // up-arrow if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A'; // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow // http://unix.stackexchange.com/a/108106 if (result.key === C0.ESC + '[1;3A') { result.key = C0.ESC + '[1;5A'; } } else if (applicationCursorMode) { result.key = C0.ESC + 'OA'; } else { result.key = C0.ESC + '[A'; } break; case 40: // down-arrow if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B'; // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow // http://unix.stackexchange.com/a/108106 if (result.key === C0.ESC + '[1;3B') { result.key = C0.ESC + '[1;5B'; } } else if (applicationCursorMode) { result.key = C0.ESC + 'OB'; } else { result.key = C0.ESC + '[B'; } break; case 45: // insert if (!ev.shiftKey && !ev.ctrlKey) { // or + are used to // copy-paste on some systems. result.key = C0.ESC + '[2~'; } break; case 46: // delete if (modifiers) { result.key = C0.ESC + '[3;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[3~'; } break; case 36: // home if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'H'; } else if (applicationCursorMode) { result.key = C0.ESC + 'OH'; } else { result.key = C0.ESC + '[H'; } break; case 35: // end if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'F'; } else if (applicationCursorMode) { result.key = C0.ESC + 'OF'; } else { result.key = C0.ESC + '[F'; } break; case 33: // page up if (ev.shiftKey) { result.type = KeyboardResultType.PAGE_UP; } else { result.key = C0.ESC + '[5~'; } break; case 34: // page down if (ev.shiftKey) { result.type = KeyboardResultType.PAGE_DOWN; } else { result.key = C0.ESC + '[6~'; } break; case 112: // F1-F12 if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'P'; } else { result.key = C0.ESC + 'OP'; } break; case 113: if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'Q'; } else { result.key = C0.ESC + 'OQ'; } break; case 114: if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'R'; } else { result.key = C0.ESC + 'OR'; } break; case 115: if (modifiers) { result.key = C0.ESC + '[1;' + (modifiers + 1) + 'S'; } else { result.key = C0.ESC + 'OS'; } break; case 116: if (modifiers) { result.key = C0.ESC + '[15;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[15~'; } break; case 117: if (modifiers) { result.key = C0.ESC + '[17;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[17~'; } break; case 118: if (modifiers) { result.key = C0.ESC + '[18;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[18~'; } break; case 119: if (modifiers) { result.key = C0.ESC + '[19;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[19~'; } break; case 120: if (modifiers) { result.key = C0.ESC + '[20;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[20~'; } break; case 121: if (modifiers) { result.key = C0.ESC + '[21;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[21~'; } break; case 122: if (modifiers) { result.key = C0.ESC + '[23;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[23~'; } break; case 123: if (modifiers) { result.key = C0.ESC + '[24;' + (modifiers + 1) + '~'; } else { result.key = C0.ESC + '[24~'; } break; default: // a-z and space if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) { if (ev.keyCode >= 65 && ev.keyCode <= 90) { result.key = String.fromCharCode(ev.keyCode - 64); } else if (ev.keyCode === 32) { // NUL result.key = String.fromCharCode(0); } else if (ev.keyCode >= 51 && ev.keyCode <= 55) { // escape, file sep, group sep, record sep, unit sep result.key = String.fromCharCode(ev.keyCode - 51 + 27); } else if (ev.keyCode === 56) { // delete result.key = String.fromCharCode(127); } else if (ev.keyCode === 219) { // ^[ - Control Sequence Introducer (CSI) result.key = String.fromCharCode(27); } else if (ev.keyCode === 220) { // ^\ - String Terminator (ST) result.key = String.fromCharCode(28); } else if (ev.keyCode === 221) { // ^] - Operating System Command (OSC) result.key = String.fromCharCode(29); } } else if ((!isMac || macOptionIsMeta) && ev.altKey && !ev.metaKey) { // On macOS this is a third level shift when !macOptionIsMeta. Use instead. const keyMapping = KEYCODE_KEY_MAPPINGS[ev.keyCode]; const key = keyMapping && keyMapping[!ev.shiftKey ? 0 : 1]; if (key) { result.key = C0.ESC + key; } else if (ev.keyCode >= 65 && ev.keyCode <= 90) { const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32; result.key = C0.ESC + String.fromCharCode(keyCode); } } else if (isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) { if (ev.keyCode === 65) { // cmd + a result.type = KeyboardResultType.SELECT_ALL; } } break; } return result; } xterm.js-3.8.1/src/handlers/000077500000000000000000000000001341514612000156615ustar00rootroot00000000000000xterm.js-3.8.1/src/handlers/AltClickHandler.ts000066400000000000000000000171371341514612000212260ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal, IBufferLine } from '../Types'; import { ICircularList } from '../common/Types'; import { C0 } from '../common/data/EscapeSequences'; const enum Direction { UP = 'A', DOWN = 'B', RIGHT = 'C', LEFT = 'D' } export class AltClickHandler { private _startRow: number; private _startCol: number; private _endRow: number; private _endCol: number; private _lines: ICircularList; constructor( private _mouseEvent: MouseEvent, private _terminal: ITerminal ) { this._lines = this._terminal.buffer.lines; this._startCol = this._terminal.buffer.x; this._startRow = this._terminal.buffer.y; const coordinates = this._terminal.mouseHelper.getCoords( this._mouseEvent, this._terminal.element, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows, false ); if (coordinates) { [this._endCol, this._endRow] = coordinates.map((coordinate: number) => { return coordinate - 1; }); } } /** * Writes the escape sequences of arrows to the terminal */ public move(): void { if (this._mouseEvent.altKey && this._endCol !== undefined && this._endRow !== undefined) { this._terminal.handler(this._arrowSequences()); } } /** * Concatenates all the arrow sequences together. * Resets the starting row to an unwrapped row, moves to the requested row, * then moves to requested col. */ private _arrowSequences(): string { // The alt buffer should try to navigate between rows if (!this._terminal.buffer.hasScrollback) { return this._resetStartingRow() + this._moveToRequestedRow() + this._moveToRequestedCol(); } // Only move horizontally for the normal buffer return this._moveHorizontallyOnly(); } /** * If the initial position of the cursor is on a row that is wrapped, move the * cursor up to the first row that is not wrapped to have accurate vertical * positioning. */ private _resetStartingRow(): string { if (this._moveToRequestedRow().length === 0) { return ''; } return repeat(this._bufferLine( this._startCol, this._startRow, this._startCol, this._startRow - this._wrappedRowsForRow(this._startRow), false ).length, this._sequence(Direction.LEFT)); } /** * Using the reset starting and ending row, move to the requested row, * ignoring wrapped rows */ private _moveToRequestedRow(): string { const startRow = this._startRow - this._wrappedRowsForRow(this._startRow); const endRow = this._endRow - this._wrappedRowsForRow(this._endRow); const rowsToMove = Math.abs(startRow - endRow) - this._wrappedRowsCount(); return repeat(rowsToMove, this._sequence(this._verticalDirection())); } /** * Move to the requested col on the ending row */ private _moveToRequestedCol(): string { let startRow; if (this._moveToRequestedRow().length > 0) { startRow = this._endRow - this._wrappedRowsForRow(this._endRow); } else { startRow = this._startRow; } const endRow = this._endRow; const direction = this._horizontalDirection(); return repeat(this._bufferLine( this._startCol, startRow, this._endCol, endRow, direction === Direction.RIGHT ).length, this._sequence(direction)); } private _moveHorizontallyOnly(): string { const direction = this._horizontalDirection(); return repeat(Math.abs(this._startCol - this._endCol), this._sequence(direction)); } /** * Utility functions */ /** * Calculates the number of wrapped rows between the unwrapped starting and * ending rows. These rows need to ignored since the cursor skips over them. */ private _wrappedRowsCount(): number { let wrappedRows = 0; const startRow = this._startRow - this._wrappedRowsForRow(this._startRow); const endRow = this._endRow - this._wrappedRowsForRow(this._endRow); for (let i = 0; i < Math.abs(startRow - endRow); i++) { const direction = this._verticalDirection() === Direction.UP ? -1 : 1; if (this._lines.get(startRow + (direction * i)).isWrapped) { wrappedRows++; } } return wrappedRows; } /** * Calculates the number of wrapped rows that make up a given row. * @param currentRow The row to determine how many wrapped rows make it up */ private _wrappedRowsForRow(currentRow: number): number { let rowCount = 0; let lineWraps = this._lines.get(currentRow).isWrapped; while (lineWraps && currentRow >= 0 && currentRow < this._terminal.rows) { rowCount++; currentRow--; lineWraps = this._lines.get(currentRow).isWrapped; } return rowCount; } /** * Direction determiners */ /** * Determines if the right or left arrow is needed */ private _horizontalDirection(): Direction { let startRow; if (this._moveToRequestedRow().length > 0) { startRow = this._endRow - this._wrappedRowsForRow(this._endRow); } else { startRow = this._startRow; } if ((this._startCol < this._endCol && startRow <= this._endRow) || // down/right or same y/right (this._startCol >= this._endCol && startRow < this._endRow)) { // down/left or same y/left return Direction.RIGHT; } return Direction.LEFT; } /** * Determines if the up or down arrow is needed */ private _verticalDirection(): Direction { if (this._startRow > this._endRow) { return Direction.UP; } return Direction.DOWN; } /** * Constructs the string of chars in the buffer from a starting row and col * to an ending row and col * @param startCol The starting column position * @param startRow The starting row position * @param endCol The ending column position * @param endRow The ending row position * @param forward Direction to move */ private _bufferLine( startCol: number, startRow: number, endCol: number, endRow: number, forward: boolean ): string { let currentCol = startCol; let currentRow = startRow; let bufferStr = ''; while (currentCol !== endCol || currentRow !== endRow) { currentCol += forward ? 1 : -1; if (forward && currentCol > this._terminal.cols - 1) { bufferStr += this._terminal.buffer.translateBufferLineToString( currentRow, false, startCol, currentCol ); currentCol = 0; startCol = 0; currentRow++; } else if (!forward && currentCol < 0) { bufferStr += this._terminal.buffer.translateBufferLineToString( currentRow, false, 0, startCol + 1 ); currentCol = this._terminal.cols - 1; startCol = currentCol; currentRow--; } } return bufferStr + this._terminal.buffer.translateBufferLineToString( currentRow, false, startCol, currentCol ); } /** * Constructs the escape sequence for clicking an arrow * @param direction The direction to move */ private _sequence(direction: Direction): string { const mod = this._terminal.applicationCursor ? 'O' : '['; return C0.ESC + mod + direction; } } /** * Returns a string repeated a given number of times * Polyfill from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat * @param count The number of times to repeat the string * @param string The string that is to be repeated */ function repeat(count: number, str: string): string { count = Math.floor(count); let rpt = ''; for (let i = 0; i < count; i++) { rpt += str; } return rpt; } xterm.js-3.8.1/src/handlers/Clipboard.test.ts000066400000000000000000000020201341514612000211000ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import * as Clipboard from './Clipboard'; describe('evaluatePastedTextProcessing', () => { it('should replace carriage return and/or line feed with carriage return', () => { const pastedText = { unix: 'foo\nbar\n', windows: 'foo\r\nbar\r\n' }; const processedText = { unix: Clipboard.prepareTextForTerminal(pastedText.unix), windows: Clipboard.prepareTextForTerminal(pastedText.windows) }; assert.equal(processedText.unix, 'foo\rbar\r'); assert.equal(processedText.windows, 'foo\rbar\r'); }); it('should bracket pasted text in bracketedPasteMode', () => { const pastedText = 'foo bar'; const unbracketedText = Clipboard.bracketTextForPaste(pastedText, false); const bracketedText = Clipboard.bracketTextForPaste(pastedText, true); assert.equal(unbracketedText, 'foo bar'); assert.equal(bracketedText, '\x1b[200~foo bar\x1b[201~'); }); }); xterm.js-3.8.1/src/handlers/Clipboard.ts000066400000000000000000000076171341514612000201430ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal, ISelectionManager } from '../Types'; interface IWindow extends Window { clipboardData?: { getData(format: string): string; setData(format: string, data: string): void; }; } declare var window: IWindow; /** * Prepares text to be pasted into the terminal by normalizing the line endings * @param text The pasted text that needs processing before inserting into the terminal */ export function prepareTextForTerminal(text: string): string { return text.replace(/\r?\n/g, '\r'); } /** * Bracket text for paste, if necessary, as per https://cirw.in/blog/bracketed-paste * @param text The pasted text to bracket */ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean): string { if (bracketedPasteMode) { return '\x1b[200~' + text + '\x1b[201~'; } return text; } /** * Binds copy functionality to the given terminal. * @param ev The original copy event to be handled */ export function copyHandler(ev: ClipboardEvent, term: ITerminal, selectionManager: ISelectionManager): void { if (term.browser.isMSIE) { window.clipboardData.setData('Text', selectionManager.selectionText); } else { ev.clipboardData.setData('text/plain', selectionManager.selectionText); } // Prevent or the original text will be copied. ev.preventDefault(); } /** * Redirect the clipboard's data to the terminal's input handler. * @param ev The original paste event to be handled * @param term The terminal on which to apply the handled paste event */ export function pasteHandler(ev: ClipboardEvent, term: ITerminal): void { ev.stopPropagation(); let text: string; const dispatchPaste = function(text: string): void { text = prepareTextForTerminal(text); text = bracketTextForPaste(text, term.bracketedPasteMode); term.handler(text); term.textarea.value = ''; term.emit('paste', text); term.cancel(ev); }; if (term.browser.isMSIE) { if (window.clipboardData) { text = window.clipboardData.getData('Text'); dispatchPaste(text); } } else { if (ev.clipboardData) { text = ev.clipboardData.getData('text/plain'); dispatchPaste(text); } } } /** * Moves the textarea under the mouse cursor and focuses it. * @param ev The original right click event to be handled. * @param textarea The terminal's textarea. */ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement): void { // Bring textarea at the cursor position textarea.style.position = 'fixed'; textarea.style.width = '20px'; textarea.style.height = '20px'; textarea.style.left = (ev.clientX - 10) + 'px'; textarea.style.top = (ev.clientY - 10) + 'px'; textarea.style.zIndex = '1000'; textarea.focus(); // Reset the terminal textarea's styling // Timeout needs to be long enough for click event to be handled. setTimeout(() => { textarea.style.position = null; textarea.style.width = null; textarea.style.height = null; textarea.style.left = null; textarea.style.top = null; textarea.style.zIndex = null; }, 200); } /** * Bind to right-click event and allow right-click copy and paste. * @param ev The original right click event to be handled. * @param textarea The terminal's textarea. * @param selectionManager The terminal's selection manager. * @param shouldSelectWord If true and there is no selection the current word will be selected */ export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager, shouldSelectWord: boolean): void { moveTextAreaUnderMouseCursor(ev, textarea); if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) { selectionManager.selectWordAtCursor(ev); } // Get textarea ready to copy from the context menu textarea.value = selectionManager.selectionText; textarea.select(); } xterm.js-3.8.1/src/public/000077500000000000000000000000001341514612000153375ustar00rootroot00000000000000xterm.js-3.8.1/src/public/Terminal.test.ts000066400000000000000000000007771341514612000204530ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { Terminal } from './Terminal'; import * as attach from '../addons/attach/attach'; describe('Terminal', () => { it('should apply addons with Terminal.applyAddon', () => { Terminal.applyAddon(attach); // Test that addon was applied successfully, adding attach to Terminal's // prototype. assert.equal(typeof (Terminal).prototype.attach, 'function'); }); }); xterm.js-3.8.1/src/public/Terminal.ts000066400000000000000000000146061341514612000174710ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings } from 'xterm'; import { ITerminal } from '../Types'; import { Terminal as TerminalCore } from '../Terminal'; import * as Strings from '../Strings'; export class Terminal implements ITerminalApi { private _core: ITerminal; constructor(options?: ITerminalOptions) { this._core = new TerminalCore(options); } public get element(): HTMLElement { return this._core.element; } public get textarea(): HTMLTextAreaElement { return this._core.textarea; } public get rows(): number { return this._core.rows; } public get cols(): number { return this._core.cols; } public get markers(): IMarker[] { return this._core.markers; } public blur(): void { this._core.blur(); } public focus(): void { this._core.focus(); } public on(type: 'blur' | 'focus' | 'linefeed' | 'selection', listener: () => void): void; public on(type: 'data', listener: (...args: any[]) => void): void; public on(type: 'key', listener: (key?: string, event?: KeyboardEvent) => void): void; public on(type: 'keypress' | 'keydown', listener: (event?: KeyboardEvent) => void): void; public on(type: 'refresh', listener: (data?: { start: number; end: number; }) => void): void; public on(type: 'resize', listener: (data?: { cols: number; rows: number; }) => void): void; public on(type: 'scroll', listener: (ydisp?: number) => void): void; public on(type: 'title', listener: (title?: string) => void): void; public on(type: string, listener: (...args: any[]) => void): void; public on(type: any, listener: any): void { this._core.on(type, listener); } public off(type: string, listener: (...args: any[]) => void): void { this._core.off(type, listener); } public emit(type: string, data?: any): void { this._core.emit(type, data); } public addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable { return this._core.addDisposableListener(type, handler); } public resize(columns: number, rows: number): void { this._core.resize(columns, rows); } public writeln(data: string): void { this._core.writeln(data); } public open(parent: HTMLElement): void { this._core.open(parent); } public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void { this._core.attachCustomKeyEventHandler(customKeyEventHandler); } public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number { return this._core.registerLinkMatcher(regex, handler, options); } public deregisterLinkMatcher(matcherId: number): void { this._core.deregisterLinkMatcher(matcherId); } public registerCharacterJoiner(handler: (text: string) => [number, number][]): number { return this._core.registerCharacterJoiner(handler); } public deregisterCharacterJoiner(joinerId: number): void { this._core.deregisterCharacterJoiner(joinerId); } public addMarker(cursorYOffset: number): IMarker { return this._core.addMarker(cursorYOffset); } public hasSelection(): boolean { return this._core.hasSelection(); } public getSelection(): string { return this._core.getSelection(); } public clearSelection(): void { this._core.clearSelection(); } public selectAll(): void { this._core.selectAll(); } public selectLines(start: number, end: number): void { this._core.selectLines(start, end); } public dispose(): void { this._core.dispose(); } public destroy(): void { this._core.destroy(); } public scrollLines(amount: number): void { this._core.scrollLines(amount); } public scrollPages(pageCount: number): void { this._core.scrollPages(pageCount); } public scrollToTop(): void { this._core.scrollToTop(); } public scrollToBottom(): void { this._core.scrollToBottom(); } public scrollToLine(line: number): void { this._core.scrollToLine(line); } public clear(): void { this._core.clear(); } public write(data: string): void { this._core.write(data); } public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'rendererType' | 'termName'): string; public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; public getOption(key: 'colors'): string[]; public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number; public getOption(key: 'handler'): (data: string) => void; public getOption(key: string): any; public getOption(key: any): any { return this._core.getOption(key); } public setOption(key: 'bellSound' | 'fontFamily' | 'termName', value: string): void; public setOption(key: 'fontWeight' | 'fontWeightBold', value: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void; public setOption(key: 'bellStyle', value: 'none' | 'visual' | 'sound' | 'both'): void; public setOption(key: 'cursorStyle', value: 'block' | 'underline' | 'bar'): void; public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; public setOption(key: 'colors', value: string[]): void; public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void; public setOption(key: 'handler', value: (data: string) => void): void; public setOption(key: 'theme', value: ITheme): void; public setOption(key: 'cols' | 'rows', value: number): void; public setOption(key: string, value: any): void; public setOption(key: any, value: any): void { this._core.setOption(key, value); } public refresh(start: number, end: number): void { this._core.refresh(start, end); } public reset(): void { this._core.reset(); } public static applyAddon(addon: any): void { addon.apply(Terminal); } public static get strings(): ILocalizableStrings { return Strings; } } xterm.js-3.8.1/src/renderer/000077500000000000000000000000001341514612000156675ustar00rootroot00000000000000xterm.js-3.8.1/src/renderer/BaseRenderLayer.ts000066400000000000000000000302711341514612000212510ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IRenderLayer, IColorSet, IRenderDimensions } from './Types'; import { CharData, ITerminal } from '../Types'; import { DIM_OPACITY, INVERTED_DEFAULT_COLOR, IGlyphIdentifier } from './atlas/Types'; import BaseCharAtlas from './atlas/BaseCharAtlas'; import { acquireCharAtlas } from './atlas/CharAtlasCache'; import { CHAR_DATA_CHAR_INDEX } from '../Buffer'; export abstract class BaseRenderLayer implements IRenderLayer { private _canvas: HTMLCanvasElement; protected _ctx: CanvasRenderingContext2D; private _scaledCharWidth: number = 0; private _scaledCharHeight: number = 0; private _scaledCellWidth: number = 0; private _scaledCellHeight: number = 0; private _scaledCharLeft: number = 0; private _scaledCharTop: number = 0; protected _charAtlas: BaseCharAtlas; /** * An object that's reused when drawing glyphs in order to reduce GC. */ private _currentGlyphIdentifier: IGlyphIdentifier = { chars: '', code: 0, bg: 0, fg: 0, bold: false, dim: false, italic: false }; constructor( private _container: HTMLElement, id: string, zIndex: number, private _alpha: boolean, protected _colors: IColorSet ) { this._canvas = document.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); this._canvas.style.zIndex = zIndex.toString(); this._initCanvas(); this._container.appendChild(this._canvas); } public dispose(): void { this._container.removeChild(this._canvas); if (this._charAtlas) { this._charAtlas.dispose(); } } private _initCanvas(): void { this._ctx = this._canvas.getContext('2d', {alpha: this._alpha}); // Draw the background if this is an opaque layer if (!this._alpha) { this.clearAll(); } } public onOptionsChanged(terminal: ITerminal): void {} public onBlur(terminal: ITerminal): void {} public onFocus(terminal: ITerminal): void {} public onCursorMove(terminal: ITerminal): void {} public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {} public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean = false): void {} public onThemeChanged(terminal: ITerminal, colorSet: IColorSet): void { this._refreshCharAtlas(terminal, colorSet); } protected setTransparency(terminal: ITerminal, alpha: boolean): void { // Do nothing when alpha doesn't change if (alpha === this._alpha) { return; } // Create new canvas and replace old one const oldCanvas = this._canvas; this._alpha = alpha; // Cloning preserves properties this._canvas = this._canvas.cloneNode(); this._initCanvas(); this._container.replaceChild(this._canvas, oldCanvas); // Regenerate char atlas and force a full redraw this._refreshCharAtlas(terminal, this._colors); this.onGridChanged(terminal, 0, terminal.rows - 1); } /** * Refreshes the char atlas, aquiring a new one if necessary. * @param terminal The terminal. * @param colorSet The color set to use for the char atlas. */ private _refreshCharAtlas(terminal: ITerminal, colorSet: IColorSet): void { if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { return; } this._charAtlas = acquireCharAtlas(terminal, colorSet, this._scaledCharWidth, this._scaledCharHeight); this._charAtlas.warmUp(); } public resize(terminal: ITerminal, dim: IRenderDimensions): void { this._scaledCellWidth = dim.scaledCellWidth; this._scaledCellHeight = dim.scaledCellHeight; this._scaledCharWidth = dim.scaledCharWidth; this._scaledCharHeight = dim.scaledCharHeight; this._scaledCharLeft = dim.scaledCharLeft; this._scaledCharTop = dim.scaledCharTop; this._canvas.width = dim.scaledCanvasWidth; this._canvas.height = dim.scaledCanvasHeight; this._canvas.style.width = `${dim.canvasWidth}px`; this._canvas.style.height = `${dim.canvasHeight}px`; // Draw the background if this is an opaque layer if (!this._alpha) { this.clearAll(); } this._refreshCharAtlas(terminal, this._colors); } public abstract reset(terminal: ITerminal): void; /** * Fills 1+ cells completely. This uses the existing fillStyle on the context. * @param x The column to start at. * @param y The row to start at * @param width The number of columns to fill. * @param height The number of rows to fill. */ protected fillCells(x: number, y: number, width: number, height: number): void { this._ctx.fillRect( x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); } /** * Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the * existing fillStyle on the context. * @param x The column to fill. * @param y The row to fill. */ protected fillBottomLineAtCells(x: number, y: number, width: number = 1): void { this._ctx.fillRect( x * this._scaledCellWidth, (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */, width * this._scaledCellWidth, window.devicePixelRatio); } /** * Fills a 1px line (2px on HDPI) at the left of the cell. This uses the * existing fillStyle on the context. * @param x The column to fill. * @param y The row to fill. */ protected fillLeftLineAtCell(x: number, y: number): void { this._ctx.fillRect( x * this._scaledCellWidth, y * this._scaledCellHeight, window.devicePixelRatio, this._scaledCellHeight); } /** * Strokes a 1px rectangle (2px on HDPI) around a cell. This uses the existing * strokeStyle on the context. * @param x The column to fill. * @param y The row to fill. */ protected strokeRectAtCell(x: number, y: number, width: number, height: number): void { this._ctx.lineWidth = window.devicePixelRatio; this._ctx.strokeRect( x * this._scaledCellWidth + window.devicePixelRatio / 2, y * this._scaledCellHeight + (window.devicePixelRatio / 2), width * this._scaledCellWidth - window.devicePixelRatio, (height * this._scaledCellHeight) - window.devicePixelRatio); } /** * Clears the entire canvas. */ protected clearAll(): void { if (this._alpha) { this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); } else { this._ctx.fillStyle = this._colors.background.css; this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); } } /** * Clears 1+ cells completely. * @param x The column to start at. * @param y The row to start at. * @param width The number of columns to clear. * @param height The number of rows to clear. */ protected clearCells(x: number, y: number, width: number, height: number): void { if (this._alpha) { this._ctx.clearRect( x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); } else { this._ctx.fillStyle = this._colors.background.css; this._ctx.fillRect( x * this._scaledCellWidth, y * this._scaledCellHeight, width * this._scaledCellWidth, height * this._scaledCellHeight); } } /** * Draws a truecolor character at the cell. The character will be clipped to * ensure that it fits with the cell, including the cell to the right if it's * a wide character. This uses the existing fillStyle on the context. * @param terminal The terminal. * @param charData The char data for the character to draw. * @param x The column to draw at. * @param y The row to draw at. * @param color The color of the character. */ protected fillCharTrueColor(terminal: ITerminal, charData: CharData, x: number, y: number): void { this._ctx.font = this._getFont(terminal, false, false); this._ctx.textBaseline = 'top'; this._clipRow(terminal, y); this._ctx.fillText( charData[CHAR_DATA_CHAR_INDEX], x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); } /** * Draws one or more characters at a cell. If possible this will draw using * the character atlas to reduce draw time. * @param terminal The terminal. * @param chars The character or characters. * @param code The character code. * @param width The width of the characters. * @param x The column to draw at. * @param y The row to draw at. * @param fg The foreground color, in the format stored within the attributes. * @param bg The background color, in the format stored within the attributes. * This is used to validate whether a cached image can be used. * @param bold Whether the text is bold. */ protected drawChars(terminal: ITerminal, chars: string, code: number, width: number, x: number, y: number, fg: number, bg: number, bold: boolean, dim: boolean, italic: boolean): void { const drawInBrightColor = terminal.options.drawBoldTextInBrightColors && bold && fg < 8 && fg !== INVERTED_DEFAULT_COLOR; fg += drawInBrightColor ? 8 : 0; this._currentGlyphIdentifier.chars = chars; this._currentGlyphIdentifier.code = code; this._currentGlyphIdentifier.bg = bg; this._currentGlyphIdentifier.fg = fg; this._currentGlyphIdentifier.bold = bold && terminal.options.enableBold; this._currentGlyphIdentifier.dim = dim; this._currentGlyphIdentifier.italic = italic; const atlasDidDraw = this._charAtlas && this._charAtlas.draw( this._ctx, this._currentGlyphIdentifier, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop ); if (!atlasDidDraw) { this._drawUncachedChars(terminal, chars, width, fg, x, y, bold && terminal.options.enableBold, dim, italic); } } /** * Draws one or more characters at one or more cells. The character(s) will be * clipped to ensure that they fit with the cell(s), including the cell to the * right if the last character is a wide character. * @param terminal The terminal. * @param chars The character. * @param width The width of the character. * @param fg The foreground color, in the format stored within the attributes. * @param x The column to draw at. * @param y The row to draw at. */ private _drawUncachedChars(terminal: ITerminal, chars: string, width: number, fg: number, x: number, y: number, bold: boolean, dim: boolean, italic: boolean): void { this._ctx.save(); this._ctx.font = this._getFont(terminal, bold, italic); this._ctx.textBaseline = 'top'; if (fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; } else if (fg < 256) { // 256 color support this._ctx.fillStyle = this._colors.ansi[fg].css; } else { this._ctx.fillStyle = this._colors.foreground.css; } this._clipRow(terminal, y); // Apply alpha to dim the character if (dim) { this._ctx.globalAlpha = DIM_OPACITY; } // Draw the character this._ctx.fillText( chars, x * this._scaledCellWidth + this._scaledCharLeft, y * this._scaledCellHeight + this._scaledCharTop); this._ctx.restore(); } /** * Clips a row to ensure no pixels will be drawn outside the cells in the row. * @param terminal The terminal. * @param y The row to clip. */ private _clipRow(terminal: ITerminal, y: number): void { this._ctx.beginPath(); this._ctx.rect( 0, y * this._scaledCellHeight, terminal.cols * this._scaledCellWidth, this._scaledCellHeight); this._ctx.clip(); } /** * Gets the current font. * @param terminal The terminal. * @param isBold If we should use the bold fontWeight. */ protected _getFont(terminal: ITerminal, isBold: boolean, isItalic: boolean): string { const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight; const fontStyle = isItalic ? 'italic' : ''; return `${fontStyle} ${fontWeight} ${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`; } } xterm.js-3.8.1/src/renderer/CharacterJoinerRegistry.test.ts000066400000000000000000000214511341514612000240140ustar00rootroot00000000000000import { assert } from 'chai'; import { MockTerminal, MockBuffer } from '../utils/TestUtils.test'; import { CircularList } from '../common/CircularList'; import { ICharacterJoinerRegistry } from './Types'; import { CharacterJoinerRegistry } from './CharacterJoinerRegistry'; import { BufferLine } from '../BufferLine'; import { IBufferLine } from '../Types'; describe('CharacterJoinerRegistry', () => { let registry: ICharacterJoinerRegistry; beforeEach(() => { const terminal = new MockTerminal(); terminal.cols = 16; terminal.buffer = new MockBuffer(); const lines = new CircularList(7); lines.set(0, lineData([['a -> b -> c -> d']])); lines.set(1, lineData([['a -> b => c -> d']])); lines.set(2, lineData([['a -> b -', 0xFFFFFFFF], ['> c -> d', 0]])); lines.set(3, lineData([['no joined ranges']])); lines.set(4, new BufferLine()); lines.set(5, lineData([['a', 0x11111111], [' -> b -> c -> '], ['d', 0x22222222]])); const line6 = lineData([['wi']]); line6.push([0, '¥', 2, '¥'.charCodeAt(0)]); line6.push([0, '', 0, null]); let sub = lineData([['deemo']]); for (let i = 0; i < sub.length; ++i) line6.push(sub.get(i)); line6.push([0, '\xf0\x9f\x98\x81', 1, 128513]); line6.push([0, ' ', 1, ' '.charCodeAt(0)]); sub = lineData([['jiabc']]); for (let i = 0; i < sub.length; ++i) line6.push(sub.get(i)); lines.set(6, line6); (terminal.buffer).setLines(lines); terminal.buffer.ydisp = 0; registry = new CharacterJoinerRegistry(terminal); }); it('has no joiners upon creation', () => { assert.deepEqual(registry.getJoinedCharacters(0), []); }); it('returns ranges matched by the registered joiners', () => { registry.registerCharacterJoiner(substringJoiner('->')); assert.deepEqual( registry.getJoinedCharacters(0), [[2, 4], [7, 9], [12, 14]] ); }); it('processes the input using all provided joiners', () => { registry.registerCharacterJoiner(substringJoiner('->')); assert.deepEqual( registry.getJoinedCharacters(1), [[2, 4], [12, 14]] ); registry.registerCharacterJoiner(substringJoiner('=>')); assert.deepEqual( registry.getJoinedCharacters(1), [[2, 4], [7, 9], [12, 14]] ); }); it('removes deregistered joiners from future calls', () => { const joiner1 = registry.registerCharacterJoiner(substringJoiner('->')); const joiner2 = registry.registerCharacterJoiner(substringJoiner('=>')); assert.deepEqual( registry.getJoinedCharacters(1), [[2, 4], [7, 9], [12, 14]] ); registry.deregisterCharacterJoiner(joiner1); assert.deepEqual( registry.getJoinedCharacters(1), [[7, 9]] ); registry.deregisterCharacterJoiner(joiner2); assert.deepEqual( registry.getJoinedCharacters(1), [] ); }); it('doesn\'t process joins on differently-styled characters', () => { registry.registerCharacterJoiner(substringJoiner('->')); assert.deepEqual( registry.getJoinedCharacters(2), [[2, 4], [12, 14]] ); }); it('returns an empty list of ranges if there is nothing to be joined', () => { registry.registerCharacterJoiner(substringJoiner('->')); assert.deepEqual( registry.getJoinedCharacters(3), [] ); }); it('returns an empty list of ranges if the line is empty', () => { registry.registerCharacterJoiner(substringJoiner('->')); assert.deepEqual( registry.getJoinedCharacters(4), [] ); }); it('returns false when trying to deregister a joiner that does not exist', () => { registry.registerCharacterJoiner(substringJoiner('->')); assert.deepEqual(registry.deregisterCharacterJoiner(123), false); assert.deepEqual( registry.getJoinedCharacters(0), [[2, 4], [7, 9], [12, 14]] ); }); it('doesn\'t process same-styled ranges that only have one character', () => { registry.registerCharacterJoiner(substringJoiner('a')); registry.registerCharacterJoiner(substringJoiner('b')); registry.registerCharacterJoiner(substringJoiner('d')); assert.deepEqual( registry.getJoinedCharacters(5), [[5, 6]] ); }); it('handles ranges that extend all the way to the end of the line', () => { registry.registerCharacterJoiner(substringJoiner('-> d')); assert.deepEqual( registry.getJoinedCharacters(2), [[12, 16]] ); }); it('handles adjacent ranges', () => { registry.registerCharacterJoiner(substringJoiner('->')); registry.registerCharacterJoiner(substringJoiner('> c ')); assert.deepEqual( registry.getJoinedCharacters(2), [[2, 4], [8, 12], [12, 14]] ); }); it('handles fullwidth characters in the middle of ranges', () => { registry.registerCharacterJoiner(substringJoiner('wi¥de')); assert.deepEqual( registry.getJoinedCharacters(6), [[0, 6]] ); }); it('handles fullwidth characters at the end of ranges', () => { registry.registerCharacterJoiner(substringJoiner('wi¥')); assert.deepEqual( registry.getJoinedCharacters(6), [[0, 4]] ); }); it('handles emojis in the middle of ranges', () => { registry.registerCharacterJoiner(substringJoiner('emo\xf0\x9f\x98\x81 ji')); assert.deepEqual( registry.getJoinedCharacters(6), [[6, 13]] ); }); it('handles emojis at the end of ranges', () => { registry.registerCharacterJoiner(substringJoiner('emo\xf0\x9f\x98\x81 ')); assert.deepEqual( registry.getJoinedCharacters(6), [[6, 11]] ); }); it('handles ranges after wide and emoji characters', () => { registry.registerCharacterJoiner(substringJoiner('abc')); assert.deepEqual( registry.getJoinedCharacters(6), [[13, 16]] ); }); describe('range merging', () => { it('inserts a new range before the existing ones', () => { registry.registerCharacterJoiner(() => [[1, 2], [2, 3]]); registry.registerCharacterJoiner(() => [[0, 1]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 1], [1, 2], [2, 3]] ); }); it('inserts in between two ranges', () => { registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); registry.registerCharacterJoiner(() => [[2, 4]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 2], [2, 4], [4, 6]] ); }); it('inserts after the last range', () => { registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); registry.registerCharacterJoiner(() => [[6, 8]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 2], [4, 6], [6, 8]] ); }); it('extends the beginning of a range', () => { registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); registry.registerCharacterJoiner(() => [[3, 5]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 2], [3, 6]] ); }); it('extends the end of a range', () => { registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); registry.registerCharacterJoiner(() => [[1, 4]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 4], [4, 6]] ); }); it('extends the last range', () => { registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); registry.registerCharacterJoiner(() => [[5, 7]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 2], [4, 7]] ); }); it('connects two ranges', () => { registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); registry.registerCharacterJoiner(() => [[1, 5]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 6]] ); }); it('connects more than two ranges', () => { registry.registerCharacterJoiner(() => [[0, 2], [4, 6], [8, 10], [12, 14]]); registry.registerCharacterJoiner(() => [[1, 10]]); assert.deepEqual( registry.getJoinedCharacters(0), [[0, 10], [12, 14]] ); }); }); }); type IPartialLineData = ([string] | [string, number]); function lineData(data: IPartialLineData[]): IBufferLine { const tline = new BufferLine(); for (let i = 0; i < data.length; ++i) { const line = data[i][0]; const attr = (data[i][1] || 0); line.split('').map(char => tline.push([attr, char, 1, char.charCodeAt(0)])); } return tline; } function substringJoiner(substring: string): (sequence: string) => [number, number][] { return (sequence: string): [number, number][] => { const ranges: [number, number][] = []; let searchIndex = 0; let matchIndex = -1; while ((matchIndex = sequence.indexOf(substring, searchIndex)) !== -1) { const matchEndIndex = matchIndex + substring.length; searchIndex = matchEndIndex; ranges.push([matchIndex, matchEndIndex]); } return ranges; }; } xterm.js-3.8.1/src/renderer/CharacterJoinerRegistry.ts000066400000000000000000000225301341514612000230350ustar00rootroot00000000000000import { CHAR_DATA_ATTR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer'; import { ITerminal, IBufferLine } from '../Types'; import { ICharacterJoinerRegistry, ICharacterJoiner } from './Types'; export class CharacterJoinerRegistry implements ICharacterJoinerRegistry { private _characterJoiners: ICharacterJoiner[] = []; private _nextCharacterJoinerId: number = 0; constructor(private _terminal: ITerminal) { } public registerCharacterJoiner(handler: (text: string) => [number, number][]): number { const joiner: ICharacterJoiner = { id: this._nextCharacterJoinerId++, handler }; this._characterJoiners.push(joiner); return joiner.id; } public deregisterCharacterJoiner(joinerId: number): boolean { for (let i = 0; i < this._characterJoiners.length; i++) { if (this._characterJoiners[i].id === joinerId) { this._characterJoiners.splice(i, 1); return true; } } return false; } public getJoinedCharacters(row: number): [number, number][] { if (this._characterJoiners.length === 0) { return []; } const line = this._terminal.buffer.lines.get(row); if (line.length === 0) { return []; } const ranges: [number, number][] = []; const lineStr = this._terminal.buffer.translateBufferLineToString(row, true); // Because some cells can be represented by multiple javascript characters, // we track the cell and the string indexes separately. This allows us to // translate the string ranges we get from the joiners back into cell ranges // for use when rendering let rangeStartColumn = 0; let currentStringIndex = 0; let rangeStartStringIndex = 0; let rangeAttr = line.get(0)[CHAR_DATA_ATTR_INDEX] >> 9; for (let x = 0; x < this._terminal.cols; x++) { const charData = line.get(x); const chars = charData[CHAR_DATA_CHAR_INDEX]; const width = charData[CHAR_DATA_WIDTH_INDEX]; const attr = charData[CHAR_DATA_ATTR_INDEX] >> 9; if (width === 0) { // If this character is of width 0, skip it. continue; } // End of range if (attr !== rangeAttr) { // If we ended up with a sequence of more than one character, // look for ranges to join. if (x - rangeStartColumn > 1) { const joinedRanges = this._getJoinedRanges( lineStr, rangeStartStringIndex, currentStringIndex, line, rangeStartColumn ); for (let i = 0; i < joinedRanges.length; i++) { ranges.push(joinedRanges[i]); } } // Reset our markers for a new range. rangeStartColumn = x; rangeStartStringIndex = currentStringIndex; rangeAttr = attr; } currentStringIndex += chars.length; } // Process any trailing ranges. if (this._terminal.cols - rangeStartColumn > 1) { const joinedRanges = this._getJoinedRanges( lineStr, rangeStartStringIndex, currentStringIndex, line, rangeStartColumn ); for (let i = 0; i < joinedRanges.length; i++) { ranges.push(joinedRanges[i]); } } return ranges; } /** * Given a segment of a line of text, find all ranges of text that should be * joined in a single rendering unit. Ranges are internally converted to * column ranges, rather than string ranges. * @param line String representation of the full line of text * @param startIndex Start position of the range to search in the string (inclusive) * @param endIndex End position of the range to search in the string (exclusive) */ private _getJoinedRanges(line: string, startIndex: number, endIndex: number, lineData: IBufferLine, startCol: number): [number, number][] { const text = line.substring(startIndex, endIndex); // At this point we already know that there is at least one joiner so // we can just pull its value and assign it directly rather than // merging it into an empty array, which incurs unnecessary writes. const joinedRanges: [number, number][] = this._characterJoiners[0].handler(text); for (let i = 1; i < this._characterJoiners.length; i++) { // We merge any overlapping ranges across the different joiners const joinerRanges = this._characterJoiners[i].handler(text); for (let j = 0; j < joinerRanges.length; j++) { CharacterJoinerRegistry._mergeRanges(joinedRanges, joinerRanges[j]); } } this._stringRangesToCellRanges(joinedRanges, lineData, startCol); return joinedRanges; } /** * Modifies the provided ranges in-place to adjust for variations between * string length and cell width so that the range represents a cell range, * rather than the string range the joiner provides. * @param ranges String ranges containing start (inclusive) and end (exclusive) index * @param line Cell data for the relevant line in the terminal * @param startCol Offset within the line to start from */ private _stringRangesToCellRanges(ranges: [number, number][], line: IBufferLine, startCol: number): void { let currentRangeIndex = 0; let currentRangeStarted = false; let currentStringIndex = 0; let currentRange = ranges[currentRangeIndex]; // If we got through all of the ranges, stop searching if (!currentRange) { return; } for (let x = startCol; x < this._terminal.cols; x++) { const charData = line.get(x); const width = charData[CHAR_DATA_WIDTH_INDEX]; const length = charData[CHAR_DATA_CHAR_INDEX].length; // We skip zero-width characters when creating the string to join the text // so we do the same here if (width === 0) { continue; } // Adjust the start of the range if (!currentRangeStarted && currentRange[0] <= currentStringIndex) { currentRange[0] = x; currentRangeStarted = true; } // Adjust the end of the range if (currentRange[1] <= currentStringIndex) { currentRange[1] = x; // We're finished with this range, so we move to the next one currentRange = ranges[++currentRangeIndex]; // If there are no more ranges left, stop searching if (!currentRange) { break; } // Ranges can be on adjacent characters. Because the end index of the // ranges are exclusive, this means that the index for the start of a // range can be the same as the end index of the previous range. To // account for the start of the next range, we check here just in case. if (currentRange[0] <= currentStringIndex) { currentRange[0] = x; currentRangeStarted = true; } else { currentRangeStarted = false; } } // Adjust the string index based on the character length to line up with // the column adjustment currentStringIndex += length; } // If there is still a range left at the end, it must extend all the way to // the end of the line. if (currentRange) { currentRange[1] = this._terminal.cols; } } /** * Merges the range defined by the provided start and end into the list of * existing ranges. The merge is done in place on the existing range for * performance and is also returned. * @param ranges Existing range list * @param newRange Tuple of two numbers representing the new range to merge in. * @returns The ranges input with the new range merged in place */ private static _mergeRanges(ranges: [number, number][], newRange: [number, number]): [number, number][] { let inRange = false; for (let i = 0; i < ranges.length; i++) { const range = ranges[i]; if (!inRange) { if (newRange[1] <= range[0]) { // Case 1: New range is before the search range ranges.splice(i, 0, newRange); return ranges; } if (newRange[1] <= range[1]) { // Case 2: New range is either wholly contained within the // search range or overlaps with the front of it range[0] = Math.min(newRange[0], range[0]); return ranges; } if (newRange[0] < range[1]) { // Case 3: New range either wholly contains the search range // or overlaps with the end of it range[0] = Math.min(newRange[0], range[0]); inRange = true; } // Case 4: New range starts after the search range continue; } else { if (newRange[1] <= range[0]) { // Case 5: New range extends from previous range but doesn't // reach the current one ranges[i - 1][1] = newRange[1]; return ranges; } if (newRange[1] <= range[1]) { // Case 6: New range extends from prvious range into the // current range ranges[i - 1][1] = Math.max(newRange[1], range[1]); ranges.splice(i, 1); inRange = false; return ranges; } // Case 7: New range extends from previous range past the // end of the current range ranges.splice(i, 1); i--; } } if (inRange) { // Case 8: New range extends past the last existing range ranges[ranges.length - 1][1] = newRange[1]; } else { // Case 9: New range starts after the last existing range ranges.push(newRange); } return ranges; } } xterm.js-3.8.1/src/renderer/ColorManager.test.ts000066400000000000000000000357251341514612000216020ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import jsdom = require('jsdom'); import { assert } from 'chai'; import { ColorManager } from './ColorManager'; describe('ColorManager', () => { let cm: ColorManager; let dom: jsdom.JSDOM; let document: Document; let window: Window; beforeEach(() => { dom = new jsdom.JSDOM(''); window = dom.window; document = window.document; (window).HTMLCanvasElement.prototype.getContext = () => ({ createLinearGradient(): any { return null; }, fillRect(): void { }, getImageData(): any { return {data: [0, 0, 0, 0xFF]}; } }); cm = new ColorManager(document, false); }); describe('constructor', () => { it('should fill all colors with values', () => { for (const key of Object.keys(cm.colors)) { if (key !== 'ansi') { // A #rrggbb or rgba(...) assert.ok((cm.colors)[key].css.length >= 7); } } assert.equal(cm.colors.ansi.length, 256); }); it('should fill 240 colors with expected values', () => { assert.equal(cm.colors.ansi[16].css, '#000000'); assert.equal(cm.colors.ansi[17].css, '#00005f'); assert.equal(cm.colors.ansi[18].css, '#000087'); assert.equal(cm.colors.ansi[19].css, '#0000af'); assert.equal(cm.colors.ansi[20].css, '#0000d7'); assert.equal(cm.colors.ansi[21].css, '#0000ff'); assert.equal(cm.colors.ansi[22].css, '#005f00'); assert.equal(cm.colors.ansi[23].css, '#005f5f'); assert.equal(cm.colors.ansi[24].css, '#005f87'); assert.equal(cm.colors.ansi[25].css, '#005faf'); assert.equal(cm.colors.ansi[26].css, '#005fd7'); assert.equal(cm.colors.ansi[27].css, '#005fff'); assert.equal(cm.colors.ansi[28].css, '#008700'); assert.equal(cm.colors.ansi[29].css, '#00875f'); assert.equal(cm.colors.ansi[30].css, '#008787'); assert.equal(cm.colors.ansi[31].css, '#0087af'); assert.equal(cm.colors.ansi[32].css, '#0087d7'); assert.equal(cm.colors.ansi[33].css, '#0087ff'); assert.equal(cm.colors.ansi[34].css, '#00af00'); assert.equal(cm.colors.ansi[35].css, '#00af5f'); assert.equal(cm.colors.ansi[36].css, '#00af87'); assert.equal(cm.colors.ansi[37].css, '#00afaf'); assert.equal(cm.colors.ansi[38].css, '#00afd7'); assert.equal(cm.colors.ansi[39].css, '#00afff'); assert.equal(cm.colors.ansi[40].css, '#00d700'); assert.equal(cm.colors.ansi[41].css, '#00d75f'); assert.equal(cm.colors.ansi[42].css, '#00d787'); assert.equal(cm.colors.ansi[43].css, '#00d7af'); assert.equal(cm.colors.ansi[44].css, '#00d7d7'); assert.equal(cm.colors.ansi[45].css, '#00d7ff'); assert.equal(cm.colors.ansi[46].css, '#00ff00'); assert.equal(cm.colors.ansi[47].css, '#00ff5f'); assert.equal(cm.colors.ansi[48].css, '#00ff87'); assert.equal(cm.colors.ansi[49].css, '#00ffaf'); assert.equal(cm.colors.ansi[50].css, '#00ffd7'); assert.equal(cm.colors.ansi[51].css, '#00ffff'); assert.equal(cm.colors.ansi[52].css, '#5f0000'); assert.equal(cm.colors.ansi[53].css, '#5f005f'); assert.equal(cm.colors.ansi[54].css, '#5f0087'); assert.equal(cm.colors.ansi[55].css, '#5f00af'); assert.equal(cm.colors.ansi[56].css, '#5f00d7'); assert.equal(cm.colors.ansi[57].css, '#5f00ff'); assert.equal(cm.colors.ansi[58].css, '#5f5f00'); assert.equal(cm.colors.ansi[59].css, '#5f5f5f'); assert.equal(cm.colors.ansi[60].css, '#5f5f87'); assert.equal(cm.colors.ansi[61].css, '#5f5faf'); assert.equal(cm.colors.ansi[62].css, '#5f5fd7'); assert.equal(cm.colors.ansi[63].css, '#5f5fff'); assert.equal(cm.colors.ansi[64].css, '#5f8700'); assert.equal(cm.colors.ansi[65].css, '#5f875f'); assert.equal(cm.colors.ansi[66].css, '#5f8787'); assert.equal(cm.colors.ansi[67].css, '#5f87af'); assert.equal(cm.colors.ansi[68].css, '#5f87d7'); assert.equal(cm.colors.ansi[69].css, '#5f87ff'); assert.equal(cm.colors.ansi[70].css, '#5faf00'); assert.equal(cm.colors.ansi[71].css, '#5faf5f'); assert.equal(cm.colors.ansi[72].css, '#5faf87'); assert.equal(cm.colors.ansi[73].css, '#5fafaf'); assert.equal(cm.colors.ansi[74].css, '#5fafd7'); assert.equal(cm.colors.ansi[75].css, '#5fafff'); assert.equal(cm.colors.ansi[76].css, '#5fd700'); assert.equal(cm.colors.ansi[77].css, '#5fd75f'); assert.equal(cm.colors.ansi[78].css, '#5fd787'); assert.equal(cm.colors.ansi[79].css, '#5fd7af'); assert.equal(cm.colors.ansi[80].css, '#5fd7d7'); assert.equal(cm.colors.ansi[81].css, '#5fd7ff'); assert.equal(cm.colors.ansi[82].css, '#5fff00'); assert.equal(cm.colors.ansi[83].css, '#5fff5f'); assert.equal(cm.colors.ansi[84].css, '#5fff87'); assert.equal(cm.colors.ansi[85].css, '#5fffaf'); assert.equal(cm.colors.ansi[86].css, '#5fffd7'); assert.equal(cm.colors.ansi[87].css, '#5fffff'); assert.equal(cm.colors.ansi[88].css, '#870000'); assert.equal(cm.colors.ansi[89].css, '#87005f'); assert.equal(cm.colors.ansi[90].css, '#870087'); assert.equal(cm.colors.ansi[91].css, '#8700af'); assert.equal(cm.colors.ansi[92].css, '#8700d7'); assert.equal(cm.colors.ansi[93].css, '#8700ff'); assert.equal(cm.colors.ansi[94].css, '#875f00'); assert.equal(cm.colors.ansi[95].css, '#875f5f'); assert.equal(cm.colors.ansi[96].css, '#875f87'); assert.equal(cm.colors.ansi[97].css, '#875faf'); assert.equal(cm.colors.ansi[98].css, '#875fd7'); assert.equal(cm.colors.ansi[99].css, '#875fff'); assert.equal(cm.colors.ansi[100].css, '#878700'); assert.equal(cm.colors.ansi[101].css, '#87875f'); assert.equal(cm.colors.ansi[102].css, '#878787'); assert.equal(cm.colors.ansi[103].css, '#8787af'); assert.equal(cm.colors.ansi[104].css, '#8787d7'); assert.equal(cm.colors.ansi[105].css, '#8787ff'); assert.equal(cm.colors.ansi[106].css, '#87af00'); assert.equal(cm.colors.ansi[107].css, '#87af5f'); assert.equal(cm.colors.ansi[108].css, '#87af87'); assert.equal(cm.colors.ansi[109].css, '#87afaf'); assert.equal(cm.colors.ansi[110].css, '#87afd7'); assert.equal(cm.colors.ansi[111].css, '#87afff'); assert.equal(cm.colors.ansi[112].css, '#87d700'); assert.equal(cm.colors.ansi[113].css, '#87d75f'); assert.equal(cm.colors.ansi[114].css, '#87d787'); assert.equal(cm.colors.ansi[115].css, '#87d7af'); assert.equal(cm.colors.ansi[116].css, '#87d7d7'); assert.equal(cm.colors.ansi[117].css, '#87d7ff'); assert.equal(cm.colors.ansi[118].css, '#87ff00'); assert.equal(cm.colors.ansi[119].css, '#87ff5f'); assert.equal(cm.colors.ansi[120].css, '#87ff87'); assert.equal(cm.colors.ansi[121].css, '#87ffaf'); assert.equal(cm.colors.ansi[122].css, '#87ffd7'); assert.equal(cm.colors.ansi[123].css, '#87ffff'); assert.equal(cm.colors.ansi[124].css, '#af0000'); assert.equal(cm.colors.ansi[125].css, '#af005f'); assert.equal(cm.colors.ansi[126].css, '#af0087'); assert.equal(cm.colors.ansi[127].css, '#af00af'); assert.equal(cm.colors.ansi[128].css, '#af00d7'); assert.equal(cm.colors.ansi[129].css, '#af00ff'); assert.equal(cm.colors.ansi[130].css, '#af5f00'); assert.equal(cm.colors.ansi[131].css, '#af5f5f'); assert.equal(cm.colors.ansi[132].css, '#af5f87'); assert.equal(cm.colors.ansi[133].css, '#af5faf'); assert.equal(cm.colors.ansi[134].css, '#af5fd7'); assert.equal(cm.colors.ansi[135].css, '#af5fff'); assert.equal(cm.colors.ansi[136].css, '#af8700'); assert.equal(cm.colors.ansi[137].css, '#af875f'); assert.equal(cm.colors.ansi[138].css, '#af8787'); assert.equal(cm.colors.ansi[139].css, '#af87af'); assert.equal(cm.colors.ansi[140].css, '#af87d7'); assert.equal(cm.colors.ansi[141].css, '#af87ff'); assert.equal(cm.colors.ansi[142].css, '#afaf00'); assert.equal(cm.colors.ansi[143].css, '#afaf5f'); assert.equal(cm.colors.ansi[144].css, '#afaf87'); assert.equal(cm.colors.ansi[145].css, '#afafaf'); assert.equal(cm.colors.ansi[146].css, '#afafd7'); assert.equal(cm.colors.ansi[147].css, '#afafff'); assert.equal(cm.colors.ansi[148].css, '#afd700'); assert.equal(cm.colors.ansi[149].css, '#afd75f'); assert.equal(cm.colors.ansi[150].css, '#afd787'); assert.equal(cm.colors.ansi[151].css, '#afd7af'); assert.equal(cm.colors.ansi[152].css, '#afd7d7'); assert.equal(cm.colors.ansi[153].css, '#afd7ff'); assert.equal(cm.colors.ansi[154].css, '#afff00'); assert.equal(cm.colors.ansi[155].css, '#afff5f'); assert.equal(cm.colors.ansi[156].css, '#afff87'); assert.equal(cm.colors.ansi[157].css, '#afffaf'); assert.equal(cm.colors.ansi[158].css, '#afffd7'); assert.equal(cm.colors.ansi[159].css, '#afffff'); assert.equal(cm.colors.ansi[160].css, '#d70000'); assert.equal(cm.colors.ansi[161].css, '#d7005f'); assert.equal(cm.colors.ansi[162].css, '#d70087'); assert.equal(cm.colors.ansi[163].css, '#d700af'); assert.equal(cm.colors.ansi[164].css, '#d700d7'); assert.equal(cm.colors.ansi[165].css, '#d700ff'); assert.equal(cm.colors.ansi[166].css, '#d75f00'); assert.equal(cm.colors.ansi[167].css, '#d75f5f'); assert.equal(cm.colors.ansi[168].css, '#d75f87'); assert.equal(cm.colors.ansi[169].css, '#d75faf'); assert.equal(cm.colors.ansi[170].css, '#d75fd7'); assert.equal(cm.colors.ansi[171].css, '#d75fff'); assert.equal(cm.colors.ansi[172].css, '#d78700'); assert.equal(cm.colors.ansi[173].css, '#d7875f'); assert.equal(cm.colors.ansi[174].css, '#d78787'); assert.equal(cm.colors.ansi[175].css, '#d787af'); assert.equal(cm.colors.ansi[176].css, '#d787d7'); assert.equal(cm.colors.ansi[177].css, '#d787ff'); assert.equal(cm.colors.ansi[178].css, '#d7af00'); assert.equal(cm.colors.ansi[179].css, '#d7af5f'); assert.equal(cm.colors.ansi[180].css, '#d7af87'); assert.equal(cm.colors.ansi[181].css, '#d7afaf'); assert.equal(cm.colors.ansi[182].css, '#d7afd7'); assert.equal(cm.colors.ansi[183].css, '#d7afff'); assert.equal(cm.colors.ansi[184].css, '#d7d700'); assert.equal(cm.colors.ansi[185].css, '#d7d75f'); assert.equal(cm.colors.ansi[186].css, '#d7d787'); assert.equal(cm.colors.ansi[187].css, '#d7d7af'); assert.equal(cm.colors.ansi[188].css, '#d7d7d7'); assert.equal(cm.colors.ansi[189].css, '#d7d7ff'); assert.equal(cm.colors.ansi[190].css, '#d7ff00'); assert.equal(cm.colors.ansi[191].css, '#d7ff5f'); assert.equal(cm.colors.ansi[192].css, '#d7ff87'); assert.equal(cm.colors.ansi[193].css, '#d7ffaf'); assert.equal(cm.colors.ansi[194].css, '#d7ffd7'); assert.equal(cm.colors.ansi[195].css, '#d7ffff'); assert.equal(cm.colors.ansi[196].css, '#ff0000'); assert.equal(cm.colors.ansi[197].css, '#ff005f'); assert.equal(cm.colors.ansi[198].css, '#ff0087'); assert.equal(cm.colors.ansi[199].css, '#ff00af'); assert.equal(cm.colors.ansi[200].css, '#ff00d7'); assert.equal(cm.colors.ansi[201].css, '#ff00ff'); assert.equal(cm.colors.ansi[202].css, '#ff5f00'); assert.equal(cm.colors.ansi[203].css, '#ff5f5f'); assert.equal(cm.colors.ansi[204].css, '#ff5f87'); assert.equal(cm.colors.ansi[205].css, '#ff5faf'); assert.equal(cm.colors.ansi[206].css, '#ff5fd7'); assert.equal(cm.colors.ansi[207].css, '#ff5fff'); assert.equal(cm.colors.ansi[208].css, '#ff8700'); assert.equal(cm.colors.ansi[209].css, '#ff875f'); assert.equal(cm.colors.ansi[210].css, '#ff8787'); assert.equal(cm.colors.ansi[211].css, '#ff87af'); assert.equal(cm.colors.ansi[212].css, '#ff87d7'); assert.equal(cm.colors.ansi[213].css, '#ff87ff'); assert.equal(cm.colors.ansi[214].css, '#ffaf00'); assert.equal(cm.colors.ansi[215].css, '#ffaf5f'); assert.equal(cm.colors.ansi[216].css, '#ffaf87'); assert.equal(cm.colors.ansi[217].css, '#ffafaf'); assert.equal(cm.colors.ansi[218].css, '#ffafd7'); assert.equal(cm.colors.ansi[219].css, '#ffafff'); assert.equal(cm.colors.ansi[220].css, '#ffd700'); assert.equal(cm.colors.ansi[221].css, '#ffd75f'); assert.equal(cm.colors.ansi[222].css, '#ffd787'); assert.equal(cm.colors.ansi[223].css, '#ffd7af'); assert.equal(cm.colors.ansi[224].css, '#ffd7d7'); assert.equal(cm.colors.ansi[225].css, '#ffd7ff'); assert.equal(cm.colors.ansi[226].css, '#ffff00'); assert.equal(cm.colors.ansi[227].css, '#ffff5f'); assert.equal(cm.colors.ansi[228].css, '#ffff87'); assert.equal(cm.colors.ansi[229].css, '#ffffaf'); assert.equal(cm.colors.ansi[230].css, '#ffffd7'); assert.equal(cm.colors.ansi[231].css, '#ffffff'); assert.equal(cm.colors.ansi[232].css, '#080808'); assert.equal(cm.colors.ansi[233].css, '#121212'); assert.equal(cm.colors.ansi[234].css, '#1c1c1c'); assert.equal(cm.colors.ansi[235].css, '#262626'); assert.equal(cm.colors.ansi[236].css, '#303030'); assert.equal(cm.colors.ansi[237].css, '#3a3a3a'); assert.equal(cm.colors.ansi[238].css, '#444444'); assert.equal(cm.colors.ansi[239].css, '#4e4e4e'); assert.equal(cm.colors.ansi[240].css, '#585858'); assert.equal(cm.colors.ansi[241].css, '#626262'); assert.equal(cm.colors.ansi[242].css, '#6c6c6c'); assert.equal(cm.colors.ansi[243].css, '#767676'); assert.equal(cm.colors.ansi[244].css, '#808080'); assert.equal(cm.colors.ansi[245].css, '#8a8a8a'); assert.equal(cm.colors.ansi[246].css, '#949494'); assert.equal(cm.colors.ansi[247].css, '#9e9e9e'); assert.equal(cm.colors.ansi[248].css, '#a8a8a8'); assert.equal(cm.colors.ansi[249].css, '#b2b2b2'); assert.equal(cm.colors.ansi[250].css, '#bcbcbc'); assert.equal(cm.colors.ansi[251].css, '#c6c6c6'); assert.equal(cm.colors.ansi[252].css, '#d0d0d0'); assert.equal(cm.colors.ansi[253].css, '#dadada'); assert.equal(cm.colors.ansi[254].css, '#e4e4e4'); assert.equal(cm.colors.ansi[255].css, '#eeeeee'); }); }); describe('setTheme', () => { it('should not throw when not setting all colors', () => { assert.doesNotThrow(() => { cm.setTheme({}); }); }); it('should set a partial set of colors, using the default if not present', () => { assert.equal(cm.colors.background.css, '#000000'); assert.equal(cm.colors.foreground.css, '#ffffff'); cm.setTheme({ background: '#FF0000', foreground: '#00FF00' }); assert.equal(cm.colors.background.css, '#FF0000'); assert.equal(cm.colors.foreground.css, '#00FF00'); cm.setTheme({ background: '#0000FF' }); assert.equal(cm.colors.background.css, '#0000FF'); // FG reverts back to default assert.equal(cm.colors.foreground.css, '#ffffff'); }); }); }); xterm.js-3.8.1/src/renderer/ColorManager.ts000066400000000000000000000153271341514612000206200ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IColorManager } from './Types'; import { IColor, IColorSet } from '../shared/Types'; import { ITheme } from 'xterm'; const DEFAULT_FOREGROUND = fromHex('#ffffff'); const DEFAULT_BACKGROUND = fromHex('#000000'); const DEFAULT_CURSOR = fromHex('#ffffff'); const DEFAULT_CURSOR_ACCENT = fromHex('#000000'); const DEFAULT_SELECTION = { css: 'rgba(255, 255, 255, 0.3)', rgba: 0xFFFFFF77 }; // An IIFE to generate DEFAULT_ANSI_COLORS. Do not mutate DEFAULT_ANSI_COLORS, instead make a copy // and mutate that. export const DEFAULT_ANSI_COLORS = (() => { const colors = [ // dark: fromHex('#2e3436'), fromHex('#cc0000'), fromHex('#4e9a06'), fromHex('#c4a000'), fromHex('#3465a4'), fromHex('#75507b'), fromHex('#06989a'), fromHex('#d3d7cf'), // bright: fromHex('#555753'), fromHex('#ef2929'), fromHex('#8ae234'), fromHex('#fce94f'), fromHex('#729fcf'), fromHex('#ad7fa8'), fromHex('#34e2e2'), fromHex('#eeeeec') ]; // Fill in the remaining 240 ANSI colors. // Generate colors (16-231) const v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]; for (let i = 0; i < 216; i++) { const r = v[(i / 36) % 6 | 0]; const g = v[(i / 6) % 6 | 0]; const b = v[i % 6]; colors.push({ css: `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`, // Use >>> 0 to force a conversion to an unsigned int rgba: ((r << 24) | (g << 16) | (b << 8) | 0xFF) >>> 0 }); } // Generate greys (232-255) for (let i = 0; i < 24; i++) { const c = 8 + i * 10; const ch = toPaddedHex(c); colors.push({ css: `#${ch}${ch}${ch}`, rgba: ((c << 24) | (c << 16) | (c << 8) | 0xFF) >>> 0 }); } return colors; })(); function fromHex(css: string): IColor { return { css, rgba: parseInt(css.slice(1), 16) << 8 | 0xFF }; } function toPaddedHex(c: number): string { const s = c.toString(16); return s.length < 2 ? '0' + s : s; } /** * Manages the source of truth for a terminal's colors. */ export class ColorManager implements IColorManager { public colors: IColorSet; private _ctx: CanvasRenderingContext2D; private _litmusColor: CanvasGradient; constructor(document: Document, public allowTransparency: boolean) { const canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; this._ctx = canvas.getContext('2d'); this._ctx.globalCompositeOperation = 'copy'; this._litmusColor = this._ctx.createLinearGradient(0, 0, 1, 1); this.colors = { foreground: DEFAULT_FOREGROUND, background: DEFAULT_BACKGROUND, cursor: DEFAULT_CURSOR, cursorAccent: DEFAULT_CURSOR_ACCENT, selection: DEFAULT_SELECTION, ansi: DEFAULT_ANSI_COLORS.slice() }; } /** * Sets the terminal's theme. * @param theme The theme to use. If a partial theme is provided then default * colors will be used where colors are not defined. */ public setTheme(theme: ITheme): void { this.colors.foreground = this._parseColor(theme.foreground, DEFAULT_FOREGROUND); this.colors.background = this._parseColor(theme.background, DEFAULT_BACKGROUND); this.colors.cursor = this._parseColor(theme.cursor, DEFAULT_CURSOR, true); this.colors.cursorAccent = this._parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT, true); this.colors.selection = this._parseColor(theme.selection, DEFAULT_SELECTION, true); this.colors.ansi[0] = this._parseColor(theme.black, DEFAULT_ANSI_COLORS[0]); this.colors.ansi[1] = this._parseColor(theme.red, DEFAULT_ANSI_COLORS[1]); this.colors.ansi[2] = this._parseColor(theme.green, DEFAULT_ANSI_COLORS[2]); this.colors.ansi[3] = this._parseColor(theme.yellow, DEFAULT_ANSI_COLORS[3]); this.colors.ansi[4] = this._parseColor(theme.blue, DEFAULT_ANSI_COLORS[4]); this.colors.ansi[5] = this._parseColor(theme.magenta, DEFAULT_ANSI_COLORS[5]); this.colors.ansi[6] = this._parseColor(theme.cyan, DEFAULT_ANSI_COLORS[6]); this.colors.ansi[7] = this._parseColor(theme.white, DEFAULT_ANSI_COLORS[7]); this.colors.ansi[8] = this._parseColor(theme.brightBlack, DEFAULT_ANSI_COLORS[8]); this.colors.ansi[9] = this._parseColor(theme.brightRed, DEFAULT_ANSI_COLORS[9]); this.colors.ansi[10] = this._parseColor(theme.brightGreen, DEFAULT_ANSI_COLORS[10]); this.colors.ansi[11] = this._parseColor(theme.brightYellow, DEFAULT_ANSI_COLORS[11]); this.colors.ansi[12] = this._parseColor(theme.brightBlue, DEFAULT_ANSI_COLORS[12]); this.colors.ansi[13] = this._parseColor(theme.brightMagenta, DEFAULT_ANSI_COLORS[13]); this.colors.ansi[14] = this._parseColor(theme.brightCyan, DEFAULT_ANSI_COLORS[14]); this.colors.ansi[15] = this._parseColor(theme.brightWhite, DEFAULT_ANSI_COLORS[15]); } private _parseColor( css: string, fallback: IColor, allowTransparency: boolean = this.allowTransparency ): IColor { if (!css) { return fallback; } // If parsing the value results in failure, then it must be ignored, and the attribute must // retain its previous value. // -- https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles this._ctx.fillStyle = this._litmusColor; this._ctx.fillStyle = css; if (typeof this._ctx.fillStyle !== 'string') { console.warn(`Color: ${css} is invalid using fallback ${fallback.css}`); return fallback; } this._ctx.fillRect(0, 0, 1, 1); const data = this._ctx.getImageData(0, 0, 1, 1).data; if (!allowTransparency && data[3] !== 0xFF) { // Ideally we'd just ignore the alpha channel, but... // // Browsers may not give back exactly the same RGB values we put in, because most/all // convert the color to a pre-multiplied representation. getImageData converts that back to // a un-premultipled representation, but the precision loss may make the RGB channels unuable // on their own. // // E.g. In Chrome #12345610 turns into #10305010, and in the extreme case, 0xFFFFFF00 turns // into 0x00000000. // // "Note: Due to the lossy nature of converting to and from premultiplied alpha color values, // pixels that have just been set using putImageData() might be returned to an equivalent // getImageData() as different values." // -- https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation // // So let's just use the fallback color in this case instead. console.warn( `Color: ${css} is using transparency, but allowTransparency is false. ` + `Using fallback ${fallback.css}.` ); return fallback; } return { css, rgba: (data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]) >>> 0 }; } } xterm.js-3.8.1/src/renderer/CursorRenderLayer.ts000066400000000000000000000256141341514612000216610ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { CHAR_DATA_WIDTH_INDEX } from '../Buffer'; import { IColorSet, IRenderDimensions } from './Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { CharData, ITerminal } from '../Types'; interface ICursorState { x: number; y: number; isFocused: boolean; style: string; width: number; } /** * The time between cursor blinks. */ const BLINK_INTERVAL = 600; export class CursorRenderLayer extends BaseRenderLayer { private _state: ICursorState; private _cursorRenderers: {[key: string]: (terminal: ITerminal, x: number, y: number, charData: CharData) => void}; private _cursorBlinkStateManager: CursorBlinkStateManager; constructor(container: HTMLElement, zIndex: number, colors: IColorSet) { super(container, 'cursor', zIndex, true, colors); this._state = { x: null, y: null, isFocused: null, style: null, width: null }; this._cursorRenderers = { 'bar': this._renderBarCursor.bind(this), 'block': this._renderBlockCursor.bind(this), 'underline': this._renderUnderlineCursor.bind(this) }; // TODO: Consider initial options? Maybe onOptionsChanged should be called at the end of open? } public resize(terminal: ITerminal, dim: IRenderDimensions): void { super.resize(terminal, dim); // Resizing the canvas discards the contents of the canvas so clear state this._state = { x: null, y: null, isFocused: null, style: null, width: null }; } public reset(terminal: ITerminal): void { this._clearCursor(); if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.dispose(); this._cursorBlinkStateManager = null; this.onOptionsChanged(terminal); } } public onBlur(terminal: ITerminal): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.pause(); } terminal.refresh(terminal.buffer.y, terminal.buffer.y); } public onFocus(terminal: ITerminal): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.resume(terminal); } else { terminal.refresh(terminal.buffer.y, terminal.buffer.y); } } public onOptionsChanged(terminal: ITerminal): void { if (terminal.options.cursorBlink) { if (!this._cursorBlinkStateManager) { this._cursorBlinkStateManager = new CursorBlinkStateManager(terminal, () => { this._render(terminal, true); }); } } else { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.dispose(); this._cursorBlinkStateManager = null; } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal terminal.refresh(terminal.buffer.y, terminal.buffer.y); } } public onCursorMove(terminal: ITerminal): void { if (this._cursorBlinkStateManager) { this._cursorBlinkStateManager.restartBlinkAnimation(terminal); } } public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void { if (!this._cursorBlinkStateManager || this._cursorBlinkStateManager.isPaused) { this._render(terminal, false); } else { this._cursorBlinkStateManager.restartBlinkAnimation(terminal); } } private _render(terminal: ITerminal, triggeredByAnimationFrame: boolean): void { // Don't draw the cursor if it's hidden if (!terminal.cursorState || terminal.cursorHidden) { this._clearCursor(); return; } const cursorY = terminal.buffer.ybase + terminal.buffer.y; const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp; // Don't draw the cursor if it's off-screen if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= terminal.rows) { this._clearCursor(); return; } const charData = terminal.buffer.lines.get(cursorY).get(terminal.buffer.x); if (!charData) { return; } if (!terminal.isFocused) { this._clearCursor(); this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, charData); this._ctx.restore(); this._state.x = terminal.buffer.x; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; this._state.style = terminal.options.cursorStyle; this._state.width = charData[CHAR_DATA_WIDTH_INDEX]; return; } // Don't draw the cursor if it's blinking if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) { this._clearCursor(); return; } if (this._state) { // The cursor is already in the correct spot, don't redraw if (this._state.x === terminal.buffer.x && this._state.y === viewportRelativeCursorY && this._state.isFocused === terminal.isFocused && this._state.style === terminal.options.cursorStyle && this._state.width === charData[CHAR_DATA_WIDTH_INDEX]) { return; } this._clearCursor(); } this._ctx.save(); this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, charData); this._ctx.restore(); this._state.x = terminal.buffer.x; this._state.y = viewportRelativeCursorY; this._state.isFocused = false; this._state.style = terminal.options.cursorStyle; this._state.width = charData[CHAR_DATA_WIDTH_INDEX]; } private _clearCursor(): void { if (this._state) { this.clearCells(this._state.x, this._state.y, this._state.width, 1); this._state = { x: null, y: null, isFocused: null, style: null, width: null }; } } private _renderBarCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void { this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; this.fillLeftLineAtCell(x, y); this._ctx.restore(); } private _renderBlockCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void { this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; this.fillCells(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1); this._ctx.fillStyle = this._colors.cursorAccent.css; this.fillCharTrueColor(terminal, charData, x, y); this._ctx.restore(); } private _renderUnderlineCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void { this._ctx.save(); this._ctx.fillStyle = this._colors.cursor.css; this.fillBottomLineAtCells(x, y); this._ctx.restore(); } private _renderBlurCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void { this._ctx.save(); this._ctx.strokeStyle = this._colors.cursor.css; this.strokeRectAtCell(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1); this._ctx.restore(); } } class CursorBlinkStateManager { public isCursorVisible: boolean; private _animationFrame: number; private _blinkStartTimeout: number; private _blinkInterval: number; /** * The time at which the animation frame was restarted, this is used on the * next render to restart the timers so they don't need to restart the timers * multiple times over a short period. */ private _animationTimeRestarted: number; constructor( terminal: ITerminal, private _renderCallback: () => void ) { this.isCursorVisible = true; if (terminal.isFocused) { this._restartInterval(); } } public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); } public dispose(): void { if (this._blinkInterval) { window.clearInterval(this._blinkInterval); this._blinkInterval = null; } if (this._blinkStartTimeout) { window.clearTimeout(this._blinkStartTimeout); this._blinkStartTimeout = null; } if (this._animationFrame) { window.cancelAnimationFrame(this._animationFrame); this._animationFrame = null; } } public restartBlinkAnimation(terminal: ITerminal): void { if (this.isPaused) { return; } // Save a timestamp so that the restart can be done on the next interval this._animationTimeRestarted = Date.now(); // Force a cursor render to ensure it's visible and in the correct position this.isCursorVisible = true; if (!this._animationFrame) { this._animationFrame = window.requestAnimationFrame(() => { this._renderCallback(); this._animationFrame = null; }); } } private _restartInterval(timeToStart: number = BLINK_INTERVAL): void { // Clear any existing interval if (this._blinkInterval) { window.clearInterval(this._blinkInterval); } // Setup the initial timeout which will hide the cursor, this is done before // the regular interval is setup in order to support restarting the blink // animation in a lightweight way (without thrashing clearInterval and // setInterval). this._blinkStartTimeout = setTimeout(() => { // Check if another animation restart was requested while this was being // started if (this._animationTimeRestarted) { const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = null; if (time > 0) { this._restartInterval(time); return; } } // Hide the cursor this.isCursorVisible = false; this._animationFrame = window.requestAnimationFrame(() => { this._renderCallback(); this._animationFrame = null; }); // Setup the blink interval this._blinkInterval = setInterval(() => { // Adjust the animation time if it was restarted if (this._animationTimeRestarted) { // calc time diff // Make restart interval do a setTimeout initially? const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = null; this._restartInterval(time); return; } // Invert visibility and render this.isCursorVisible = !this.isCursorVisible; this._animationFrame = window.requestAnimationFrame(() => { this._renderCallback(); this._animationFrame = null; }); }, BLINK_INTERVAL); }, timeToStart); } public pause(): void { this.isCursorVisible = true; if (this._blinkInterval) { window.clearInterval(this._blinkInterval); this._blinkInterval = null; } if (this._blinkStartTimeout) { window.clearTimeout(this._blinkStartTimeout); this._blinkStartTimeout = null; } if (this._animationFrame) { window.cancelAnimationFrame(this._animationFrame); this._animationFrame = null; } } public resume(terminal: ITerminal): void { this._animationTimeRestarted = null; this._restartInterval(); this.restartBlinkAnimation(terminal); } } xterm.js-3.8.1/src/renderer/GridCache.test.ts000066400000000000000000000032451341514612000210320ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { GridCache } from './GridCache'; describe('GridCache', () => { let grid: GridCache; beforeEach(() => { grid = new GridCache(); }); describe('constructor', () => { it('should create an empty cache', () => { assert.equal(grid.cache.length, 0); }); }); describe('resize', () => { it('should fill all new elements with null', () => { grid.resize(2, 2); assert.equal(grid.cache.length, 2); assert.equal(grid.cache[0].length, 2); assert.equal(grid.cache[0][0], null); assert.equal(grid.cache[0][1], null); assert.equal(grid.cache[1].length, 2); assert.equal(grid.cache[1][0], null); assert.equal(grid.cache[1][1], null); grid.resize(3, 2); assert.equal(grid.cache.length, 3); assert.equal(grid.cache[2][0], null); assert.equal(grid.cache[2][1], null); }); it('should remove rows/cols from the cache when reduced', () => { grid.resize(2, 2); grid.resize(1, 1); assert.equal(grid.cache.length, 1); assert.equal(grid.cache[0].length, 1); }); it('should not touch existing cache entries if they fit in the new cache', () => { grid.resize(1, 1); assert.equal(grid.cache[0][0], null); grid.cache[0][0] = 1; grid.resize(2, 1); assert.equal(grid.cache[0][0], 1); }); }); describe('clear', () => { it('should make all entries null', () => { grid.resize(1, 1); grid.cache[0][0] = 1; grid.clear(); assert.equal(grid.cache[0][0], null); }); }); }); xterm.js-3.8.1/src/renderer/GridCache.ts000066400000000000000000000013231341514612000200470ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ export class GridCache { public cache: T[][]; public constructor() { this.cache = []; } public resize(width: number, height: number): void { for (let x = 0; x < width; x++) { if (this.cache.length <= x) { this.cache.push([]); } for (let y = this.cache[x].length; y < height; y++) { this.cache[x].push(null); } this.cache[x].length = height; } this.cache.length = width; } public clear(): void { for (let x = 0; x < this.cache.length; x++) { for (let y = 0; y < this.cache[x].length; y++) { this.cache[x][y] = null; } } } } xterm.js-3.8.1/src/renderer/LinkRenderLayer.ts000066400000000000000000000044721341514612000213000ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ILinkHoverEvent, ITerminal, ILinkifierAccessor, LinkHoverEventTypes } from '../Types'; import { IColorSet, IRenderDimensions } from './Types'; import { BaseRenderLayer } from './BaseRenderLayer'; import { INVERTED_DEFAULT_COLOR } from './atlas/Types'; export class LinkRenderLayer extends BaseRenderLayer { private _state: ILinkHoverEvent = null; constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) { super(container, 'link', zIndex, true, colors); terminal.linkifier.on(LinkHoverEventTypes.HOVER, (e: ILinkHoverEvent) => this._onLinkHover(e)); terminal.linkifier.on(LinkHoverEventTypes.LEAVE, (e: ILinkHoverEvent) => this._onLinkLeave(e)); } public resize(terminal: ITerminal, dim: IRenderDimensions): void { super.resize(terminal, dim); // Resizing the canvas discards the contents of the canvas so clear state this._state = null; } public reset(terminal: ITerminal): void { this._clearCurrentLink(); } private _clearCurrentLink(): void { if (this._state) { this.clearCells(this._state.x1, this._state.y1, this._state.cols - this._state.x1, 1); const middleRowCount = this._state.y2 - this._state.y1 - 1; if (middleRowCount > 0) { this.clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount); } this.clearCells(0, this._state.y2, this._state.x2, 1); this._state = null; } } private _onLinkHover(e: ILinkHoverEvent): void { if (e.fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; } else if (e.fg < 256) { // 256 color support this._ctx.fillStyle = this._colors.ansi[e.fg].css; } else { this._ctx.fillStyle = this._colors.foreground.css; } if (e.y1 === e.y2) { // Single line link this.fillBottomLineAtCells(e.x1, e.y1, e.x2 - e.x1); } else { // Multi-line link this.fillBottomLineAtCells(e.x1, e.y1, e.cols - e.x1); for (let y = e.y1 + 1; y < e.y2; y++) { this.fillBottomLineAtCells(0, y, e.cols); } this.fillBottomLineAtCells(0, e.y2, e.x2); } this._state = e; } private _onLinkLeave(e: ILinkHoverEvent): void { this._clearCurrentLink(); } } xterm.js-3.8.1/src/renderer/Renderer.ts000066400000000000000000000250601341514612000200100ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { TextRenderLayer } from './TextRenderLayer'; import { SelectionRenderLayer } from './SelectionRenderLayer'; import { CursorRenderLayer } from './CursorRenderLayer'; import { ColorManager } from './ColorManager'; import { IRenderLayer, IColorSet, IRenderer, IRenderDimensions, ICharacterJoinerRegistry } from './Types'; import { ITerminal, CharacterJoinerHandler } from '../Types'; import { LinkRenderLayer } from './LinkRenderLayer'; import { EventEmitter } from '../common/EventEmitter'; import { RenderDebouncer } from '../ui/RenderDebouncer'; import { ScreenDprMonitor } from '../ui/ScreenDprMonitor'; import { ITheme } from 'xterm'; import { CharacterJoinerRegistry } from '../renderer/CharacterJoinerRegistry'; export class Renderer extends EventEmitter implements IRenderer { private _renderDebouncer: RenderDebouncer; private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; private _screenDprMonitor: ScreenDprMonitor; private _isPaused: boolean = false; private _needsFullRefresh: boolean = false; private _characterJoinerRegistry: ICharacterJoinerRegistry; public colorManager: ColorManager; public dimensions: IRenderDimensions; constructor(private _terminal: ITerminal, theme: ITheme) { super(); const allowTransparency = this._terminal.options.allowTransparency; this.colorManager = new ColorManager(document, allowTransparency); this._characterJoinerRegistry = new CharacterJoinerRegistry(_terminal); if (theme) { this.colorManager.setTheme(theme); } this._renderLayers = [ new TextRenderLayer(this._terminal.screenElement, 0, this.colorManager.colors, this._characterJoinerRegistry, allowTransparency), new SelectionRenderLayer(this._terminal.screenElement, 1, this.colorManager.colors), new LinkRenderLayer(this._terminal.screenElement, 2, this.colorManager.colors, this._terminal), new CursorRenderLayer(this._terminal.screenElement, 3, this.colorManager.colors) ]; this.dimensions = { scaledCharWidth: null, scaledCharHeight: null, scaledCellWidth: null, scaledCellHeight: null, scaledCharLeft: null, scaledCharTop: null, scaledCanvasWidth: null, scaledCanvasHeight: null, canvasWidth: null, canvasHeight: null, actualCellWidth: null, actualCellHeight: null }; this._devicePixelRatio = window.devicePixelRatio; this._updateDimensions(); this.onOptionsChanged(); this._renderDebouncer = new RenderDebouncer(this._terminal, this._renderRows.bind(this)); this._screenDprMonitor = new ScreenDprMonitor(); this._screenDprMonitor.setListener(() => this.onWindowResize(window.devicePixelRatio)); this.register(this._screenDprMonitor); // Detect whether IntersectionObserver is detected and enable renderer pause // and resume based on terminal visibility if so if ('IntersectionObserver' in window) { const observer = new IntersectionObserver(e => this.onIntersectionChange(e[0]), { threshold: 0 }); observer.observe(this._terminal.element); this.register({ dispose: () => observer.disconnect() }); } } public dispose(): void { super.dispose(); this._renderLayers.forEach(l => l.dispose()); } public onIntersectionChange(entry: IntersectionObserverEntry): void { this._isPaused = entry.intersectionRatio === 0; if (!this._isPaused && this._needsFullRefresh) { this._terminal.refresh(0, this._terminal.rows - 1); } } public onWindowResize(devicePixelRatio: number): void { // If the device pixel ratio changed, the char atlas needs to be regenerated // and the terminal needs to refreshed if (this._devicePixelRatio !== devicePixelRatio) { this._devicePixelRatio = devicePixelRatio; this.onResize(this._terminal.cols, this._terminal.rows); } } public setTheme(theme: ITheme): IColorSet { this.colorManager.setTheme(theme); // Clear layers and force a full render this._renderLayers.forEach(l => { l.onThemeChanged(this._terminal, this.colorManager.colors); l.reset(this._terminal); }); if (this._isPaused) { this._needsFullRefresh = true; } else { this._terminal.refresh(0, this._terminal.rows - 1); } return this.colorManager.colors; } public onResize(cols: number, rows: number): void { // Update character and canvas dimensions this._updateDimensions(); // Resize all render layers this._renderLayers.forEach(l => l.resize(this._terminal, this.dimensions)); // Force a refresh if (this._isPaused) { this._needsFullRefresh = true; } else { this._terminal.refresh(0, this._terminal.rows - 1); } // Resize the screen this._terminal.screenElement.style.width = `${this.dimensions.canvasWidth}px`; this._terminal.screenElement.style.height = `${this.dimensions.canvasHeight}px`; this.emit('resize', { width: this.dimensions.canvasWidth, height: this.dimensions.canvasHeight }); } public onCharSizeChanged(): void { this.onResize(this._terminal.cols, this._terminal.rows); } public onBlur(): void { this._runOperation(l => l.onBlur(this._terminal)); } public onFocus(): void { this._runOperation(l => l.onFocus(this._terminal)); } public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean = false): void { this._runOperation(l => l.onSelectionChanged(this._terminal, start, end, columnSelectMode)); } public onCursorMove(): void { this._runOperation(l => l.onCursorMove(this._terminal)); } public onOptionsChanged(): void { this.colorManager.allowTransparency = this._terminal.options.allowTransparency; this._runOperation(l => l.onOptionsChanged(this._terminal)); } public clear(): void { this._runOperation(l => l.reset(this._terminal)); } private _runOperation(operation: (layer: IRenderLayer) => void): void { if (this._isPaused) { this._needsFullRefresh = true; } else { this._renderLayers.forEach(l => operation(l)); } } /** * Queues a refresh between two rows (inclusive), to be done on next animation * frame. * @param start The start row. * @param end The end row. */ public refreshRows(start: number, end: number): void { if (this._isPaused) { this._needsFullRefresh = true; return; } this._renderDebouncer.refresh(start, end); } /** * Performs the refresh loop callback, calling refresh only if a refresh is * necessary before queueing up the next one. */ private _renderRows(start: number, end: number): void { this._renderLayers.forEach(l => l.onGridChanged(this._terminal, start, end)); this._terminal.emit('refresh', { start, end }); } /** * Recalculates the character and canvas dimensions. */ private _updateDimensions(): void { // Perform a new measure if the CharMeasure dimensions are not yet available if (!this._terminal.charMeasure.width || !this._terminal.charMeasure.height) { return; } // Calculate the scaled character width. Width is floored as it must be // drawn to an integer grid in order for the CharAtlas "stamps" to not be // blurry. When text is drawn to the grid not using the CharAtlas, it is // clipped to ensure there is no overlap with the next cell. this.dimensions.scaledCharWidth = Math.floor(this._terminal.charMeasure.width * window.devicePixelRatio); // Calculate the scaled character height. Height is ceiled in case // devicePixelRatio is a floating point number in order to ensure there is // enough space to draw the character to the cell. this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * window.devicePixelRatio); // Calculate the scaled cell height, if lineHeight is not 1 then the value // will be floored because since lineHeight can never be lower then 1, there // is a guarentee that the scaled line height will always be larger than // scaled char height. this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight); // Calculate the y coordinate within a cell that text should draw from in // order to draw in the center of a cell. this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2); // Calculate the scaled cell width, taking the letterSpacing into account. this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing); // Calculate the x coordinate with a cell that text should draw from in // order to draw in the center of a cell. this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2); // Recalculate the canvas dimensions; scaled* define the actual number of // pixel in the canvas this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight; this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth; // The the size of the canvas on the page. It's very important that this // rounds to nearest integer and not ceils as browsers often set // window.devicePixelRatio as something like 1.100000023841858, when it's // actually 1.1. Ceiling causes blurriness as the backing canvas image is 1 // pixel too large for the canvas element size. this.dimensions.canvasHeight = Math.round(this.dimensions.scaledCanvasHeight / window.devicePixelRatio); this.dimensions.canvasWidth = Math.round(this.dimensions.scaledCanvasWidth / window.devicePixelRatio); // Get the _actual_ dimensions of an individual cell. This needs to be // derived from the canvasWidth/Height calculated above which takes into // account window.devicePixelRatio. CharMeasure.width/height by itself is // insufficient when the page is not at 100% zoom level as CharMeasure is // measured in CSS pixels, but the actual char size on the canvas can // differ. this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._terminal.rows; this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._terminal.cols; } public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return this._characterJoinerRegistry.registerCharacterJoiner(handler); } public deregisterCharacterJoiner(joinerId: number): boolean { return this._characterJoinerRegistry.deregisterCharacterJoiner(joinerId); } } xterm.js-3.8.1/src/renderer/SelectionRenderLayer.ts000066400000000000000000000075361341514612000223340ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal } from '../Types'; import { IColorSet, IRenderDimensions } from './Types'; import { BaseRenderLayer } from './BaseRenderLayer'; interface ISelectionState { start: [number, number]; end: [number, number]; columnSelectMode: boolean; ydisp: number; } export class SelectionRenderLayer extends BaseRenderLayer { private _state: ISelectionState; constructor(container: HTMLElement, zIndex: number, colors: IColorSet) { super(container, 'selection', zIndex, true, colors); this._clearState(); } private _clearState(): void { this._state = { start: null, end: null, columnSelectMode: null, ydisp: null }; } public resize(terminal: ITerminal, dim: IRenderDimensions): void { super.resize(terminal, dim); // Resizing the canvas discards the contents of the canvas so clear state this._clearState(); } public reset(terminal: ITerminal): void { if (this._state.start && this._state.end) { this._clearState(); this.clearAll(); } } public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void { // Selection has not changed if (!this._didStateChange(start, end, columnSelectMode, terminal.buffer.ydisp)) { return; } // Remove all selections this.clearAll(); // Selection does not exist if (!start || !end) { return; } // Translate from buffer position to viewport position const viewportStartRow = start[1] - terminal.buffer.ydisp; const viewportEndRow = end[1] - terminal.buffer.ydisp; const viewportCappedStartRow = Math.max(viewportStartRow, 0); const viewportCappedEndRow = Math.min(viewportEndRow, terminal.rows - 1); // No need to draw the selection if (viewportCappedStartRow >= terminal.rows || viewportCappedEndRow < 0) { return; } this._ctx.fillStyle = this._colors.selection.css; if (columnSelectMode) { const startCol = start[0]; const width = end[0] - startCol; const height = viewportCappedEndRow - viewportCappedStartRow + 1; this.fillCells(startCol, viewportCappedStartRow, width, height); } else { // Draw first row const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols; this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1); // Draw middle rows const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0); this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount); // Draw final row if (viewportCappedStartRow !== viewportCappedEndRow) { // Only draw viewportEndRow if it's not the same as viewportStartRow const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols; this.fillCells(0, viewportCappedEndRow, endCol, 1); } } // Save state for next render this._state.start = [start[0], start[1]]; this._state.end = [end[0], end[1]]; this._state.columnSelectMode = columnSelectMode; this._state.ydisp = terminal.buffer.ydisp; } private _didStateChange(start: [number, number], end: [number, number], columnSelectMode: boolean, ydisp: number): boolean { return !this._areCoordinatesEqual(start, this._state.start) || !this._areCoordinatesEqual(end, this._state.end) || columnSelectMode !== this._state.columnSelectMode || ydisp !== this._state.ydisp; } private _areCoordinatesEqual(coord1: [number, number], coord2: [number, number]): boolean { if (!coord1 || !coord2) { return false; } return coord1[0] === coord2[0] && coord1[1] === coord2[1]; } } xterm.js-3.8.1/src/renderer/TextRenderLayer.ts000066400000000000000000000260761341514612000213330ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { CHAR_DATA_ATTR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, NULL_CELL_CODE } from '../Buffer'; import { FLAGS, IColorSet, IRenderDimensions, ICharacterJoinerRegistry } from './Types'; import { CharData, ITerminal } from '../Types'; import { INVERTED_DEFAULT_COLOR } from './atlas/Types'; import { GridCache } from './GridCache'; import { BaseRenderLayer } from './BaseRenderLayer'; /** * This CharData looks like a null character, which will forc a clear and render * when the character changes (a regular space ' ' character may not as it's * drawn state is a cleared cell). */ // const OVERLAP_OWNED_CHAR_DATA: CharData = [null, '', 0, -1]; export class TextRenderLayer extends BaseRenderLayer { private _state: GridCache; private _characterWidth: number; private _characterFont: string; private _characterOverlapCache: { [key: string]: boolean } = {}; private _characterJoinerRegistry: ICharacterJoinerRegistry; constructor(container: HTMLElement, zIndex: number, colors: IColorSet, characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean) { super(container, 'text', zIndex, alpha, colors); this._state = new GridCache(); this._characterJoinerRegistry = characterJoinerRegistry; } public resize(terminal: ITerminal, dim: IRenderDimensions): void { super.resize(terminal, dim); // Clear the character width cache if the font or width has changed const terminalFont = this._getFont(terminal, false, false); if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) { this._characterWidth = dim.scaledCharWidth; this._characterFont = terminalFont; this._characterOverlapCache = {}; } // Resizing the canvas discards the contents of the canvas so clear state this._state.clear(); this._state.resize(terminal.cols, terminal.rows); } public reset(terminal: ITerminal): void { this._state.clear(); this.clearAll(); } private _forEachCell( terminal: ITerminal, firstRow: number, lastRow: number, joinerRegistry: ICharacterJoinerRegistry | null, callback: ( code: number, chars: string, width: number, x: number, y: number, fg: number, bg: number, flags: number ) => void ): void { for (let y = firstRow; y <= lastRow; y++) { const row = y + terminal.buffer.ydisp; const line = terminal.buffer.lines.get(row); const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : []; for (let x = 0; x < terminal.cols; x++) { const charData = line.get(x); let code: number = charData[CHAR_DATA_CODE_INDEX]; // Can either represent character(s) for a single cell or multiple cells // if indicated by a character joiner. let chars: string = charData[CHAR_DATA_CHAR_INDEX]; const attr: number = charData[CHAR_DATA_ATTR_INDEX]; let width: number = charData[CHAR_DATA_WIDTH_INDEX]; // If true, indicates that the current character(s) to draw were joined. let isJoined = false; let lastCharX = x; // The character to the left is a wide character, drawing is owned by // the char at x-1 if (width === 0) { continue; } // Process any joined character ranges as needed. Because of how the // ranges are produced, we know that they are valid for the characters // and attributes of our input. if (joinedRanges.length > 0 && x === joinedRanges[0][0]) { isJoined = true; const range = joinedRanges.shift(); // We already know the exact start and end column of the joined range, // so we get the string and width representing it directly chars = terminal.buffer.translateBufferLineToString( row, true, range[0], range[1] ); width = range[1] - range[0]; code = Infinity; // Skip over the cells occupied by this range in the loop lastCharX = range[1] - 1; } // If the character is an overlapping char and the character to the // right is a space, take ownership of the cell to the right. We skip // this check for joined characters because their rendering likely won't // yield the same result as rendering the last character individually. if (!isJoined && this._isOverlapping(charData)) { // If the character is overlapping, we want to force a re-render on every // frame. This is specifically to work around the case where two // overlaping chars `a` and `b` are adjacent, the cursor is moved to b and a // space is added. Without this, the first half of `b` would never // get removed, and `a` would not re-render because it thinks it's // already in the correct state. // this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA; if (lastCharX < line.length - 1 && line.get(lastCharX + 1)[CHAR_DATA_CODE_INDEX] === NULL_CELL_CODE) { width = 2; // this._clearChar(x + 1, y); // The overlapping char's char data will force a clear and render when the // overlapping char is no longer to the left of the character and also when // the space changes to another character. // this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA; } } const flags = attr >> 18; let bg = attr & 0x1ff; let fg = (attr >> 9) & 0x1ff; // If inverse flag is on, the foreground should become the background. if (flags & FLAGS.INVERSE) { const temp = bg; bg = fg; fg = temp; if (fg === 256) { fg = INVERTED_DEFAULT_COLOR; } if (bg === 257) { bg = INVERTED_DEFAULT_COLOR; } } callback( code, chars, width, x, y, fg, bg, flags ); x = lastCharX; } } } /** * Draws the background for a specified range of columns. Tries to batch adjacent cells of the * same color together to reduce draw calls. */ private _drawBackground(terminal: ITerminal, firstRow: number, lastRow: number): void { const ctx = this._ctx; const cols = terminal.cols; let startX: number = 0; let startY: number = 0; let prevFillStyle: string | null = null; ctx.save(); this._forEachCell(terminal, firstRow, lastRow, null, (code, chars, width, x, y, fg, bg, flags) => { // libvte and xterm both draw the background (but not foreground) of invisible characters, // so we should too. let nextFillStyle = null; // null represents default background color if (bg === INVERTED_DEFAULT_COLOR) { nextFillStyle = this._colors.foreground.css; } else if (bg < 256) { nextFillStyle = this._colors.ansi[bg].css; } if (prevFillStyle === null) { // This is either the first iteration, or the default background was set. Either way, we // don't need to draw anything. startX = x; startY = y; } if (y !== startY) { // our row changed, draw the previous row ctx.fillStyle = prevFillStyle; this.fillCells(startX, startY, cols - startX, 1); startX = x; startY = y; } else if (prevFillStyle !== nextFillStyle) { // our color changed, draw the previous characters in this row ctx.fillStyle = prevFillStyle; this.fillCells(startX, startY, x - startX, 1); startX = x; startY = y; } prevFillStyle = nextFillStyle; }); // flush the last color we encountered if (prevFillStyle !== null) { ctx.fillStyle = prevFillStyle; this.fillCells(startX, startY, cols - startX, 1); } ctx.restore(); } private _drawForeground(terminal: ITerminal, firstRow: number, lastRow: number): void { this._forEachCell(terminal, firstRow, lastRow, this._characterJoinerRegistry, (code, chars, width, x, y, fg, bg, flags) => { if (flags & FLAGS.INVISIBLE) { return; } if (flags & FLAGS.UNDERLINE) { this._ctx.save(); if (fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background.css; } else if (fg < 256) { // 256 color support this._ctx.fillStyle = this._colors.ansi[fg].css; } else { this._ctx.fillStyle = this._colors.foreground.css; } this.fillBottomLineAtCells(x, y, width); this._ctx.restore(); } this.drawChars( terminal, chars, code, width, x, y, fg, bg, !!(flags & FLAGS.BOLD), !!(flags & FLAGS.DIM), !!(flags & FLAGS.ITALIC) ); }); } public onGridChanged(terminal: ITerminal, firstRow: number, lastRow: number): void { // Resize has not been called yet if (this._state.cache.length === 0) { return; } if (this._charAtlas) { this._charAtlas.beginFrame(); } this.clearCells(0, firstRow, terminal.cols, lastRow - firstRow + 1); this._drawBackground(terminal, firstRow, lastRow); this._drawForeground(terminal, firstRow, lastRow); } public onOptionsChanged(terminal: ITerminal): void { this.setTransparency(terminal, terminal.options.allowTransparency); } /** * Whether a character is overlapping to the next cell. */ private _isOverlapping(charData: CharData): boolean { // Only single cell characters can be overlapping, rendering issues can // occur without this check if (charData[CHAR_DATA_WIDTH_INDEX] !== 1) { return false; } // We assume that any ascii character will not overlap const code = charData[CHAR_DATA_CODE_INDEX]; if (code < 256) { return false; } // Deliver from cache if available const char = charData[CHAR_DATA_CHAR_INDEX]; if (this._characterOverlapCache.hasOwnProperty(char)) { return this._characterOverlapCache[char]; } // Setup the font this._ctx.save(); this._ctx.font = this._characterFont; // Measure the width of the character, but Math.floor it // because that is what the renderer does when it calculates // the character dimensions we are comparing against const overlaps = Math.floor(this._ctx.measureText(char).width) > this._characterWidth; // Restore the original context this._ctx.restore(); // Cache and return this._characterOverlapCache[char] = overlaps; return overlaps; } /** * Clear the charcater at the cell specified. * @param x The column of the char. * @param y The row of the char. */ // private _clearChar(x: number, y: number): void { // let colsToClear = 1; // // Clear the adjacent character if it was wide // const state = this._state.cache[x][y]; // if (state && state[CHAR_DATA_WIDTH_INDEX] === 2) { // colsToClear = 2; // } // this.clearCells(x, y, colsToClear, 1); // } } xterm.js-3.8.1/src/renderer/Types.ts000066400000000000000000000066161341514612000173540ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal, CharacterJoinerHandler } from '../Types'; import { IEventEmitter, ITheme, IDisposable } from 'xterm'; import { IColorSet } from '../shared/Types'; /** * Flags used to render terminal text properly. */ export const enum FLAGS { BOLD = 1, UNDERLINE = 2, BLINK = 4, INVERSE = 8, INVISIBLE = 16, DIM = 32, ITALIC = 64 } /** * Note that IRenderer implementations should emit the refresh event after * rendering rows to the screen. */ export interface IRenderer extends IEventEmitter, IDisposable { dimensions: IRenderDimensions; colorManager: IColorManager; dispose(): void; setTheme(theme: ITheme): IColorSet; onWindowResize(devicePixelRatio: number): void; onResize(cols: number, rows: number): void; onCharSizeChanged(): void; onBlur(): void; onFocus(): void; onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void; onCursorMove(): void; onOptionsChanged(): void; clear(): void; refreshRows(start: number, end: number): void; registerCharacterJoiner(handler: CharacterJoinerHandler): number; deregisterCharacterJoiner(joinerId: number): boolean; } export interface IColorManager { colors: IColorSet; } // TODO: We should probably rewrite the imports for IColorSet, but there's a lot of them export { IColorSet }; export interface IRenderDimensions { scaledCharWidth: number; scaledCharHeight: number; scaledCellWidth: number; scaledCellHeight: number; scaledCharLeft: number; scaledCharTop: number; scaledCanvasWidth: number; scaledCanvasHeight: number; canvasWidth: number; canvasHeight: number; actualCellWidth: number; actualCellHeight: number; } export interface IRenderLayer extends IDisposable { /** * Called when the terminal loses focus. */ onBlur(terminal: ITerminal): void; /** * * Called when the terminal gets focus. */ onFocus(terminal: ITerminal): void; /** * Called when the cursor is moved. */ onCursorMove(terminal: ITerminal): void; /** * Called when options change. */ onOptionsChanged(terminal: ITerminal): void; /** * Called when the theme changes. */ onThemeChanged(terminal: ITerminal, colorSet: IColorSet): void; /** * Called when the data in the grid has changed (or needs to be rendered * again). */ onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void; /** * Calls when the selection changes. */ onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number], columnSelectMode: boolean): void; /** * Registers a handler to join characters to render as a group */ registerCharacterJoiner?(joiner: ICharacterJoiner): void; /** * Deregisters the specified character joiner handler */ deregisterCharacterJoiner?(joinerId: number): void; /** * Resize the render layer. */ resize(terminal: ITerminal, dim: IRenderDimensions): void; /** * Clear the state of the render layer. */ reset(terminal: ITerminal): void; } export interface ICharacterJoiner { id: number; handler: CharacterJoinerHandler; } export interface ICharacterJoinerRegistry { registerCharacterJoiner(handler: (text: string) => [number, number][]): number; deregisterCharacterJoiner(joinerId: number): boolean; getJoinedCharacters(row: number): [number, number][]; } xterm.js-3.8.1/src/renderer/atlas/000077500000000000000000000000001341514612000167735ustar00rootroot00000000000000xterm.js-3.8.1/src/renderer/atlas/BaseCharAtlas.ts000066400000000000000000000032441341514612000220030ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IGlyphIdentifier } from './Types'; import { IDisposable } from 'xterm'; export default abstract class BaseCharAtlas implements IDisposable { private _didWarmUp: boolean = false; public dispose(): void { } /** * Perform any work needed to warm the cache before it can be used. May be called multiple times. * Implement _doWarmUp instead if you only want to get called once. */ public warmUp(): void { if (!this._didWarmUp) { this._doWarmUp(); this._didWarmUp = true; } } /** * Perform any work needed to warm the cache before it can be used. Used by the default * implementation of warmUp(), and will only be called once. */ protected _doWarmUp(): void { } /** * Called when we start drawing a new frame. * * TODO: We rely on this getting called by TextRenderLayer. This should really be called by * Renderer instead, but we need to make Renderer the source-of-truth for the char atlas, instead * of BaseRenderLayer. */ public beginFrame(): void { } /** * May be called before warmUp finishes, however it is okay for the implementation to * do nothing and return false in that case. * * @param ctx Where to draw the character onto. * @param glyph Information about what to draw * @param x The position on the context to start drawing at * @param y The position on the context to start drawing at * @returns The success state. True if we drew the character. */ public abstract draw( ctx: CanvasRenderingContext2D, glyph: IGlyphIdentifier, x: number, y: number ): boolean; } xterm.js-3.8.1/src/renderer/atlas/CharAtlasCache.ts000066400000000000000000000062401341514612000221330ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal } from '../../Types'; import { IColorSet } from '../Types'; import { ICharAtlasConfig } from '../../shared/atlas/Types'; import { generateConfig, configEquals } from './CharAtlasUtils'; import BaseCharAtlas from './BaseCharAtlas'; import DynamicCharAtlas from './DynamicCharAtlas'; import NoneCharAtlas from './NoneCharAtlas'; import StaticCharAtlas from './StaticCharAtlas'; const charAtlasImplementations = { 'none': NoneCharAtlas, 'static': StaticCharAtlas, 'dynamic': DynamicCharAtlas }; interface ICharAtlasCacheEntry { atlas: BaseCharAtlas; config: ICharAtlasConfig; // N.B. This implementation potentially holds onto copies of the terminal forever, so // this may cause memory leaks. ownedBy: ITerminal[]; } const charAtlasCache: ICharAtlasCacheEntry[] = []; /** * Acquires a char atlas, either generating a new one or returning an existing * one that is in use by another terminal. * @param terminal The terminal. * @param colors The colors to use. */ export function acquireCharAtlas( terminal: ITerminal, colors: IColorSet, scaledCharWidth: number, scaledCharHeight: number ): BaseCharAtlas { const newConfig = generateConfig(scaledCharWidth, scaledCharHeight, terminal, colors); // TODO: Currently if a terminal changes configs it will not free the entry reference (until it's disposed) // Check to see if the terminal already owns this config for (let i = 0; i < charAtlasCache.length; i++) { const entry = charAtlasCache[i]; const ownedByIndex = entry.ownedBy.indexOf(terminal); if (ownedByIndex >= 0) { if (configEquals(entry.config, newConfig)) { return entry.atlas; } // The configs differ, release the terminal from the entry if (entry.ownedBy.length === 1) { charAtlasCache.splice(i, 1); } else { entry.ownedBy.splice(ownedByIndex, 1); } break; } } // Try match a char atlas from the cache for (let i = 0; i < charAtlasCache.length; i++) { const entry = charAtlasCache[i]; if (configEquals(entry.config, newConfig)) { // Add the terminal to the cache entry and return entry.ownedBy.push(terminal); return entry.atlas; } } const newEntry: ICharAtlasCacheEntry = { atlas: new charAtlasImplementations[terminal.options.experimentalCharAtlas]( document, newConfig ), config: newConfig, ownedBy: [terminal] }; charAtlasCache.push(newEntry); return newEntry.atlas; } /** * Removes a terminal reference from the cache, allowing its memory to be freed. * @param terminal The terminal to remove. */ export function removeTerminalFromCache(terminal: ITerminal): void { for (let i = 0; i < charAtlasCache.length; i++) { const index = charAtlasCache[i].ownedBy.indexOf(terminal); if (index !== -1) { if (charAtlasCache[i].ownedBy.length === 1) { // Remove the cache entry if it's the only terminal charAtlasCache.splice(i, 1); } else { // Remove the reference from the cache entry charAtlasCache[i].ownedBy.splice(index, 1); } break; } } } xterm.js-3.8.1/src/renderer/atlas/CharAtlasUtils.ts000066400000000000000000000035561341514612000222370ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal } from '../../Types'; import { IColorSet } from '../Types'; import { ICharAtlasConfig } from '../../shared/atlas/Types'; export function generateConfig(scaledCharWidth: number, scaledCharHeight: number, terminal: ITerminal, colors: IColorSet): ICharAtlasConfig { // null out some fields that don't matter const clonedColors = { foreground: colors.foreground, background: colors.background, cursor: null, cursorAccent: null, selection: null, // For the static char atlas, we only use the first 16 colors, but we need all 256 for the // dynamic character atlas. ansi: colors.ansi.slice(0, 16) }; return { type: terminal.options.experimentalCharAtlas, devicePixelRatio: window.devicePixelRatio, scaledCharWidth, scaledCharHeight, fontFamily: terminal.options.fontFamily, fontSize: terminal.options.fontSize, fontWeight: terminal.options.fontWeight, fontWeightBold: terminal.options.fontWeightBold, allowTransparency: terminal.options.allowTransparency, colors: clonedColors }; } export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean { for (let i = 0; i < a.colors.ansi.length; i++) { if (a.colors.ansi[i].rgba !== b.colors.ansi[i].rgba) { return false; } } return a.type === b.type && a.devicePixelRatio === b.devicePixelRatio && a.fontFamily === b.fontFamily && a.fontSize === b.fontSize && a.fontWeight === b.fontWeight && a.fontWeightBold === b.fontWeightBold && a.allowTransparency === b.allowTransparency && a.scaledCharWidth === b.scaledCharWidth && a.scaledCharHeight === b.scaledCharHeight && a.colors.foreground === b.colors.foreground && a.colors.background === b.colors.background; } xterm.js-3.8.1/src/renderer/atlas/DynamicCharAtlas.ts000066400000000000000000000274631341514612000225260ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { DIM_OPACITY, IGlyphIdentifier, INVERTED_DEFAULT_COLOR } from './Types'; import { ICharAtlasConfig } from '../../shared/atlas/Types'; import { IColor } from '../../shared/Types'; import BaseCharAtlas from './BaseCharAtlas'; import { DEFAULT_ANSI_COLORS } from '../ColorManager'; import { clearColor } from '../../shared/atlas/CharAtlasGenerator'; import LRUMap from './LRUMap'; import { isFirefox, isSafari } from '../../shared/utils/Browser'; // In practice we're probably never going to exhaust a texture this large. For debugging purposes, // however, it can be useful to set this to a really tiny value, to verify that LRU eviction works. const TEXTURE_WIDTH = 1024; const TEXTURE_HEIGHT = 1024; const TRANSPARENT_COLOR = { css: 'rgba(0, 0, 0, 0)', rgba: 0 }; // Drawing to the cache is expensive: If we have to draw more than this number of glyphs to the // cache in a single frame, give up on trying to cache anything else, and try to finish the current // frame ASAP. // // This helps to limit the amount of damage a program can do when it would otherwise thrash the // cache. const FRAME_CACHE_DRAW_LIMIT = 100; /** * The number of milliseconds to wait before generating the ImageBitmap, this is to debounce/batch * the operation as window.createImageBitmap is asynchronous. */ const GLYPH_BITMAP_COMMIT_DELAY = 100; interface IGlyphCacheValue { index: number; isEmpty: boolean; inBitmap: boolean; } function getGlyphCacheKey(glyph: IGlyphIdentifier): number { // Note that this only returns a valid key when code < 256 // Layout: // 0b00000000000000000000000000000001: italic (1) // 0b00000000000000000000000000000010: dim (1) // 0b00000000000000000000000000000100: bold (1) // 0b00000000000000000000111111111000: fg (9) // 0b00000000000111111111000000000000: bg (9) // 0b00011111111000000000000000000000: code (8) // 0b11100000000000000000000000000000: unused (3) return glyph.code << 21 | glyph.bg << 12 | glyph.fg << 3 | (glyph.bold ? 0 : 4) + (glyph.dim ? 0 : 2) + (glyph.italic ? 0 : 1); } export default class DynamicCharAtlas extends BaseCharAtlas { // An ordered map that we're using to keep track of where each glyph is in the atlas texture. // It's ordered so that we can determine when to remove the old entries. private _cacheMap: LRUMap; // The texture that the atlas is drawn to private _cacheCanvas: HTMLCanvasElement; private _cacheCtx: CanvasRenderingContext2D; // A temporary context that glyphs are drawn to before being transfered to the atlas. private _tmpCtx: CanvasRenderingContext2D; // The number of characters stored in the atlas by width/height private _width: number; private _height: number; private _drawToCacheCount: number = 0; // An array of glyph keys that are waiting on the bitmap to be generated. private _glyphsWaitingOnBitmap: IGlyphCacheValue[] = []; // The timeout that is used to batch bitmap generation so it's not requested for every new glyph. private _bitmapCommitTimeout: number | null = null; // The bitmap to draw from, this is much faster on other browsers than others. private _bitmap: ImageBitmap | null = null; constructor(document: Document, private _config: ICharAtlasConfig) { super(); this._cacheCanvas = document.createElement('canvas'); this._cacheCanvas.width = TEXTURE_WIDTH; this._cacheCanvas.height = TEXTURE_HEIGHT; // The canvas needs alpha because we use clearColor to convert the background color to alpha. // It might also contain some characters with transparent backgrounds if allowTransparency is // set. this._cacheCtx = this._cacheCanvas.getContext('2d', {alpha: true}); const tmpCanvas = document.createElement('canvas'); tmpCanvas.width = this._config.scaledCharWidth; tmpCanvas.height = this._config.scaledCharHeight; this._tmpCtx = tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency}); this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth); this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight); const capacity = this._width * this._height; this._cacheMap = new LRUMap(capacity); this._cacheMap.prealloc(capacity); // This is useful for debugging // document.body.appendChild(this._cacheCanvas); } public dispose(): void { if (this._bitmapCommitTimeout !== null) { window.clearTimeout(this._bitmapCommitTimeout); this._bitmapCommitTimeout = null; } } public beginFrame(): void { this._drawToCacheCount = 0; } public draw( ctx: CanvasRenderingContext2D, glyph: IGlyphIdentifier, x: number, y: number ): boolean { // Space is always an empty cell, special case this as it's so common if (glyph.code === 32) { return true; } const glyphKey = getGlyphCacheKey(glyph); const cacheValue = this._cacheMap.get(glyphKey); if (cacheValue !== null && cacheValue !== undefined) { this._drawFromCache(ctx, cacheValue, x, y); return true; } else if (this._canCache(glyph) && this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) { let index; if (this._cacheMap.size < this._cacheMap.capacity) { index = this._cacheMap.size; } else { // we're out of space, so our call to set will delete this item index = this._cacheMap.peek().index; } const cacheValue = this._drawToCache(glyph, index); this._cacheMap.set(glyphKey, cacheValue); this._drawFromCache(ctx, cacheValue, x, y); return true; } return false; } private _canCache(glyph: IGlyphIdentifier): boolean { // Only cache ascii and extended characters for now, to be safe. In the future, we could do // something more complicated to determine the expected width of a character. // // If we switch the renderer over to webgl at some point, we may be able to use blending modes // to draw overlapping glyphs from the atlas: // https://github.com/servo/webrender/issues/464#issuecomment-255632875 // https://webglfundamentals.org/webgl/lessons/webgl-text-texture.html return glyph.code < 256; } private _toCoordinateX(index: number): number { return (index % this._width) * this._config.scaledCharWidth; } private _toCoordinateY(index: number): number { return Math.floor(index / this._width) * this._config.scaledCharHeight; } private _drawFromCache( ctx: CanvasRenderingContext2D, cacheValue: IGlyphCacheValue, x: number, y: number ): void { // We don't actually need to do anything if this is whitespace. if (cacheValue.isEmpty) { return; } const cacheX = this._toCoordinateX(cacheValue.index); const cacheY = this._toCoordinateY(cacheValue.index); ctx.drawImage( cacheValue.inBitmap ? this._bitmap : this._cacheCanvas, cacheX, cacheY, this._config.scaledCharWidth, this._config.scaledCharHeight, x, y, this._config.scaledCharWidth, this._config.scaledCharHeight ); } private _getColorFromAnsiIndex(idx: number): IColor { if (idx < this._config.colors.ansi.length) { return this._config.colors.ansi[idx]; } return DEFAULT_ANSI_COLORS[idx]; } private _getBackgroundColor(glyph: IGlyphIdentifier): IColor { if (this._config.allowTransparency) { // The background color might have some transparency, so we need to render it as fully // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice // around the anti-aliased edges of the glyph, and it would look too dark. return TRANSPARENT_COLOR; } else if (glyph.bg === INVERTED_DEFAULT_COLOR) { return this._config.colors.foreground; } else if (glyph.bg < 256) { return this._getColorFromAnsiIndex(glyph.bg); } return this._config.colors.background; } private _getForegroundColor(glyph: IGlyphIdentifier): IColor { if (glyph.fg === INVERTED_DEFAULT_COLOR) { return this._config.colors.background; } else if (glyph.fg < 256) { // 256 color support return this._getColorFromAnsiIndex(glyph.fg); } return this._config.colors.foreground; } // TODO: We do this (or something similar) in multiple places. We should split this off // into a shared function. private _drawToCache(glyph: IGlyphIdentifier, index: number): IGlyphCacheValue { this._drawToCacheCount++; this._tmpCtx.save(); // draw the background const backgroundColor = this._getBackgroundColor(glyph); // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, regardless of // transparency in backgroundColor this._tmpCtx.globalCompositeOperation = 'copy'; this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight); this._tmpCtx.globalCompositeOperation = 'source-over'; // draw the foreground/glyph const fontWeight = glyph.bold ? this._config.fontWeightBold : this._config.fontWeight; const fontStyle = glyph.italic ? 'italic' : ''; this._tmpCtx.font = `${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`; this._tmpCtx.textBaseline = 'top'; this._tmpCtx.fillStyle = this._getForegroundColor(glyph).css; // Apply alpha to dim the character if (glyph.dim) { this._tmpCtx.globalAlpha = DIM_OPACITY; } // Draw the character this._tmpCtx.fillText(glyph.chars, 0, 0); this._tmpCtx.restore(); // clear the background from the character to avoid issues with drawing over the previous // character if it extends past it's bounds const imageData = this._tmpCtx.getImageData( 0, 0, this._config.scaledCharWidth, this._config.scaledCharHeight ); let isEmpty = false; if (!this._config.allowTransparency) { isEmpty = clearColor(imageData, backgroundColor); } // copy the data from imageData to _cacheCanvas const x = this._toCoordinateX(index); const y = this._toCoordinateY(index); // putImageData doesn't do any blending, so it will overwrite any existing cache entry for us this._cacheCtx.putImageData(imageData, x, y); // Add the glyph and queue it to the bitmap (if the browser supports it) const cacheValue = { index, isEmpty, inBitmap: false }; this._addGlyphToBitmap(cacheValue); return cacheValue; } private _addGlyphToBitmap(cacheValue: IGlyphCacheValue): void { // Support is patchy for createImageBitmap at the moment, pass a canvas back // if support is lacking as drawImage works there too. Firefox is also // included here as ImageBitmap appears both buggy and has horrible // performance (tested on v55). if (!('createImageBitmap' in window) || isFirefox || isSafari) { return; } // Add the glyph to the queue this._glyphsWaitingOnBitmap.push(cacheValue); // Check if bitmap generation timeout already exists if (this._bitmapCommitTimeout !== null) { return; } this._bitmapCommitTimeout = window.setTimeout(() => this._generateBitmap(), GLYPH_BITMAP_COMMIT_DELAY); } private _generateBitmap(): void { const glyphsMovingToBitmap = this._glyphsWaitingOnBitmap; this._glyphsWaitingOnBitmap = []; window.createImageBitmap(this._cacheCanvas).then(bitmap => { // Set bitmap this._bitmap = bitmap; // Mark all new glyphs as in bitmap, excluding glyphs that came in after // the bitmap was requested for (let i = 0; i < glyphsMovingToBitmap.length; i++) { const value = glyphsMovingToBitmap[i]; // It doesn't matter if the value was already evicted, it will be // released from memory after this block if so. value.inBitmap = true; } }); this._bitmapCommitTimeout = null; } } xterm.js-3.8.1/src/renderer/atlas/LRUMap.test.ts000066400000000000000000000034601341514612000214240ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import LRUMap from './LRUMap'; describe('LRUMap', () => { it('can be used to store and retrieve values', () => { const map = new LRUMap(10); map.set(1, 'valuea'); map.set(2, 'valueb'); map.set(3, 'valuec'); assert.strictEqual(map.get(1), 'valuea'); assert.strictEqual(map.get(2), 'valueb'); assert.strictEqual(map.get(3), 'valuec'); }); it('maintains a size from insertions', () => { const map = new LRUMap(10); assert.strictEqual(map.size, 0); map.set(1, 'value'); assert.strictEqual(map.size, 1); map.set(2, 'value'); assert.strictEqual(map.size, 2); }); it('deletes the oldest entry when the capacity is exceeded', () => { const map = new LRUMap(4); map.set(1, 'value'); map.set(2, 'value'); map.set(3, 'value'); map.set(4, 'value'); map.set(5, 'value'); assert.isNull(map.get(1)); assert.isNotNull(map.get(2)); assert.isNotNull(map.get(3)); assert.isNotNull(map.get(4)); assert.isNotNull(map.get(5)); assert.strictEqual(map.size, 4); }); it('prevents a recently accessed entry from getting deleted', () => { const map = new LRUMap(2); map.set(1, 'value'); map.set(2, 'value'); map.get(1); // a would normally get deleted here, except that we called get() map.set(3, 'value'); assert.isNotNull(map.get(1)); // b got deleted instead of a assert.isNull(map.get(2)); assert.isNotNull(map.get(3)); }); it('supports mutation', () => { const map = new LRUMap(10); map.set(1, 'oldvalue'); map.set(1, 'newvalue'); // mutation doesn't change the size assert.strictEqual(map.size, 1); assert.strictEqual(map.get(1), 'newvalue'); }); }); xterm.js-3.8.1/src/renderer/atlas/LRUMap.ts000066400000000000000000000066671341514612000204620ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ interface ILinkedListNode { prev: ILinkedListNode; next: ILinkedListNode; key: number; value: T; } export default class LRUMap { private _map: { [key: number]: ILinkedListNode } = {}; private _head: ILinkedListNode = null; private _tail: ILinkedListNode = null; private _nodePool: ILinkedListNode[] = []; public size: number = 0; constructor(public capacity: number) { } private _unlinkNode(node: ILinkedListNode): void { const prev = node.prev; const next = node.next; if (node === this._head) { this._head = next; } if (node === this._tail) { this._tail = prev; } if (prev !== null) { prev.next = next; } if (next !== null) { next.prev = prev; } } private _appendNode(node: ILinkedListNode): void { const tail = this._tail; if (tail !== null) { tail.next = node; } node.prev = tail; node.next = null; this._tail = node; if (this._head === null) { this._head = node; } } /** * Preallocate a bunch of linked-list nodes. Allocating these nodes ahead of time means that * they're more likely to live next to each other in memory, which seems to improve performance. * * Each empty object only consumes about 60 bytes of memory, so this is pretty cheap, even for * large maps. */ public prealloc(count: number): void { const nodePool = this._nodePool; for (let i = 0; i < count; i++) { nodePool.push({ prev: null, next: null, key: null, value: null }); } } public get(key: number): T | null { // This is unsafe: We're assuming our keyspace doesn't overlap with Object.prototype. However, // it's faster than calling hasOwnProperty, and in our case, it would never overlap. const node = this._map[key]; if (node !== undefined) { this._unlinkNode(node); this._appendNode(node); return node.value; } return null; } /** * Gets a value from a key without marking it as the most recently used item. */ public peekValue(key: number): T | null { const node = this._map[key]; if (node !== undefined) { return node.value; } return null; } public peek(): T | null { const head = this._head; return head === null ? null : head.value; } public set(key: number, value: T): void { // This is unsafe: See note above. let node = this._map[key]; if (node !== undefined) { // already exists, we just need to mutate it and move it to the end of the list node = this._map[key]; this._unlinkNode(node); node.value = value; } else if (this.size >= this.capacity) { // we're out of space: recycle the head node, move it to the tail node = this._head; this._unlinkNode(node); delete this._map[node.key]; node.key = key; node.value = value; this._map[key] = node; } else { // make a new element const nodePool = this._nodePool; if (nodePool.length > 0) { // use a preallocated node if we can node = nodePool.pop(); node.key = key; node.value = value; } else { node = { prev: null, next: null, key, value }; } this._map[key] = node; this.size++; } this._appendNode(node); } } xterm.js-3.8.1/src/renderer/atlas/NoneCharAtlas.ts000066400000000000000000000011341341514612000220240ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT * * A dummy CharAtlas implementation that always fails to draw characters. */ import { IGlyphIdentifier } from './Types'; import { ICharAtlasConfig } from '../../shared/atlas/Types'; import BaseCharAtlas from './BaseCharAtlas'; export default class NoneCharAtlas extends BaseCharAtlas { constructor(document: Document, config: ICharAtlasConfig) { super(); } public draw( ctx: CanvasRenderingContext2D, glyph: IGlyphIdentifier, x: number, y: number ): boolean { return false; } } xterm.js-3.8.1/src/renderer/atlas/StaticCharAtlas.ts000066400000000000000000000053401341514612000223570ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { DIM_OPACITY, IGlyphIdentifier } from './Types'; import { CHAR_ATLAS_CELL_SPACING, ICharAtlasConfig } from '../../shared/atlas/Types'; import { generateStaticCharAtlasTexture } from '../../shared/atlas/CharAtlasGenerator'; import BaseCharAtlas from './BaseCharAtlas'; export default class StaticCharAtlas extends BaseCharAtlas { private _texture: HTMLCanvasElement | ImageBitmap; constructor(private _document: Document, private _config: ICharAtlasConfig) { super(); } private _canvasFactory = (width: number, height: number) => { const canvas = this._document.createElement('canvas'); canvas.width = width; canvas.height = height; // This is useful for debugging // document.body.appendChild(canvas); return canvas; } protected _doWarmUp(): void { const result = generateStaticCharAtlasTexture(window, this._canvasFactory, this._config); if (result instanceof HTMLCanvasElement) { this._texture = result; } else { result.then(texture => { this._texture = texture; }); } } private _isCached(glyph: IGlyphIdentifier, colorIndex: number): boolean { const isAscii = glyph.code < 256; // A color is basic if it is one of the 4 bit ANSI colors. const isBasicColor = glyph.fg < 16; const isDefaultColor = glyph.fg >= 256; const isDefaultBackground = glyph.bg >= 256; return isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground && !glyph.italic; } public draw( ctx: CanvasRenderingContext2D, glyph: IGlyphIdentifier, x: number, y: number ): boolean { // we're not warmed up yet if (this._texture === null || this._texture === undefined) { return false; } let colorIndex = 0; if (glyph.fg < 256) { colorIndex = 2 + glyph.fg + (glyph.bold ? 16 : 0); } else { // If default color and bold if (glyph.bold) { colorIndex = 1; } } if (!this._isCached(glyph, colorIndex)) { return false; } ctx.save(); // ImageBitmap's draw about twice as fast as from a canvas const charAtlasCellWidth = this._config.scaledCharWidth + CHAR_ATLAS_CELL_SPACING; const charAtlasCellHeight = this._config.scaledCharHeight + CHAR_ATLAS_CELL_SPACING; // Apply alpha to dim the character if (glyph.dim) { ctx.globalAlpha = DIM_OPACITY; } ctx.drawImage( this._texture, glyph.code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, charAtlasCellWidth, this._config.scaledCharHeight, x, y, charAtlasCellWidth, this._config.scaledCharHeight ); ctx.restore(); return true; } } xterm.js-3.8.1/src/renderer/atlas/Types.ts000066400000000000000000000004741341514612000204540ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ export const INVERTED_DEFAULT_COLOR = -1; export const DIM_OPACITY = 0.5; export interface IGlyphIdentifier { chars: string; code: number; bg: number; fg: number; bold: boolean; dim: boolean; italic: boolean; } xterm.js-3.8.1/src/renderer/dom/000077500000000000000000000000001341514612000164465ustar00rootroot00000000000000xterm.js-3.8.1/src/renderer/dom/DomRenderer.ts000066400000000000000000000343251341514612000212330ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { IRenderer, IRenderDimensions, IColorSet } from '../Types'; import { ILinkHoverEvent, ITerminal, CharacterJoinerHandler, LinkHoverEventTypes } from '../../Types'; import { ITheme } from 'xterm'; import { EventEmitter } from '../../common/EventEmitter'; import { ColorManager } from '../ColorManager'; import { RenderDebouncer } from '../../ui/RenderDebouncer'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from './DomRendererRowFactory'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; const ROW_CONTAINER_CLASS = 'xterm-rows'; const FG_CLASS_PREFIX = 'xterm-fg-'; const BG_CLASS_PREFIX = 'xterm-bg-'; const FOCUS_CLASS = 'xterm-focus'; const SELECTION_CLASS = 'xterm-selection'; let nextTerminalId = 1; // TODO: Pull into an addon when TS composite projects allow easier sharing of code (not just // interfaces) between core and addons /** * A fallback renderer for when canvas is slow. This is not meant to be * particularly fast or feature complete, more just stable and usable for when * canvas is not an option. */ export class DomRenderer extends EventEmitter implements IRenderer { private _renderDebouncer: RenderDebouncer; private _rowFactory: DomRendererRowFactory; private _terminalClass: number = nextTerminalId++; private _themeStyleElement: HTMLStyleElement; private _dimensionsStyleElement: HTMLStyleElement; private _rowContainer: HTMLElement; private _rowElements: HTMLElement[] = []; private _selectionContainer: HTMLElement; public dimensions: IRenderDimensions; public colorManager: ColorManager; constructor(private _terminal: ITerminal, theme: ITheme | undefined) { super(); const allowTransparency = this._terminal.options.allowTransparency; this.colorManager = new ColorManager(document, allowTransparency); this.setTheme(theme); this._rowContainer = document.createElement('div'); this._rowContainer.classList.add(ROW_CONTAINER_CLASS); this._rowContainer.style.lineHeight = 'normal'; this._rowContainer.setAttribute('aria-hidden', 'true'); this._refreshRowElements(this._terminal.cols, this._terminal.rows); this._selectionContainer = document.createElement('div'); this._selectionContainer.classList.add(SELECTION_CLASS); this._selectionContainer.setAttribute('aria-hidden', 'true'); this.dimensions = { scaledCharWidth: null, scaledCharHeight: null, scaledCellWidth: null, scaledCellHeight: null, scaledCharLeft: null, scaledCharTop: null, scaledCanvasWidth: null, scaledCanvasHeight: null, canvasWidth: null, canvasHeight: null, actualCellWidth: null, actualCellHeight: null }; this._updateDimensions(); this._renderDebouncer = new RenderDebouncer(this._terminal, this._renderRows.bind(this)); this._rowFactory = new DomRendererRowFactory(document); this._terminal.element.classList.add(TERMINAL_CLASS_PREFIX + this._terminalClass); this._terminal.screenElement.appendChild(this._rowContainer); this._terminal.screenElement.appendChild(this._selectionContainer); this._terminal.linkifier.on(LinkHoverEventTypes.HOVER, (e: ILinkHoverEvent) => this._onLinkHover(e)); this._terminal.linkifier.on(LinkHoverEventTypes.LEAVE, (e: ILinkHoverEvent) => this._onLinkLeave(e)); } public dispose(): void { this._terminal.element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass); this._terminal.screenElement.removeChild(this._rowContainer); this._terminal.screenElement.removeChild(this._selectionContainer); this._terminal.screenElement.removeChild(this._themeStyleElement); this._terminal.screenElement.removeChild(this._dimensionsStyleElement); super.dispose(); } private _updateDimensions(): void { this.dimensions.scaledCharWidth = this._terminal.charMeasure.width * window.devicePixelRatio; this.dimensions.scaledCharHeight = this._terminal.charMeasure.height * window.devicePixelRatio; this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth; this.dimensions.scaledCellHeight = this.dimensions.scaledCharHeight; this.dimensions.scaledCharLeft = 0; this.dimensions.scaledCharTop = 0; this.dimensions.scaledCanvasWidth = this.dimensions.scaledCellWidth * this._terminal.cols; this.dimensions.scaledCanvasHeight = this.dimensions.scaledCellHeight * this._terminal.rows; this.dimensions.canvasWidth = this._terminal.charMeasure.width * this._terminal.cols; this.dimensions.canvasHeight = this._terminal.charMeasure.height * this._terminal.rows; this.dimensions.actualCellWidth = this._terminal.charMeasure.width; this.dimensions.actualCellHeight = this._terminal.charMeasure.height; this._rowElements.forEach(element => { element.style.width = `${this.dimensions.canvasWidth}px`; element.style.height = `${this._terminal.charMeasure.height}px`; }); if (!this._dimensionsStyleElement) { this._dimensionsStyleElement = document.createElement('style'); this._terminal.screenElement.appendChild(this._dimensionsStyleElement); } const styles = `${this._terminalSelector} .${ROW_CONTAINER_CLASS} span {` + ` display: inline-block;` + ` height: 100%;` + ` vertical-align: top;` + ` width: ${this._terminal.charMeasure.width}px` + `}`; this._dimensionsStyleElement.innerHTML = styles; this._selectionContainer.style.height = (this._terminal)._viewportElement.style.height; this._rowContainer.style.width = `${this.dimensions.canvasWidth}px`; this._rowContainer.style.height = `${this.dimensions.canvasHeight}px`; } public setTheme(theme: ITheme | undefined): IColorSet { if (theme) { this.colorManager.setTheme(theme); } if (!this._themeStyleElement) { this._themeStyleElement = document.createElement('style'); this._terminal.screenElement.appendChild(this._themeStyleElement); } // Base CSS let styles = `${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` + ` color: ${this.colorManager.colors.foreground.css};` + ` background-color: ${this.colorManager.colors.background.css};` + ` font-family: ${this._terminal.getOption('fontFamily')};` + ` font-size: ${this._terminal.getOption('fontSize')}px;` + `}`; // Text styles styles += `${this._terminalSelector} span:not(.${BOLD_CLASS}) {` + ` font-weight: ${this._terminal.options.fontWeight};` + `}` + `${this._terminalSelector} span.${BOLD_CLASS} {` + ` font-weight: ${this._terminal.options.fontWeightBold};` + `}` + `${this._terminalSelector} span.${ITALIC_CLASS} {` + ` font-style: italic;` + `}`; // Cursor styles += `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS} {` + ` outline: 1px solid ${this.colorManager.colors.cursor.css};` + ` outline-offset: -1px;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${this.colorManager.colors.cursor.css};` + ` color: ${this.colorManager.colors.cursorAccent.css};` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` + ` box-shadow: 1px 0 0 ${this.colorManager.colors.cursor.css} inset;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` + ` box-shadow: 0 -1px 0 ${this.colorManager.colors.cursor.css} inset;` + `}`; // Selection styles += `${this._terminalSelector} .${SELECTION_CLASS} {` + ` position: absolute;` + ` top: 0;` + ` left: 0;` + ` z-index: 1;` + ` pointer-events: none;` + `}` + `${this._terminalSelector} .${SELECTION_CLASS} div {` + ` position: absolute;` + ` background-color: ${this.colorManager.colors.selection.css};` + `}`; // Colors this.colorManager.colors.ansi.forEach((c, i) => { styles += `${this._terminalSelector} .${FG_CLASS_PREFIX}${i} { color: ${c.css}; }` + `${this._terminalSelector} .${BG_CLASS_PREFIX}${i} { background-color: ${c.css}; }`; }); this._themeStyleElement.innerHTML = styles; return this.colorManager.colors; } public onWindowResize(devicePixelRatio: number): void { this._updateDimensions(); } private _refreshRowElements(cols: number, rows: number): void { // Add missing elements for (let i = this._rowElements.length; i <= rows; i++) { const row = document.createElement('div'); this._rowContainer.appendChild(row); this._rowElements.push(row); } // Remove excess elements while (this._rowElements.length > rows) { this._rowContainer.removeChild(this._rowElements.pop()); } } public onResize(cols: number, rows: number): void { this._refreshRowElements(cols, rows); this._updateDimensions(); } public onCharSizeChanged(): void { this._updateDimensions(); } public onBlur(): void { this._rowContainer.classList.remove(FOCUS_CLASS); } public onFocus(): void { this._rowContainer.classList.add(FOCUS_CLASS); } public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void { // Remove all selections while (this._selectionContainer.children.length) { this._selectionContainer.removeChild(this._selectionContainer.children[0]); } // Selection does not exist if (!start || !end) { return; } // Translate from buffer position to viewport position const viewportStartRow = start[1] - this._terminal.buffer.ydisp; const viewportEndRow = end[1] - this._terminal.buffer.ydisp; const viewportCappedStartRow = Math.max(viewportStartRow, 0); const viewportCappedEndRow = Math.min(viewportEndRow, this._terminal.rows - 1); // No need to draw the selection if (viewportCappedStartRow >= this._terminal.rows || viewportCappedEndRow < 0) { return; } // Create the selections const documentFragment = document.createDocumentFragment(); if (columnSelectMode) { documentFragment.appendChild( this._createSelectionElement(viewportCappedStartRow, start[0], end[0], viewportCappedEndRow - viewportCappedStartRow + 1) ); } else { // Draw first row const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols; documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol)); // Draw middle rows const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1; documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._terminal.cols, middleRowsCount)); // Draw final row if (viewportCappedStartRow !== viewportCappedEndRow) { // Only draw viewportEndRow if it's not the same as viewporttartRow const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols; documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol)); } } this._selectionContainer.appendChild(documentFragment); } /** * Creates a selection element at the specified position. * @param row The row of the selection. * @param colStart The start column. * @param colEnd The end columns. */ private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement { const element = document.createElement('div'); element.style.height = `${rowCount * this._terminal.charMeasure.height}px`; element.style.top = `${row * this._terminal.charMeasure.height}px`; element.style.left = `${colStart * this._terminal.charMeasure.width}px`; element.style.width = `${this._terminal.charMeasure.width * (colEnd - colStart)}px`; return element; } public onCursorMove(): void { // No-op, the cursor is drawn when rows are drawn } public onOptionsChanged(): void { // Force a refresh this._updateDimensions(); this.setTheme(undefined); this._terminal.refresh(0, this._terminal.rows - 1); } public clear(): void { this._rowElements.forEach(e => e.innerHTML = ''); } public refreshRows(start: number, end: number): void { this._renderDebouncer.refresh(start, end); } private _renderRows(start: number, end: number): void { const terminal = this._terminal; const cursorAbsoluteY = terminal.buffer.ybase + terminal.buffer.y; const cursorX = this._terminal.buffer.x; for (let y = start; y <= end; y++) { const rowElement = this._rowElements[y]; rowElement.innerHTML = ''; const row = y + terminal.buffer.ydisp; const lineData = terminal.buffer.lines.get(row); const cursorStyle = terminal.options.cursorStyle; rowElement.appendChild(this._rowFactory.createRow(lineData, row === cursorAbsoluteY, cursorStyle, cursorX, terminal.charMeasure.width, terminal.cols)); } this._terminal.emit('refresh', {start, end}); } private get _terminalSelector(): string { return `.${TERMINAL_CLASS_PREFIX}${this._terminalClass}`; } public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return -1; } public deregisterCharacterJoiner(joinerId: number): boolean { return false; } private _onLinkHover(e: ILinkHoverEvent): void { this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, true); } private _onLinkLeave(e: ILinkHoverEvent): void { this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, false); } private _setCellUnderline(x: number, x2: number, y: number, y2: number, cols: number, enabled: boolean): void { while (x !== x2 || y !== y2) { const span = this._rowElements[y].children[x]; span.style.textDecoration = enabled ? 'underline' : 'none'; x = (x + 1) % cols; if (x === 0) { y++; } } } } xterm.js-3.8.1/src/renderer/dom/DomRendererRowFactory.test.ts000066400000000000000000000140421341514612000242230ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import jsdom = require('jsdom'); import { assert } from 'chai'; import { DomRendererRowFactory } from './DomRendererRowFactory'; import { DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from '../../Buffer'; import { FLAGS } from '../Types'; import { BufferLine } from '../../BufferLine'; import { IBufferLine } from '../../Types'; describe('DomRendererRowFactory', () => { let dom: jsdom.JSDOM; let rowFactory: DomRendererRowFactory; let lineData: IBufferLine; beforeEach(() => { dom = new jsdom.JSDOM(''); rowFactory = new DomRendererRowFactory(dom.window.document); lineData = createEmptyLineData(2); }); describe('createRow', () => { it('should create an element for every character in the row', () => { const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), ' ' + ' ' ); }); it('should set correct attributes for double width characters', () => { lineData.set(0, [DEFAULT_ATTR, '語', 2, '語'.charCodeAt(0)]); // There should be no element for the following "empty" cell lineData.set(1, [DEFAULT_ATTR, '', 0, undefined]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), '' ); }); it('should add class for cursor and cursor style', () => { for (const style of ['block', 'bar', 'underline']) { const fragment = rowFactory.createRow(lineData, true, style, 0, 5, 20); assert.equal(getFragmentHtml(fragment), ` ` + ' ' ); } }); it('should not render cells that go beyond the terminal\'s columns', () => { lineData.set(0, [DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]); lineData.set(1, [DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 1); assert.equal(getFragmentHtml(fragment), 'a' ); }); describe('attributes', () => { it('should add class for bold', () => { lineData.set(0, [DEFAULT_ATTR | (FLAGS.BOLD << 18), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' + ' ' ); }); it('should add class for italic', () => { lineData.set(0, [DEFAULT_ATTR | (FLAGS.ITALIC << 18), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' + ' ' ); }); it('should add classes for 256 foreground colors', () => { const defaultAttrNoFgColor = (0 << 9) | (256 << 0); for (let i = 0; i < 256; i++) { lineData.set(0, [defaultAttrNoFgColor | (i << 9), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), `a` + ' ' ); } }); it('should add classes for 256 background colors', () => { const defaultAttrNoBgColor = (257 << 9) | (0 << 0); for (let i = 0; i < 256; i++) { lineData.set(0, [defaultAttrNoBgColor | (i << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), `a` + ' ' ); } }); it('should correctly invert colors', () => { lineData.set(0, [(FLAGS.INVERSE << 18) | (2 << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' + ' ' ); }); it('should correctly invert default fg color', () => { lineData.set(0, [(FLAGS.INVERSE << 18) | (257 << 9) | (1 << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' + ' ' ); }); it('should correctly invert default bg color', () => { lineData.set(0, [(FLAGS.INVERSE << 18) | (1 << 9) | (256 << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), 'a' + ' ' ); }); it('should turn bold fg text bright', () => { for (let i = 0; i < 8; i++) { lineData.set(0, [(FLAGS.BOLD << 18) | (i << 9) | (256 << 0), 'a', 1, 'a'.charCodeAt(0)]); const fragment = rowFactory.createRow(lineData, false, undefined, 0, 5, 20); assert.equal(getFragmentHtml(fragment), `a` + ' ' ); } }); }); }); function getFragmentHtml(fragment: DocumentFragment): string { const element = dom.window.document.createElement('div'); element.appendChild(fragment); return element.innerHTML; } function createEmptyLineData(cols: number): IBufferLine { const lineData = new BufferLine(); for (let i = 0; i < cols; i++) { lineData.push([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); } return lineData; } }); xterm.js-3.8.1/src/renderer/dom/DomRendererRowFactory.ts000066400000000000000000000057661341514612000232620ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_ATTR_INDEX, CHAR_DATA_WIDTH_INDEX } from '../../Buffer'; import { FLAGS } from '../Types'; import { IBufferLine } from '../../Types'; export const BOLD_CLASS = 'xterm-bold'; export const ITALIC_CLASS = 'xterm-italic'; export const CURSOR_CLASS = 'xterm-cursor'; export const CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block'; export const CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar'; export const CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline'; export class DomRendererRowFactory { constructor( private _document: Document ) { } public createRow(lineData: IBufferLine, isCursorRow: boolean, cursorStyle: string | undefined, cursorX: number, cellWidth: number, cols: number): DocumentFragment { const fragment = this._document.createDocumentFragment(); let colCount = 0; for (let x = 0; x < lineData.length; x++) { // Don't allow any buffer to the right to be displayed if (colCount >= cols) { continue; } const charData = lineData.get(x); const char: string = charData[CHAR_DATA_CHAR_INDEX]; const attr: number = charData[CHAR_DATA_ATTR_INDEX]; const width: number = charData[CHAR_DATA_WIDTH_INDEX]; // The character to the left is a wide character, drawing is owned by the char at x-1 if (width === 0) { continue; } const charElement = this._document.createElement('span'); if (width > 1) { charElement.style.width = `${cellWidth * width}px`; } const flags = attr >> 18; let bg = attr & 0x1ff; let fg = (attr >> 9) & 0x1ff; if (isCursorRow && x === cursorX) { charElement.classList.add(CURSOR_CLASS); switch (cursorStyle) { case 'bar': charElement.classList.add(CURSOR_STYLE_BAR_CLASS); break; case 'underline': charElement.classList.add(CURSOR_STYLE_UNDERLINE_CLASS); break; default: charElement.classList.add(CURSOR_STYLE_BLOCK_CLASS); break; } } // If inverse flag is on, the foreground should become the background. if (flags & FLAGS.INVERSE) { const temp = bg; bg = fg; fg = temp; if (fg === 256) { fg = 0; } if (bg === 257) { bg = 15; } } if (flags & FLAGS.BOLD) { // Convert the FG color to the bold variant if (fg < 8) { fg += 8; } charElement.classList.add(BOLD_CLASS); } if (flags & FLAGS.ITALIC) { charElement.classList.add(ITALIC_CLASS); } charElement.textContent = char; if (fg !== 257) { charElement.classList.add(`xterm-fg-${fg}`); } if (bg !== 256) { charElement.classList.add(`xterm-bg-${bg}`); } fragment.appendChild(charElement); colCount += width; } return fragment; } } xterm.js-3.8.1/src/shared/000077500000000000000000000000001341514612000153275ustar00rootroot00000000000000xterm.js-3.8.1/src/shared/Types.ts000066400000000000000000000005271341514612000170070ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ export interface IColor { css: string; rgba: number; // 32-bit int with rgba in each byte } export interface IColorSet { foreground: IColor; background: IColor; cursor: IColor; cursorAccent: IColor; selection: IColor; ansi: IColor[]; } xterm.js-3.8.1/src/shared/atlas/000077500000000000000000000000001341514612000164335ustar00rootroot00000000000000xterm.js-3.8.1/src/shared/atlas/CharAtlasGenerator.ts000066400000000000000000000116541341514612000225230ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { FontWeight } from 'xterm'; import { CHAR_ATLAS_CELL_SPACING, ICharAtlasConfig } from './Types'; import { IColor } from '../Types'; import { isFirefox, isSafari } from '../utils/Browser'; declare const Promise: any; export interface IOffscreenCanvas { width: number; height: number; getContext(type: '2d', config?: Canvas2DContextAttributes): CanvasRenderingContext2D; transferToImageBitmap(): ImageBitmap; } /** * Generates a char atlas. * @param context The window or worker context. * @param canvasFactory A function to generate a canvas with a width or height. * @param config The config for the new char atlas. */ export function generateStaticCharAtlasTexture(context: Window, canvasFactory: (width: number, height: number) => HTMLCanvasElement | IOffscreenCanvas, config: ICharAtlasConfig): HTMLCanvasElement | Promise { const cellWidth = config.scaledCharWidth + CHAR_ATLAS_CELL_SPACING; const cellHeight = config.scaledCharHeight + CHAR_ATLAS_CELL_SPACING; const canvas = canvasFactory( /*255 ascii chars*/255 * cellWidth, (/*default+default bold*/2 + /*0-15*/16 + /*0-15 bold*/16) * cellHeight ); const ctx = canvas.getContext('2d', {alpha: config.allowTransparency}); ctx.fillStyle = config.colors.background.css; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.fillStyle = config.colors.foreground.css; ctx.font = getFont(config.fontWeight, config); ctx.textBaseline = 'top'; // Default color for (let i = 0; i < 256; i++) { ctx.save(); ctx.beginPath(); ctx.rect(i * cellWidth, 0, cellWidth, cellHeight); ctx.clip(); ctx.fillText(String.fromCharCode(i), i * cellWidth, 0); ctx.restore(); } // Default color bold ctx.save(); ctx.font = getFont(config.fontWeightBold, config); for (let i = 0; i < 256; i++) { ctx.save(); ctx.beginPath(); ctx.rect(i * cellWidth, cellHeight, cellWidth, cellHeight); ctx.clip(); ctx.fillText(String.fromCharCode(i), i * cellWidth, cellHeight); ctx.restore(); } ctx.restore(); // Colors 0-15 ctx.font = getFont(config.fontWeight, config); for (let colorIndex = 0; colorIndex < 16; colorIndex++) { const y = (colorIndex + 2) * cellHeight; // Draw ascii characters for (let i = 0; i < 256; i++) { ctx.save(); ctx.beginPath(); ctx.rect(i * cellWidth, y, cellWidth, cellHeight); ctx.clip(); ctx.fillStyle = config.colors.ansi[colorIndex].css; ctx.fillText(String.fromCharCode(i), i * cellWidth, y); ctx.restore(); } } // Colors 0-15 bold ctx.font = getFont(config.fontWeightBold, config); for (let colorIndex = 0; colorIndex < 16; colorIndex++) { const y = (colorIndex + 2 + 16) * cellHeight; // Draw ascii characters for (let i = 0; i < 256; i++) { ctx.save(); ctx.beginPath(); ctx.rect(i * cellWidth, y, cellWidth, cellHeight); ctx.clip(); ctx.fillStyle = config.colors.ansi[colorIndex].css; ctx.fillText(String.fromCharCode(i), i * cellWidth, y); ctx.restore(); } } ctx.restore(); // Support is patchy for createImageBitmap at the moment, pass a canvas back // if support is lacking as drawImage works there too. Firefox is also // included here as ImageBitmap appears both buggy and has horrible // performance (tested on v55). if (!('createImageBitmap' in context) || isFirefox || isSafari) { // Don't attempt to clear background colors if createImageBitmap is not supported if (canvas instanceof HTMLCanvasElement) { // Just return the HTMLCanvas if it's a HTMLCanvasElement return canvas; } // Transfer to an ImageBitmap is this is an OffscreenCanvas return new Promise((r: (bitmap: ImageBitmap) => void) => r(canvas.transferToImageBitmap())); } const charAtlasImageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // Remove the background color from the image so characters may overlap clearColor(charAtlasImageData, config.colors.background); return context.createImageBitmap(charAtlasImageData); } /** * Makes a partiicular rgb color in an ImageData completely transparent. * @returns True if the result is "empty", meaning all pixels are fully transparent. */ export function clearColor(imageData: ImageData, color: IColor): boolean { let isEmpty = true; const r = color.rgba >>> 24; const g = color.rgba >>> 16 & 0xFF; const b = color.rgba >>> 8 & 0xFF; for (let offset = 0; offset < imageData.data.length; offset += 4) { if (imageData.data[offset] === r && imageData.data[offset + 1] === g && imageData.data[offset + 2] === b) { imageData.data[offset + 3] = 0; } else { isEmpty = false; } } return isEmpty; } function getFont(fontWeight: FontWeight, config: ICharAtlasConfig): string { return `${fontWeight} ${config.fontSize * config.devicePixelRatio}px ${config.fontFamily}`; } xterm.js-3.8.1/src/shared/atlas/Types.ts000066400000000000000000000010051341514612000201030ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { FontWeight } from 'xterm'; import { IColorSet } from '../Types'; export const CHAR_ATLAS_CELL_SPACING = 1; export interface ICharAtlasConfig { type: 'none' | 'static' | 'dynamic'; devicePixelRatio: number; fontSize: number; fontFamily: string; fontWeight: FontWeight; fontWeightBold: FontWeight; scaledCharWidth: number; scaledCharHeight: number; allowTransparency: boolean; colors: IColorSet; } xterm.js-3.8.1/src/shared/utils/000077500000000000000000000000001341514612000164675ustar00rootroot00000000000000xterm.js-3.8.1/src/shared/utils/Browser.ts000066400000000000000000000022751341514612000204700ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ const isNode = (typeof navigator === 'undefined') ? true : false; const userAgent = (isNode) ? 'node' : navigator.userAgent; const platform = (isNode) ? 'node' : navigator.platform; export const isFirefox = !!~userAgent.indexOf('Firefox'); export const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent); export const isMSIE = !!~userAgent.indexOf('MSIE') || !!~userAgent.indexOf('Trident'); // Find the users platform. We use this to interpret the meta key // and ISO third level shifts. // http://stackoverflow.com/q/19877924/577598 export const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], platform); export const isIpad = platform === 'iPad'; export const isIphone = platform === 'iPhone'; export const isMSWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform); export const isLinux = platform.indexOf('Linux') >= 0; /** * Return if the given array contains the given element * @param arr The array to search for the given element. * @param el The element to look for into the array */ function contains(arr: any[], el: any): boolean { return arr.indexOf(el) >= 0; } xterm.js-3.8.1/src/ui/000077500000000000000000000000001341514612000144765ustar00rootroot00000000000000xterm.js-3.8.1/src/ui/CharMeasure.test.ts000066400000000000000000000031341341514612000202240ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import jsdom = require('jsdom'); import { ICharMeasure } from '../Types'; import { assert } from 'chai'; import { CharMeasure } from './CharMeasure'; describe('CharMeasure', () => { let dom: jsdom.JSDOM; let window: Window; let document: Document; let container: HTMLElement; let charMeasure: ICharMeasure; beforeEach(() => { dom = new jsdom.JSDOM(''); window = dom.window; document = window.document; container = document.createElement('div'); document.body.appendChild(container); charMeasure = new CharMeasure(document, container); }); describe('measure', () => { it('should have _measureElement', () => { assert.isDefined((charMeasure)._measureElement, 'new CharMeasure() should have created _measureElement'); }); it('should be performed sync', () => { // Mock getBoundingClientRect since jsdom doesn't have a layout engine (charMeasure)._measureElement.getBoundingClientRect = () => { return { width: 1, height: 1 }; }; charMeasure.measure({}); assert.equal(charMeasure.height, 1); assert.equal(charMeasure.width, 1); }); it('should NOT do a measure when the parent is hidden', done => { charMeasure.measure({}); setTimeout(() => { const firstWidth = charMeasure.width; container.style.display = 'none'; container.style.fontSize = '2em'; charMeasure.measure({}); assert.equal(charMeasure.width, firstWidth); done(); }, 0); }); }); }); xterm.js-3.8.1/src/ui/CharMeasure.ts000066400000000000000000000035161341514612000172520ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { ICharMeasure, ITerminalOptions } from '../Types'; import { EventEmitter } from '../common/EventEmitter'; /** * Utility class that measures the size of a character. Measurements are done in * the DOM rather than with a canvas context because support for extracting the * height of characters is patchy across browsers. */ export class CharMeasure extends EventEmitter implements ICharMeasure { private _document: Document; private _parentElement: HTMLElement; private _measureElement: HTMLElement; private _width: number; private _height: number; constructor(document: Document, parentElement: HTMLElement) { super(); this._document = document; this._parentElement = parentElement; this._measureElement = this._document.createElement('span'); this._measureElement.classList.add('xterm-char-measure-element'); this._measureElement.textContent = 'W'; this._measureElement.setAttribute('aria-hidden', 'true'); this._parentElement.appendChild(this._measureElement); } public get width(): number { return this._width; } public get height(): number { return this._height; } public measure(options: ITerminalOptions): void { this._measureElement.style.fontFamily = options.fontFamily; this._measureElement.style.fontSize = `${options.fontSize}px`; const geometry = this._measureElement.getBoundingClientRect(); // The element is likely currently display:none, we should retain the // previous value. if (geometry.width === 0 || geometry.height === 0) { return; } if (this._width !== geometry.width || this._height !== geometry.height) { this._width = geometry.width; this._height = Math.ceil(geometry.height); this.emit('charsizechanged'); } } } xterm.js-3.8.1/src/ui/Lifecycle.ts000066400000000000000000000013271341514612000167500ustar00rootroot00000000000000/** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable } from 'xterm'; /** * Adds a disposabe listener to a node in the DOM, returning the disposable. * @param type The event type. * @param handler The handler for the listener. */ export function addDisposableDomListener( node: Element | Window | Document, type: string, handler: (e: any) => void, useCapture?: boolean ): IDisposable { node.addEventListener(type, handler, useCapture); return { dispose: () => { if (!handler) { // Already disposed return; } node.removeEventListener(type, handler, useCapture); node = null; handler = null; } }; } xterm.js-3.8.1/src/ui/MouseZoneManager.ts000066400000000000000000000145411341514612000202720ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal } from '../Types'; import { IMouseZoneManager, IMouseZone } from './Types'; import { Disposable } from '../common/Lifecycle'; import { addDisposableDomListener } from './Lifecycle'; const HOVER_DURATION = 500; /** * The MouseZoneManager allows components to register zones within the terminal * that trigger hover and click callbacks. * * This class was intentionally made not so robust initially as the only case it * needed to support was single-line links which never overlap. Improvements can * be made in the future. */ export class MouseZoneManager extends Disposable implements IMouseZoneManager { private _zones: IMouseZone[] = []; private _areZonesActive: boolean = false; private _mouseMoveListener: (e: MouseEvent) => any; private _clickListener: (e: MouseEvent) => any; private _tooltipTimeout: number = null; private _currentZone: IMouseZone = null; private _lastHoverCoords: [number, number] = [null, null]; constructor( private _terminal: ITerminal ) { super(); this.register(addDisposableDomListener(this._terminal.element, 'mousedown', e => this._onMouseDown(e))); // These events are expensive, only listen to it when mouse zones are active this._mouseMoveListener = e => this._onMouseMove(e); this._clickListener = e => this._onClick(e); } public dispose(): void { super.dispose(); this._deactivate(); } public add(zone: IMouseZone): void { this._zones.push(zone); if (this._zones.length === 1) { this._activate(); } } public clearAll(start?: number, end?: number): void { // Exit if there's nothing to clear if (this._zones.length === 0) { return; } // Clear all if start/end weren't set if (!end) { start = 0; end = this._terminal.rows - 1; } // Iterate through zones and clear them out if they're within the range for (let i = 0; i < this._zones.length; i++) { const zone = this._zones[i]; if ((zone.y1 > start && zone.y1 <= end + 1) || (zone.y2 > start && zone.y2 <= end + 1) || (zone.y1 < start && zone.y2 > end + 1)) { if (this._currentZone && this._currentZone === zone) { this._currentZone.leaveCallback(); this._currentZone = null; } this._zones.splice(i--, 1); } } // Deactivate the mouse zone manager if all the zones have been removed if (this._zones.length === 0) { this._deactivate(); } } private _activate(): void { if (!this._areZonesActive) { this._areZonesActive = true; this._terminal.element.addEventListener('mousemove', this._mouseMoveListener); this._terminal.element.addEventListener('click', this._clickListener); } } private _deactivate(): void { if (this._areZonesActive) { this._areZonesActive = false; this._terminal.element.removeEventListener('mousemove', this._mouseMoveListener); this._terminal.element.removeEventListener('click', this._clickListener); } } private _onMouseMove(e: MouseEvent): void { // TODO: Ideally this would only clear the hover state when the mouse moves // outside of the mouse zone if (this._lastHoverCoords[0] !== e.pageX || this._lastHoverCoords[1] !== e.pageY) { this._onHover(e); // Record the current coordinates this._lastHoverCoords = [e.pageX, e.pageY]; } } private _onHover(e: MouseEvent): void { const zone = this._findZoneEventAt(e); // Do nothing if the zone is the same if (zone === this._currentZone) { return; } // Fire the hover end callback and cancel any existing timer if a new zone // is being hovered if (this._currentZone) { this._currentZone.leaveCallback(); this._currentZone = null; if (this._tooltipTimeout) { clearTimeout(this._tooltipTimeout); } } // Exit if there is not zone if (!zone) { return; } this._currentZone = zone; // Trigger the hover callback if (zone.hoverCallback) { zone.hoverCallback(e); } // Restart the tooltip timeout this._tooltipTimeout = setTimeout(() => this._onTooltip(e), HOVER_DURATION); } private _onTooltip(e: MouseEvent): void { this._tooltipTimeout = null; const zone = this._findZoneEventAt(e); if (zone && zone.tooltipCallback) { zone.tooltipCallback(e); } } private _onMouseDown(e: MouseEvent): void { // Ignore the event if there are no zones active if (!this._areZonesActive) { return; } // Find the active zone, prevent event propagation if found to prevent other // components from handling the mouse event. const zone = this._findZoneEventAt(e); if (zone) { if (zone.willLinkActivate(e)) { e.preventDefault(); e.stopImmediatePropagation(); } } } private _onClick(e: MouseEvent): void { // Find the active zone and click it if found const zone = this._findZoneEventAt(e); if (zone) { zone.clickCallback(e); e.preventDefault(); e.stopImmediatePropagation(); } } private _findZoneEventAt(e: MouseEvent): IMouseZone { const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.options.lineHeight, this._terminal.cols, this._terminal.rows); if (!coords) { return null; } const x = coords[0]; const y = coords[1]; for (let i = 0; i < this._zones.length; i++) { const zone = this._zones[i]; if (zone.y1 === zone.y2) { // Single line link if (y === zone.y1 && x >= zone.x1 && x < zone.x2) { return zone; } } else { // Multi-line link if ((y === zone.y1 && x >= zone.x1) || (y === zone.y2 && x < zone.x2) || (y > zone.y1 && y < zone.y2)) { return zone; } } } return null; } } export class MouseZone implements IMouseZone { constructor( public x1: number, public y1: number, public x2: number, public y2: number, public clickCallback: (e: MouseEvent) => any, public hoverCallback: (e: MouseEvent) => any, public tooltipCallback: (e: MouseEvent) => any, public leaveCallback: () => void, public willLinkActivate: (e: MouseEvent) => boolean ) { } } xterm.js-3.8.1/src/ui/RenderDebouncer.ts000066400000000000000000000034201341514612000201130ustar00rootroot00000000000000import { ITerminal } from '../Types'; import { IDisposable } from 'xterm'; /** * Debounces calls to render terminal rows using animation frames. */ export class RenderDebouncer implements IDisposable { private _rowStart: number; private _rowEnd: number; private _animationFrame: number = null; constructor( private _terminal: ITerminal, private _callback: (start: number, end: number) => void ) { } public dispose(): void { if (this._animationFrame) { window.cancelAnimationFrame(this._animationFrame); this._animationFrame = null; } } public refresh(rowStart: number, rowEnd: number): void { // Get the min/max row start/end for the arg values rowStart = rowStart !== null && rowStart !== undefined ? rowStart : 0; rowEnd = rowEnd !== null && rowEnd !== undefined ? rowEnd : this._terminal.rows - 1; // Check whether the row start/end values have already been set const isRowStartSet = this._rowStart !== undefined && this._rowStart !== null; const isRowEndSet = this._rowEnd !== undefined && this._rowEnd !== null; // Set the properties to the updated values this._rowStart = isRowStartSet ? Math.min(this._rowStart, rowStart) : rowStart; this._rowEnd = isRowEndSet ? Math.max(this._rowEnd, rowEnd) : rowEnd; if (this._animationFrame) { return; } this._animationFrame = window.requestAnimationFrame(() => this._innerRefresh()); } private _innerRefresh(): void { // Clamp values this._rowStart = Math.max(this._rowStart, 0); this._rowEnd = Math.min(this._rowEnd, this._terminal.rows - 1); // Run render callback this._callback(this._rowStart, this._rowEnd); // Reset debouncer this._rowStart = null; this._rowEnd = null; this._animationFrame = null; } } xterm.js-3.8.1/src/ui/ScreenDprMonitor.ts000066400000000000000000000037661341514612000203170ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Disposable } from '../common/Lifecycle'; export type ScreenDprListener = (newDevicePixelRatio?: number, oldDevicePixelRatio?: number) => void; /** * The screen device pixel ratio monitor allows listening for when the * window.devicePixelRatio value changes. This is done not with polling but with * the use of window.matchMedia to watch media queries. When the event fires, * the listener will be reattached using a different media query to ensure that * any further changes will register. * * The listener should fire on both window zoom changes and switching to a * monitor with a different DPI. */ export class ScreenDprMonitor extends Disposable { private _currentDevicePixelRatio: number; private _outerListener: MediaQueryListListener; private _listener: ScreenDprListener; private _resolutionMediaMatchList: MediaQueryList; public setListener(listener: ScreenDprListener): void { if (this._listener) { this.clearListener(); } this._listener = listener; this._outerListener = () => { this._listener(window.devicePixelRatio, this._currentDevicePixelRatio); this._updateDpr(); }; this._updateDpr(); } public dispose(): void { super.dispose(); this.clearListener(); } private _updateDpr(): void { // Clear listeners for old DPR if (this._resolutionMediaMatchList) { this._resolutionMediaMatchList.removeListener(this._outerListener); } // Add listeners for new DPR this._currentDevicePixelRatio = window.devicePixelRatio; this._resolutionMediaMatchList = window.matchMedia(`screen and (resolution: ${window.devicePixelRatio}dppx)`); this._resolutionMediaMatchList.addListener(this._outerListener); } public clearListener(): void { if (!this._listener) { return; } this._resolutionMediaMatchList.removeListener(this._outerListener); this._listener = null; this._outerListener = null; } } xterm.js-3.8.1/src/ui/Types.ts000066400000000000000000000011201341514612000161440ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable } from 'xterm'; export interface IMouseZoneManager extends IDisposable { add(zone: IMouseZone): void; clearAll(start?: number, end?: number): void; } export interface IMouseZone { x1: number; x2: number; y1: number; y2: number; clickCallback: (e: MouseEvent) => any; hoverCallback: (e: MouseEvent) => any | undefined; tooltipCallback: (e: MouseEvent) => any | undefined; leaveCallback: () => any | undefined; willLinkActivate: (e: MouseEvent) => boolean; } xterm.js-3.8.1/src/utils/000077500000000000000000000000001341514612000152215ustar00rootroot00000000000000xterm.js-3.8.1/src/utils/Clone.test.ts000066400000000000000000000045071341514612000176150ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ import { assert, expect } from 'chai'; import { clone } from './Clone'; describe('clone', () => { it('should clone simple objects', () => { const test = { a: 1, b: 2 }; assert.deepEqual(clone(test), { a: 1, b: 2 }); }); it('should clone nested objects', () => { const test = { bar: { a: 1, b: 2, c: { foo: 'bar' } } }; assert.deepEqual(clone(test), { bar: { a: 1, b: 2, c: { foo: 'bar' } } }); }); it('should clone null values', () => { const test: any = { a: null }; assert.deepEqual(clone(test), { a: null }); }); it('should clone array values', () => { const test = { a: [1, 2, 3], b: [1, null, 'test', { foo: 'bar' }] }; assert.deepEqual(clone(test), { a: [1, 2, 3], b: [1, null, 'test', { foo: 'bar' }] }); }); it('should stop mutation from occuring on the original object', () => { const test = { a: 1, b: 2, c: { foo: 'bar' } }; const cloned = clone(test); test.a = 5; test.c.foo = 'barbaz'; assert.deepEqual(cloned, { a: 1, b: 2, c: { foo: 'bar' } }); }); it('should clone to a maximum depth of 5 by default', () => { const test = { a: { b: { c: { d: { e: { f: 'foo' } } } } } }; const cloned = clone(test); test.a.b.c.d.e.f = 'bar'; // The values at a greater depth then 5 should not be cloned assert.equal(cloned.a.b.c.d.e.f, 'bar'); }); it('should allow an optional maximum depth to be set', () => { const test = { a: { b: { c: 'foo' } } }; const cloned = clone(test, 2); test.a.b.c = 'bar'; // The values at a greater depth then 2 should not be cloned assert.equal(cloned.a.b.c, 'bar'); }); it('should not throw when cloning a recursive reference', () => { const test = { a: { b: { c: {} } } }; test.a.b.c = test; expect(() => clone(test)).to.not.throw(); }); }); xterm.js-3.8.1/src/utils/Clone.ts000066400000000000000000000012511341514612000166300ustar00rootroot00000000000000/** * Copyright (c) 2016 The xterm.js authors. All rights reserved. * @license MIT */ /* * A simple utility for cloning values */ export const clone = (val: T, depth: number = 5): T => { if (typeof val !== 'object') { return val; } // cloning null always returns null if (val === null) { return null; } // If we're cloning an array, use an array as the base, otherwise use an object const clonedObject: any = Array.isArray(val) ? [] : {}; for (const key in val) { // Recursively clone eack item unless we're at the maximum depth clonedObject[key] = depth <= 1 ? val[key] : clone(val[key], depth - 1); } return clonedObject as T; }; xterm.js-3.8.1/src/utils/MouseHelper.test.ts000066400000000000000000000054221341514612000210020ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import jsdom = require('jsdom'); import { assert } from 'chai'; import { MouseHelper } from './MouseHelper'; import { MockCharMeasure, MockRenderer } from './TestUtils.test'; const CHAR_WIDTH = 10; const CHAR_HEIGHT = 20; describe('MouseHelper.getCoords', () => { let dom: jsdom.JSDOM; let window: Window; let document: Document; let mouseHelper: MouseHelper; let charMeasure: MockCharMeasure; beforeEach(() => { dom = new jsdom.JSDOM(''); window = dom.window; document = window.document; charMeasure = new MockCharMeasure(); charMeasure.width = CHAR_WIDTH; charMeasure.height = CHAR_HEIGHT; const renderer = new MockRenderer(); renderer.dimensions = { actualCellWidth: CHAR_WIDTH, actualCellHeight: CHAR_HEIGHT }; mouseHelper = new MouseHelper(renderer); }); describe('when charMeasure is not initialized', () => { it('should return null', () => { charMeasure = new MockCharMeasure(); assert.equal(mouseHelper.getCoords({ pageX: 0, pageY: 0 }, document.createElement('div'), charMeasure, 1, 10, 10), null); }); }); describe('when pageX/pageY are not supported', () => { it('should return null', () => { assert.equal(mouseHelper.getCoords({ pageX: undefined, pageY: undefined }, document.createElement('div'), charMeasure, 1, 10, 10), null); }); }); it('should return the cell that was clicked', () => { let coords: [number, number]; coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH / 2, pageY: CHAR_HEIGHT / 2 }, document.createElement('div'), charMeasure, 1, 10, 10); assert.deepEqual(coords, [1, 1]); coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 1, 10, 10); assert.deepEqual(coords, [1, 1]); coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH, pageY: CHAR_HEIGHT + 1 }, document.createElement('div'), charMeasure, 1, 10, 10); assert.deepEqual(coords, [1, 2]); coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH + 1, pageY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 1, 10, 10); assert.deepEqual(coords, [2, 1]); }); it('should ensure the coordinates are returned within the terminal bounds', () => { let coords: [number, number]; coords = mouseHelper.getCoords({ pageX: -1, pageY: -1 }, document.createElement('div'), charMeasure, 1, 10, 10); assert.deepEqual(coords, [1, 1]); // Event are double the cols/rows coords = mouseHelper.getCoords({ pageX: CHAR_WIDTH * 20, pageY: CHAR_HEIGHT * 20 }, document.createElement('div'), charMeasure, 1, 10, 10); assert.deepEqual(coords, [10, 10], 'coordinates should never come back as larger than the terminal'); }); }); xterm.js-3.8.1/src/utils/MouseHelper.ts000066400000000000000000000074601341514612000200300ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ICharMeasure } from '../Types'; import { IRenderer } from '../renderer/Types'; export class MouseHelper { constructor(private _renderer: IRenderer) {} public static getCoordsRelativeToElement(event: {pageX: number, pageY: number}, element: HTMLElement): [number, number] { // Ignore browsers that don't support MouseEvent.pageX if (event.pageX === null || event.pageX === undefined) { return null; } const originalElement = element; let x = event.pageX; let y = event.pageY; // Converts the coordinates from being relative to the document to being // relative to the terminal. while (element) { x -= element.offsetLeft; y -= element.offsetTop; element = element.offsetParent; } element = originalElement; while (element && element !== element.ownerDocument.body) { x += element.scrollLeft; y += element.scrollTop; element = element.parentElement; } return [x, y]; } /** * Gets coordinates within the terminal for a particular mouse event. The result * is returned as an array in the form [x, y] instead of an object as it's a * little faster and this function is used in some low level code. * @param event The mouse event. * @param element The terminal's container element. * @param charMeasure The char measure object used to determine character sizes. * @param colCount The number of columns in the terminal. * @param rowCount The number of rows n the terminal. * @param isSelection Whether the request is for the selection or not. This will * apply an offset to the x value such that the left half of the cell will * select that cell and the right half will select the next cell. */ public getCoords(event: {pageX: number, pageY: number}, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number, isSelection?: boolean): [number, number] { // Coordinates cannot be measured if charMeasure has not been initialized if (!charMeasure.width || !charMeasure.height) { return null; } const coords = MouseHelper.getCoordsRelativeToElement(event, element); if (!coords) { return null; } coords[0] = Math.ceil((coords[0] + (isSelection ? this._renderer.dimensions.actualCellWidth / 2 : 0)) / this._renderer.dimensions.actualCellWidth); coords[1] = Math.ceil(coords[1] / this._renderer.dimensions.actualCellHeight); // Ensure coordinates are within the terminal viewport. Note that selections // need an addition point of precision to cover the end point (as characters // cover half of one char and half of the next). coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[1] = Math.min(Math.max(coords[1], 1), rowCount); return coords; } /** * Gets coordinates within the terminal for a particular mouse event, wrapping * them to the bounds of the terminal and adding 32 to both the x and y values * as expected by xterm. * @param event The mouse event. * @param element The terminal's container element. * @param charMeasure The char measure object used to determine character sizes. * @param colCount The number of columns in the terminal. * @param rowCount The number of rows in the terminal. */ public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, lineHeight: number, colCount: number, rowCount: number): { x: number, y: number } { const coords = this.getCoords(event, element, charMeasure, lineHeight, colCount, rowCount); let x = coords[0]; let y = coords[1]; // xterm sends raw bytes and starts at 32 (SP) for each. x += 32; y += 32; return { x, y }; } } xterm.js-3.8.1/src/utils/TestUtils.test.ts000066400000000000000000000300771341514612000205160ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IColorSet, IRenderer, IRenderDimensions, IColorManager } from '../renderer/Types'; import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminal, IBuffer, IBufferSet, IBrowser, ICharMeasure, ISelectionManager, ITerminalOptions, ILinkifier, IMouseHelper, ILinkMatcherOptions, CharacterJoinerHandler, IBufferLine, IBufferStringIterator } from '../Types'; import { ICircularList, XtermListener } from '../common/Types'; import { Buffer } from '../Buffer'; import * as Browser from '../shared/utils/Browser'; import { ITheme, IDisposable, IMarker } from 'xterm'; import { Terminal } from '../Terminal'; export class TestTerminal extends Terminal { writeSync(data: string): void { this.writeBuffer.push(data); this._innerWrite(); } } export class MockTerminal implements ITerminal { markers: IMarker[]; addMarker(cursorYOffset: number): IMarker { throw new Error('Method not implemented.'); } selectLines(start: number, end: number): void { throw new Error('Method not implemented.'); } scrollToLine(line: number): void { throw new Error('Method not implemented.'); } static string: any; getOption(key: any): any { throw new Error('Method not implemented.'); } setOption(key: any, value: any): void { throw new Error('Method not implemented.'); } blur(): void { throw new Error('Method not implemented.'); } focus(): void { throw new Error('Method not implemented.'); } resize(columns: number, rows: number): void { throw new Error('Method not implemented.'); } writeln(data: string): void { throw new Error('Method not implemented.'); } open(parent: HTMLElement): void { throw new Error('Method not implemented.'); } attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void { throw new Error('Method not implemented.'); } registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => boolean | void, options?: ILinkMatcherOptions): number { throw new Error('Method not implemented.'); } deregisterLinkMatcher(matcherId: number): void { throw new Error('Method not implemented.'); } hasSelection(): boolean { throw new Error('Method not implemented.'); } getSelection(): string { throw new Error('Method not implemented.'); } clearSelection(): void { throw new Error('Method not implemented.'); } selectAll(): void { throw new Error('Method not implemented.'); } dispose(): void { throw new Error('Method not implemented.'); } destroy(): void { throw new Error('Method not implemented.'); } scrollPages(pageCount: number): void { throw new Error('Method not implemented.'); } scrollToTop(): void { throw new Error('Method not implemented.'); } scrollToBottom(): void { throw new Error('Method not implemented.'); } clear(): void { throw new Error('Method not implemented.'); } write(data: string): void { throw new Error('Method not implemented.'); } bracketedPasteMode: boolean; mouseHelper: IMouseHelper; renderer: IRenderer; linkifier: ILinkifier; isFocused: boolean; options: ITerminalOptions = {}; element: HTMLElement; screenElement: HTMLElement; rowContainer: HTMLElement; selectionContainer: HTMLElement; selectionManager: ISelectionManager; charMeasure: ICharMeasure; textarea: HTMLTextAreaElement; rows: number; cols: number; browser: IBrowser = Browser; writeBuffer: string[]; children: HTMLElement[]; cursorHidden: boolean; cursorState: number; scrollback: number; buffers: IBufferSet; buffer: IBuffer; viewport: IViewport; applicationCursor: boolean; handler(data: string): void { throw new Error('Method not implemented.'); } on(event: string, callback: () => void): void { throw new Error('Method not implemented.'); } off(type: string, listener: XtermListener): void { throw new Error('Method not implemented.'); } addDisposableListener(type: string, handler: XtermListener): IDisposable { throw new Error('Method not implemented.'); } scrollLines(disp: number, suppressScrollEvent: boolean): void { throw new Error('Method not implemented.'); } scrollToRow(absoluteRow: number): number { throw new Error('Method not implemented.'); } cancel(ev: Event, force?: boolean): void { throw new Error('Method not implemented.'); } log(text: string): void { throw new Error('Method not implemented.'); } emit(event: string, data: any): void { throw new Error('Method not implemented.'); } reset(): void { throw new Error('Method not implemented.'); } showCursor(): void { throw new Error('Method not implemented.'); } refresh(start: number, end: number): void { throw new Error('Method not implemented.'); } registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; } deregisterCharacterJoiner(joinerId: number): void { } } export class MockCharMeasure implements ICharMeasure { width: number; height: number; measure(options: ITerminalOptions): void { throw new Error('Method not implemented.'); } } export class MockInputHandlingTerminal implements IInputHandlingTerminal { element: HTMLElement; options: ITerminalOptions = {}; cols: number; rows: number; charset: { [key: string]: string; }; gcharset: number; glevel: number; charsets: { [key: string]: string; }[]; applicationKeypad: boolean; applicationCursor: boolean; originMode: boolean; insertMode: boolean; wraparoundMode: boolean; bracketedPasteMode: boolean; curAttr: number; savedCurAttr: number; savedCols: number; x10Mouse: boolean; vt200Mouse: boolean; normalMouse: boolean; mouseEvents: boolean; sendFocus: boolean; utfMouse: boolean; sgrMouse: boolean; urxvtMouse: boolean; cursorHidden: boolean; buffers: IBufferSet; buffer: IBuffer = new MockBuffer(); viewport: IViewport; selectionManager: ISelectionManager; focus(): void { throw new Error('Method not implemented.'); } convertEol: boolean; bell(): void { throw new Error('Method not implemented.'); } updateRange(y: number): void { throw new Error('Method not implemented.'); } scroll(isWrapped?: boolean): void { throw new Error('Method not implemented.'); } nextStop(x?: number): number { throw new Error('Method not implemented.'); } setgLevel(g: number): void { throw new Error('Method not implemented.'); } eraseAttr(): number { throw new Error('Method not implemented.'); } eraseRight(x: number, y: number): void { throw new Error('Method not implemented.'); } eraseLine(y: number): void { throw new Error('Method not implemented.'); } eraseLeft(x: number, y: number): void { throw new Error('Method not implemented.'); } prevStop(x?: number): number { throw new Error('Method not implemented.'); } is(term: string): boolean { throw new Error('Method not implemented.'); } setgCharset(g: number, charset: { [key: string]: string; }): void { throw new Error('Method not implemented.'); } resize(x: number, y: number): void { throw new Error('Method not implemented.'); } log(text: string, data?: any): void { throw new Error('Method not implemented.'); } reset(): void { throw new Error('Method not implemented.'); } showCursor(): void { throw new Error('Method not implemented.'); } refresh(start: number, end: number): void { throw new Error('Method not implemented.'); } matchColor(r1: number, g1: number, b1: number): number { throw new Error('Method not implemented.'); } error(text: string, data?: any): void { throw new Error('Method not implemented.'); } setOption(key: string, value: any): void { (this.options)[key] = value; } on(type: string, listener: XtermListener): void { throw new Error('Method not implemented.'); } off(type: string, listener: XtermListener): void { throw new Error('Method not implemented.'); } emit(type: string, data?: any): void { throw new Error('Method not implemented.'); } addDisposableListener(type: string, handler: XtermListener): IDisposable { throw new Error('Method not implemented.'); } tabSet(): void { throw new Error('Method not implemented.'); } handler(data: string): void { throw new Error('Method not implemented.'); } handleTitle(title: string): void { throw new Error('Method not implemented.'); } index(): void { throw new Error('Method not implemented.'); } reverseIndex(): void { throw new Error('Method not implemented.'); } } export class MockBuffer implements IBuffer { isCursorInViewport: boolean; lines: ICircularList; ydisp: number; ybase: number; hasScrollback: boolean; y: number; x: number; tabs: any; scrollBottom: number; scrollTop: number; savedY: number; savedX: number; translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string { return Buffer.prototype.translateBufferLineToString.apply(this, arguments); } getWrappedRangeForLine(y: number): { first: number; last: number; } { return Buffer.prototype.getWrappedRangeForLine.apply(this, arguments); } nextStop(x?: number): number { throw new Error('Method not implemented.'); } prevStop(x?: number): number { throw new Error('Method not implemented.'); } setLines(lines: ICircularList): void { this.lines = lines; } stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] { return Buffer.prototype.stringIndexToBufferIndex.apply(this, arguments); } iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator { return Buffer.prototype.iterator.apply(this, arguments); } } export class MockRenderer implements IRenderer { dispose(): void { throw new Error('Method not implemented.'); } colorManager: IColorManager; on(type: string, listener: XtermListener): void { throw new Error('Method not implemented.'); } off(type: string, listener: XtermListener): void { throw new Error('Method not implemented.'); } emit(type: string, data?: any): void { throw new Error('Method not implemented.'); } addDisposableListener(type: string, handler: XtermListener): IDisposable { throw new Error('Method not implemented.'); } dimensions: IRenderDimensions; setTheme(theme: ITheme): IColorSet { return {}; } onResize(cols: number, rows: number): void {} onCharSizeChanged(): void {} onBlur(): void {} onFocus(): void {} onSelectionChanged(start: [number, number], end: [number, number]): void {} onCursorMove(): void {} onOptionsChanged(): void {} onWindowResize(devicePixelRatio: number): void {} clear(): void {} refreshRows(start: number, end: number): void {} registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; } deregisterCharacterJoiner(): boolean { return true; } } export class MockViewport implements IViewport { dispose(): void { throw new Error('Method not implemented.'); } scrollBarWidth: number = 0; onThemeChanged(colors: IColorSet): void { throw new Error('Method not implemented.'); } onWheel(ev: WheelEvent): void { throw new Error('Method not implemented.'); } onTouchStart(ev: TouchEvent): void { throw new Error('Method not implemented.'); } onTouchMove(ev: TouchEvent): void { throw new Error('Method not implemented.'); } syncScrollArea(): void { } getLinesScrolled(ev: WheelEvent): number { throw new Error('Method not implemented.'); } } export class MockCompositionHelper implements ICompositionHelper { compositionstart(): void { throw new Error('Method not implemented.'); } compositionupdate(ev: CompositionEvent): void { throw new Error('Method not implemented.'); } compositionend(): void { throw new Error('Method not implemented.'); } updateCompositionElements(dontRecurse?: boolean): void { throw new Error('Method not implemented.'); } keydown(ev: KeyboardEvent): boolean { return true; } } xterm.js-3.8.1/src/xterm.css000066400000000000000000000077121341514612000157410ustar00rootroot00000000000000/** * Copyright (c) 2014 The xterm.js authors. All rights reserved. * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) * https://github.com/chjj/term.js * @license MIT * * 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. */ /** * Default styles for xterm.js */ .xterm { font-family: courier-new, courier, monospace; font-feature-settings: "liga" 0; position: relative; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .xterm.focus, .xterm:focus { outline: none; } .xterm .xterm-helpers { position: absolute; top: 0; /** * The z-index of the helpers must be higher than the canvases in order for * IMEs to appear on top. */ z-index: 10; } .xterm .xterm-helper-textarea { /* * HACK: to fix IE's blinking cursor * Move textarea out of the screen to the far left, so that the cursor is not visible. */ position: absolute; opacity: 0; left: -9999em; top: 0; width: 0; height: 0; z-index: -10; /** Prevent wrapping so the IME appears against the textarea at the correct position */ white-space: nowrap; overflow: hidden; resize: none; } .xterm .composition-view { /* TODO: Composition position got messed up somewhere */ background: #000; color: #FFF; display: none; position: absolute; white-space: nowrap; z-index: 1; } .xterm .composition-view.active { display: block; } .xterm .xterm-viewport { /* On OS X this is required in order for the scroll bar to appear fully opaque */ background-color: #000; overflow-y: scroll; cursor: default; position: absolute; right: 0; left: 0; top: 0; bottom: 0; } .xterm .xterm-screen { position: relative; } .xterm .xterm-screen canvas { position: absolute; left: 0; top: 0; } .xterm .xterm-scroll-area { visibility: hidden; } .xterm-char-measure-element { display: inline-block; visibility: hidden; position: absolute; top: 0; left: -9999em; line-height: normal; } .xterm { cursor: text; } .xterm.enable-mouse-events { /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */ cursor: default; } .xterm.xterm-cursor-pointer { cursor: pointer; } .xterm.xterm-cursor-crosshair { /* Column selection mode */ cursor: crosshair; } .xterm .xterm-accessibility, .xterm .xterm-message { position: absolute; left: 0; top: 0; bottom: 0; right: 0; z-index: 100; color: transparent; } .xterm .live-region { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; } xterm.js-3.8.1/src/xterm.ts000066400000000000000000000003271341514612000155720ustar00rootroot00000000000000/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT * * This file is the entry point for browserify. */ import { Terminal } from './public/Terminal'; module.exports = Terminal; xterm.js-3.8.1/tsconfig.json000066400000000000000000000007201341514612000160000ustar00rootroot00000000000000{ "compilerOptions": { "module": "commonjs", "target": "es5", "lib": [ "dom", "es5", "es6", "scripthost", "es2015.promise" ], "rootDir": "src", "outDir": "lib", "sourceMap": true, "removeComments": true, "preserveWatchOutput": true, "noUnusedLocals": true, "noImplicitAny": true }, "include": [ "src/**/*", "typings/xterm.d.ts" ], "exclude": [ "src/addons/**/*" ] } xterm.js-3.8.1/tslint.json000066400000000000000000000053141341514612000155050ustar00rootroot00000000000000{ "rulesDirectory": [ "tslint-consistent-codestyle" ], "rules": { "array-type": [ true, "array" ], "class-name": true, "comment-format": [ true, "check-space" ], "curly": [ true, "ignore-same-line" ], "indent": [ true, "spaces" ], "interface-name": [ true, "always-prefix" ], "interface-over-type-literal": true, "typedef": [ true, "call-signature", "parameter" ], "eofline": true, "new-parens": true, "no-duplicate-imports": true, "no-eval": true, "no-internal-module": true, "no-trailing-whitespace": true, "one-variable-per-declaration": true, "no-unsafe-finally": true, "no-var-keyword": true, "prefer-const": true, "quotemark": [ true, "single" ], "semicolon": [ true, "always" ], "trailing-comma": [ true, { "multiline": { "objects": "never", "arrays": "never", "functions": "never", "typeLiterals": "ignore" }, "esSpecCompliant": true } ], "triple-equals": true, "typedef-whitespace": [ true, { "call-signature": "nospace", "index-signature": "nospace", "parameter": "nospace", "property-declaration": "nospace", "variable-declaration": "nospace" } ], "variable-name": [ true, "ban-keywords", "check-format", "allow-leading-underscore" ], "whitespace": [ true, "check-branch", "check-decl", "check-module", "check-operator", "check-rest-spread", "check-separator", "check-type", "check-type-operator", "check-preblock" ], "naming-convention": [ true, {"type": "default", "format": "camelCase", "leadingUnderscore": "forbid"}, {"type": "type", "format": "PascalCase"}, {"type": "class", "format": "PascalCase"}, {"type": "property", "modifiers": ["const"], "format": "UPPER_CASE"}, {"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "allow"}, // TODO: Change allow to require when there aren't many PRs out // {"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]}, {"type": "interface", "prefix": "I"} ], "no-else-after-return": { "options": "allow-else-if" }, "prefer-const-enum": [ true ] } } xterm.js-3.8.1/typings/000077500000000000000000000000001341514612000147675ustar00rootroot00000000000000xterm.js-3.8.1/typings/xterm.d.ts000066400000000000000000000571011341514612000167240ustar00rootroot00000000000000/** * @license MIT * * This contains the type declarations for the xterm.js library. Note that * some interfaces differ between this file and the actual implementation in * src/, that's because this file declares the *public* API which is intended * to be stable and consumed by external programs. */ declare module 'xterm' { /** * A string representing text font weight. */ export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; /** * A string representing a renderer type. */ export type RendererType = 'dom' | 'canvas'; /** * An object containing start up options for the terminal. */ export interface ITerminalOptions { /** * Whether background should support non-opaque color. It must be set before * executing open() method and can't be changed later without excuting it again. * Warning: Enabling this option can reduce performances somewhat. */ allowTransparency?: boolean; /** * A data uri of the sound to use for the bell (needs bellStyle = 'sound'). */ bellSound?: string; /** * The type of the bell notification the terminal will use. */ bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; /** * When enabled the cursor will be set to the beginning of the next line * with every new line. This equivalent to sending '\r\n' for each '\n'. * Normally the termios settings of the underlying PTY deals with the * translation of '\n' to '\r\n' and this setting should not be used. If you * deal with data from a non-PTY related source, this settings might be * useful. */ convertEol?: boolean; /** * The number of columns in the terminal. */ cols?: number; /** * Whether the cursor blinks. */ cursorBlink?: boolean; /** * The style of the cursor. */ cursorStyle?: 'block' | 'underline' | 'bar'; /** * Whether input should be disabled. */ disableStdin?: boolean; /** * Whether to draw bold text in bright colors. The default is true. */ drawBoldTextInBrightColors?: boolean; /** * Whether to enable the rendering of bold text. * * @deprecated Use fontWeight and fontWeightBold instead. */ enableBold?: boolean; /** * What character atlas implementation to use. The character atlas caches drawn characters, * speeding up rendering significantly. However, it can introduce some minor rendering * artifacts. * * - 'none': Don't use an atlas. * - 'static': Generate an atlas when the terminal starts or is reconfigured. This atlas will * only contain ASCII characters in 16 colors. * - 'dynamic': Generate an atlas using a LRU cache as characters are requested. Limited to * ASCII characters (for now), but supports 256 colors. For characters covered by the static * cache, it's slightly slower in comparison, since there's more overhead involved in * managing the cache. * * Currently defaults to 'static'. This option may be removed in the future. If it is, passed * parameters will be ignored. */ experimentalCharAtlas?: 'none' | 'static' | 'dynamic'; /** * The font size used to render text. */ fontSize?: number; /** * The font family used to render text. */ fontFamily?: string; /** * The font weight used to render non-bold text. */ fontWeight?: FontWeight; /** * The font weight used to render bold text. */ fontWeightBold?: FontWeight; /** * The spacing in whole pixels between characters.. */ letterSpacing?: number; /** * The line height used to render text. */ lineHeight?: number; /** * Whether to treat option as the meta key. */ macOptionIsMeta?: boolean; /** * Whether holding a modifier key will force normal selection behavior, * regardless of whether the terminal is in mouse events mode. This will * also prevent mouse events from being emitted by the terminal. For example, * this allows you to use xterm.js' regular selection inside tmux with * mouse mode enabled. */ macOptionClickForcesSelection?: boolean; /** * The type of renderer to use, this allows using the fallback DOM renderer * when canvas is too slow for the environment. The following features do * not work when the DOM renderer is used: * * - Line height * - Letter spacing * - Cursor blink */ rendererType?: RendererType; /** * Whether to select the word under the cursor on right click, this is * standard behavior in a lot of macOS applications. */ rightClickSelectsWord?: boolean; /** * The number of rows in the terminal. */ rows?: number; /** * Whether screen reader support is enabled. When on this will expose * supporting elements in the DOM to support NVDA on Windows and VoiceOver * on macOS. */ screenReaderMode?: boolean; /** * The amount of scrollback in the terminal. Scrollback is the amount of rows * that are retained when lines are scrolled beyond the initial viewport. */ scrollback?: number; /** * The size of tab stops in the terminal. */ tabStopWidth?: number; /** * The color theme of the terminal. */ theme?: ITheme; } /** * Contains colors to theme the terminal with. */ export interface ITheme { /** The default foreground color */ foreground?: string, /** The default background color */ background?: string, /** The cursor color */ cursor?: string, /** The accent color of the cursor (used as the foreground color for a block cursor) */ cursorAccent?: string, /** The selection color (can be transparent) */ selection?: string, /** ANSI black (eg. `\x1b[30m`) */ black?: string, /** ANSI red (eg. `\x1b[31m`) */ red?: string, /** ANSI green (eg. `\x1b[32m`) */ green?: string, /** ANSI yellow (eg. `\x1b[33m`) */ yellow?: string, /** ANSI blue (eg. `\x1b[34m`) */ blue?: string, /** ANSI magenta (eg. `\x1b[35m`) */ magenta?: string, /** ANSI cyan (eg. `\x1b[36m`) */ cyan?: string, /** ANSI white (eg. `\x1b[37m`) */ white?: string, /** ANSI bright black (eg. `\x1b[1;30m`) */ brightBlack?: string, /** ANSI bright red (eg. `\x1b[1;31m`) */ brightRed?: string, /** ANSI bright green (eg. `\x1b[1;32m`) */ brightGreen?: string, /** ANSI bright yellow (eg. `\x1b[1;33m`) */ brightYellow?: string, /** ANSI bright blue (eg. `\x1b[1;34m`) */ brightBlue?: string, /** ANSI bright magenta (eg. `\x1b[1;35m`) */ brightMagenta?: string, /** ANSI bright cyan (eg. `\x1b[1;36m`) */ brightCyan?: string, /** ANSI bright white (eg. `\x1b[1;37m`) */ brightWhite?: string } /** * An object containing options for a link matcher. */ export interface ILinkMatcherOptions { /** * The index of the link from the regex.match(text) call. This defaults to 0 * (for regular expressions without capture groups). */ matchIndex?: number; /** * A callback that validates whether to create an individual link, pass * whether the link is valid to the callback. */ validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; /** * A callback that fires when the mouse hovers over a link for a moment. */ tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; /** * A callback that fires when the mouse leaves a link. Note that this can * happen even when tooltipCallback hasn't fired for the link yet. */ leaveCallback?: (event: MouseEvent, uri: string) => boolean | void; /** * The priority of the link matcher, this defines the order in which the link * matcher is evaluated relative to others, from highest to lowest. The * default value is 0. */ priority?: number; /** * A callback that fires when the mousedown and click events occur that * determines whether a link will be activated upon click. This enables * only activating a link when a certain modifier is held down, if not the * mouse event will continue propagation (eg. double click to select word). */ willLinkActivate?: (event: MouseEvent, uri: string) => boolean; } export interface IEventEmitter { on(type: string, listener: (...args: any[]) => void): void; off(type: string, listener: (...args: any[]) => void): void; emit(type: string, data?: any): void; addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; } /** * An object that can be disposed via a dispose function. */ export interface IDisposable { dispose(): void; } export interface IMarker extends IDisposable { readonly id: number; readonly isDisposed: boolean; readonly line: number; } export interface ILocalizableStrings { blankLine: string; promptLabel: string; tooMuchOutput: string; } /** * The class that represents an xterm.js terminal. */ export class Terminal implements IEventEmitter, IDisposable { /** * The element containing the terminal. */ element: HTMLElement; /** * The textarea that accepts input for the terminal. */ textarea: HTMLTextAreaElement; /** * The number of rows in the terminal's viewport. */ rows: number; /** * The number of columns in the terminal's viewport. */ cols: number; /** * (EXPERIMENTAL) Get all markers registered against the buffer. If the alt * buffer is active this will always return []. */ markers: IMarker[]; /** * Natural language strings that can be localized. */ static strings: ILocalizableStrings; /** * Creates a new `Terminal` object. * * @param options An object containing a set of options. */ constructor(options?: ITerminalOptions); /** * Unfocus the terminal. */ blur(): void; /** * Focus the terminal. */ focus(): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'blur' | 'focus' | 'linefeed' | 'selection', listener: () => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'data', listener: (...args: any[]) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'key', listener: (key?: string, event?: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'keypress' | 'keydown', listener: (event?: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'refresh', listener: (data?: {start: number, end: number}) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'resize', listener: (data?: {cols: number, rows: number}) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'scroll', listener: (ydisp?: number) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: 'title', listener: (title?: string) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. */ on(type: string, listener: (...args: any[]) => void): void; /** * Deregisters an event listener. * @param type The type of the event. * @param listener The listener. */ off(type: 'blur' | 'focus' | 'linefeed' | 'selection' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; /** * Emits an event on the terminal. * @param type The type of event * @param data data associated with the event. * @deprecated This is being removed from the API with no replacement, see * issue #1505. */ emit(type: string, data?: any): void; /** * Adds an event listener to the Terminal, returning an IDisposable that can * be used to conveniently remove the event listener. * @param type The type of event. * @param handler The event handler. */ addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable; /** * Resizes the terminal. * @param x The number of columns to resize to. * @param y The number of rows to resize to. */ resize(columns: number, rows: number): void; /** * Writes text to the terminal, followed by a break line character (\n). * @param data The text to write to the terminal. */ writeln(data: string): void; /** * Opens the terminal within an element. * @param parent The element to create the terminal within. This element * must be visible (have dimensions) when `open` is called as several DOM- * based measurements need to be performed when this function is called. */ open(parent: HTMLElement): void; /** * Attaches a custom key event handler which is run before keys are * processed, giving consumers of xterm.js ultimate control as to what keys * should be processed by the terminal and what keys should not. * @param customKeyEventHandler The custom KeyboardEvent handler to attach. * This is a function that takes a KeyboardEvent, allowing consumers to stop * propogation and/or prevent the default action. The function returns * whether the event should be processed by xterm.js. */ attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; /** * (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to * be matched and handled. * @param regex The regular expression to search for, specifically this * searches the textContent of the rows. You will want to use \s to match a * space ' ' character for example. * @param handler The callback when the link is called. * @param options Options for the link matcher. * @return The ID of the new matcher, this can be used to deregister. */ registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number; /** * (EXPERIMENTAL) Deregisters a link matcher if it has been registered. * @param matcherId The link matcher's ID (returned after register) */ deregisterLinkMatcher(matcherId: number): void; /** * (EXPERIMENTAL) Registers a character joiner, allowing custom sequences of * characters to be rendered as a single unit. This is useful in particular * for rendering ligatures and graphemes, among other things. * * Each registered character joiner is called with a string of text * representing a portion of a line in the terminal that can be rendered as * a single unit. The joiner must return a sorted array, where each entry is * itself an array of length two, containing the start (inclusive) and end * (exclusive) index of a substring of the input that should be rendered as * a single unit. When multiple joiners are provided, the results of each * are collected. If there are any overlapping substrings between them, they * are combined into one larger unit that is drawn together. * * All character joiners that are registered get called every time a line is * rendered in the terminal, so it is essential for the handler function to * run as quickly as possible to avoid slowdowns when rendering. Similarly, * joiners should strive to return the smallest possible substrings to * render together, since they aren't drawn as optimally as individual * characters. * * NOTE: character joiners are only used by the canvas renderer. * * @param handler The function that determines character joins. It is called * with a string of text that is eligible for joining and returns an array * where each entry is an array containing the start (inclusive) and end * (exclusive) indexes of ranges that should be rendered as a single unit. * @return The ID of the new joiner, this can be used to deregister */ registerCharacterJoiner(handler: (text: string) => [number, number][]): number; /** * (EXPERIMENTAL) Deregisters the character joiner if one was registered. * NOTE: character joiners are only used by the canvas renderer. * @param joinerId The character joiner's ID (returned after register) */ deregisterCharacterJoiner(joinerId: number): void; /** * (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the * alt buffer is active, undefined is returned. * @param cursorYOffset The y position offset of the marker from the cursor. */ addMarker(cursorYOffset: number): IMarker; /** * Gets whether the terminal has an active selection. */ hasSelection(): boolean; /** * Gets the terminal's current selection, this is useful for implementing * copy behavior outside of xterm.js. */ getSelection(): string; /** * Clears the current terminal selection. */ clearSelection(): void; /** * Selects all text within the terminal. */ selectAll(): void; /** * Selects text in the buffer between 2 lines. * @param start The 0-based line index to select from (inclusive). * @param end The 0-based line index to select to (inclusive). */ selectLines(start: number, end: number): void; /* * Disposes of the terminal, detaching it from the DOM and removing any * active listeners. */ dispose(): void; /** * Destroys the terminal and detaches it from the DOM. * * @deprecated Use dispose() instead. */ destroy(): void; /** * Scroll the display of the terminal * @param amount The number of lines to scroll down (negative scroll up). */ scrollLines(amount: number): void; /** * Scroll the display of the terminal by a number of pages. * @param pageCount The number of pages to scroll (negative scrolls up). */ scrollPages(pageCount: number): void; /** * Scrolls the display of the terminal to the top. */ scrollToTop(): void; /** * Scrolls the display of the terminal to the bottom. */ scrollToBottom(): void; /** * Scrolls to a line within the buffer. * @param line The 0-based line index to scroll to. */ scrollToLine(line: number): void; /** * Clear the entire buffer, making the prompt line the new first line. */ clear(): void; /** * Writes text to the terminal. * @param data The text to write to the terminal. */ write(data: string): void; /** * Retrieves an option's value from the terminal. * @param key The option key. */ getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold'| 'rendererType' | 'termName'): string; /** * Retrieves an option's value from the terminal. * @param key The option key. */ getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean; /** * Retrieves an option's value from the terminal. * @param key The option key. */ getOption(key: 'colors'): string[]; /** * Retrieves an option's value from the terminal. * @param key The option key. */ getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number; /** * Retrieves an option's value from the terminal. * @param key The option key. */ getOption(key: 'handler'): (data: string) => void; /** * Retrieves an option's value from the terminal. * @param key The option key. */ getOption(key: string): any; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'fontFamily' | 'termName' | 'bellSound', value: string): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'fontWeight' | 'fontWeightBold', value: null | 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'bellStyle', value: null | 'none' | 'visual' | 'sound' | 'both'): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'cursorStyle', value: null | 'block' | 'underline' | 'bar'): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'enableBold' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'colors', value: string[]): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'handler', value: (data: string) => void): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'theme', value: ITheme): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: 'cols' | 'rows', value: number): void; /** * Sets an option on the terminal. * @param key The option key. * @param value The option value. */ setOption(key: string, value: any): void; /** * Tells the renderer to refresh terminal content between two rows * (inclusive) at the next opportunity. * @param start The row to start from (between 0 and this.rows - 1). * @param end The row to end at (between start and this.rows - 1). */ refresh(start: number, end: number): void; /** * Perform a full reset (RIS, aka '\x1bc'). */ reset(): void /** * Applies an addon to the Terminal prototype, making it available to all * newly created Terminals. * @param addon The addon to apply. */ static applyAddon(addon: any): void; } } xterm.js-3.8.1/yarn.lock000066400000000000000000006517341341514612000151350ustar00rootroot00000000000000# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 "@fimbul/bifrost@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.11.0.tgz#83cacc21464198b12e3cc1c2204ae6c6d7afd158" dependencies: "@fimbul/ymir" "^0.11.0" get-caller-file "^1.0.2" tslib "^1.8.1" tsutils "^2.24.0" "@fimbul/ymir@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@fimbul/ymir/-/ymir-0.11.0.tgz#892a01997f1f80c7e4e437cf5ca51c95994c136f" dependencies: inversify "^4.10.0" reflect-metadata "^0.1.12" tslib "^1.8.1" "@types/chai@^3.4.34": version "3.5.2" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-3.5.2.tgz#c11cd2817d3a401b7ba0f5a420f35c56139b1c1e" "@types/events@*": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" "@types/glob@^5.0.35": version "5.0.35" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" dependencies: "@types/events" "*" "@types/minimatch" "*" "@types/node" "*" "@types/jsdom@11.0.1": version "11.0.1" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-11.0.1.tgz#6de58d77a47eff95fc5b687ad57141eb9632e1af" dependencies: "@types/node" "*" "@types/tough-cookie" "*" parse5 "^3.0.2" "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" "@types/mocha@^2.2.33": version "2.2.48" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.48.tgz#3523b126a0b049482e1c3c11877460f76622ffab" "@types/node@*": version "10.5.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.2.tgz#f19f05314d5421fe37e74153254201a7bf00a707" "@types/node@6.0.108": version "6.0.108" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.108.tgz#852e8496bcfc5e74cae83a5eb3b30e5661e9b7b9" "@types/tapable@*": version "1.0.4" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" "@types/tough-cookie@*": version "2.3.3" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9" "@types/uglify-js@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.3.tgz#801a5ca1dc642861f47c46d14b700ed2d610840b" dependencies: source-map "^0.6.1" "@types/webpack@^4.4.11": version "4.4.11" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.11.tgz#0ca832870d55c4e92498c01d22d00d02b0f62ae9" dependencies: "@types/node" "*" "@types/tapable" "*" "@types/uglify-js" "*" source-map "^0.6.0" "@webassemblyjs/ast@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" dependencies: "@webassemblyjs/helper-module-context" "1.5.13" "@webassemblyjs/helper-wasm-bytecode" "1.5.13" "@webassemblyjs/wast-parser" "1.5.13" debug "^3.1.0" mamacro "^0.0.3" "@webassemblyjs/floating-point-hex-parser@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298" "@webassemblyjs/helper-api-error@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59" "@webassemblyjs/helper-buffer@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e" dependencies: debug "^3.1.0" "@webassemblyjs/helper-code-frame@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58" dependencies: "@webassemblyjs/wast-printer" "1.5.13" "@webassemblyjs/helper-fsm@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924" "@webassemblyjs/helper-module-context@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5" dependencies: debug "^3.1.0" mamacro "^0.0.3" "@webassemblyjs/helper-wasm-bytecode@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747" "@webassemblyjs/helper-wasm-section@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-buffer" "1.5.13" "@webassemblyjs/helper-wasm-bytecode" "1.5.13" "@webassemblyjs/wasm-gen" "1.5.13" debug "^3.1.0" "@webassemblyjs/ieee754@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364" dependencies: ieee754 "^1.1.11" "@webassemblyjs/leb128@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee" dependencies: long "4.0.0" "@webassemblyjs/utf8@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469" "@webassemblyjs/wasm-edit@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-buffer" "1.5.13" "@webassemblyjs/helper-wasm-bytecode" "1.5.13" "@webassemblyjs/helper-wasm-section" "1.5.13" "@webassemblyjs/wasm-gen" "1.5.13" "@webassemblyjs/wasm-opt" "1.5.13" "@webassemblyjs/wasm-parser" "1.5.13" "@webassemblyjs/wast-printer" "1.5.13" debug "^3.1.0" "@webassemblyjs/wasm-gen@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-wasm-bytecode" "1.5.13" "@webassemblyjs/ieee754" "1.5.13" "@webassemblyjs/leb128" "1.5.13" "@webassemblyjs/utf8" "1.5.13" "@webassemblyjs/wasm-opt@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-buffer" "1.5.13" "@webassemblyjs/wasm-gen" "1.5.13" "@webassemblyjs/wasm-parser" "1.5.13" debug "^3.1.0" "@webassemblyjs/wasm-parser@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-api-error" "1.5.13" "@webassemblyjs/helper-wasm-bytecode" "1.5.13" "@webassemblyjs/ieee754" "1.5.13" "@webassemblyjs/leb128" "1.5.13" "@webassemblyjs/utf8" "1.5.13" "@webassemblyjs/wast-parser@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/floating-point-hex-parser" "1.5.13" "@webassemblyjs/helper-api-error" "1.5.13" "@webassemblyjs/helper-code-frame" "1.5.13" "@webassemblyjs/helper-fsm" "1.5.13" long "^3.2.0" mamacro "^0.0.3" "@webassemblyjs/wast-printer@1.5.13": version "1.5.13" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/wast-parser" "1.5.13" long "^3.2.0" JSONStream@^1.0.3: version "1.3.3" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.3.tgz#27b4b8fbbfeab4e71bcf551e7f27be8d952239bf" dependencies: jsonparse "^1.2.0" through ">=2.2.7 <3" abab@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" accepts@~1.2.12: version "1.2.13" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.2.13.tgz#e5f1f3928c6d95fd96558c36ec3d9d0de4a6ecea" dependencies: mime-types "~2.1.6" negotiator "0.5.3" acorn-dynamic-import@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" dependencies: acorn "^5.0.0" acorn-globals@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538" dependencies: acorn "^5.0.0" acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" dependencies: acorn "^3.0.4" acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.5.2.tgz#2ca723df19d997b05824b69f6c7fb091fc42c322" dependencies: acorn "^5.7.1" acorn-dynamic-import "^3.0.0" xtend "^4.0.1" acorn@4.X: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" acorn@^3.0.4, acorn@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" acorn@^5.0.0, acorn@^5.2.1, acorn@^5.3.0, acorn@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" acorn@^5.6.2: version "5.7.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.2.tgz#91fa871883485d06708800318404e72bfb26dcc5" ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" ajv@^5.1.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: co "^4.6.0" fast-deep-equal "^1.0.0" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" ajv@^6.1.0: version "6.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" uri-js "^4.2.1" align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" dependencies: kind-of "^3.0.2" longest "^1.0.1" repeat-string "^1.5.2" amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" ansi-gray@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" dependencies: ansi-wrap "0.1.0" ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: color-convert "^1.9.0" ansi-wrap@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" dependencies: micromatch "^2.1.5" normalize-path "^2.0.0" anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" dependencies: micromatch "^3.1.4" normalize-path "^2.1.1" append-transform@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" dependencies: default-require-extensions "^1.0.0" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" dependencies: delegates "^1.0.0" readable-stream "^2.0.6" argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" dependencies: sprintf-js "~1.0.2" arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" dependencies: arr-flatten "^1.0.1" arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" arr-flatten@^1.0.1, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" array-each@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" array-filter@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" array-slice@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" array-uniq@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" dependencies: bn.js "^4.0.0" inherits "^2.0.1" minimalistic-assert "^1.0.0" asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" assert@^1.1.1, assert@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" dependencies: util "0.10.3" assertion-error@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" async@^1.4.0: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" async@^2.5.0: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" dependencies: lodash "^4.17.10" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" atob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" aws4@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: chalk "^1.1.3" esutils "^2.0.2" js-tokens "^3.0.2" babel-generator@^6.18.0: version "6.26.1" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" dependencies: babel-messages "^6.23.0" babel-runtime "^6.26.0" babel-types "^6.26.0" detect-indent "^4.0.0" jsesc "^1.3.0" lodash "^4.17.4" source-map "^0.5.7" trim-right "^1.0.1" babel-messages@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" dependencies: babel-runtime "^6.22.0" babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: core-js "^2.4.0" regenerator-runtime "^0.11.0" babel-template@^6.16.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" dependencies: babel-runtime "^6.26.0" babel-traverse "^6.26.0" babel-types "^6.26.0" babylon "^6.18.0" lodash "^4.17.4" babel-traverse@^6.18.0, babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" dependencies: babel-code-frame "^6.26.0" babel-messages "^6.23.0" babel-runtime "^6.26.0" babel-types "^6.26.0" babylon "^6.18.0" debug "^2.6.8" globals "^9.18.0" invariant "^2.2.2" lodash "^4.17.4" babel-types@^6.18.0, babel-types@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" dependencies: babel-runtime "^6.26.0" esutils "^2.0.2" lodash "^4.17.4" to-fast-properties "^1.0.3" babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" base64-js@^1.0.2: version "1.3.0" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" dependencies: cache-base "^1.0.1" class-utils "^0.3.5" component-emitter "^1.2.1" define-property "^1.0.0" isobject "^3.0.1" mixin-deep "^1.2.0" pascalcase "^0.1.1" bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" dependencies: tweetnacl "^0.14.3" beeper@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" binary-extensions@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" bl@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" dependencies: readable-stream "^2.3.5" safe-buffer "^5.1.1" bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" bluebird@~3.4.6: version "3.4.7" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" dependencies: hoek "2.x.x" brace-expansion@^1.0.0, brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" dependencies: balanced-match "^1.0.0" concat-map "0.0.1" braces@^1.8.2: version "1.8.5" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" dependencies: expand-range "^1.8.1" preserve "^0.2.0" repeat-element "^1.1.2" braces@^2.3.0, braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" extend-shallow "^2.0.1" fill-range "^4.0.0" isobject "^3.0.1" repeat-element "^1.1.2" snapdragon "^0.8.1" snapdragon-node "^2.0.1" split-string "^3.0.2" to-regex "^3.0.1" brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" browser-pack@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774" dependencies: JSONStream "^1.0.3" combine-source-map "~0.8.0" defined "^1.0.0" safe-buffer "^5.1.1" through2 "^2.0.0" umd "^3.0.0" browser-process-hrtime@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" browser-resolve@^1.11.0, browser-resolve@^1.7.0: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" dependencies: resolve "1.1.7" browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" dependencies: buffer-xor "^1.0.3" cipher-base "^1.0.0" create-hash "^1.1.0" evp_bytestokey "^1.0.3" inherits "^2.0.1" safe-buffer "^5.0.1" browserify-cipher@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" dependencies: browserify-aes "^1.0.4" browserify-des "^1.0.0" evp_bytestokey "^1.0.0" browserify-des@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" dependencies: cipher-base "^1.0.1" des.js "^1.0.0" inherits "^2.0.1" safe-buffer "^5.1.2" browserify-rsa@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" dependencies: bn.js "^4.1.0" randombytes "^2.0.1" browserify-sign@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" dependencies: bn.js "^4.1.1" browserify-rsa "^4.0.0" create-hash "^1.1.0" create-hmac "^1.1.2" elliptic "^6.0.0" inherits "^2.0.1" parse-asn1 "^5.0.0" browserify-zlib@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" dependencies: pako "~1.0.5" browserify-zlib@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" dependencies: pako "~0.2.0" browserify@^13.3.0: version "13.3.0" resolved "https://registry.yarnpkg.com/browserify/-/browserify-13.3.0.tgz#b5a9c9020243f0c70e4675bec8223bc627e415ce" dependencies: JSONStream "^1.0.3" assert "^1.4.0" browser-pack "^6.0.1" browser-resolve "^1.11.0" browserify-zlib "~0.1.2" buffer "^4.1.0" cached-path-relative "^1.0.0" concat-stream "~1.5.1" console-browserify "^1.1.0" constants-browserify "~1.0.0" crypto-browserify "^3.0.0" defined "^1.0.0" deps-sort "^2.0.0" domain-browser "~1.1.0" duplexer2 "~0.1.2" events "~1.1.0" glob "^7.1.0" has "^1.0.0" htmlescape "^1.1.0" https-browserify "~0.0.0" inherits "~2.0.1" insert-module-globals "^7.0.0" labeled-stream-splicer "^2.0.0" module-deps "^4.0.8" os-browserify "~0.1.1" parents "^1.0.1" path-browserify "~0.0.0" process "~0.11.0" punycode "^1.3.2" querystring-es3 "~0.2.0" read-only-stream "^2.0.0" readable-stream "^2.0.2" resolve "^1.1.4" shasum "^1.0.0" shell-quote "^1.6.1" stream-browserify "^2.0.0" stream-http "^2.0.0" string_decoder "~0.10.0" subarg "^1.0.0" syntax-error "^1.1.1" through2 "^2.0.0" timers-browserify "^1.0.1" tty-browserify "~0.0.0" url "~0.11.0" util "~0.10.1" vm-browserify "~0.0.1" xtend "^4.0.0" buffer-crc32@^0.2.5: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" buffer-from@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" buffer@^4.1.0, buffer@^4.3.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" isarray "^1.0.0" builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" cacache@^10.0.4: version "10.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" dependencies: bluebird "^3.5.1" chownr "^1.0.1" glob "^7.1.2" graceful-fs "^4.1.11" lru-cache "^4.1.1" mississippi "^2.0.0" mkdirp "^0.5.1" move-concurrently "^1.0.1" promise-inflight "^1.0.1" rimraf "^2.6.2" ssri "^5.2.4" unique-filename "^1.1.0" y18n "^4.0.0" cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" dependencies: collection-visit "^1.0.0" component-emitter "^1.2.1" get-value "^2.0.6" has-value "^1.0.0" isobject "^3.0.1" set-value "^2.0.0" to-object-path "^0.3.0" union-value "^1.0.0" unset-value "^1.0.0" cached-path-relative@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7" caching-transform@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-1.0.1.tgz#6dbdb2f20f8d8fbce79f3e94e9d1742dcdf5c0a1" dependencies: md5-hex "^1.2.0" mkdirp "^0.5.1" write-file-atomic "^1.1.4" camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" camelcase@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" catharsis@~0.8.8: version "0.8.9" resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.9.tgz#98cc890ca652dd2ef0e70b37925310ff9e90fc8b" dependencies: underscore-contrib "~0.3.0" center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" dependencies: align-text "^0.1.3" lazy-cache "^1.0.3" chai@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" dependencies: assertion-error "^1.0.1" deep-eql "^0.1.3" type-detect "^1.0.0" chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" has-ansi "^2.0.0" strip-ansi "^3.0.0" supports-color "^2.0.0" chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" chokidar@^1.4.3: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: anymatch "^1.3.0" async-each "^1.0.0" glob-parent "^2.0.0" inherits "^2.0.1" is-binary-path "^1.0.0" is-glob "^2.0.0" path-is-absolute "^1.0.0" readdirp "^2.0.0" optionalDependencies: fsevents "^1.0.0" chokidar@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" dependencies: anymatch "^2.0.0" async-each "^1.0.0" braces "^2.3.0" glob-parent "^3.1.0" inherits "^2.0.1" is-binary-path "^1.0.0" is-glob "^4.0.0" lodash.debounce "^4.0.8" normalize-path "^2.1.1" path-is-absolute "^1.0.0" readdirp "^2.0.0" upath "^1.0.5" optionalDependencies: fsevents "^1.2.2" chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" chrome-trace-event@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" dependencies: tslib "^1.9.0" cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" dependencies: arr-union "^3.1.0" define-property "^0.2.5" isobject "^3.0.0" static-extend "^0.1.1" cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" dependencies: restore-cursor "^2.0.0" cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" dependencies: center-align "^0.1.1" right-align "^0.1.1" wordwrap "0.0.2" cliui@^3.0.3: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" wrap-ansi "^2.0.0" cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" dependencies: string-width "^2.1.1" strip-ansi "^4.0.0" wrap-ansi "^2.0.0" clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" clone-stats@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" clone@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" clone@^1.0.0, clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" clone@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" cloneable-readable@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.2.tgz#d591dee4a8f8bc15da43ce97dceeba13d43e2a65" dependencies: inherits "^2.0.1" process-nextick-args "^2.0.0" readable-stream "^2.3.5" co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" dependencies: map-visit "^1.0.0" object-visit "^1.0.0" color-convert@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" dependencies: color-name "1.1.1" color-name@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" combine-source-map@^0.8.0, combine-source-map@~0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" dependencies: convert-source-map "~1.1.0" inline-source-map "~0.6.0" lodash.memoize "~3.0.3" source-map "~0.5.3" combined-stream@1.0.6, combined-stream@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" dependencies: delayed-stream "~1.0.0" commander@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d" commander@2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: graceful-readlink ">= 1.0.0" commander@^2.12.1: version "2.16.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50" commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" concat-stream@^1.5.0, concat-stream@^1.6.1: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: buffer-from "^1.0.0" inherits "^2.0.3" readable-stream "^2.2.2" typedarray "^0.0.6" concat-stream@~1.5.0, concat-stream@~1.5.1: version "1.5.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" dependencies: inherits "~2.0.1" readable-stream "~2.0.0" typedarray "~0.0.5" concat-with-sourcemaps@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" dependencies: source-map "^0.6.1" concurrently@^3.5.1: version "3.6.0" resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.6.0.tgz#c25e34b156a9d5bd4f256a0d85f6192438ae481f" dependencies: chalk "^2.4.1" commander "2.6.0" date-fns "^1.23.0" lodash "^4.5.1" read-pkg "^3.0.0" rx "2.3.24" spawn-command "^0.0.2-1" supports-color "^3.2.3" tree-kill "^1.1.0" configstore@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021" dependencies: graceful-fs "^4.1.2" mkdirp "^0.5.0" object-assign "^4.0.1" os-tmpdir "^1.0.0" osenv "^0.1.0" uuid "^2.0.1" write-file-atomic "^1.1.2" xdg-basedir "^2.0.0" console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" dependencies: date-now "^0.1.4" console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" constants-browserify@^1.0.0, constants-browserify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" content-disposition@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" content-type@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" convert-source-map@1.X, convert-source-map@^1.1.1, convert-source-map@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" convert-source-map@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" cookie@0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.1.5.tgz#6ab9948a4b1ae21952cd2588530a4722d4044d7c" copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" dependencies: aproba "^1.1.1" fs-write-stream-atomic "^1.0.8" iferr "^0.1.5" mkdirp "^0.5.1" rimraf "^2.5.4" run-queue "^1.0.0" copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" copy-props@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-1.6.0.tgz#f0324bbee99771101e7b3ada112f313c393db8ed" dependencies: each-props "^1.2.1" is-plain-object "^2.0.1" core-js@^2.4.0: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" coveralls@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.2.tgz#f5a0bcd90ca4e64e088b710fa8dda640aea4884f" dependencies: growl "~> 1.10.0" js-yaml "^3.11.0" lcov-parse "^0.0.10" log-driver "^1.2.7" minimist "^1.2.0" request "^2.85.0" crc-32@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" dependencies: exit-on-epipe "~1.0.1" printj "~1.1.0" create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" dependencies: bn.js "^4.1.0" elliptic "^6.0.0" create-hash@^1.1.0, create-hash@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" dependencies: cipher-base "^1.0.1" inherits "^2.0.1" md5.js "^1.3.4" ripemd160 "^2.0.1" sha.js "^2.4.0" create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: version "1.1.7" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" dependencies: cipher-base "^1.0.3" create-hash "^1.1.0" inherits "^2.0.1" ripemd160 "^2.0.0" safe-buffer "^5.0.1" sha.js "^2.4.8" cross-spawn@^4: version "4.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" dependencies: lru-cache "^4.0.1" which "^1.2.9" cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" dependencies: lru-cache "^4.0.1" shebang-command "^1.2.0" which "^1.2.9" cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" dependencies: nice-try "^1.0.4" path-key "^2.0.1" semver "^5.5.0" shebang-command "^1.2.0" which "^1.2.9" crypto-browserify@^3.0.0, crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" dependencies: browserify-cipher "^1.0.0" browserify-sign "^4.0.0" create-ecdh "^4.0.0" create-hash "^1.1.0" create-hmac "^1.1.0" diffie-hellman "^5.0.0" inherits "^2.0.1" pbkdf2 "^3.0.3" public-encrypt "^4.0.0" randombytes "^2.0.0" randomfill "^1.0.3" css@2.X: version "2.2.3" resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be" dependencies: inherits "^2.0.1" source-map "^0.1.38" source-map-resolve "^0.5.1" urix "^0.1.0" cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": version "0.3.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797" "cssstyle@>= 0.3.1 < 0.4.0": version "0.3.1" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.3.1.tgz#6da9b4cff1bc5d716e6e5fe8e04fcb1b50a49adf" dependencies: cssom "0.3.x" cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" dependencies: es5-ext "^0.10.9" dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" dependencies: assert-plus "^1.0.0" data-urls@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.0.0.tgz#24802de4e81c298ea8a9388bb0d8e461c774684f" dependencies: abab "^1.0.4" whatwg-mimetype "^2.0.0" whatwg-url "^6.4.0" date-fns@^1.23.0: version "1.29.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" dateformat@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" debug-fabulous@0.0.X: version "0.0.4" resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-0.0.4.tgz#fa071c5d87484685424807421ca4b16b0b1a0763" dependencies: debug "2.X" lazy-debug-legacy "0.0.X" object-assign "4.1.0" debug-log@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" debug@2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" debug@2.X, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: ms "2.0.0" debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: ms "2.0.0" debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: ms "0.7.1" decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" decamelize@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7" dependencies: xregexp "4.0.0" decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" deep-eql@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" dependencies: type-detect "0.1.1" deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" default-require-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" dependencies: strip-bom "^2.0.0" defaults@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" dependencies: clone "^1.0.2" define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" dependencies: is-descriptor "^1.0.2" isobject "^3.0.1" defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" depd@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" deprecated@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" deps-sort@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5" dependencies: JSONStream "^1.0.3" shasum "^1.0.0" subarg "^1.0.0" through2 "^2.0.0" des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" detect-file@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" dependencies: repeating "^2.0.0" detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" detect-newline@2.X: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" detective@^4.0.0: version "4.7.1" resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" dependencies: acorn "^5.2.1" defined "^1.0.0" diff@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" dependencies: bn.js "^4.1.0" miller-rabin "^4.0.0" randombytes "^2.0.0" domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" domain-browser@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" domexception@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" dependencies: webidl-conversions "^4.0.2" duplexer2@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" dependencies: readable-stream "~1.1.9" duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" dependencies: readable-stream "^2.0.2" duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" duplexify@^3.2.0, duplexify@^3.4.2, duplexify@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" readable-stream "^2.0.0" stream-shift "^1.0.0" each-props@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" dependencies: is-plain-object "^2.0.1" object.defaults "^1.1.0" ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" dependencies: jsbn "~0.1.0" ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" dependencies: bn.js "^4.4.0" brorand "^1.0.1" hash.js "^1.0.0" hmac-drbg "^1.0.0" inherits "^2.0.1" minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" dependencies: once "^1.4.0" end-of-stream@~0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" dependencies: once "~1.3.0" enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" dependencies: graceful-fs "^4.1.2" memory-fs "^0.4.0" tapable "^1.0.0" errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" dependencies: prr "~1.0.1" error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" dependencies: is-arrayish "^0.2.1" es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: version "0.10.45" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.45.tgz#0bfdf7b473da5919d5adf3bd25ceb754fccc3653" dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" next-tick "1" es6-iterator@^2.0.1, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" dependencies: d "1" es5-ext "^0.10.35" es6-symbol "^3.1.1" es6-promise@^3.0.2, es6-promise@^3.1.2: version "3.3.1" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" es6-symbol@^3.1.1, es6-symbol@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" dependencies: d "1" es5-ext "~0.10.14" escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5, escape-string-regexp@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" escodegen@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.10.0.tgz#f647395de22519fbd0d928ffcf1d17e0dec2603e" dependencies: esprima "^3.1.3" estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: source-map "~0.6.1" eslint-scope@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" espree@~3.1.7: version "3.1.7" resolved "https://registry.yarnpkg.com/espree/-/espree-3.1.7.tgz#fd5deec76a97a5120a9cd3a7cb1177a0923b11d2" dependencies: acorn "^3.3.0" acorn-jsx "^3.0.0" esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" dependencies: estraverse "^4.1.0" estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" etag@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" event-stream@~3.3.0: version "3.3.4" resolved "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" dependencies: duplexer "~0.1.1" from "~0" map-stream "~0.1.0" pause-stream "0.0.11" split "0.3" stream-combiner "~0.0.4" through "~2.3.1" events@^1.0.0, events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" dependencies: md5.js "^1.3.4" safe-buffer "^5.1.1" execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" dependencies: cross-spawn "^5.0.1" get-stream "^3.0.0" is-stream "^1.1.0" npm-run-path "^2.0.0" p-finally "^1.0.0" signal-exit "^3.0.0" strip-eof "^1.0.0" exit-on-epipe@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" dependencies: is-posix-bracket "^0.1.0" expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" dependencies: debug "^2.3.3" define-property "^0.2.5" extend-shallow "^2.0.1" posix-character-classes "^0.1.0" regex-not "^1.0.0" snapdragon "^0.8.1" to-regex "^3.0.1" expand-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" dependencies: fill-range "^2.1.0" expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" dependencies: homedir-polyfill "^1.0.1" express-ws@2.0.0-rc.1: version "2.0.0-rc.1" resolved "https://registry.yarnpkg.com/express-ws/-/express-ws-2.0.0-rc.1.tgz#9a7c7fabdf1fb8d91dbeedcc30a6f2b5bcb76826" dependencies: ws "^1.0.0" express@4.13.4: version "4.13.4" resolved "https://registry.yarnpkg.com/express/-/express-4.13.4.tgz#3c0b76f3c77590c8345739061ec0bd3ba067ec24" dependencies: accepts "~1.2.12" array-flatten "1.1.1" content-disposition "0.5.1" content-type "~1.0.1" cookie "0.1.5" cookie-signature "1.0.6" debug "~2.2.0" depd "~1.1.0" escape-html "~1.0.3" etag "~1.7.0" finalhandler "0.4.1" fresh "0.3.0" merge-descriptors "1.0.1" methods "~1.1.2" on-finished "~2.3.0" parseurl "~1.3.1" path-to-regexp "0.1.7" proxy-addr "~1.0.10" qs "4.0.0" range-parser "~1.0.3" send "0.13.1" serve-static "~1.10.2" type-is "~1.6.6" utils-merge "1.0.0" vary "~1.0.1" extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" extend@^3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" external-editor@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" tmp "^0.0.33" extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" dependencies: is-extglob "^1.0.0" extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" dependencies: array-unique "^0.3.2" define-property "^1.0.0" expand-brackets "^2.1.4" extend-shallow "^2.0.1" fragment-cache "^0.2.1" regex-not "^1.0.0" snapdragon "^0.8.1" to-regex "^3.0.1" extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" extsprintf@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" fancy-log@^1.1.0: version "1.3.2" resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" dependencies: ansi-gray "^0.1.1" color-support "^1.1.3" time-stamp "^1.0.0" fast-deep-equal@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" dependencies: escape-string-regexp "^1.0.5" filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" fill-range@^2.1.0: version "2.2.4" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" dependencies: is-number "^2.1.0" isobject "^2.0.0" randomatic "^3.0.0" repeat-element "^1.1.2" repeat-string "^1.5.2" fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" repeat-string "^1.6.1" to-regex-range "^2.1.0" finalhandler@0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.4.1.tgz#85a17c6c59a94717d262d61230d4b0ebe3d4a14d" dependencies: debug "~2.2.0" escape-html "~1.0.3" on-finished "~2.3.0" unpipe "~1.0.0" find-cache-dir@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" dependencies: commondir "^1.0.1" mkdirp "^0.5.1" pkg-dir "^1.0.0" find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" dependencies: commondir "^1.0.1" make-dir "^1.0.0" pkg-dir "^2.0.0" find-index@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" dependencies: locate-path "^2.0.0" find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" dependencies: locate-path "^3.0.0" findup-sync@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" dependencies: detect-file "^1.0.0" is-glob "^3.1.0" micromatch "^3.0.4" resolve-dir "^1.0.1" findup-sync@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" dependencies: glob "~5.0.0" fined@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" dependencies: expand-tilde "^2.0.2" is-plain-object "^2.0.3" object.defaults "^1.1.0" object.pick "^1.2.0" parse-filepath "^1.0.1" first-chunk-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" flagged-respawn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" flush-write-stream@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" dependencies: inherits "^2.0.1" readable-stream "^2.0.4" for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" for-own@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" dependencies: for-in "^1.0.1" for-own@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" dependencies: for-in "^1.0.1" foreground-child@^1.5.3, foreground-child@^1.5.6: version "1.5.6" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" dependencies: cross-spawn "^4" signal-exit "^3.0.0" forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" form-data@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" dependencies: asynckit "^0.4.0" combined-stream "1.0.6" mime-types "^2.1.12" forwarded@~0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" dependencies: map-cache "^0.2.2" fresh@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" dependencies: inherits "^2.0.1" readable-stream "^2.0.0" from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" fs-extra@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" dependencies: graceful-fs "^4.1.2" jsonfile "^2.1.0" klaw "^1.0.0" fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" dependencies: minipass "^2.2.1" fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" dependencies: graceful-fs "^4.1.2" iferr "^0.1.5" imurmurhash "^0.1.4" readable-stream "1 || 2" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" fsevents@^1.0.0, fsevents@^1.2.2: version "1.2.4" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" dependencies: nan "^2.9.2" node-pre-gyp "^0.10.0" function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" has-unicode "^2.0.0" object-assign "^4.1.0" signal-exit "^3.0.0" string-width "^1.0.1" strip-ansi "^3.0.1" wide-align "^1.1.0" gaze@^0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" dependencies: globule "~0.1.0" get-assigned-identifiers@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" get-caller-file@^1.0.1, get-caller-file@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" dependencies: assert-plus "^1.0.0" glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" dependencies: glob-parent "^2.0.0" is-glob "^2.0.0" glob-parent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" dependencies: is-glob "^2.0.0" glob-parent@^3.0.0, glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" dependencies: is-glob "^3.1.0" path-dirname "^1.0.0" glob-stream@^3.1.5: version "3.1.18" resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" dependencies: glob "^4.3.1" glob2base "^0.0.12" minimatch "^2.0.1" ordered-read-streams "^0.1.0" through2 "^0.6.1" unique-stream "^1.0.0" glob-stream@^5.3.2: version "5.3.5" resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-5.3.5.tgz#a55665a9a8ccdc41915a87c701e32d4e016fad22" dependencies: extend "^3.0.0" glob "^5.0.3" glob-parent "^3.0.0" micromatch "^2.3.7" ordered-read-streams "^0.3.0" through2 "^0.6.0" to-absolute-glob "^0.1.1" unique-stream "^2.0.2" glob-watcher@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" dependencies: gaze "^0.5.1" glob2base@^0.0.12: version "0.0.12" resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" dependencies: find-index "^0.1.1" glob@7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.0.2" once "^1.3.0" path-is-absolute "^1.0.0" glob@^4.3.1: version "4.5.3" resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" dependencies: inflight "^1.0.4" inherits "2" minimatch "^2.0.1" once "^1.3.0" glob@^5.0.3, glob@~5.0.0: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" dependencies: inflight "^1.0.4" inherits "2" minimatch "2 || 3" once "^1.3.0" path-is-absolute "^1.0.0" glob@^7.0.5, glob@^7.0.6, glob@^7.1.0, glob@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" glob@^7.1.2: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" glob@~3.1.21: version "3.1.21" resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" dependencies: graceful-fs "~1.2.0" inherits "1" minimatch "~0.2.11" global-modules-path@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.0.tgz#b0e2bac6beac39745f7db5c59d26a36a0b94f7dc" global-modules@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" dependencies: global-prefix "^1.0.1" is-windows "^1.0.1" resolve-dir "^1.0.0" global-prefix@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" dependencies: expand-tilde "^2.0.2" homedir-polyfill "^1.0.1" ini "^1.3.4" is-windows "^1.0.1" which "^1.2.14" globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" globule@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" dependencies: glob "~3.1.21" lodash "~1.0.1" minimatch "~0.2.11" glogg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" dependencies: sparkles "^1.0.0" got@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca" dependencies: duplexify "^3.2.0" infinity-agent "^2.0.0" is-redirect "^1.0.0" is-stream "^1.0.0" lowercase-keys "^1.0.0" nested-error-stacks "^1.0.0" object-assign "^3.0.0" prepend-http "^1.0.0" read-all-stream "^3.0.0" timed-out "^2.0.0" graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" graceful-fs@^3.0.0: version "3.0.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" dependencies: natives "^1.1.0" graceful-fs@~1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" "growl@~> 1.10.0": version "1.10.5" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" gulp-cli@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-1.4.0.tgz#6f5bbe2cd0bdb4849d12cf9e1246a5861f8b4f88" dependencies: archy "^1.0.0" chalk "^1.1.0" copy-props "^1.4.1" fancy-log "^1.1.0" gulplog "^1.0.0" interpret "^1.0.0" liftoff "^2.3.0" lodash.isfunction "^3.0.8" lodash.isplainobject "^4.0.4" lodash.sortby "^4.5.0" matchdep "^1.0.0" mute-stdout "^1.0.0" pretty-hrtime "^1.0.0" semver-greatest-satisfied-range "^1.0.0" tildify "^1.0.0" v8flags "^2.0.9" wreck "^6.3.0" yargs "^3.28.0" gulp-concat@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" dependencies: concat-with-sourcemaps "^1.0.0" through2 "^2.0.0" vinyl "^2.0.0" gulp-mocha@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/gulp-mocha/-/gulp-mocha-3.0.1.tgz#ab0ca2c39403718174dddad750e63a61be17e041" dependencies: gulp-util "^3.0.0" mocha "^3.0.0" plur "^2.1.0" req-cwd "^1.0.1" temp "^0.8.3" through "^2.3.4" gulp-sourcemaps@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz#b86ff349d801ceb56e1d9e7dc7bbcb4b7dee600c" dependencies: convert-source-map "^1.1.1" graceful-fs "^4.1.2" strip-bom "^2.0.0" through2 "^2.0.0" vinyl "^1.0.0" gulp-sourcemaps@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.9.1.tgz#80ac2d3845d13e68dd962524d8a967a440b0b753" dependencies: acorn "4.X" convert-source-map "1.X" css "2.X" debug-fabulous "0.0.X" detect-newline "2.X" graceful-fs "4.X" source-map "0.X" strip-bom "2.X" through2 "2.X" vinyl "1.X" gulp-typescript@^3.1.3: version "3.2.4" resolved "https://registry.yarnpkg.com/gulp-typescript/-/gulp-typescript-3.2.4.tgz#17c6b941078b02c0522974ed6c963ab190920a47" dependencies: gulp-util "~3.0.7" source-map "~0.5.3" through2 "~2.0.1" vinyl-fs "~2.4.3" gulp-util@3.0.8, gulp-util@^3.0.0, gulp-util@~3.0.7: version "3.0.8" resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" dependencies: array-differ "^1.0.0" array-uniq "^1.0.2" beeper "^1.0.0" chalk "^1.0.0" dateformat "^2.0.0" fancy-log "^1.1.0" gulplog "^1.0.0" has-gulplog "^0.1.0" lodash._reescape "^3.0.0" lodash._reevaluate "^3.0.0" lodash._reinterpolate "^3.0.0" lodash.template "^3.0.0" minimist "^1.1.0" multipipe "^0.1.2" object-assign "^3.0.0" replace-ext "0.0.1" through2 "^2.0.0" vinyl "^0.5.0" gulp@3.9.1: version "3.9.1" resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" dependencies: archy "^1.0.0" chalk "^1.0.0" deprecated "^0.0.1" gulp-util "^3.0.0" interpret "^1.0.0" liftoff "^2.1.0" minimist "^1.1.0" orchestrator "^0.3.0" pretty-hrtime "^1.0.0" semver "^4.1.0" tildify "^1.0.0" v8flags "^2.0.2" vinyl-fs "^0.3.0" gulplog@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" dependencies: glogg "^1.0.0" handlebars@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: async "^1.4.0" optimist "^0.6.1" source-map "^0.4.4" optionalDependencies: uglify-js "^2.6" har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" har-validator@~5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" dependencies: ajv "^5.1.0" har-schema "^2.0.0" has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" dependencies: ansi-regex "^2.0.0" has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" has-gulplog@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" dependencies: sparkles "^1.0.0" has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" dependencies: get-value "^2.0.3" has-values "^0.1.4" isobject "^2.0.0" has-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" dependencies: get-value "^2.0.6" has-values "^1.0.0" isobject "^3.0.0" has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" has-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" dependencies: is-number "^3.0.0" kind-of "^4.0.0" has@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" dependencies: function-bind "^1.1.1" hash-base@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.5" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.1" he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" homedir-polyfill@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" dependencies: parse-passwd "^1.0.0" hosted-git-info@^2.1.4: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" dependencies: whatwg-encoding "^1.0.1" htmlescape@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" http-errors@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" dependencies: inherits "~2.0.1" statuses "1" http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" sshpk "^1.7.0" https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" https-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" dependencies: safer-buffer ">= 2.1.2 < 3" iconv-lite@^0.4.4: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" dependencies: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.11, ieee754@^1.1.4: version "1.1.12" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" ignore-by-default@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" ignore-walk@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" dependencies: minimatch "^3.0.4" import-local@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" dependencies: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" infinity-agent@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/infinity-agent/-/infinity-agent-2.0.3.tgz#45e0e2ff7a9eb030b27d62b74b3744b7a7ac4216" inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" dependencies: once "^1.3.0" wrappy "1" inherits@1: version "1.0.2" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" inline-source-map@~0.6.0: version "0.6.2" resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" dependencies: source-map "~0.5.3" inquirer@^6.0.0: version "6.2.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.0" cli-cursor "^2.1.0" cli-width "^2.0.0" external-editor "^3.0.0" figures "^2.0.0" lodash "^4.17.10" mute-stream "0.0.7" run-async "^2.2.0" rxjs "^6.1.0" string-width "^2.1.0" strip-ansi "^4.0.0" through "^2.3.6" insert-module-globals@^7.0.0: version "7.2.0" resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.2.0.tgz#ec87e5b42728479e327bd5c5c71611ddfb4752ba" dependencies: JSONStream "^1.0.3" acorn-node "^1.5.2" combine-source-map "^0.8.0" concat-stream "^1.6.1" is-buffer "^1.1.0" path-is-absolute "^1.0.1" process "~0.11.0" through2 "^2.0.0" undeclared-identifiers "^1.1.2" xtend "^4.0.0" interpret@^1.0.0, interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: loose-envify "^1.0.0" inversify@^4.10.0: version "4.13.0" resolved "https://registry.yarnpkg.com/inversify/-/inversify-4.13.0.tgz#0ab40570bfa4474b04d5b919bbab3a4f682a72f5" invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" ipaddr.js@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.0.5.tgz#5fa78cf301b825c78abc3042d812723049ea23c7" irregular-plurals@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" dependencies: is-relative "^1.0.0" is-windows "^1.0.1" is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" dependencies: binary-extensions "^1.0.0" is-buffer@^1.1.0, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" is-builtin-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" dependencies: builtin-modules "^1.0.0" is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" dependencies: kind-of "^6.0.0" is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" kind-of "^5.0.0" is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" dependencies: is-accessor-descriptor "^1.0.0" is-data-descriptor "^1.0.0" kind-of "^6.0.2" is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" is-equal-shallow@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" dependencies: is-primitive "^2.0.0" is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" dependencies: is-plain-object "^2.0.4" is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" dependencies: is-extglob "^1.0.0" is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" dependencies: is-extglob "^2.1.0" is-glob@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" dependencies: is-extglob "^2.1.1" is-npm@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" dependencies: kind-of "^3.0.2" is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" dependencies: kind-of "^3.0.2" is-number@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" dependencies: isobject "^3.0.1" is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" is-redirect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" dependencies: is-unc-path "^1.0.0" is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" is-unc-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" dependencies: unc-path-regex "^0.1.2" is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" is-valid-glob@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-0.3.0.tgz#d4b55c69f51886f9b65c70d6c2622d37e29f48fe" is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" isarray@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.4.tgz#38e7bcbb0f3ba1b7933c86ba1894ddfc3781bbb7" isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" istanbul-lib-coverage@^1.1.2, istanbul-lib-coverage@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" istanbul-lib-hook@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b" dependencies: append-transform "^0.4.0" istanbul-lib-instrument@^1.10.0: version "1.10.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b" dependencies: babel-generator "^6.18.0" babel-template "^6.16.0" babel-traverse "^6.18.0" babel-types "^6.18.0" babylon "^6.18.0" istanbul-lib-coverage "^1.2.0" semver "^5.3.0" istanbul-lib-report@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz#2df12188c0fa77990c0d2176d2d0ba3394188259" dependencies: istanbul-lib-coverage "^1.1.2" mkdirp "^0.5.1" path-parse "^1.0.5" supports-color "^3.1.2" istanbul-lib-source-maps@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz#20fb54b14e14b3fb6edb6aca3571fd2143db44e6" dependencies: debug "^3.1.0" istanbul-lib-coverage "^1.1.2" mkdirp "^0.5.1" rimraf "^2.6.1" source-map "^0.5.3" istanbul-reports@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.0.tgz#c6c2867fa65f59eb7dcedb7f845dfc76aaee70f9" dependencies: handlebars "^4.0.11" "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" js-yaml@^3.11.0, js-yaml@^3.7.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" dependencies: argparse "^1.0.7" esprima "^4.0.0" js2xmlparser@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-1.0.0.tgz#5a170f2e8d6476ce45405e04823242513782fe30" jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" jsdoc@3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.4.3.tgz#e5740d6145c681f6679e6c17783a88dbdd97ccd3" dependencies: bluebird "~3.4.6" catharsis "~0.8.8" escape-string-regexp "~1.0.5" espree "~3.1.7" js2xmlparser "~1.0.0" klaw "~1.3.0" marked "~0.3.6" mkdirp "~0.5.1" requizzle "~0.2.1" strip-json-comments "~2.0.1" taffydb "2.6.2" underscore "~1.8.3" jsdom@^11.11.0: version "11.11.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.11.0.tgz#df486efad41aee96c59ad7a190e2449c7eb1110e" dependencies: abab "^1.0.4" acorn "^5.3.0" acorn-globals "^4.1.0" array-equal "^1.0.0" cssom ">= 0.3.2 < 0.4.0" cssstyle ">= 0.3.1 < 0.4.0" data-urls "^1.0.0" domexception "^1.0.0" escodegen "^1.9.0" html-encoding-sniffer "^1.0.2" left-pad "^1.2.0" nwsapi "^2.0.0" parse5 "4.0.0" pn "^1.1.0" request "^2.83.0" request-promise-native "^1.0.5" sax "^1.2.4" symbol-tree "^3.2.2" tough-cookie "^2.3.3" w3c-hr-time "^1.0.1" webidl-conversions "^4.0.2" whatwg-encoding "^1.0.3" whatwg-mimetype "^2.1.0" whatwg-url "^6.4.1" ws "^4.0.0" xml-name-validator "^3.0.0" jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" json-stable-stringify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" dependencies: jsonify "~0.0.0" json-stable-stringify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" dependencies: jsonify "~0.0.0" json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" json3@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" optionalDependencies: graceful-fs "^4.1.6" jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" dependencies: assert-plus "1.0.0" extsprintf "1.3.0" json-schema "0.2.3" verror "1.10.0" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" klaw@^1.0.0, klaw@~1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" optionalDependencies: graceful-fs "^4.1.9" labeled-stream-splicer@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz#9cffa32fd99e1612fd1d86a8db962416d5292926" dependencies: inherits "^2.0.1" isarray "^2.0.4" stream-splicer "^2.0.0" latest-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb" dependencies: package-json "^1.0.0" lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" lazy-debug-legacy@0.0.X: version "0.0.1" resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" dependencies: readable-stream "^2.0.5" lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" dependencies: invert-kv "^1.0.0" lcov-parse@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" left-pad@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" liftoff@^2.1.0, liftoff@^2.3.0: version "2.5.0" resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" dependencies: extend "^3.0.0" findup-sync "^2.0.0" fined "^1.0.1" flagged-respawn "^1.0.0" is-plain-object "^2.0.4" object.map "^1.0.0" rechoir "^0.6.2" resolve "^1.1.7" load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" pify "^2.0.0" pinkie-promise "^2.0.0" strip-bom "^2.0.0" load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" dependencies: graceful-fs "^4.1.2" parse-json "^4.0.0" pify "^3.0.0" strip-bom "^3.0.0" loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" loader-utils@~0.2.2: version "0.2.17" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" dependencies: big.js "^3.1.3" emojis-list "^2.0.0" json5 "^0.5.0" object-assign "^4.0.1" locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" dependencies: p-locate "^2.0.0" path-exists "^3.0.0" locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" dependencies: p-locate "^3.0.0" path-exists "^3.0.0" lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" dependencies: lodash._basecopy "^3.0.0" lodash.keys "^3.0.0" lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" lodash._basecreate@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" lodash._basetostring@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" lodash._basevalues@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" dependencies: lodash._bindcallback "^3.0.0" lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" lodash._isiterateecall@^3.0.0: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" lodash._reescape@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" lodash._reevaluate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" lodash._root@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" lodash.assign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" dependencies: lodash._baseassign "^3.0.0" lodash._createassigner "^3.0.0" lodash.keys "^3.0.0" lodash.create@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" dependencies: lodash._baseassign "^3.0.0" lodash._basecreate "^3.0.0" lodash._isiterateecall "^3.0.0" lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" lodash.defaults@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c" dependencies: lodash.assign "^3.0.0" lodash.restparam "^3.0.0" lodash.escape@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" dependencies: lodash._root "^3.0.0" lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" lodash.isfunction@^3.0.8: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" lodash.isplainobject@^4.0.4: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" dependencies: lodash._getnative "^3.0.0" lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" lodash.memoize@~3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" lodash.sortby@^4.5.0, lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" lodash.template@^3.0.0: version "3.6.2" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" dependencies: lodash._basecopy "^3.0.0" lodash._basetostring "^3.0.0" lodash._basevalues "^3.0.0" lodash._isiterateecall "^3.0.0" lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" lodash.keys "^3.0.0" lodash.restparam "^3.0.0" lodash.templatesettings "^3.0.0" lodash.templatesettings@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" dependencies: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.5.1: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" log-driver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" long@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" long@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: js-tokens "^3.0.0 || ^4.0.0" lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" lru-cache@2: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" lru-cache@^4.0.1, lru-cache@^4.1.1: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" dependencies: pseudomap "^1.0.2" yallist "^2.1.2" make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" dependencies: pify "^3.0.0" make-iterator@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" dependencies: kind-of "^6.0.2" mamacro@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" dependencies: object-visit "^1.0.0" marked@~0.3.6: version "0.3.19" resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790" matchdep@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-1.0.1.tgz#a57a33804491fbae208aba8f68380437abc2dca5" dependencies: findup-sync "~0.3.0" micromatch "^2.3.7" resolve "~1.1.6" stack-trace "0.0.9" math-random@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" md5-hex@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-1.3.0.tgz#d2c4afe983c4370662179b8cad145219135046c4" dependencies: md5-o-matic "^0.1.1" md5-o-matic@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3" md5.js@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" dependencies: hash-base "^3.0.0" inherits "^2.0.1" media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" mem@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" dependencies: mimic-fn "^1.0.0" memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" dependencies: errno "^0.1.3" readable-stream "^2.0.1" merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" merge-source-map@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" dependencies: source-map "^0.6.1" merge-stream@^1.0.0, merge-stream@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" dependencies: readable-stream "^2.0.1" methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" micromatch@^2.1.5, micromatch@^2.3.7: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: arr-diff "^2.0.0" array-unique "^0.2.1" braces "^1.8.2" expand-brackets "^0.1.4" extglob "^0.3.1" filename-regex "^2.0.0" is-extglob "^1.0.0" is-glob "^2.0.1" kind-of "^3.0.2" normalize-path "^2.0.1" object.omit "^2.0.0" parse-glob "^3.0.4" regex-cache "^0.4.2" micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" braces "^2.3.1" define-property "^2.0.2" extend-shallow "^3.0.2" extglob "^2.0.4" fragment-cache "^0.2.1" kind-of "^6.0.2" nanomatch "^1.2.9" object.pick "^1.3.0" regex-not "^1.0.0" snapdragon "^0.8.1" to-regex "^3.0.2" miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" dependencies: bn.js "^4.0.0" brorand "^1.0.1" mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.6: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: mime-db "~1.33.0" mime@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" "minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: brace-expansion "^1.1.7" minimatch@^2.0.1: version "2.0.10" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" dependencies: brace-expansion "^1.0.0" minimatch@~0.2.11: version "0.2.14" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" dependencies: lru-cache "2" sigmund "~1.0.0" minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" minimist@^1.1.0, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" minipass@^2.2.1, minipass@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" dependencies: safe-buffer "^5.1.2" yallist "^3.0.0" minizlib@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" dependencies: minipass "^2.2.1" mississippi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" dependencies: concat-stream "^1.5.0" duplexify "^3.4.2" end-of-stream "^1.1.0" flush-write-stream "^1.0.0" from2 "^2.1.0" parallel-transform "^1.1.0" pump "^2.0.1" pumpify "^1.3.3" stream-each "^1.1.0" through2 "^2.0.0" mixin-deep@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" dependencies: for-in "^1.0.2" is-extendable "^1.0.1" mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" mocha@^3.0.0: version "3.5.3" resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" dependencies: browser-stdout "1.3.0" commander "2.9.0" debug "2.6.8" diff "3.2.0" escape-string-regexp "1.0.5" glob "7.1.1" growl "1.9.2" he "1.1.1" json3 "3.3.2" lodash.create "3.1.1" mkdirp "0.5.1" supports-color "3.1.2" module-deps@^4.0.8: version "4.1.1" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd" dependencies: JSONStream "^1.0.3" browser-resolve "^1.7.0" cached-path-relative "^1.0.0" concat-stream "~1.5.0" defined "^1.0.0" detective "^4.0.0" duplexer2 "^0.1.2" inherits "^2.0.1" parents "^1.0.0" readable-stream "^2.0.2" resolve "^1.1.3" stream-combiner2 "^1.1.1" subarg "^1.0.0" through2 "^2.0.0" xtend "^4.0.0" move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" dependencies: aproba "^1.1.1" copy-concurrently "^1.0.0" fs-write-stream-atomic "^1.0.8" mkdirp "^0.5.1" rimraf "^2.5.4" run-queue "^1.0.3" ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" multipipe@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" dependencies: duplexer2 "0.0.2" mute-stdout@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.0.tgz#5b32ea07eb43c9ded6130434cf926f46b2a7fd4d" mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" nan@2.10.0, nan@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" define-property "^2.0.2" extend-shallow "^3.0.2" fragment-cache "^0.2.1" is-windows "^1.0.2" kind-of "^6.0.2" object.pick "^1.3.0" regex-not "^1.0.0" snapdragon "^0.8.1" to-regex "^3.0.1" natives@^1.1.0: version "1.1.4" resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.4.tgz#2f0f224fc9a7dd53407c7667c84cf8dbe773de58" needle@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" dependencies: debug "^2.1.2" iconv-lite "^0.4.4" sax "^1.2.4" negotiator@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.5.3.tgz#269d5c476810ec92edbe7b6c2f28316384f9a7e8" neo-async@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" nested-error-stacks@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz#19f619591519f096769a5ba9a86e6eeec823c3cf" dependencies: inherits "~2.0.1" next-tick@1: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" nice-try@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" node-libs-browser@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" dependencies: assert "^1.1.1" browserify-zlib "^0.2.0" buffer "^4.3.0" console-browserify "^1.1.0" constants-browserify "^1.0.0" crypto-browserify "^3.11.0" domain-browser "^1.1.1" events "^1.0.0" https-browserify "^1.0.0" os-browserify "^0.3.0" path-browserify "0.0.0" process "^0.11.10" punycode "^1.2.4" querystring-es3 "^0.2.0" readable-stream "^2.3.3" stream-browserify "^2.0.1" stream-http "^2.7.2" string_decoder "^1.0.0" timers-browserify "^2.0.4" tty-browserify "0.0.0" url "^0.11.0" util "^0.10.3" vm-browserify "0.0.4" node-pre-gyp@^0.10.0: version "0.10.3" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" dependencies: detect-libc "^1.0.2" mkdirp "^0.5.1" needle "^2.2.1" nopt "^4.0.1" npm-packlist "^1.1.6" npmlog "^4.0.2" rc "^1.2.7" rimraf "^2.6.1" semver "^5.3.0" tar "^4" node-pty@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.7.6.tgz#bff6148c9c5836ca7e73c7aaaec067dcbdac2f7b" dependencies: nan "2.10.0" nodemon@1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.10.2.tgz#ec511e14c3ad0858fc121c6006890ed27b7c412e" dependencies: chokidar "^1.4.3" debug "^2.2.0" es6-promise "^3.0.2" ignore-by-default "^1.0.0" lodash.defaults "^3.1.2" minimatch "^3.0.0" ps-tree "^1.0.1" touch "1.0.0" undefsafe "0.0.3" update-notifier "0.5.0" nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" dependencies: abbrev "1" osenv "^0.1.4" nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" dependencies: abbrev "1" normalize-package-data@^2.3.2: version "2.4.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" dependencies: remove-trailing-separator "^1.0.1" npm-bundled@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" npm-packlist@^1.1.6: version "1.1.10" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.10.tgz#1039db9e985727e464df066f4cf0ab6ef85c398a" dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" dependencies: path-key "^2.0.0" npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" gauge "~2.7.3" set-blocking "~2.0.0" number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" nwsapi@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.4.tgz#dc79040a5f77b97716dc79565fc7fc3ef7d50570" nyc@^11.8.0: version "11.9.0" resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.9.0.tgz#4106e89e8fbe73623a1fc8b6ecb7abaa271ae1e4" dependencies: archy "^1.0.0" arrify "^1.0.1" caching-transform "^1.0.0" convert-source-map "^1.5.1" debug-log "^1.0.1" default-require-extensions "^1.0.0" find-cache-dir "^0.1.1" find-up "^2.1.0" foreground-child "^1.5.3" glob "^7.0.6" istanbul-lib-coverage "^1.1.2" istanbul-lib-hook "^1.1.0" istanbul-lib-instrument "^1.10.0" istanbul-lib-report "^1.1.3" istanbul-lib-source-maps "^1.2.3" istanbul-reports "^1.4.0" md5-hex "^1.2.0" merge-source-map "^1.1.0" micromatch "^3.1.10" mkdirp "^0.5.0" resolve-from "^2.0.0" rimraf "^2.6.2" signal-exit "^3.0.1" spawn-wrap "^1.4.2" test-exclude "^4.2.0" yargs "11.1.0" yargs-parser "^8.0.0" oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" object-assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" kind-of "^3.0.3" object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" dependencies: isobject "^3.0.0" object.defaults@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" dependencies: array-each "^1.0.1" array-slice "^1.0.0" for-own "^1.0.0" isobject "^3.0.0" object.map@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" dependencies: for-own "^1.0.0" make-iterator "^1.0.0" object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" dependencies: for-own "^0.1.4" is-extendable "^0.1.1" object.pick@^1.2.0, object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" dependencies: isobject "^3.0.1" on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" dependencies: ee-first "1.1.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" once@~1.3.0: version "1.3.3" resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" dependencies: mimic-fn "^1.0.0" optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" dependencies: minimist "~0.0.1" wordwrap "~0.0.2" optionator@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.4" levn "~0.3.0" prelude-ls "~1.1.2" type-check "~0.3.2" wordwrap "~1.0.0" options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" orchestrator@^0.3.0: version "0.3.8" resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" dependencies: end-of-stream "~0.1.5" sequencify "~0.0.7" stream-consume "~0.1.0" ordered-read-streams@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" ordered-read-streams@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b" dependencies: is-stream "^1.0.1" readable-stream "^2.0.1" os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" os-browserify@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54" os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" os-locale@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" dependencies: lcid "^1.0.0" os-locale@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" dependencies: execa "^0.7.0" lcid "^1.0.0" mem "^1.1.0" os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" osenv@^0.1.0, osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" dependencies: p-try "^1.0.0" p-limit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" dependencies: p-try "^2.0.0" p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" dependencies: p-limit "^1.1.0" p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" dependencies: p-limit "^2.0.0" p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" p-try@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" package-json@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0" dependencies: got "^3.2.0" registry-url "^3.0.0" pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" pako@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" parallel-transform@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" dependencies: cyclist "~0.2.2" inherits "^2.0.3" readable-stream "^2.1.5" parents@^1.0.0, parents@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" dependencies: path-platform "~0.11.15" parse-asn1@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" create-hash "^1.1.0" evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" parse-filepath@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" dependencies: is-absolute "^1.0.0" map-cache "^0.2.0" path-root "^0.1.1" parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" dependencies: glob-base "^0.3.0" is-dotfile "^1.0.0" is-extglob "^1.0.0" is-glob "^2.0.0" parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" dependencies: error-ex "^1.2.0" parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" parse5@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" dependencies: "@types/node" "*" parseurl@~1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" path-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" path-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" dependencies: pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" path-platform@~0.11.15: version "0.11.15" resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" path-root-regex@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" path-root@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" dependencies: path-root-regex "^0.1.0" path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" dependencies: graceful-fs "^4.1.2" pify "^2.0.0" pinkie-promise "^2.0.0" path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" dependencies: pify "^3.0.0" pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" dependencies: through "~2.3" pbkdf2@^3.0.3: version "3.0.16" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" ripemd160 "^2.0.1" safe-buffer "^5.0.1" sha.js "^2.4.8" performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" pkg-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" dependencies: find-up "^1.0.0" pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" dependencies: find-up "^2.1.0" plur@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" dependencies: irregular-plurals "^1.0.0" pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" prepend-http@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" printj@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" process@^0.11.10, process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" proxy-addr@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.0.10.tgz#0d40a82f801fc355567d2ecb65efe3f077f121c5" dependencies: forwarded "~0.1.0" ipaddr.js "1.0.5" prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" ps-tree@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" dependencies: event-stream "~3.3.0" pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" psl@^1.1.24: version "1.1.28" resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.28.tgz#4fb6ceb08a1e2214d4fd4de0ca22dae13740bc7b" public-encrypt@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" dependencies: bn.js "^4.1.0" browserify-rsa "^4.0.0" create-hash "^1.1.0" parse-asn1 "^5.0.0" randombytes "^2.0.1" pump@^2.0.0, pump@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" dependencies: end-of-stream "^1.1.0" once "^1.3.1" pumpify@^1.3.3: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" dependencies: duplexify "^3.6.0" inherits "^2.0.3" pump "^2.0.0" punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" qs@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/qs/-/qs-4.0.0.tgz#c31d9b74ec27df75e543a86c78728ed8d4623607" qs@~6.5.1: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" querystring-es3@^0.2.0, querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" randomatic@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923" dependencies: is-number "^4.0.0" kind-of "^6.0.0" math-random "^1.0.1" randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" dependencies: safe-buffer "^5.1.0" randomfill@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" dependencies: randombytes "^2.0.5" safe-buffer "^5.1.0" range-parser@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.0.3.tgz#6872823535c692e2c2a0103826afd82c2e0ff175" rc@^1.0.1, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" dependencies: deep-extend "^0.6.0" ini "~1.3.0" minimist "^1.2.0" strip-json-comments "~2.0.1" read-all-stream@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa" dependencies: pinkie-promise "^2.0.0" readable-stream "^2.0.0" read-only-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" dependencies: readable-stream "^2.0.2" read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" dependencies: find-up "^1.0.0" read-pkg "^1.0.0" read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" path-type "^1.0.0" read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" dependencies: load-json-file "^4.0.0" normalize-package-data "^2.3.2" path-type "^3.0.0" "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: core-util-is "~1.0.0" inherits "~2.0.3" isarray "~1.0.0" process-nextick-args "~2.0.0" safe-buffer "~5.1.1" string_decoder "~1.1.1" util-deprecate "~1.0.1" "readable-stream@>=1.0.33-1 <1.1.0-0": version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" dependencies: core-util-is "~1.0.0" inherits "~2.0.1" isarray "0.0.1" string_decoder "~0.10.x" readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" dependencies: core-util-is "~1.0.0" inherits "~2.0.1" isarray "0.0.1" string_decoder "~0.10.x" readable-stream@~2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" dependencies: core-util-is "~1.0.0" inherits "~2.0.1" isarray "~1.0.0" process-nextick-args "~1.0.6" string_decoder "~0.10.x" util-deprecate "~1.0.1" readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" dependencies: graceful-fs "^4.1.2" minimatch "^3.0.2" readable-stream "^2.0.2" set-immediate-shim "^1.0.1" rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" dependencies: resolve "^1.1.6" reflect-metadata@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2" regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" dependencies: is-equal-shallow "^0.1.3" regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" dependencies: extend-shallow "^3.0.2" safe-regex "^1.1.0" registry-url@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" dependencies: rc "^1.0.1" remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" repeating@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" dependencies: is-finite "^1.0.0" repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" dependencies: is-finite "^1.0.0" replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" replace-ext@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" req-cwd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-1.0.1.tgz#0d73aeae9266e697a78f7976019677e76acf0fff" dependencies: req-from "^1.0.1" req-from@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/req-from/-/req-from-1.0.1.tgz#bf81da5147947d32d13b947dc12a58ad4587350e" dependencies: resolve-from "^2.0.0" request-promise-core@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" dependencies: lodash "^4.13.1" request-promise-native@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" dependencies: request-promise-core "1.1.1" stealthy-require "^1.1.0" tough-cookie ">=2.3.3" request@^2.83.0, request@^2.85.0: version "2.87.0" resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" dependencies: aws-sign2 "~0.7.0" aws4 "^1.6.0" caseless "~0.12.0" combined-stream "~1.0.5" extend "~3.0.1" forever-agent "~0.6.1" form-data "~2.3.1" har-validator "~5.0.3" http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" mime-types "~2.1.17" oauth-sign "~0.8.2" performance-now "^2.1.0" qs "~6.5.1" safe-buffer "^5.1.1" tough-cookie "~2.3.3" tunnel-agent "^0.6.0" uuid "^3.1.0" require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" requizzle@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.1.tgz#6943c3530c4d9a7e46f1cddd51c158fc670cdbde" dependencies: underscore "~1.6.0" resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" dependencies: resolve-from "^3.0.0" resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" dependencies: expand-tilde "^2.0.0" global-modules "^1.0.0" resolve-from@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" resolve@1.1.7, resolve@~1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2: version "1.8.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" dependencies: path-parse "^1.0.5" restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" dependencies: onetime "^2.0.0" signal-exit "^3.0.2" ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" dependencies: align-text "^0.1.1" rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: glob "^7.0.5" rimraf@~2.2.6: version "2.2.8" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" dependencies: hash-base "^3.0.0" inherits "^2.0.1" run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" dependencies: is-promise "^2.1.0" run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" dependencies: aproba "^1.1.1" rx@2.3.24: version "2.3.24" resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.24.tgz#14f950a4217d7e35daa71bbcbe58eff68ea4b2b7" rxjs@^6.1.0: version "6.3.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.1.tgz#878a1a8c64b8a5da11dcf74b5033fe944cdafb84" dependencies: tslib "^1.9.0" safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" dependencies: ret "~0.1.10" "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" sander@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad" dependencies: es6-promise "^3.1.2" graceful-fs "^4.1.3" mkdirp "^0.5.1" rimraf "^2.5.2" sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" schema-utils@^0.4.4, schema-utils@^0.4.5: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" dependencies: ajv "^6.1.0" ajv-keywords "^3.1.0" semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" dependencies: semver "^5.0.3" semver-greatest-satisfied-range@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" dependencies: sver-compat "^1.5.0" "semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.3.0, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" semver@^4.1.0: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" semver@^5.0.1: version "5.5.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" send@0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/send/-/send-0.13.1.tgz#a30d5f4c82c8a9bae9ad00a1d9b1bdbe6f199ed7" dependencies: debug "~2.2.0" depd "~1.1.0" destroy "~1.0.4" escape-html "~1.0.3" etag "~1.7.0" fresh "0.3.0" http-errors "~1.3.1" mime "1.3.4" ms "0.7.1" on-finished "~2.3.0" range-parser "~1.0.3" statuses "~1.2.1" send@0.13.2: version "0.13.2" resolved "https://registry.yarnpkg.com/send/-/send-0.13.2.tgz#765e7607c8055452bba6f0b052595350986036de" dependencies: debug "~2.2.0" depd "~1.1.0" destroy "~1.0.4" escape-html "~1.0.3" etag "~1.7.0" fresh "0.3.0" http-errors "~1.3.1" mime "1.3.4" ms "0.7.1" on-finished "~2.3.0" range-parser "~1.0.3" statuses "~1.2.1" sequencify@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" serialize-javascript@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" serve-static@~1.10.2: version "1.10.3" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.10.3.tgz#ce5a6ecd3101fed5ec09827dac22a9c29bfb0535" dependencies: escape-html "~1.0.3" parseurl "~1.3.1" send "0.13.2" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" set-value@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" is-plain-object "^2.0.1" to-object-path "^0.3.0" set-value@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" is-plain-object "^2.0.3" split-string "^3.0.1" setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" shasum@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" dependencies: json-stable-stringify "~0.0.0" sha.js "~2.4.4" shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" dependencies: shebang-regex "^1.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" shell-quote@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" dependencies: array-filter "~0.0.0" array-map "~0.0.0" array-reduce "~0.0.0" jsonify "~0.0.0" sigmund@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" simple-concat@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" dependencies: define-property "^1.0.0" isobject "^3.0.0" snapdragon-util "^3.0.1" snapdragon-util@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" dependencies: base "^0.11.1" debug "^2.2.0" define-property "^0.2.5" extend-shallow "^2.0.1" map-cache "^0.2.2" source-map "^0.5.6" source-map-resolve "^0.5.0" use "^3.1.0" sorcery@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.10.0.tgz#8ae90ad7d7cb05fc59f1ab0c637845d5c15a52b7" dependencies: buffer-crc32 "^0.2.5" minimist "^1.2.0" sander "^0.5.0" sourcemap-codec "^1.3.0" source-list-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" source-map-loader@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.3.tgz#d4b0c8cd47d54edce3e6bfa0f523f452b5b0e521" dependencies: async "^2.5.0" loader-utils "~0.2.2" source-map "~0.6.1" source-map-resolve@^0.5.0, source-map-resolve@^0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" dependencies: atob "^2.1.1" decode-uri-component "^0.2.0" resolve-url "^0.2.1" source-map-url "^0.4.0" urix "^0.1.0" source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" source-map@0.X: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" source-map@^0.1.38: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" dependencies: amdefine ">=0.0.4" source-map@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" dependencies: amdefine ">=0.0.4" source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" sourcemap-codec@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz#c8fd92d91889e902a07aee392bdd2c5863958ba2" sparkles@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" spawn-command@^0.0.2-1: version "0.0.2-1" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" spawn-wrap@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c" dependencies: foreground-child "^1.5.6" mkdirp "^0.5.0" os-homedir "^1.0.1" rimraf "^2.6.2" signal-exit "^3.0.2" which "^1.3.0" spdx-correct@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" spdx-expression-parse@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" dependencies: extend-shallow "^3.0.0" split@0.3: version "0.3.3" resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" dependencies: through "2" sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: version "1.14.2" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" dashdash "^1.12.0" getpass "^0.1.1" safer-buffer "^2.0.2" optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" jsbn "~0.1.0" tweetnacl "~0.14.0" ssri@^5.2.4: version "5.3.0" resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" dependencies: safe-buffer "^5.1.1" stack-trace@0.0.9: version "0.0.9" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" dependencies: define-property "^0.2.5" object-copy "^0.1.0" statuses@1: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" statuses@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.2.1.tgz#dded45cc18256d51ed40aec142489d5c61026d28" stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" stream-browserify@^2.0.0, stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" dependencies: inherits "~2.0.1" readable-stream "^2.0.2" stream-combiner2@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" dependencies: duplexer2 "~0.1.0" readable-stream "^2.0.2" stream-combiner@~0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" dependencies: duplexer "~0.1.1" stream-consume@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.1.tgz#d3bdb598c2bd0ae82b8cac7ac50b1107a7996c48" stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" dependencies: end-of-stream "^1.1.0" stream-shift "^1.0.0" stream-http@^2.0.0, stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" readable-stream "^2.3.6" to-arraybuffer "^1.0.0" xtend "^4.0.0" stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" stream-splicer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83" dependencies: inherits "^2.0.1" readable-stream "^2.0.2" string-length@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" dependencies: strip-ansi "^3.0.0" string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" "string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" string_decoder@^1.0.0, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: safe-buffer "~5.1.0" string_decoder@~0.10.0, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" dependencies: ansi-regex "^3.0.0" strip-bom-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" dependencies: first-chunk-stream "^1.0.0" strip-bom "^2.0.0" strip-bom@2.X, strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" dependencies: is-utf8 "^0.2.0" strip-bom@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" dependencies: first-chunk-stream "^1.0.0" is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" subarg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" dependencies: minimist "^1.1.0" supports-color@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" dependencies: has-flag "^1.0.0" supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" supports-color@^3.1.2, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: has-flag "^1.0.0" supports-color@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" dependencies: has-flag "^3.0.0" supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" dependencies: has-flag "^3.0.0" sver-compat@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" dependencies: es6-iterator "^2.0.1" es6-symbol "^3.1.1" symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" syntax-error@^1.1.1: version "1.4.0" resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" dependencies: acorn-node "^1.2.0" taffydb@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" tapable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" tar@^4: version "4.4.4" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" dependencies: chownr "^1.0.1" fs-minipass "^1.2.5" minipass "^2.3.3" minizlib "^1.1.0" mkdirp "^0.5.0" safe-buffer "^5.1.2" yallist "^3.0.2" temp@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59" dependencies: os-tmpdir "^1.0.0" rimraf "~2.2.6" test-exclude@^4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa" dependencies: arrify "^1.0.1" micromatch "^3.1.8" object-assign "^4.1.0" read-pkg-up "^1.0.1" require-main-filename "^1.0.1" through2-filter@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" dependencies: through2 "~2.0.0" xtend "~4.0.0" through2@2.X, through2@^2.0.0, through2@^2.0.3, through2@~2.0.0, through2@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" dependencies: readable-stream "^2.1.5" xtend "~4.0.1" through2@^0.6.0, through2@^0.6.1: version "0.6.5" resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" dependencies: readable-stream ">=1.0.33-1 <1.1.0-0" xtend ">=4.0.0 <4.1.0-0" through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" tildify@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" dependencies: os-homedir "^1.0.0" time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" timed-out@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a" timers-browserify@^1.0.1: version "1.4.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" dependencies: process "~0.11.0" timers-browserify@^2.0.4: version "2.0.10" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" dependencies: setimmediate "^1.0.4" tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" dependencies: os-tmpdir "~1.0.2" to-absolute-glob@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" dependencies: extend-shallow "^2.0.1" to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" dependencies: is-number "^3.0.0" repeat-string "^1.6.1" to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" dependencies: define-property "^2.0.2" extend-shallow "^3.0.2" regex-not "^1.0.2" safe-regex "^1.1.0" touch@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de" dependencies: nopt "~1.0.10" tough-cookie@>=2.3.3, tough-cookie@^2.3.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" dependencies: psl "^1.1.24" punycode "^1.4.1" tough-cookie@~2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: punycode "^1.4.1" tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" dependencies: punycode "^2.1.0" tree-kill@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" ts-loader@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-4.5.0.tgz#a1ce70b2dc799941fb2197605f0d67874097859b" dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" loader-utils "^1.0.2" micromatch "^3.1.4" semver "^5.0.1" tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" tslint-consistent-codestyle@^1.13.0: version "1.13.2" resolved "https://registry.yarnpkg.com/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.13.2.tgz#c60662f08a83d62797e47e5d74737470b5e17714" dependencies: "@fimbul/bifrost" "^0.11.0" tslib "^1.7.1" tsutils "^2.27.0" tslint@^5.9.1: version "5.10.0" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.10.0.tgz#11e26bccb88afa02dd0d9956cae3d4540b5f54c3" dependencies: babel-code-frame "^6.22.0" builtin-modules "^1.1.1" chalk "^2.3.0" commander "^2.12.1" diff "^3.2.0" glob "^7.1.1" js-yaml "^3.7.0" minimatch "^3.0.4" resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" tsutils "^2.12.1" tsutils@^2.12.1, tsutils@^2.24.0, tsutils@^2.27.0: version "2.27.2" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.2.tgz#60ba88a23d6f785ec4b89c6e8179cac9b431f1c7" dependencies: tslib "^1.8.1" tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" tty-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" dependencies: prelude-ls "~1.1.2" type-detect@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" type-detect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" type-is@~1.6.6: version "1.6.16" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" dependencies: media-typer "0.3.0" mime-types "~2.1.18" typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" typescript@3.0: version "3.0.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8" uglify-es@^3.3.4: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" dependencies: commander "~2.13.0" source-map "~0.6.1" uglify-js@^2.6: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" dependencies: source-map "~0.5.1" yargs "~3.10.0" optionalDependencies: uglify-to-browserify "~1.0.0" uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" uglifyjs-webpack-plugin@^1.2.4: version "1.3.0" resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de" dependencies: cacache "^10.0.4" find-cache-dir "^1.0.0" schema-utils "^0.4.5" serialize-javascript "^1.4.0" source-map "^0.6.1" uglify-es "^3.3.4" webpack-sources "^1.1.0" worker-farm "^1.5.2" ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" umd@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf" unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" undeclared-identifiers@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/undeclared-identifiers/-/undeclared-identifiers-1.1.2.tgz#7d850a98887cff4bd0bf64999c014d08ed6d1acc" dependencies: acorn-node "^1.3.0" get-assigned-identifiers "^1.2.0" simple-concat "^1.0.0" xtend "^4.0.1" undefsafe@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f" underscore-contrib@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/underscore-contrib/-/underscore-contrib-0.3.0.tgz#665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7" dependencies: underscore "1.6.0" underscore@1.6.0, underscore@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" underscore@~1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" dependencies: arr-union "^3.1.0" get-value "^2.0.6" is-extendable "^0.1.1" set-value "^0.4.3" unique-filename@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" dependencies: imurmurhash "^0.1.4" unique-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" unique-stream@^2.0.2: version "2.2.1" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.2.1.tgz#5aa003cfbe94c5ff866c4e7d668bb1c4dbadb369" dependencies: json-stable-stringify "^1.0.0" through2-filter "^2.0.0" unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" dependencies: has-value "^0.3.1" isobject "^3.0.0" upath@^1.0.5: version "1.1.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" update-notifier@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" dependencies: chalk "^1.0.0" configstore "^1.0.0" is-npm "^1.0.0" latest-version "^1.0.0" repeating "^1.1.2" semver-diff "^2.0.0" string-length "^1.0.0" uri-js@^4.2.1: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" dependencies: punycode "^2.1.0" urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: punycode "1.3.2" querystring "0.2.0" use@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" dependencies: kind-of "^6.0.2" user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: inherits "2.0.1" util@^0.10.3, util@~0.10.1: version "0.10.4" resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" dependencies: inherits "2.0.3" utils-merge@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" uuid@^3.1.0: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" v8-compile-cache@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" v8flags@^2.0.2, v8flags@^2.0.9: version "2.1.1" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" dependencies: user-home "^1.1.1" vali-date@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6" validate-npm-package-license@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" vary@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/vary/-/vary-1.0.1.tgz#99e4981566a286118dfb2b817357df7993376d10" verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" extsprintf "^1.2.0" vinyl-buffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vinyl-buffer/-/vinyl-buffer-1.0.1.tgz#96c1a3479b8c5392542c612029013b5b27f88bbf" dependencies: bl "^1.2.1" through2 "^2.0.3" vinyl-fs@^0.3.0: version "0.3.14" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" dependencies: defaults "^1.0.0" glob-stream "^3.1.5" glob-watcher "^0.0.6" graceful-fs "^3.0.0" mkdirp "^0.5.0" strip-bom "^1.0.0" through2 "^0.6.1" vinyl "^0.4.0" vinyl-fs@~2.4.3: version "2.4.4" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-2.4.4.tgz#be6ff3270cb55dfd7d3063640de81f25d7532239" dependencies: duplexify "^3.2.0" glob-stream "^5.3.2" graceful-fs "^4.0.0" gulp-sourcemaps "1.6.0" is-valid-glob "^0.3.0" lazystream "^1.0.0" lodash.isequal "^4.0.0" merge-stream "^1.0.0" mkdirp "^0.5.0" object-assign "^4.0.0" readable-stream "^2.0.4" strip-bom "^2.0.0" strip-bom-stream "^1.0.0" through2 "^2.0.0" through2-filter "^2.0.0" vali-date "^1.0.0" vinyl "^1.0.0" vinyl-source-stream@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-1.1.2.tgz#62b53a135610a896e98ca96bee3a87f008a8e780" dependencies: through2 "^2.0.3" vinyl "^0.4.3" vinyl@1.X, vinyl@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" dependencies: clone "^1.0.0" clone-stats "^0.0.1" replace-ext "0.0.1" vinyl@^0.4.0, vinyl@^0.4.3: version "0.4.6" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" dependencies: clone "^0.2.0" clone-stats "^0.0.1" vinyl@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" dependencies: clone "^1.0.0" clone-stats "^0.0.1" replace-ext "0.0.1" vinyl@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.0.tgz#d85b07da96e458d25b2ffe19fece9f2caa13ed86" dependencies: clone "^2.1.1" clone-buffer "^1.0.0" clone-stats "^1.0.0" cloneable-readable "^1.0.0" remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" vm-browserify@0.0.4, vm-browserify@~0.0.1: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" dependencies: indexof "0.0.1" w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" dependencies: browser-process-hrtime "^0.1.2" watchpack@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" dependencies: chokidar "^2.0.2" graceful-fs "^4.1.2" neo-async "^2.5.0" webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" webpack-cli@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.1.0.tgz#d71a83687dcfeb758fdceeb0fe042f96bcf62994" dependencies: chalk "^2.4.1" cross-spawn "^6.0.5" enhanced-resolve "^4.0.0" global-modules-path "^2.1.0" import-local "^1.0.0" inquirer "^6.0.0" interpret "^1.1.0" loader-utils "^1.1.0" supports-color "^5.4.0" v8-compile-cache "^2.0.0" yargs "^12.0.1" webpack-sources@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" dependencies: source-list-map "^2.0.0" source-map "~0.6.1" webpack-sources@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.2.0.tgz#18181e0d013fce096faf6f8e6d41eeffffdceac2" dependencies: source-list-map "^2.0.0" source-map "~0.6.1" webpack@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.17.1.tgz#0f026e3d823f3fc604f811ed3ea8f0d9b267fb1e" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-module-context" "1.5.13" "@webassemblyjs/wasm-edit" "1.5.13" "@webassemblyjs/wasm-opt" "1.5.13" "@webassemblyjs/wasm-parser" "1.5.13" acorn "^5.6.2" acorn-dynamic-import "^3.0.0" ajv "^6.1.0" ajv-keywords "^3.1.0" chrome-trace-event "^1.0.0" enhanced-resolve "^4.1.0" eslint-scope "^4.0.0" json-parse-better-errors "^1.0.2" loader-runner "^2.3.0" loader-utils "^1.1.0" memory-fs "~0.4.1" micromatch "^3.1.8" mkdirp "~0.5.0" neo-async "^2.5.0" node-libs-browser "^2.0.0" schema-utils "^0.4.4" tapable "^1.0.0" uglifyjs-webpack-plugin "^1.2.4" watchpack "^1.5.0" webpack-sources "^1.0.1" whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3" dependencies: iconv-lite "0.4.19" whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4" whatwg-url@^6.4.0, whatwg-url@^6.4.1: version "6.5.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1" webidl-conversions "^4.0.2" which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" which@^1.2.14, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: isexe "^2.0.0" wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" dependencies: string-width "^1.0.2 || 2" window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" window-size@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" worker-farm@^1.5.2: version "1.6.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" dependencies: errno "~0.1.7" wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" wreck@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/wreck/-/wreck-6.3.0.tgz#a1369769f07bbb62d6a378336a7871fc773c740b" dependencies: boom "2.x.x" hoek "2.x.x" write-file-atomic@^1.1.2, write-file-atomic@^1.1.4: version "1.3.4" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" dependencies: graceful-fs "^4.1.11" imurmurhash "^0.1.4" slide "^1.1.5" ws@^1.0.0: version "1.1.5" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" dependencies: options ">=0.0.5" ultron "1.0.x" ws@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" dependencies: async-limiter "~1.0.0" safe-buffer "~5.1.0" xdg-basedir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" dependencies: os-homedir "^1.0.0" xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" y18n@^3.2.0, y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" "y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" yallist@^3.0.0, yallist@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" yargs-parser@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" dependencies: camelcase "^4.1.0" yargs-parser@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" dependencies: camelcase "^4.1.0" yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" dependencies: camelcase "^4.1.0" yargs@11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" dependencies: cliui "^4.0.0" decamelize "^1.1.1" find-up "^2.1.0" get-caller-file "^1.0.1" os-locale "^2.0.0" require-directory "^2.1.1" require-main-filename "^1.0.1" set-blocking "^2.0.0" string-width "^2.0.0" which-module "^2.0.0" y18n "^3.2.1" yargs-parser "^9.0.2" yargs@^12.0.1: version "12.0.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.1.tgz#6432e56123bb4e7c3562115401e98374060261c2" dependencies: cliui "^4.0.0" decamelize "^2.0.0" find-up "^3.0.0" get-caller-file "^1.0.1" os-locale "^2.0.0" require-directory "^2.1.1" require-main-filename "^1.0.1" set-blocking "^2.0.0" string-width "^2.0.0" which-module "^2.0.0" y18n "^3.2.1 || ^4.0.0" yargs-parser "^10.1.0" yargs@^3.28.0: version "3.32.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" dependencies: camelcase "^2.0.1" cliui "^3.0.3" decamelize "^1.1.1" os-locale "^1.4.0" string-width "^1.0.1" window-size "^0.1.4" y18n "^3.2.0" yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" dependencies: camelcase "^1.0.2" cliui "^2.1.0" decamelize "^1.0.0" window-size "0.1.0" zmodem.js@^0.1.5: version "0.1.7" resolved "https://registry.yarnpkg.com/zmodem.js/-/zmodem.js-0.1.7.tgz#247affb76d2b1e3042b3fc8b4a087b9d5db8d1ed" dependencies: crc-32 "^1.1.1"