Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd70d298fc | ||
|
|
70d0ed3ebf | ||
|
|
5a5c6ab621 | ||
|
|
a34bb9724f | ||
|
|
824a9c6dbf | ||
|
|
c9eaae6bd5 | ||
|
|
9f5790fcaf | ||
|
|
6aeb0435ce | ||
|
|
70ec2496db | ||
|
|
55314e0fc7 | ||
|
|
243d76617b | ||
|
|
6cc66a0b20 | ||
|
|
1e928df531 | ||
|
|
db9bec02f5 | ||
|
|
0dc033692f | ||
|
|
1d30091188 | ||
|
|
64f80f8623 | ||
|
|
83b06c6bb0 | ||
|
|
5cbc2f0ea5 | ||
|
|
2bc8adee8c | ||
|
|
c6eb7f136c | ||
|
|
48b87c2f40 | ||
|
|
ca818fbf04 | ||
|
|
264fe4edc7 | ||
|
|
317c0ec2de | ||
|
|
3b2e89d878 | ||
|
|
15a94240eb | ||
|
|
f75b7db90e | ||
|
|
81dc48ef00 | ||
|
|
3638ef808b | ||
|
|
f24cba722a | ||
|
|
30fcdd6ccb | ||
|
|
b0df236dea | ||
|
|
d91e33ff39 | ||
|
|
1ee316ca83 | ||
|
|
dcbf014402 | ||
|
|
20b0d4fdcc | ||
|
|
2da1255c26 | ||
|
|
5bddb93745 | ||
|
|
51932161af | ||
|
|
e0d9871195 | ||
|
|
634d4af4dd | ||
|
|
a507e4f002 | ||
|
|
c15fed382d | ||
|
|
9937d54410 | ||
|
|
64f6610f11 | ||
|
|
e6f21f0bfc | ||
|
|
c63732ef5f | ||
|
|
99496de242 | ||
|
|
29cb576737 | ||
|
|
bf4ef599ad | ||
|
|
5ddb33419e | ||
|
|
bc10fbdfae | ||
|
|
6dcf7267a5 | ||
|
|
5712365cac | ||
|
|
1fc5a9aada | ||
|
|
38cd1bd6d7 | ||
|
|
771642d17d | ||
|
|
bb19b2040b | ||
|
|
12840dae6c | ||
|
|
521d7dbdaa | ||
|
|
2cba2ff3d4 | ||
|
|
547db92693 | ||
|
|
bf334238b7 | ||
|
|
5808de648b | ||
|
|
9ab0455b05 | ||
|
|
55ab36662a | ||
|
|
b1ad564b0d | ||
|
|
bc70b3b37d | ||
|
|
d426c99866 | ||
|
|
356377975b | ||
|
|
0de3d839e6 | ||
|
|
0fcd6b1d22 | ||
|
|
99b5464392 | ||
|
|
70965a8fd6 | ||
|
|
77ca15497c | ||
|
|
56ecc77dfc | ||
|
|
a995b610cf | ||
|
|
332bc6be76 | ||
|
|
26de9fe4c5 | ||
|
|
00dcb4f6e3 | ||
|
|
996c641a79 | ||
|
|
a077860464 | ||
|
|
5e68acfc76 | ||
|
|
269540bfed | ||
|
|
868f15173d | ||
|
|
06a93344bb | ||
|
|
e8e7870be2 | ||
|
|
a9417c6878 | ||
|
|
605d61b64f | ||
|
|
8feaacf57e | ||
|
|
553c651166 | ||
|
|
1ec0bac9c6 | ||
|
|
b1c6ae8a06 | ||
|
|
8a124d0b35 | ||
|
|
0ad02a4afa | ||
|
|
9389f899d7 | ||
|
|
050a34921b | ||
|
|
32e878db81 | ||
|
|
0bf7bf56a5 | ||
|
|
e5d130b698 | ||
|
|
941e57a218 | ||
|
|
c02eea6ad3 | ||
|
|
fe0d878b23 | ||
|
|
763f148fa1 | ||
|
|
8f0ada708f | ||
|
|
8117999ab3 | ||
|
|
10dd22a3d4 | ||
|
|
738f37e86c | ||
|
|
729c0abf04 | ||
|
|
fe54da34f8 | ||
|
|
09de0f31fa | ||
|
|
6ee524864e | ||
|
|
8fd350f0fc | ||
|
|
ebbeeefd86 | ||
|
|
76a890aa74 | ||
|
|
5ce522a401 | ||
|
|
68906b5def | ||
|
|
dddaa16573 | ||
|
|
d581559dd4 | ||
|
|
3e77db8149 | ||
|
|
bd994f9e57 | ||
|
|
9115846017 | ||
|
|
441009d359 | ||
|
|
ab7d63a8c8 | ||
|
|
a7acee726d | ||
|
|
a6f047331f | ||
|
|
a224acb50b | ||
|
|
42a1e4ef9b | ||
|
|
dff867b8a7 | ||
|
|
8bf8147028 | ||
|
|
ccf6d8f87d | ||
|
|
ac817ccc66 | ||
|
|
37c07eaaf9 | ||
|
|
71e20801f0 | ||
|
|
bae1bd6a42 | ||
|
|
c8b194931f | ||
|
|
c7a4881552 | ||
|
|
26fd963fbd | ||
|
|
122a34b40e | ||
|
|
33abd69e85 | ||
|
|
76a686beb2 | ||
|
|
53e8ac9b0c | ||
|
|
4b459fe0fd | ||
|
|
be4c8341ba | ||
|
|
a33bc027f1 | ||
|
|
3c9d8b0d84 | ||
|
|
26fc53990b | ||
|
|
852dac4d62 | ||
|
|
76719413f9 | ||
|
|
fca673d653 | ||
|
|
446c3958df | ||
|
|
59e72b8b07 | ||
|
|
15e56bd809 | ||
|
|
ab5cc0ad05 | ||
|
|
b89bce17ad | ||
|
|
7fcbddac04 | ||
|
|
4e1bbee67e | ||
|
|
ecfa44b0e8 | ||
|
|
db76868c26 | ||
|
|
b4c7989566 | ||
|
|
7b38b9ed57 | ||
|
|
c2fccc0350 | ||
|
|
b56ad5e8fe | ||
|
|
abfcf27962 | ||
|
|
6a04614549 | ||
|
|
89f981836f | ||
|
|
33d4d400f0 | ||
|
|
749ed6372a | ||
|
|
81fccee906 | ||
|
|
4caac78241 | ||
|
|
7ff03bb475 | ||
|
|
28c3a2026c | ||
|
|
a9c85fe825 | ||
|
|
c46e799dd0 | ||
|
|
ed1a31d1cf | ||
|
|
7f7156bafa | ||
|
|
62ce64f97d | ||
|
|
04a226f813 | ||
|
|
68b012ca06 | ||
|
|
220828f7db | ||
|
|
7df7f8f598 | ||
|
|
05aff199b2 | ||
|
|
fc22fc39e2 | ||
|
|
01c20807c2 | ||
|
|
243ce59806 | ||
|
|
5d6e0356b3 | ||
|
|
8324b43cc2 | ||
|
|
4c3ccc264c | ||
|
|
197c83d993 | ||
|
|
aaded29855 | ||
|
|
e5656b9982 | ||
|
|
c06b08df5b | ||
|
|
fc16752cec | ||
|
|
588d3b12e0 | ||
|
|
7de7bdac71 | ||
|
|
6e8e78db57 | ||
|
|
dcc991bd5c | ||
|
|
04cb9f33ff | ||
|
|
9bd167c10d | ||
|
|
dc6aab88b0 | ||
|
|
43fc6f1fa3 | ||
|
|
c2aa0d75ec | ||
|
|
c24ae84b00 | ||
|
|
92605a9734 | ||
|
|
b32a34b97c | ||
|
|
12923a6649 | ||
|
|
e7c7160f85 | ||
|
|
2a0962183e | ||
|
|
95d9ad46cf | ||
|
|
e60513f554 | ||
|
|
a557a82b40 | ||
|
|
3dd6f368d6 | ||
|
|
5ce012bdac | ||
|
|
a2338c49b9 | ||
|
|
dd6886f358 | ||
|
|
5ddf6301c2 | ||
|
|
14dd2c0afb | ||
|
|
fc2a0d3e65 | ||
|
|
0f8fc63a5e | ||
|
|
f57a40eeb3 | ||
|
|
2c9c95f531 | ||
|
|
7048f6edf7 | ||
|
|
c333cfa073 | ||
|
|
64d77d2491 | ||
|
|
9604674c65 | ||
|
|
d9294779a6 | ||
|
|
3117fe9465 | ||
|
|
0a74b9aa3b | ||
|
|
7e90576391 | ||
|
|
0a34885fbc | ||
|
|
61201525a8 | ||
|
|
1b886a4420 | ||
|
|
6f70984ab6 | ||
|
|
38fa2d78da | ||
|
|
fe905bace5 | ||
|
|
e7e1434293 | ||
|
|
363c647a5f | ||
|
|
aac60eb029 | ||
|
|
06ca03ae9c | ||
|
|
ff927b8e79 | ||
|
|
670b0d58c7 | ||
|
|
3113cdf0ea | ||
|
|
d2cef6f29d | ||
|
|
d62bd5f748 | ||
|
|
e6fd1381ce | ||
|
|
65b2be7831 | ||
|
|
e2d0de2686 | ||
|
|
495b2d7fbb | ||
|
|
5e662d0eb7 | ||
|
|
892db98c96 | ||
|
|
10fbf0dccc | ||
|
|
4595a18197 | ||
|
|
577a3d81e5 | ||
|
|
55fb5dbcb5 | ||
|
|
8e7eeff45c | ||
|
|
ec7130b46f | ||
|
|
dc17f0d02e | ||
|
|
14cd4315c7 | ||
|
|
644d881787 | ||
|
|
7fdfc62237 | ||
|
|
9fa0f00c23 | ||
|
|
fe6aaa3664 | ||
|
|
6e6b527d6e | ||
|
|
94d0009994 | ||
|
|
7cd99d1559 | ||
|
|
d91fa3b6fb | ||
|
|
466dca361c | ||
|
|
8b24c39a1d | ||
|
|
9dd64dfb49 | ||
|
|
0010a5a109 | ||
|
|
9cb5b005bb | ||
|
|
06a3aa6cf8 | ||
|
|
d4e9d34d2d | ||
|
|
a3a7017f6b | ||
|
|
fedc1fd325 | ||
|
|
11d5f51e67 | ||
|
|
2c810ce763 | ||
|
|
b61e81d5b4 | ||
|
|
534a9e7f5a | ||
|
|
14a62c0f8b | ||
|
|
89d29bbc10 | ||
|
|
ac8db6e8ec | ||
|
|
0a7c1bfd98 | ||
|
|
4efb8d24ac | ||
|
|
38d08873bc | ||
|
|
8074d754d3 | ||
|
|
f3db84decb | ||
|
|
13644ce351 | ||
|
|
e1c1b07a0b | ||
|
|
42ec3b492a | ||
|
|
c18794f202 | ||
|
|
0862fd1f59 | ||
|
|
6663a947ee | ||
|
|
3e8bb98197 | ||
|
|
9b2f68a25d | ||
|
|
11a7cd6983 | ||
|
|
7d904e455d | ||
|
|
c3cf6a22e9 | ||
|
|
12a150a438 | ||
|
|
8b46e84263 | ||
|
|
237e6819bf | ||
|
|
8faea59e31 | ||
|
|
cab79c473f | ||
|
|
c656ed04b0 | ||
|
|
29000fb79e | ||
|
|
03fa017d4a | ||
|
|
26af6ffd4d | ||
|
|
7d8e28a7dd | ||
|
|
a52b7e7a4e | ||
|
|
aaedcfc700 | ||
|
|
fc7b22dc48 | ||
|
|
5b44e36317 | ||
|
|
82f5de3bb9 | ||
|
|
dcf30b40c0 | ||
|
|
3b89134c05 | ||
|
|
d76d50677d | ||
|
|
8790a94a56 | ||
|
|
308535dc5c | ||
|
|
efe5b20b73 | ||
|
|
d1a6e8de3b | ||
|
|
f3cf646bbf | ||
|
|
a39b309b74 | ||
|
|
1aeb562066 | ||
|
|
107d1a1496 | ||
|
|
39edbfb655 | ||
|
|
246b0edaa1 | ||
|
|
4afa08da8d | ||
|
|
dc5b525559 | ||
|
|
30251e5d7c | ||
|
|
81e231ea20 | ||
|
|
36930e9661 | ||
|
|
838c11b69c | ||
|
|
05041722f1 | ||
|
|
36eb7efe5e | ||
|
|
87c9370e8c | ||
|
|
0b018fd4e9 | ||
|
|
39c4356ef6 | ||
|
|
59b79e9b04 | ||
|
|
b2e940f305 | ||
|
|
c3bc59b5e1 | ||
|
|
e3126ba3f8 | ||
|
|
f951abb7a9 | ||
|
|
549a8cd31b | ||
|
|
874ba72f4e | ||
|
|
ea1fd3d01b | ||
|
|
4c6b993458 | ||
|
|
323cc44e0e | ||
|
|
8bf81875ca | ||
|
|
15cf76c767 | ||
|
|
3df272fb92 | ||
|
|
04a8137f09 | ||
|
|
3217a4bee4 | ||
|
|
2aab58473e | ||
|
|
5d3bac6a86 | ||
|
|
09d67a6c52 | ||
|
|
af7588ef16 | ||
|
|
a6e85ad5f5 | ||
|
|
d5e83902a5 | ||
|
|
055614bbbc | ||
|
|
0d803ac895 | ||
|
|
253babff62 | ||
|
|
fac964bcf5 | ||
|
|
02d809c194 | ||
|
|
5d1b6cfe97 | ||
|
|
e37a9b62b7 | ||
|
|
623853f3f4 | ||
|
|
6c892e7161 | ||
|
|
be304c6ee7 | ||
|
|
00f380a75c | ||
|
|
af29effbf3 | ||
|
|
57300f5159 | ||
|
|
9d34ed8781 | ||
|
|
f3dd0a4ec8 | ||
|
|
9371520a73 | ||
|
|
fd5be55d81 | ||
|
|
e0c0fb6917 | ||
|
|
f2f0f460c4 | ||
|
|
08928d8b8e | ||
|
|
cb4728f8f5 | ||
|
|
4a71a41380 | ||
|
|
9c3b1105e2 | ||
|
|
edf897182e | ||
|
|
7c1559b8ad | ||
|
|
b01165c1f8 | ||
|
|
b5d9c6b113 | ||
|
|
5a56849df8 | ||
|
|
c908f849c5 | ||
|
|
fed92ac5c8 | ||
|
|
00f4232ecc | ||
|
|
fa1cd89aa9 | ||
|
|
5193f4fae0 | ||
|
|
740ae96f5f | ||
|
|
55ca998224 | ||
|
|
fdff1ca793 | ||
|
|
fa729b8306 | ||
|
|
eee99f62e1 | ||
|
|
da9f86f1a7 | ||
|
|
0535f9425b | ||
|
|
c86fd87819 | ||
|
|
3a866cf202 | ||
|
|
e2aaa8d362 | ||
|
|
524db02228 | ||
|
|
cf9d2348a1 | ||
|
|
92122bf29b | ||
|
|
abb7a49584 | ||
|
|
beff0db301 | ||
|
|
ab1526b085 | ||
|
|
b91e737017 | ||
|
|
fa093e2bd2 | ||
|
|
2f001f8af9 | ||
|
|
f87300d30e | ||
|
|
04ae96617d | ||
|
|
7a80efd8bd | ||
|
|
df268ad5cd | ||
|
|
e721bdc9f0 | ||
|
|
ae99ad83d0 | ||
|
|
c491537de1 | ||
|
|
383d59bbc3 | ||
|
|
2471910244 | ||
|
|
2a36ff0839 | ||
|
|
136c838f30 | ||
|
|
bd3936cc9f | ||
|
|
dab80b58ae | ||
|
|
23cfa3d824 | ||
|
|
289ff8092d | ||
|
|
47b01786e2 | ||
|
|
6bc1881727 | ||
|
|
21d765b227 | ||
|
|
d82c008052 | ||
|
|
11823ce890 | ||
|
|
670b179140 | ||
|
|
637c2eae43 | ||
|
|
53e8df40bb | ||
|
|
180a7af75d | ||
|
|
0fd0f45d8b | ||
|
|
8754f8d895 | ||
|
|
c02cc84498 | ||
|
|
647216876b | ||
|
|
f3bd6145a5 | ||
|
|
7320515820 | ||
|
|
8fa1a46584 | ||
|
|
fa7d214e78 | ||
|
|
178b611b0c | ||
|
|
1f28c22323 | ||
|
|
ae0f963799 | ||
|
|
82cac90005 | ||
|
|
8ebfa27210 | ||
|
|
4babc92d7c | ||
|
|
1a3846161a | ||
|
|
ff7d0e58dc | ||
|
|
3d64f91a5c | ||
|
|
f980f32fe5 | ||
|
|
dc67c94545 | ||
|
|
fff56eea1b | ||
|
|
bd2158613d | ||
|
|
537fbad705 | ||
|
|
52cc8fa5fe | ||
|
|
7988f634f3 | ||
|
|
a5e34e2288 | ||
|
|
ad3764ae4a | ||
|
|
685bc54bf2 | ||
|
|
a54eb8ac44 | ||
|
|
6a87253d94 | ||
|
|
6406a88ddf | ||
|
|
18b2a1c3b4 | ||
|
|
58056df23d | ||
|
|
a88e27cc7d | ||
|
|
7cab2501da | ||
|
|
dd93e62a41 | ||
|
|
d238a1998c | ||
|
|
6bbb21490c | ||
|
|
9f0fa2f815 | ||
|
|
f5fb10063c | ||
|
|
721bd272b5 | ||
|
|
c170156147 | ||
|
|
a2f6f2be03 | ||
|
|
412260392f | ||
|
|
5f8585a625 | ||
|
|
ee0d15c878 | ||
|
|
3f0b584f25 | ||
|
|
41fd20c6e4 | ||
|
|
01e9524098 | ||
|
|
d9d99c1c27 | ||
|
|
f53f5735e1 | ||
|
|
7a88a4b0e9 | ||
|
|
85733fea56 | ||
|
|
369e1f4c1e | ||
|
|
702fd24ce8 | ||
|
|
961e62207c | ||
|
|
cc950614c5 | ||
|
|
556140e7d9 | ||
|
|
987f09d880 |
@@ -0,0 +1,16 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{j,t}s]
|
||||
max_line_length = 100
|
||||
|
||||
[*.css]
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,11 @@
|
||||
<!-- Enter your issue description here -->
|
||||
|
||||
## Details
|
||||
- Browser and browser version:
|
||||
- OS version:
|
||||
- xterm.js version:
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
+5
-2
@@ -1,6 +1,5 @@
|
||||
node_modules/
|
||||
*.swp
|
||||
build/*
|
||||
.lock-wscript
|
||||
out/
|
||||
Makefile.gyp
|
||||
@@ -8,4 +7,8 @@ Makefile.gyp
|
||||
*.target.gyp.mk
|
||||
*.node
|
||||
example/*.log
|
||||
docs/_build
|
||||
docs/
|
||||
npm-debug.log
|
||||
/.idea/
|
||||
.env
|
||||
build/
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Antonis Kalipetis <akalipetis@sourcelair.com>
|
||||
Antonis Kalipetis <akalipetis@sourcelair.com> <akalipetis@gmail.com>
|
||||
Daniel Imms <daimms@microsoft.com> <tyriar@tyriar.com>
|
||||
Paris Kasidiaris <paris@sourcelair.com>
|
||||
Paris Kasidiaris <pariskasidiaris@gmail.com>
|
||||
Thanasis Daglis <thanasis@sourcelair.com>
|
||||
Thanasis Daglis <thanasis@sourcelair.com> <ath.daglis@gmail.com>
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 6
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
notifications:
|
||||
email: false
|
||||
@@ -0,0 +1,31 @@
|
||||
List of xterm.js contributors. Updated before every release.
|
||||
|
||||
Alessandro Nadalin <alessandro.nadalin@gmail.com>
|
||||
Alexander Olsson <noseglid@gmail.com>
|
||||
Antonis Kalipetis <akalipetis@sourcelair.com>
|
||||
Anton Skshidlevsky <meefik@gmail.com>
|
||||
Anton Yurovskykh <anton.yurovskykh@gmail.com>
|
||||
Austin Robertson <austinrobertson@gmail.com>
|
||||
ayapi <colors.aya@gmail.com>
|
||||
Benjamin Fischer <benjamin.fischer@rwth-aachen.de>
|
||||
Carson Anderson <carson@betterservers.com>
|
||||
Christopher Jeffrey <chjjeffrey@gmail.com>
|
||||
Daniel Imms <daimms@microsoft.com>
|
||||
Daniel Risacher <drisacher@gmail.com>
|
||||
Dan Kaplun <dbkaplun@twitch.tv>
|
||||
Darin Morrison <freebroccolo@users.noreply.github.com>
|
||||
Elliot Saba <staticfloat@gmail.com>
|
||||
hiro-su <h.sugipon@gmail.com>
|
||||
Ian Lewis <ianlewis@google.com>
|
||||
imoses <ido@twiggle.com>
|
||||
Jean Bruenn <himself@jeanbruenn.info>
|
||||
Jörg Breitbart <jerch@rockborn.de>
|
||||
Mikko Karvonen <mikko.karvonen@arm.com>
|
||||
Paris Kasidiaris <pariskasidiaris@gmail.com>
|
||||
Paris Kasidiaris <paris@sourcelair.com>
|
||||
runarberg <runar@greenqloud.com>
|
||||
Shuanglei Tao <tsl0922@gmail.com>
|
||||
Steven Silvester <steven.silvester@ieee.org>
|
||||
Thanasis Daglis <thanasis@sourcelair.com>
|
||||
Tine Jozelj <tine.jozelj@outlook.com>
|
||||
YuviPanda <yuvipanda@gmail.com>
|
||||
@@ -0,0 +1,52 @@
|
||||
# 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](README.md#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))
|
||||
- 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.)
|
||||
@@ -0,0 +1,4 @@
|
||||
FROM node:4-onbuild
|
||||
MAINTAINER Paris Kasidiaris <paris@sourcelair.com>
|
||||
|
||||
EXPOSE 3000
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2014, sourceLair Limited (https://github.com/sourcelair/)
|
||||
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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
web: npm start
|
||||
@@ -1,19 +1,102 @@
|
||||
# xterm.js — xterm, in the browser
|
||||
# xterm.js
|
||||
|
||||
Xterm.js is a full xterm clone, written completely in JavaScript.
|
||||
[](https://travis-ci.org/sourcelair/xterm.js) [](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
Xterm.js supplies a modular, event-based interface that lets developers build addons and themes that augment the experience of running a fully-featured terminal inside the browser.
|
||||
Xterm.js is a terminal front-end component written in JavaScript that works in the browser.
|
||||
|
||||
Xterm.js serves as foundation for the terminal found at www.sourcelair.com.
|
||||
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
|
||||
|
||||
### Contribution and License Agreement
|
||||
## 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)
|
||||
|
||||
## 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
|
||||
|
||||
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.
|
||||
|
||||
## 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 48+
|
||||
- Edge 13+
|
||||
- Firefox 44+
|
||||
- Internet Explorer 11+
|
||||
- Opera 35+
|
||||
- Safari 8+
|
||||
|
||||
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.
|
||||
|
||||
## Demo
|
||||
|
||||
To launch the demo simply run:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
Then open http://0.0.0.0:3000 in a web browser (use http://127.0.0.1:3000 if running under Windows).
|
||||
|
||||
## Addons
|
||||
|
||||
Addons are JavaScript modules that attach functions to the `Terminal` prototype to extend its functionality. There are a handful available in the main repository in the `dist/addons` directory, you can even write your own (though they may break when the internals of xterm.js change across versions).
|
||||
|
||||
To use an addon, just include the JavaScript file after xterm.js and before the `Terminal` object has been instantiated. The function should then be exposed on the `Terminal` object:
|
||||
|
||||
```html
|
||||
<script src="node_modules/dist/xterm.js"></script>
|
||||
<script src="node_modules/dist/addons/fit/fit.js"></script>
|
||||
```
|
||||
|
||||
```js
|
||||
var xterm = new Terminal();
|
||||
// init code...
|
||||
xterm.fit();
|
||||
```
|
||||
|
||||
## 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).
|
||||
|
||||
## Development and Contribution
|
||||
|
||||
Xterm.js is maintained by [SourceLair](https://www.sourcelair.com/) and a few external contributors, but we would love to receive contributions from everyone!
|
||||
|
||||
To contribute either code, documentation or issues to xterm.js please read the [Contributing document](CONTRIBUTING.md) before.
|
||||
|
||||
The development of xterm.js does not require any special tool. All you need is an editor that supports JavaScript and a browser (if you would like to run the demo you will need Node.js to get all features).
|
||||
|
||||
It is recommended though to use a development tool that uses xterm.js internally, to develop for xterm.js. [Eating our own dogfood](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) has been proved extremely beneficial for this project. Known tools that use xterm.js internally are:
|
||||
|
||||
#### [SourceLair](https://www.sourcelair.com)
|
||||
|
||||
Visit https://lair.io/sourcelair/xterm and follow the instructions. All development will happen in your browser.
|
||||
|
||||
#### [Visual Studio Code](http://code.visualstudio.com/)
|
||||
|
||||
[Download Visual Studio Code](http://code.visualstudio.com/Download), clone xterm.js and you are all set.
|
||||
|
||||
## 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.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2015, SourceLair, Private Company (www.sourcelair.com) (MIT License)
|
||||
Copyright (c) 2014-2016, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
|
||||
|
||||
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Implements the attach method, that
|
||||
* attaches the terminal to a WebSocket stream.
|
||||
*
|
||||
* The bidirectional argument indicates, whether the terminal should
|
||||
* send data to the socket as well and is true, by default.
|
||||
*/
|
||||
|
||||
(function (attach) {
|
||||
if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../src/xterm'], attach);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
attach(this.Xterm);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This module provides methods for attaching a terminal to a WebSocket
|
||||
* stream.
|
||||
*
|
||||
* @module xterm/addons/attach/attach
|
||||
*/
|
||||
var exports = {};
|
||||
|
||||
/**
|
||||
* Attaches the given terminal to the given socket.
|
||||
*
|
||||
* @param {Xterm} term - The terminal to be attached to the given socket.
|
||||
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||
* to the socket as well.
|
||||
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
exports.attach = function (term, socket, bidirectional, buffered) {
|
||||
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
|
||||
term.socket = socket;
|
||||
|
||||
term._flushBuffer = function () {
|
||||
term.write(term._attachSocketBuffer);
|
||||
term._attachSocketBuffer = null;
|
||||
clearTimeout(term._attachSocketBufferTimer);
|
||||
term._attachSocketBufferTimer = null;
|
||||
};
|
||||
|
||||
term._pushToBuffer = function (data) {
|
||||
if (term._attachSocketBuffer) {
|
||||
term._attachSocketBuffer += data;
|
||||
} else {
|
||||
term._attachSocketBuffer = data;
|
||||
setTimeout(term._flushBuffer, 10);
|
||||
}
|
||||
};
|
||||
|
||||
term._getMessage = function (ev) {
|
||||
if (buffered) {
|
||||
term._pushToBuffer(ev.data);
|
||||
} else {
|
||||
term.write(ev.data);
|
||||
}
|
||||
};
|
||||
|
||||
term._sendData = function (data) {
|
||||
socket.send(data);
|
||||
};
|
||||
|
||||
socket.addEventListener('message', term._getMessage);
|
||||
|
||||
if (bidirectional) {
|
||||
term.on('data', term._sendData);
|
||||
}
|
||||
|
||||
socket.addEventListener('close', term.detach.bind(term, socket));
|
||||
socket.addEventListener('error', term.detach.bind(term, socket));
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches the given terminal from the given socket
|
||||
*
|
||||
* @param {Xterm} term - The terminal to be detached from the given socket.
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
exports.detach = function (term, socket) {
|
||||
term.off('data', term._sendData);
|
||||
|
||||
socket = (typeof socket == 'undefined') ? term.socket : socket;
|
||||
|
||||
if (socket) {
|
||||
socket.removeEventListener('message', term._getMessage);
|
||||
}
|
||||
|
||||
delete term.socket;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches the current terminal to the given socket
|
||||
*
|
||||
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||
* to the socket as well.
|
||||
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
Xterm.prototype.attach = function (socket, bidirectional, buffered) {
|
||||
return exports.attach(this, socket, bidirectional, buffered);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches the current terminal from the given socket.
|
||||
*
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
Xterm.prototype.detach = function (socket) {
|
||||
return exports.detach(this, socket);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
(function (fit) {
|
||||
if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../src/xterm'], fit);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
fit(this.Xterm);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
Xterm.prototype.proposeGeometry = function () {
|
||||
var parentElementStyle = window.getComputedStyle(this.element.parentElement),
|
||||
parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
|
||||
parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')),
|
||||
elementStyle = window.getComputedStyle(this.element),
|
||||
elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
|
||||
elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')),
|
||||
availableHeight = parentElementHeight - elementPaddingVer,
|
||||
availableWidth = parentElementWidth - elementPaddingHor,
|
||||
container = this.rowContainer,
|
||||
subjectRow = this.rowContainer.firstElementChild,
|
||||
contentBuffer = subjectRow.innerHTML,
|
||||
characterHeight,
|
||||
rows,
|
||||
characterWidth,
|
||||
cols,
|
||||
geometry;
|
||||
|
||||
subjectRow.style.display = 'inline';
|
||||
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
|
||||
characterWidth = parseInt(subjectRow.offsetWidth);
|
||||
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
|
||||
characterHeight = parseInt(subjectRow.offsetHeight);
|
||||
subjectRow.innerHTML = contentBuffer;
|
||||
|
||||
rows = parseInt(availableHeight / characterHeight);
|
||||
/*
|
||||
* The following hack takes place in order to get "fit" work properly
|
||||
* in Mozilla Firefox.
|
||||
* Most probably, because of a dimension calculation bug, Firefox
|
||||
* calculates the width to be 1px less than it is actually drawn on
|
||||
* screen.
|
||||
*/
|
||||
if (navigator.userAgent.match(/Firefox/)) {
|
||||
characterWidth++;
|
||||
}
|
||||
cols = parseInt(availableWidth / characterWidth);
|
||||
|
||||
geometry = {cols: cols, rows: rows};
|
||||
return geometry;
|
||||
};
|
||||
|
||||
Xterm.prototype.fit = function () {
|
||||
var geometry = this.proposeGeometry();
|
||||
|
||||
this.resize(geometry.cols, geometry.rows);
|
||||
};
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Fullscreen addon for xterm.js
|
||||
*
|
||||
* Implements the toggleFullscreen function.
|
||||
*
|
||||
* If the `fullscreen` argument has been supplied, then
|
||||
* if it is true, the fullscreen mode gets turned on,
|
||||
* if it is false or null, the fullscreen mode gets turned off.
|
||||
*
|
||||
* If the `fullscreen` argument has not been supplied, the
|
||||
* fullscreen mode is being toggled.
|
||||
*/
|
||||
(function (fullscreen) {
|
||||
if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../src/xterm'], fullscreen);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
fullscreen(this.Xterm);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
Xterm.prototype.toggleFullscreen = function (fullscreen) {
|
||||
var fn;
|
||||
|
||||
if (typeof fullscreen == 'undefined') {
|
||||
fn = (this.element.classList.contains('fullscreen')) ? 'remove' : 'add';
|
||||
} else if (!fullscreen) {
|
||||
fn = 'remove';
|
||||
} else {
|
||||
fn = 'add';
|
||||
}
|
||||
|
||||
this.element.classList[fn]('fullscreen');
|
||||
};
|
||||
});
|
||||
@@ -1,182 +0,0 @@
|
||||
(function (linkify) {
|
||||
if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../src/xterm'], linkify);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
linkify(this.Xterm);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This module provides methods for convertings valid URL substrings
|
||||
* into HTML anchor elements (links), inside a terminal view.
|
||||
*
|
||||
* @module xterm/addons/linkify/linkify
|
||||
*/
|
||||
var exports = {},
|
||||
protocolClause = '(https?:\\/\\/)',
|
||||
domainCharacterSet = '[\\da-z\\.-]+',
|
||||
negatedDomainCharacterSet = '[^\\da-z\\.-]+',
|
||||
domainBodyClause = '(' + domainCharacterSet + ')',
|
||||
tldClause = '([a-z\\.]{2,6})',
|
||||
hostClause = domainBodyClause + '\\.' + tldClause,
|
||||
pathClause = '([\\/\\w\\.-]*)*\\/?',
|
||||
negatedPathCharacterSet = '[^\\/\\w\\.-]+',
|
||||
bodyClause = hostClause + pathClause,
|
||||
start = '(?:^|' + negatedDomainCharacterSet + ')(',
|
||||
end = ')($|' + negatedPathCharacterSet + ')',
|
||||
lenientUrlClause = start + protocolClause + '?' + bodyClause + end,
|
||||
strictUrlClause = start + protocolClause + bodyClause + end,
|
||||
lenientUrlRegex = new RegExp(lenientUrlClause),
|
||||
strictUrlRegex = new RegExp(strictUrlClause);
|
||||
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the given terminal line into
|
||||
* hyperlinks. The terminal line can be either the HTML element itself
|
||||
* or the index of the termina line in the children of the terminal
|
||||
* rows container.
|
||||
*
|
||||
* @param {Xterm} terminal - The terminal that owns the given line.
|
||||
* @param {number|HTMLDivElement} line - The terminal line that should get
|
||||
* "linkified".
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
* @emits linkify
|
||||
* @emits linkify:line
|
||||
*/
|
||||
exports.linkifyTerminalLine = function (terminal, line, lenient) {
|
||||
if (typeof line == 'number') {
|
||||
line = terminal.rowContainer.children[line];
|
||||
} else if (! (line instanceof HTMLDivElement)) {
|
||||
var message = 'The "line" argument should be either a number';
|
||||
message += ' or an HTMLDivElement';
|
||||
|
||||
throw new TypeError(message);
|
||||
}
|
||||
|
||||
var buffer = document.createElement('span'),
|
||||
nodes = line.childNodes;
|
||||
|
||||
for (var j=0; j<nodes.length; j++) {
|
||||
var node = nodes[j],
|
||||
match;
|
||||
|
||||
/*
|
||||
* Since we cannot access the TextNode's HTML representation
|
||||
* from the instance itself, we assign its data as textContent
|
||||
* to a dummy buffer span, in order to retrieve the TextNode's
|
||||
* HTML representation from the buffer's innerHTML.
|
||||
*/
|
||||
buffer.textContent = node.data;
|
||||
|
||||
var nodeHTML = buffer.innerHTML;
|
||||
|
||||
/*
|
||||
* Apply function only on TextNodes
|
||||
*/
|
||||
if (node.nodeType != node.TEXT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (lenient) {
|
||||
match = node.data.match(lenientUrlRegex);
|
||||
} else {
|
||||
match = node.data.match(strictUrlRegex);
|
||||
}
|
||||
|
||||
/*
|
||||
* If no URL was found in the current text, return.
|
||||
*/
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var url = match[1],
|
||||
startsWithProtocol = new RegExp('^' + protocolClause),
|
||||
urlHasProtocol = url.match(startsWithProtocol),
|
||||
href = (urlHasProtocol) ? url : 'http://' + url,
|
||||
link = '<a href="' + href + '" >' + url + '</a>',
|
||||
newHTML = nodeHTML.replace(url, link);
|
||||
|
||||
line.innerHTML = line.innerHTML.replace(nodeHTML, newHTML);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event gets emitted when conversion of all URL susbtrings
|
||||
* to HTML anchor elements (links) has finished, for a specific
|
||||
* line of the current Xterm instance.
|
||||
*
|
||||
* @event linkify:line
|
||||
*/
|
||||
terminal.emit('linkify:line', line);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the terminal view into hyperlinks.
|
||||
*
|
||||
* @param {Xterm} terminal - The terminal that should get "linkified".
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
* @emits linkify
|
||||
* @emits linkify:line
|
||||
*/
|
||||
exports.linkify = function (terminal, lenient) {
|
||||
var rows = terminal.rowContainer.children;
|
||||
|
||||
lenient = (typeof lenient == "boolean") ? lenient : true;
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
var line = rows[i];
|
||||
|
||||
exports.linkifyTerminalLine(terminal, line, lenient);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event gets emitted when conversion of all URL substrings to
|
||||
* HTML anchor elements (links) has finished for the current Xterm
|
||||
* instance's view.
|
||||
*
|
||||
* @event linkify
|
||||
*/
|
||||
terminal.emit('linkify');
|
||||
};
|
||||
|
||||
/*
|
||||
* Extend Xterm prototype.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the current terminal linte into
|
||||
* hyperlinks.
|
||||
*
|
||||
* @memberof Xterm
|
||||
* @param {number|HTMLDivElement} line - The terminal line that should get
|
||||
* "linkified".
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
*/
|
||||
Xterm.prototype.linkifyTerminalLine = function (line, lenient) {
|
||||
return exports.linkifyTerminalLine(this, line, lenient);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the current terminal into hyperlinks.
|
||||
*
|
||||
* @memberof Xterm
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
*/
|
||||
Xterm.prototype.linkify = function (lenient) {
|
||||
return exports.linkify(this, lenient);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
#! /usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# $BUILD_DIR should default to "build"
|
||||
BUILD_DIR=${BUILD_DIR:=build}
|
||||
|
||||
# Create the build directory
|
||||
mkdir -p $BUILD_DIR
|
||||
|
||||
|
||||
# Clean out/* to prevent confusion if files were deleted in src/
|
||||
rm -rf out/*
|
||||
|
||||
# Build all TypeScript files (including tests) to out/
|
||||
tsc
|
||||
|
||||
# Concat all xterm.js files into a single file and output as a UMD to $BUILD_DIR/xterm.js
|
||||
browserify ./out/xterm.js --standalone Terminal --debug --outfile ./$BUILD_DIR/xterm.js
|
||||
cat ./$BUILD_DIR/xterm.js | exorcist ./$BUILD_DIR/xterm.js.map -b ./$BUILD_DIR > ./$BUILD_DIR/xterm.temp.js
|
||||
rm ./$BUILD_DIR/xterm.js
|
||||
mv ./$BUILD_DIR/xterm.temp.js ./$BUILD_DIR/xterm.js
|
||||
|
||||
# Resolve the chain of sourcemaps so that ./$BUILD_DIR/xterm.js.map points at ./src
|
||||
sorcery -i $BUILD_DIR/xterm.js
|
||||
|
||||
# Copy all CSS files from src/ to $BUILD_DIR/
|
||||
cd src
|
||||
find . -name '*.css' | cpio -pdm ../$BUILD_DIR
|
||||
cd ..
|
||||
|
||||
# Copy addons from out/ to $BUILD_DIR/
|
||||
cd out/addons
|
||||
find . -name '*.js' | cpio -pdm ../../$BUILD_DIR/addons
|
||||
cd ../..
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#! /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
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#! /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:]]')
|
||||
CURRENT_BOWER_JSON_VERSION=$(cat bower.json \
|
||||
| grep version \
|
||||
| head -1 \
|
||||
| awk -F: '{ print $2 }' \
|
||||
| sed 's/[",]//g' \
|
||||
| tr -d '[[:space:]]')
|
||||
|
||||
# Build xterm.js into `dist`
|
||||
export BUILD_DIR=dist
|
||||
./bin/build
|
||||
|
||||
# Update AUTHORS file
|
||||
sh bin/generate-authors
|
||||
|
||||
# Update version in package.json and bower.json
|
||||
sed -i "s/\"version\": \"$CURRENT_PACKAGE_JSON_VERSION\"/\"version\": \"$NEW_VERSION\"/g" package.json
|
||||
sed -i "s/\"version\": \"$CURRENT_BOWER_JSON_VERSION\"/\"version\": \"$NEW_VERSION\"/g" bower.json
|
||||
|
||||
git commit -S -s -a -m "Bump version to $NEW_VERSION"
|
||||
git tag $NEW_VERSION
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#! /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
|
||||
+6
-2
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"name": "xterm.js",
|
||||
"version": "0.31",
|
||||
"ignore": ["demo", "test", ".gitignore"]
|
||||
"version": "2.2.0",
|
||||
"ignore": ["demo", "test", ".gitignore"],
|
||||
"main": [
|
||||
"dist/xterm.js",
|
||||
"dist/xterm.css"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"source": {
|
||||
"include": [
|
||||
"src/xterm.js",
|
||||
"addons/attach/attach.js",
|
||||
"addons/fit/fit.js",
|
||||
"addons/fullscreen/fullscreen.js",
|
||||
"addons/linkify/linkify.js"
|
||||
]
|
||||
},
|
||||
"opts": {
|
||||
"readme": "README.md"
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var expressWs = require('express-ws')(app);
|
||||
var os = require('os');
|
||||
var pty = require('pty.js');
|
||||
|
||||
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('/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', [], {
|
||||
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 () {
|
||||
process.kill(term.pid);
|
||||
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);
|
||||
+28
-10
@@ -2,18 +2,36 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>xterm.js demo</title>
|
||||
<link rel="stylesheet" href="../src/xterm.css" />
|
||||
<link rel="stylesheet" href="../addons/fullscreen/fullscreen.css" />
|
||||
<link rel="stylesheet" href="/build/xterm.css" />
|
||||
<link rel="stylesheet" href="/build/addons/fullscreen/fullscreen.css" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<script src="../src/xterm.js" ></script>
|
||||
<script src="../addons/fit/fit.js" ></script>
|
||||
<script src="../addons/fullscreen/fullscreen.js" ></script>
|
||||
<script src="main.js" defer ></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/1.0.0/fetch.min.js"></script>
|
||||
<script src="/build/xterm.js" ></script>
|
||||
<script src="/build/addons/attach/attach.js" ></script>
|
||||
<script src="/build/addons/fit/fit.js" ></script>
|
||||
<script src="/build/addons/fullscreen/fullscreen.js" ></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
xterm.js: xterm, in the browser
|
||||
</h1>
|
||||
<h1>xterm.js: xterm, in the browser</h1>
|
||||
<div id="terminal-container"></div>
|
||||
<div>
|
||||
<h2>Options</h2>
|
||||
<label><input type="checkbox" id="option-cursor-blink"> cursorBlink</label>
|
||||
<div>
|
||||
<h3>Size</h3>
|
||||
<div>
|
||||
<div style="display: inline-block; margin-right: 16px;">
|
||||
<label for="cols">Columns</label>
|
||||
<input type="number" id="cols" />
|
||||
</div>
|
||||
<div style="display: inline-block; margin-right: 16px;">
|
||||
<label for="rows">Rows</label>
|
||||
<input type="number" id="rows" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p><strong>Attention:</strong> 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.</p>
|
||||
<script src="main.js" defer ></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
+119
-25
@@ -1,32 +1,126 @@
|
||||
var term,
|
||||
protocol,
|
||||
socketURL,
|
||||
socket,
|
||||
pid,
|
||||
charWidth,
|
||||
charHeight;
|
||||
|
||||
var terminalContainer = document.getElementById('terminal-container'),
|
||||
term = new Terminal(),
|
||||
shellprompt = '> ';
|
||||
optionElements = {
|
||||
cursorBlink: document.querySelector('#option-cursor-blink')
|
||||
},
|
||||
colsElement = document.getElementById('cols'),
|
||||
rowsElement = document.getElementById('rows');
|
||||
|
||||
term.open(terminalContainer);
|
||||
term.fit();
|
||||
function setTerminalSize () {
|
||||
var cols = parseInt(colsElement.value),
|
||||
rows = parseInt(rowsElement.value),
|
||||
width = (cols * charWidth).toString() + 'px',
|
||||
height = (rows * charHeight).toString() + 'px';
|
||||
|
||||
term.prompt = function () {
|
||||
term.write('\r\n' + shellprompt);
|
||||
};
|
||||
terminalContainer.style.width = width;
|
||||
terminalContainer.style.height = height;
|
||||
term.resize(cols, rows);
|
||||
}
|
||||
|
||||
term.writeln('Welcome to xterm.js');
|
||||
term.writeln('Just type some keys in the prompt below.');
|
||||
term.writeln('');
|
||||
term.prompt();
|
||||
colsElement.addEventListener('change', setTerminalSize);
|
||||
rowsElement.addEventListener('change', setTerminalSize);
|
||||
|
||||
term.on('key', function (key, ev) {
|
||||
var printable = (!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey);
|
||||
optionElements.cursorBlink.addEventListener('change', createTerminal);
|
||||
|
||||
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);
|
||||
createTerminal();
|
||||
|
||||
function createTerminal() {
|
||||
// Clean terminal
|
||||
while (terminalContainer.children.length) {
|
||||
terminalContainer.removeChild(terminalContainer.children[0]);
|
||||
}
|
||||
});
|
||||
term = new Terminal({
|
||||
cursorBlink: optionElements.cursorBlink.checked
|
||||
});
|
||||
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();
|
||||
|
||||
var initialGeometry = term.proposeGeometry(),
|
||||
cols = initialGeometry.cols,
|
||||
rows = initialGeometry.rows;
|
||||
|
||||
colsElement.value = cols;
|
||||
rowsElement.value = rows;
|
||||
|
||||
fetch('/terminals?cols=' + cols + '&rows=' + rows, {method: 'POST'}).then(function (res) {
|
||||
|
||||
charWidth = Math.ceil(term.element.offsetWidth / cols);
|
||||
charHeight = Math.ceil(term.element.offsetHeight / rows);
|
||||
|
||||
res.text().then(function (pid) {
|
||||
window.pid = pid;
|
||||
socketURL += pid;
|
||||
socket = new WebSocket(socketURL);
|
||||
socket.onopen = runRealTerminal;
|
||||
socket.onclose = runFakeTerminal;
|
||||
socket.onerror = runFakeTerminal;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
+4
-4
@@ -9,8 +9,8 @@ h1 {
|
||||
}
|
||||
|
||||
#terminal-container {
|
||||
width: 960px;
|
||||
height: 600px;
|
||||
width: 800px;
|
||||
height: 450px;
|
||||
margin: 0 auto;
|
||||
padding: 2px;
|
||||
}
|
||||
@@ -21,6 +21,6 @@ h1 {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
#terminal-container .terminal .terminal-cursor {
|
||||
#terminal-container .terminal:focus .terminal-cursor {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2206
File diff suppressed because it is too large
Load Diff
Vendored
+5157
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"source": {
|
||||
"include": [
|
||||
"src/"
|
||||
],
|
||||
"exclude": [
|
||||
"src/test/"
|
||||
],
|
||||
"excludePattern": "src/.+\\.test\\.(js|ts)$"
|
||||
},
|
||||
"opts": {
|
||||
"readme": "README.md",
|
||||
"template": "node_modules/docdash",
|
||||
"encoding": "utf8",
|
||||
"destination": "docs/",
|
||||
"recurse": true,
|
||||
"verbose": true
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown"
|
||||
],
|
||||
"templates": {
|
||||
"cleverLinks": false,
|
||||
"monospaceLinks": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "xterm",
|
||||
"version": "2.2.0",
|
||||
"ignore": [
|
||||
"demo",
|
||||
"test",
|
||||
".gitignore"
|
||||
],
|
||||
"main": "dist/xterm.js",
|
||||
"repository": "https://github.com/sourcelair/xterm.js",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^3.4.34",
|
||||
"@types/mocha": "^2.2.33",
|
||||
"@types/node": "^6.0.41",
|
||||
"browserify": "^13.1.0",
|
||||
"chai": "3.5.0",
|
||||
"docdash": "0.4.0",
|
||||
"exorcist": "^0.4.0",
|
||||
"express": "4.13.4",
|
||||
"express-ws": "2.0.0-rc.1",
|
||||
"glob": "^7.0.5",
|
||||
"jsdoc": "3.4.3",
|
||||
"mocha": "2.5.3",
|
||||
"nodemon": "1.10.2",
|
||||
"pty.js": "0.3.1",
|
||||
"sleep": "^3.0.1",
|
||||
"sorcery": "^0.10.0",
|
||||
"typescript": "^2.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "nodemon --watch src --watch addons --watch demo --exec bash -c './bin/build && node demo/app'",
|
||||
"test": "./bin/build && mocha --recursive ./out",
|
||||
"build:docs": "jsdoc -c jsdoc.json",
|
||||
"build": "./bin/build"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
import { assert } from 'chai';
|
||||
import { CompositionHelper } from './CompositionHelper';
|
||||
|
||||
describe('CompositionHelper', () => {
|
||||
var terminal;
|
||||
var compositionHelper;
|
||||
var compositionView;
|
||||
var textarea;
|
||||
var handledText;
|
||||
|
||||
beforeEach(() => {
|
||||
compositionView = {
|
||||
classList: {
|
||||
add: () => {},
|
||||
remove: () => {},
|
||||
},
|
||||
getBoundingClientRect: () => {
|
||||
return { width: 0 };
|
||||
},
|
||||
style: {
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
textContent: ''
|
||||
};
|
||||
textarea = {
|
||||
value: '',
|
||||
style: {
|
||||
left: 0,
|
||||
top: 0
|
||||
}
|
||||
};
|
||||
terminal = {
|
||||
element: {
|
||||
querySelector: () => {
|
||||
return { offsetLeft: 0, offsetTop: 0 };
|
||||
}
|
||||
},
|
||||
handler: function (text) {
|
||||
handledText += text;
|
||||
}
|
||||
};
|
||||
handledText = '';
|
||||
compositionHelper = new CompositionHelper(textarea, compositionView, terminal);
|
||||
});
|
||||
|
||||
describe('Input', () => {
|
||||
it('Should insert simple characters', function (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', function (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', function (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', function (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', function (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', function (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', function (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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from './Interfaces';
|
||||
|
||||
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() {
|
||||
this.isComposing = true;
|
||||
this.compositionPosition.start = this.textarea.value.length;
|
||||
this.compositionView.textContent = '';
|
||||
this.compositionView.classList.add('active');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the compositionupdate event, updating the composition view.
|
||||
* @param {CompositionEvent} ev The event.
|
||||
*/
|
||||
public compositionupdate(ev: CompositionEvent) {
|
||||
this.compositionView.textContent = ev.data;
|
||||
this.updateCompositionElements();
|
||||
var self = this;
|
||||
setTimeout(function() {
|
||||
self.compositionPosition.end = self.textarea.value.length;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the compositionend event, hiding the composition view and sending the composition to
|
||||
* the handler.
|
||||
*/
|
||||
public compositionend() {
|
||||
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) {
|
||||
if (this.isComposing || this.isSendingComposition) {
|
||||
if (ev.keyCode === 229) {
|
||||
// Continue composing if the keyCode is the "composition character"
|
||||
return false;
|
||||
} else if (ev.keyCode === 16 || ev.keyCode === 17 || ev.keyCode === 18) {
|
||||
// Continue composing if the keyCode is a modifier key
|
||||
return false;
|
||||
} else {
|
||||
// Finish composition immediately. This is mainly here for the case where enter is
|
||||
// pressed and the handler needs to be triggered before the command is executed.
|
||||
this.finalizeComposition(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.keyCode === 229) {
|
||||
// If the "composition character" is used but gets to this point it means a non-composition
|
||||
// character (eg. numbers and punctuation) was pressed when the IME was active.
|
||||
this.handleAnyTextareaChanges();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the composition, resuming regular input actions. This is called when a composition
|
||||
* is ending.
|
||||
* @param 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) {
|
||||
this.compositionView.classList.remove('active');
|
||||
this.isComposing = false;
|
||||
this.clearTextareaPosition();
|
||||
|
||||
if (!waitForPropogation) {
|
||||
// Cancel any delayed composition send requests and send the input immediately.
|
||||
this.isSendingComposition = false;
|
||||
var input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end);
|
||||
this.terminal.handler(input);
|
||||
} else {
|
||||
// Make a deep copy of the composition position here as a new compositionstart event may
|
||||
// fire before the setTimeout executes.
|
||||
var currentCompositionPosition = {
|
||||
start: this.compositionPosition.start,
|
||||
end: this.compositionPosition.end,
|
||||
}
|
||||
|
||||
// Since composition* events happen before the changes take place in the textarea on most
|
||||
// browsers, use a setTimeout with 0ms time to allow the native compositionend event to
|
||||
// complete. This ensures the correct character is retrieved, this solution was used
|
||||
// because:
|
||||
// - The compositionend event's data property is unreliable, at least on Chromium
|
||||
// - The last compositionupdate event's data property does not always accurately describe
|
||||
// the character, a counter example being Korean where an ending consonsant can move to
|
||||
// the following character if the following input is a vowel.
|
||||
var self = this;
|
||||
this.isSendingComposition = true;
|
||||
setTimeout(function () {
|
||||
// Ensure that the input has not already been sent
|
||||
if (self.isSendingComposition) {
|
||||
self.isSendingComposition = false;
|
||||
var input;
|
||||
if (self.isComposing) {
|
||||
// Use the end position to get the string if a new composition has started.
|
||||
input = self.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
|
||||
} else {
|
||||
// Don't use the end position here in order to pick up any characters after the
|
||||
// composition has finished, for example when typing a non-composition character
|
||||
// (eg. 2) after a composition character.
|
||||
input = self.textarea.value.substring(currentCompositionPosition.start);
|
||||
}
|
||||
self.terminal.handler(input);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any changes made to the textarea after the current event chain is allowed to complete.
|
||||
* This should be called when not currently composing but a keydown event with the "composition
|
||||
* character" (229) is triggered, in order to allow non-composition text to be entered when an
|
||||
* IME is active.
|
||||
*/
|
||||
private handleAnyTextareaChanges() {
|
||||
var oldValue = this.textarea.value;
|
||||
var self = this;
|
||||
setTimeout(function() {
|
||||
// Ignore if a composition has started since the timeout
|
||||
if (!self.isComposing) {
|
||||
var newValue = self.textarea.value;
|
||||
var diff = newValue.replace(oldValue, '');
|
||||
if (diff.length > 0) {
|
||||
self.terminal.handler(diff);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions the composition view on top of the cursor and the textarea just below it (so the
|
||||
* IME helper dialog is positioned correctly).
|
||||
* @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) {
|
||||
if (!this.isComposing) {
|
||||
return;
|
||||
}
|
||||
var cursor = <HTMLElement>this.terminal.element.querySelector('.terminal-cursor');
|
||||
if (cursor) {
|
||||
// Take .xterm-rows offsetTop into account as well in case it's positioned absolutely within
|
||||
// the .xterm element.
|
||||
var xtermRows = <HTMLElement>this.terminal.element.querySelector('.xterm-rows');
|
||||
var cursorTop = xtermRows.offsetTop + cursor.offsetTop;
|
||||
|
||||
this.compositionView.style.left = cursor.offsetLeft + 'px';
|
||||
this.compositionView.style.top = cursorTop + 'px';
|
||||
this.compositionView.style.height = cursor.offsetHeight + 'px';
|
||||
this.compositionView.style.lineHeight = cursor.offsetHeight + 'px';
|
||||
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||
// text is.
|
||||
var compositionViewBounds = this.compositionView.getBoundingClientRect();
|
||||
this.textarea.style.left = cursor.offsetLeft + '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.bind(this, true), 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the textarea's position so that the cursor does not blink on IE.
|
||||
* @private
|
||||
*/
|
||||
private clearTextareaPosition() {
|
||||
this.textarea.style.left = '';
|
||||
this.textarea.style.top = '';
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
function EventEmitter() {
|
||||
this._events = this._events || {};
|
||||
}
|
||||
|
||||
EventEmitter.prototype.addListener = function(type, listener) {
|
||||
this._events[type] = this._events[type] || [];
|
||||
this._events[type].push(listener);
|
||||
};
|
||||
|
||||
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
||||
|
||||
EventEmitter.prototype.removeListener = function(type, listener) {
|
||||
if (!this._events[type]) return;
|
||||
|
||||
var obj = this._events[type]
|
||||
, i = obj.length;
|
||||
|
||||
while (i--) {
|
||||
if (obj[i] === listener || obj[i].listener === listener) {
|
||||
obj.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
||||
|
||||
EventEmitter.prototype.removeAllListeners = function(type) {
|
||||
if (this._events[type]) delete this._events[type];
|
||||
};
|
||||
|
||||
EventEmitter.prototype.once = function(type, listener) {
|
||||
var self = this;
|
||||
function on() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
this.removeListener(type, on);
|
||||
return listener.apply(this, args);
|
||||
}
|
||||
on.listener = listener;
|
||||
return this.on(type, on);
|
||||
};
|
||||
|
||||
EventEmitter.prototype.emit = function(type) {
|
||||
if (!this._events[type]) return;
|
||||
|
||||
var args = Array.prototype.slice.call(arguments, 1)
|
||||
, obj = this._events[type]
|
||||
, l = obj.length
|
||||
, i = 0;
|
||||
|
||||
for (; i < l; i++) {
|
||||
obj[i].apply(this, args);
|
||||
}
|
||||
};
|
||||
|
||||
EventEmitter.prototype.listeners = function(type) {
|
||||
return this._events[type] = this._events[type] || [];
|
||||
};
|
||||
|
||||
export { EventEmitter };
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export interface IBrowser {
|
||||
isNode: boolean;
|
||||
userAgent: string;
|
||||
platform: string;
|
||||
isFirefox: boolean;
|
||||
isMSIE: boolean;
|
||||
isMac: boolean;
|
||||
isIpad: boolean;
|
||||
isIphone: boolean;
|
||||
isMSWindows: boolean;
|
||||
}
|
||||
|
||||
export interface ITerminal {
|
||||
element: HTMLElement;
|
||||
rowContainer: HTMLElement;
|
||||
textarea: HTMLTextAreaElement;
|
||||
ydisp: number;
|
||||
lines: string[];
|
||||
rows: number;
|
||||
browser: IBrowser;
|
||||
|
||||
/**
|
||||
* Emit the 'data' event and populate the given data.
|
||||
* @param data The data to populate in the event.
|
||||
*/
|
||||
handler(data: string);
|
||||
on(event: string, callback: () => void);
|
||||
scrollDisp(disp: number, suppressScrollEvent: boolean);
|
||||
cancel(ev: Event, force?: boolean);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import { assert } from 'chai';
|
||||
import { Viewport } from './Viewport';
|
||||
|
||||
describe('Viewport', () => {
|
||||
var terminal;
|
||||
var viewportElement;
|
||||
var charMeasureElement;
|
||||
var viewport;
|
||||
var scrollAreaElement;
|
||||
|
||||
const CHARACTER_HEIGHT = 10;
|
||||
|
||||
beforeEach(() => {
|
||||
terminal = {
|
||||
lines: [],
|
||||
rows: 0,
|
||||
ydisp: 0,
|
||||
on: () => {},
|
||||
rowContainer: {
|
||||
style: {
|
||||
lineHeight: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
viewportElement = {
|
||||
addEventListener: () => {},
|
||||
style: {
|
||||
height: 0,
|
||||
lineHeight: 0
|
||||
}
|
||||
};
|
||||
scrollAreaElement = {
|
||||
style: {
|
||||
height: 0
|
||||
}
|
||||
};
|
||||
charMeasureElement = {
|
||||
getBoundingClientRect: () => {
|
||||
return { width: null, height: CHARACTER_HEIGHT };
|
||||
}
|
||||
};
|
||||
viewport = new Viewport(terminal, viewportElement, scrollAreaElement, charMeasureElement);
|
||||
});
|
||||
|
||||
describe('refresh', () => {
|
||||
it('should set the line-height of the terminal', () => {
|
||||
assert.equal(viewportElement.style.lineHeight, CHARACTER_HEIGHT + 'px');
|
||||
assert.equal(terminal.rowContainer.style.lineHeight, CHARACTER_HEIGHT + 'px');
|
||||
charMeasureElement.getBoundingClientRect = () => {
|
||||
return { width: null, height: 1 };
|
||||
};
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.lineHeight, '1px');
|
||||
assert.equal(terminal.rowContainer.style.lineHeight, '1px');
|
||||
});
|
||||
it('should set the height of the viewport when the line-height changed', () => {
|
||||
terminal.lines.push('');
|
||||
terminal.lines.push('');
|
||||
terminal.rows = 1;
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
|
||||
charMeasureElement.getBoundingClientRect = () => {
|
||||
return { width: null, height: 20 };
|
||||
};
|
||||
viewport.refresh();
|
||||
assert.equal(viewportElement.style.height, 20 + 'px');
|
||||
});
|
||||
});
|
||||
|
||||
describe('syncScrollArea', () => {
|
||||
it('should sync the scroll area', () => {
|
||||
terminal.lines.push('');
|
||||
terminal.rows = 1;
|
||||
assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px');
|
||||
viewport.syncScrollArea();
|
||||
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
|
||||
assert.equal(scrollAreaElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
|
||||
terminal.lines.push('');
|
||||
viewport.syncScrollArea();
|
||||
assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px');
|
||||
assert.equal(scrollAreaElement.style.height, 2 * CHARACTER_HEIGHT + 'px');
|
||||
});
|
||||
});
|
||||
});
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from './Interfaces';
|
||||
|
||||
/**
|
||||
* Represents the viewport of a terminal, the visible area within the larger buffer of output.
|
||||
* Logic for the virtual scroll bar is included in this object.
|
||||
*/
|
||||
export class Viewport {
|
||||
private currentRowHeight: number;
|
||||
private lastRecordedBufferLength: number;
|
||||
private lastRecordedViewportHeight: number;
|
||||
|
||||
/**
|
||||
* 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 charMeasureElement A DOM element used to measure the character size of. the terminal.
|
||||
*/
|
||||
constructor(
|
||||
private terminal: ITerminal,
|
||||
private viewportElement: HTMLElement,
|
||||
private scrollArea: HTMLElement,
|
||||
private charMeasureElement: HTMLElement
|
||||
) {
|
||||
this.currentRowHeight = 0;
|
||||
this.lastRecordedBufferLength = 0;
|
||||
this.lastRecordedViewportHeight = 0;
|
||||
|
||||
this.terminal.on('scroll', this.syncScrollArea.bind(this));
|
||||
this.terminal.on('resize', this.syncScrollArea.bind(this));
|
||||
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));
|
||||
|
||||
this.syncScrollArea();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes row height, setting line-height, viewport height and scroll area height if
|
||||
* necessary.
|
||||
* @param charSize A character size measurement bounding rect object, if it doesn't exist it will
|
||||
* be created.
|
||||
*/
|
||||
private refresh(charSize?: ClientRect): void {
|
||||
var size = charSize || this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height > 0) {
|
||||
var rowHeightChanged = size.height !== this.currentRowHeight;
|
||||
if (rowHeightChanged) {
|
||||
this.currentRowHeight = size.height;
|
||||
this.viewportElement.style.lineHeight = size.height + 'px';
|
||||
this.terminal.rowContainer.style.lineHeight = size.height + 'px';
|
||||
}
|
||||
var viewportHeightChanged = this.lastRecordedViewportHeight !== this.terminal.rows;
|
||||
if (rowHeightChanged || viewportHeightChanged) {
|
||||
this.lastRecordedViewportHeight = this.terminal.rows;
|
||||
this.viewportElement.style.height = size.height * this.terminal.rows + 'px';
|
||||
}
|
||||
this.scrollArea.style.height = (size.height * this.lastRecordedBufferLength) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates dimensions and synchronizes the scroll area if necessary.
|
||||
*/
|
||||
public syncScrollArea(): void {
|
||||
if (this.lastRecordedBufferLength !== this.terminal.lines.length) {
|
||||
// If buffer height changed
|
||||
this.lastRecordedBufferLength = this.terminal.lines.length;
|
||||
this.refresh();
|
||||
} else if (this.lastRecordedViewportHeight !== this.terminal.rows) {
|
||||
// If viewport height changed
|
||||
this.refresh();
|
||||
} else {
|
||||
// If size has changed, refresh viewport
|
||||
var size = this.charMeasureElement.getBoundingClientRect();
|
||||
if (size.height !== this.currentRowHeight) {
|
||||
this.refresh(size);
|
||||
}
|
||||
}
|
||||
|
||||
// Sync scrollTop
|
||||
var scrollTop = this.terminal.ydisp * this.currentRowHeight;
|
||||
if (this.viewportElement.scrollTop !== scrollTop) {
|
||||
this.viewportElement.scrollTop = scrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles scroll events on the viewport, calculating the new viewport and requesting the
|
||||
* terminal to scroll to it.
|
||||
* @param ev The scroll event.
|
||||
*/
|
||||
private onScroll(ev: Event) {
|
||||
var newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
|
||||
var diff = newRow - this.terminal.ydisp;
|
||||
this.terminal.scrollDisp(diff, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual
|
||||
* scrolling to `onScroll`, this event needs to be attached manually by the consumer of
|
||||
* `Viewport`.
|
||||
* @param ev The mouse wheel event.
|
||||
*/
|
||||
public onWheel(ev: WheelEvent) {
|
||||
if (ev.deltaY === 0) {
|
||||
// Do nothing if it's not a vertical scroll event
|
||||
return;
|
||||
}
|
||||
// Fallback to WheelEvent.DOM_DELTA_PIXEL
|
||||
var multiplier = 1;
|
||||
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
||||
multiplier = this.currentRowHeight;
|
||||
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
|
||||
multiplier = this.currentRowHeight * this.terminal.rows;
|
||||
}
|
||||
this.viewportElement.scrollTop += ev.deltaY * multiplier;
|
||||
// Prevent the page from scrolling when the terminal scrolls
|
||||
ev.preventDefault();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Implements the attach method, that attaches the terminal to a WebSocket stream.
|
||||
* @module xterm/addons/attach/attach
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
(function (attach) {
|
||||
if (typeof exports === 'object' && typeof module === 'object') {
|
||||
/*
|
||||
* CommonJS environment
|
||||
*/
|
||||
module.exports = attach(require('../../xterm'));
|
||||
} else if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../xterm'], attach);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
attach(window.Terminal);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
'use strict';
|
||||
|
||||
var exports = {};
|
||||
|
||||
/**
|
||||
* Attaches the given terminal to the given socket.
|
||||
*
|
||||
* @param {Xterm} term - The terminal to be attached to the given socket.
|
||||
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||
* to the socket as well.
|
||||
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
exports.attach = function (term, socket, bidirectional, buffered) {
|
||||
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
|
||||
term.socket = socket;
|
||||
|
||||
term._flushBuffer = function () {
|
||||
term.write(term._attachSocketBuffer);
|
||||
term._attachSocketBuffer = null;
|
||||
clearTimeout(term._attachSocketBufferTimer);
|
||||
term._attachSocketBufferTimer = null;
|
||||
};
|
||||
|
||||
term._pushToBuffer = function (data) {
|
||||
if (term._attachSocketBuffer) {
|
||||
term._attachSocketBuffer += data;
|
||||
} else {
|
||||
term._attachSocketBuffer = data;
|
||||
setTimeout(term._flushBuffer, 10);
|
||||
}
|
||||
};
|
||||
|
||||
term._getMessage = function (ev) {
|
||||
if (buffered) {
|
||||
term._pushToBuffer(ev.data);
|
||||
} else {
|
||||
term.write(ev.data);
|
||||
}
|
||||
};
|
||||
|
||||
term._sendData = function (data) {
|
||||
socket.send(data);
|
||||
};
|
||||
|
||||
socket.addEventListener('message', term._getMessage);
|
||||
|
||||
if (bidirectional) {
|
||||
term.on('data', term._sendData);
|
||||
}
|
||||
|
||||
socket.addEventListener('close', term.detach.bind(term, socket));
|
||||
socket.addEventListener('error', term.detach.bind(term, socket));
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches the given terminal from the given socket
|
||||
*
|
||||
* @param {Xterm} term - The terminal to be detached from the given socket.
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
exports.detach = function (term, socket) {
|
||||
term.off('data', term._sendData);
|
||||
|
||||
socket = (typeof socket == 'undefined') ? term.socket : socket;
|
||||
|
||||
if (socket) {
|
||||
socket.removeEventListener('message', term._getMessage);
|
||||
}
|
||||
|
||||
delete term.socket;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches the current terminal to the given socket
|
||||
*
|
||||
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||
* to the socket as well.
|
||||
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
Xterm.prototype.attach = function (socket, bidirectional, buffered) {
|
||||
return exports.attach(this, socket, bidirectional, buffered);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches the current terminal from the given socket.
|
||||
*
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
Xterm.prototype.detach = function (socket) {
|
||||
return exports.detach(this, socket);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "xterm.attach",
|
||||
"main": "attach.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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).
|
||||
* @module xterm/addons/fit/fit
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
(function (fit) {
|
||||
if (typeof exports === 'object' && typeof module === 'object') {
|
||||
/*
|
||||
* CommonJS environment
|
||||
*/
|
||||
module.exports = fit(require('../../xterm'));
|
||||
} else if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../xterm'], fit);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
fit(window.Terminal);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
var exports = {};
|
||||
|
||||
exports.proposeGeometry = function (term) {
|
||||
var parentElementStyle = window.getComputedStyle(term.element.parentElement),
|
||||
parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
|
||||
parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')),
|
||||
elementStyle = window.getComputedStyle(term.element),
|
||||
elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
|
||||
elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')),
|
||||
availableHeight = parentElementHeight - elementPaddingVer,
|
||||
availableWidth = parentElementWidth - elementPaddingHor,
|
||||
container = term.rowContainer,
|
||||
subjectRow = term.rowContainer.firstElementChild,
|
||||
contentBuffer = subjectRow.innerHTML,
|
||||
characterHeight,
|
||||
rows,
|
||||
characterWidth,
|
||||
cols,
|
||||
geometry;
|
||||
|
||||
subjectRow.style.display = 'inline';
|
||||
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
|
||||
characterWidth = subjectRow.getBoundingClientRect().width;
|
||||
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
|
||||
characterHeight = parseInt(subjectRow.offsetHeight);
|
||||
subjectRow.innerHTML = contentBuffer;
|
||||
|
||||
rows = parseInt(availableHeight / characterHeight);
|
||||
cols = parseInt(availableWidth / characterWidth) - 1;
|
||||
|
||||
geometry = {cols: cols, rows: rows};
|
||||
return geometry;
|
||||
};
|
||||
|
||||
exports.fit = function (term) {
|
||||
var geometry = exports.proposeGeometry(term);
|
||||
|
||||
term.resize(geometry.cols, geometry.rows);
|
||||
};
|
||||
|
||||
Xterm.prototype.proposeGeometry = function () {
|
||||
return exports.proposeGeometry(this);
|
||||
};
|
||||
|
||||
Xterm.prototype.fit = function () {
|
||||
return exports.fit(this);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "xterm.fit",
|
||||
"main": "fit.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -7,4 +7,4 @@
|
||||
width: auto;
|
||||
height: auto;
|
||||
z-index: 255;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Fullscreen addon for xterm.js
|
||||
* @module xterm/addons/fullscreen/fullscreen
|
||||
* @license MIT
|
||||
*/
|
||||
(function (fullscreen) {
|
||||
if (typeof exports === 'object' && typeof module === 'object') {
|
||||
/*
|
||||
* CommonJS environment
|
||||
*/
|
||||
module.exports = fullscreen(require('../../xterm'));
|
||||
} else if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../xterm'], fullscreen);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
fullscreen(window.Terminal);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
var exports = {};
|
||||
|
||||
/**
|
||||
* Toggle the given terminal's fullscreen mode.
|
||||
* @param {Xterm} term - The terminal to toggle full screen mode
|
||||
* @param {boolean} fullscreen - Toggle fullscreen on (true) or off (false)
|
||||
*/
|
||||
exports.toggleFullScreen = function (term, fullscreen) {
|
||||
var fn;
|
||||
|
||||
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');
|
||||
};
|
||||
|
||||
Xterm.prototype.toggleFullscreen = function (fullscreen) {
|
||||
exports.toggleFullScreen(this, fullscreen);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "xterm.fullscreen",
|
||||
"main": "fullscreen.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Methods for turning URL subscrings in the terminal's content into links (`a` DOM elements).
|
||||
* @module xterm/addons/linkify/linkify
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
(function (linkify) {
|
||||
if (typeof exports === 'object' && typeof module === 'object') {
|
||||
/*
|
||||
* CommonJS environment
|
||||
*/
|
||||
module.exports = linkify(require('../../xterm'));
|
||||
} else if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../xterm'], linkify);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
linkify(window.Terminal);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
'use strict';
|
||||
|
||||
var exports = {},
|
||||
protocolClause = '(https?:\\/\\/)',
|
||||
domainCharacterSet = '[\\da-z\\.-]+',
|
||||
negatedDomainCharacterSet = '[^\\da-z\\.-]+',
|
||||
domainBodyClause = '(' + domainCharacterSet + ')',
|
||||
tldClause = '([a-z\\.]{2,6})',
|
||||
ipClause = '((\\d{1,3}\\.){3}\\d{1,3})',
|
||||
portClause = '(:\\d{1,5})',
|
||||
hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + ')' + portClause + '?',
|
||||
pathClause = '(\\/[\\/\\w\\.-]*)*',
|
||||
negatedPathCharacterSet = '[^\\/\\w\\.-]+',
|
||||
bodyClause = hostClause + pathClause,
|
||||
start = '(?:^|' + negatedDomainCharacterSet + ')(',
|
||||
end = ')($|' + negatedPathCharacterSet + ')',
|
||||
lenientUrlClause = start + protocolClause + '?' + bodyClause + end,
|
||||
strictUrlClause = start + protocolClause + bodyClause + end,
|
||||
lenientUrlRegex = new RegExp(lenientUrlClause),
|
||||
strictUrlRegex = new RegExp(strictUrlClause);
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the given terminal line into
|
||||
* hyperlinks. The terminal line can be either the HTML element itself
|
||||
* or the index of the termina line in the children of the terminal
|
||||
* rows container.
|
||||
*
|
||||
* @param {Xterm} terminal - The terminal that owns the given line.
|
||||
* @param {number|HTMLDivElement} line - The terminal line that should get
|
||||
* "linkified".
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
* @param {string} target - Sets target="" attribute with value provided to links.
|
||||
* Default doesn't set target attribute
|
||||
* @emits linkify
|
||||
* @emits linkify:line
|
||||
*/
|
||||
exports.linkifyTerminalLine = function (terminal, line, lenient, target) {
|
||||
if (typeof line == 'number') {
|
||||
line = terminal.rowContainer.children[line];
|
||||
} else if (! (line instanceof HTMLDivElement)) {
|
||||
var message = 'The "line" argument should be either a number';
|
||||
message += ' or an HTMLDivElement';
|
||||
|
||||
throw new TypeError(message);
|
||||
}
|
||||
|
||||
if (typeof target === 'undefined') {
|
||||
target = '';
|
||||
} else {
|
||||
target = 'target="' + target + '"';
|
||||
}
|
||||
|
||||
var buffer = document.createElement('span'),
|
||||
nodes = line.childNodes;
|
||||
|
||||
for (var j=0; j<nodes.length; j++) {
|
||||
var node = nodes[j],
|
||||
match;
|
||||
|
||||
/**
|
||||
* Since we cannot access the TextNode's HTML representation
|
||||
* from the instance itself, we assign its data as textContent
|
||||
* to a dummy buffer span, in order to retrieve the TextNode's
|
||||
* HTML representation from the buffer's innerHTML.
|
||||
*/
|
||||
buffer.textContent = node.data;
|
||||
|
||||
var nodeHTML = buffer.innerHTML;
|
||||
|
||||
/**
|
||||
* Apply function only on TextNodes
|
||||
*/
|
||||
if (node.nodeType != node.TEXT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var url = exports.findLinkMatch(node.data, lenient);
|
||||
|
||||
if (!url) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var startsWithProtocol = new RegExp('^' + protocolClause),
|
||||
urlHasProtocol = url.match(startsWithProtocol),
|
||||
href = (urlHasProtocol) ? url : 'http://' + url,
|
||||
link = '<a href="' + href + '" ' + target + '>' + url + '</a>',
|
||||
newHTML = nodeHTML.replace(url, link);
|
||||
|
||||
line.innerHTML = line.innerHTML.replace(nodeHTML, newHTML);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event gets emitted when conversion of all URL susbtrings
|
||||
* to HTML anchor elements (links) has finished, for a specific
|
||||
* line of the current Xterm instance.
|
||||
*
|
||||
* @event linkify:line
|
||||
*/
|
||||
terminal.emit('linkify:line', line);
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds a link within a block of text.
|
||||
*
|
||||
* @param {string} text - The text to search .
|
||||
* @param {boolean} lenient - Whether to use the lenient search.
|
||||
* @return {string} A URL.
|
||||
*/
|
||||
exports.findLinkMatch = function (text, lenient) {
|
||||
var match = text.match(lenient ? lenientUrlRegex : strictUrlRegex);
|
||||
if (!match || match.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the terminal view into hyperlinks.
|
||||
*
|
||||
* @param {Xterm} terminal - The terminal that should get "linkified".
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
* @param {string} target - Sets target="" attribute with value provided to links.
|
||||
* Default doesn't set target attribute
|
||||
* @emits linkify
|
||||
* @emits linkify:line
|
||||
*/
|
||||
exports.linkify = function (terminal, lenient, target) {
|
||||
var rows = terminal.rowContainer.children;
|
||||
|
||||
lenient = (typeof lenient == "boolean") ? lenient : true;
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
var line = rows[i];
|
||||
|
||||
exports.linkifyTerminalLine(terminal, line, lenient, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event gets emitted when conversion of all URL substrings to
|
||||
* HTML anchor elements (links) has finished for the current Xterm
|
||||
* instance's view.
|
||||
*
|
||||
* @event linkify
|
||||
*/
|
||||
terminal.emit('linkify');
|
||||
};
|
||||
|
||||
/**
|
||||
* Extend Xterm prototype.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the current terminal linte into
|
||||
* hyperlinks.
|
||||
*
|
||||
* @memberof Xterm
|
||||
* @param {number|HTMLDivElement} line - The terminal line that should get
|
||||
* "linkified".
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
* @param {string} target - Sets target="" attribute with value provided to links.
|
||||
* Default doesn't set target attribute
|
||||
*/
|
||||
Xterm.prototype.linkifyTerminalLine = function (line, lenient, target) {
|
||||
return exports.linkifyTerminalLine(this, line, lenient, target);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the current terminal into hyperlinks.
|
||||
*
|
||||
* @memberof Xterm
|
||||
* @param {boolean} lenient - The regex type that will be used to identify links. If lenient is
|
||||
* false, the regex requires a protocol clause. Defaults to true.
|
||||
* @param {string} target - Sets target="" attribute with value provided to links.
|
||||
* Default doesn't set target attribute
|
||||
*/
|
||||
Xterm.prototype.linkify = function (lenient, target) {
|
||||
return exports.linkify(this, lenient, target);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "xterm.linkify",
|
||||
"main": "linkify.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "xterm.terminado",
|
||||
"main": "terminado.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* This module provides methods for attaching a terminal to a terminado WebSocket stream.
|
||||
*
|
||||
* @module xterm/addons/terminado/terminado
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
(function (attach) {
|
||||
if (typeof exports === 'object' && typeof module === 'object') {
|
||||
/*
|
||||
* CommonJS environment
|
||||
*/
|
||||
module.exports = attach(require('../../xterm'));
|
||||
} else if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../xterm'], attach);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
attach(window.Terminal);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
'use strict';
|
||||
|
||||
var exports = {};
|
||||
|
||||
/**
|
||||
* Attaches the given terminal to the given socket.
|
||||
*
|
||||
* @param {Xterm} term - The terminal to be attached to the given socket.
|
||||
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||
* to the socket as well.
|
||||
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
exports.terminadoAttach = function (term, socket, bidirectional, buffered) {
|
||||
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
|
||||
term.socket = socket;
|
||||
|
||||
term._flushBuffer = function () {
|
||||
term.write(term._attachSocketBuffer);
|
||||
term._attachSocketBuffer = null;
|
||||
clearTimeout(term._attachSocketBufferTimer);
|
||||
term._attachSocketBufferTimer = null;
|
||||
};
|
||||
|
||||
term._pushToBuffer = function (data) {
|
||||
if (term._attachSocketBuffer) {
|
||||
term._attachSocketBuffer += data;
|
||||
} else {
|
||||
term._attachSocketBuffer = data;
|
||||
setTimeout(term._flushBuffer, 10);
|
||||
}
|
||||
};
|
||||
|
||||
term._getMessage = function (ev) {
|
||||
var data = JSON.parse(ev.data)
|
||||
if( data[0] == "stdout" ) {
|
||||
if (buffered) {
|
||||
term._pushToBuffer(data[1]);
|
||||
} else {
|
||||
term.write(data[1]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
term._sendData = function (data) {
|
||||
socket.send(JSON.stringify(['stdin', data]));
|
||||
};
|
||||
|
||||
term._setSize = function (size) {
|
||||
socket.send(JSON.stringify(['set_size', size.rows, size.cols]));
|
||||
};
|
||||
|
||||
socket.addEventListener('message', term._getMessage);
|
||||
|
||||
if (bidirectional) {
|
||||
term.on('data', term._sendData);
|
||||
}
|
||||
term.on('resize', term._setSize);
|
||||
|
||||
socket.addEventListener('close', term.terminadoDetach.bind(term, socket));
|
||||
socket.addEventListener('error', term.terminadoDetach.bind(term, socket));
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches the given terminal from the given socket
|
||||
*
|
||||
* @param {Xterm} term - The terminal to be detached from the given socket.
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
exports.terminadoDetach = function (term, socket) {
|
||||
term.off('data', term._sendData);
|
||||
|
||||
socket = (typeof socket == 'undefined') ? term.socket : socket;
|
||||
|
||||
if (socket) {
|
||||
socket.removeEventListener('message', term._getMessage);
|
||||
}
|
||||
|
||||
delete term.socket;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches the current terminal to the given socket
|
||||
*
|
||||
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||
* to the socket as well.
|
||||
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms.
|
||||
*/
|
||||
Xterm.prototype.terminadoAttach = function (socket, bidirectional, buffered) {
|
||||
return exports.terminadoAttach(this, socket, bidirectional, buffered);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detaches the current terminal from the given socket.
|
||||
*
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal.
|
||||
*/
|
||||
Xterm.prototype.terminadoDetach = function (socket) {
|
||||
return exports.terminadoDetach(this, socket);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { assert } from 'chai';
|
||||
import * as Terminal from '../xterm';
|
||||
import * as Clipboard from './Clipboard';
|
||||
|
||||
|
||||
describe('evaluateCopiedTextProcessing', function () {
|
||||
it('should strip trailing whitespaces and replace nbsps with spaces', function () {
|
||||
var nonBreakingSpace = String.fromCharCode(160),
|
||||
copiedText = 'echo' + nonBreakingSpace + 'hello' + nonBreakingSpace,
|
||||
processedText = Clipboard.prepareTextForClipboard(copiedText);
|
||||
|
||||
// No trailing spaces
|
||||
assert.equal(processedText.match(/\s+$/), null);
|
||||
|
||||
// No non-breaking space
|
||||
assert.equal(processedText.indexOf(nonBreakingSpace), -1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* Clipboard handler module: exports methods for handling all clipboard-related events in the
|
||||
* terminal.
|
||||
* @module xterm/handlers/Clipboard
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { ITerminal } from '../Interfaces';
|
||||
|
||||
// We are extending the ClipboardEvent interface, since TypeScript has not declared the
|
||||
// clientX and clientY properties that we use.
|
||||
interface IClipboardEvent extends ClipboardEvent {
|
||||
clientX: number;
|
||||
clientY: number;
|
||||
}
|
||||
|
||||
interface IWindow extends Window {
|
||||
clipboardData?: {
|
||||
getData(format: string): string;
|
||||
setData(format: string, data: string);
|
||||
};
|
||||
}
|
||||
|
||||
declare var window: IWindow;
|
||||
|
||||
/**
|
||||
* Prepares text copied from terminal selection, to be saved in the clipboard by:
|
||||
* 1. stripping all trailing white spaces
|
||||
* 2. converting all non-breaking spaces to regular spaces
|
||||
* @param {string} text The copied text that needs processing for storing in clipboard
|
||||
* @returns {string}
|
||||
*/
|
||||
export function prepareTextForClipboard(text: string): string {
|
||||
let space = String.fromCharCode(32),
|
||||
nonBreakingSpace = String.fromCharCode(160),
|
||||
allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'),
|
||||
processedText = text.split('\n').map(function (line) {
|
||||
// Strip all trailing white spaces and convert all non-breaking spaces
|
||||
// to regular spaces.
|
||||
let processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
|
||||
|
||||
return processedLine;
|
||||
}).join('\n');
|
||||
|
||||
return processedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds copy functionality to the given terminal.
|
||||
* @param {ClipboardEvent} ev The original copy event to be handled
|
||||
*/
|
||||
export function copyHandler(ev: IClipboardEvent, term: ITerminal) {
|
||||
// We cast `window` to `any` type, because TypeScript has not declared the `clipboardData`
|
||||
// property that we use below for Internet Explorer.
|
||||
let copiedText = window.getSelection().toString(),
|
||||
text = prepareTextForClipboard(copiedText);
|
||||
|
||||
if (term.browser.isMSIE) {
|
||||
window.clipboardData.setData('Text', text);
|
||||
} else {
|
||||
ev.clipboardData.setData('text/plain', text);
|
||||
}
|
||||
|
||||
ev.preventDefault(); // Prevent or the original text will be copied.
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the clipboard's data to the terminal's input handler.
|
||||
* @param {ClipboardEvent} ev The original paste event to be handled
|
||||
* @param {Terminal} term The terminal on which to apply the handled paste event
|
||||
*/
|
||||
export function pasteHandler(ev: IClipboardEvent, term: ITerminal) {
|
||||
ev.stopPropagation();
|
||||
|
||||
let text: string;
|
||||
|
||||
let dispatchPaste = function(text) {
|
||||
term.handler(text);
|
||||
term.textarea.value = '';
|
||||
return 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to right-click event and allow right-click copy and paste.
|
||||
*
|
||||
* **Logic**
|
||||
* If text is selected and right-click happens on selected text, then
|
||||
* do nothing to allow seamless copying.
|
||||
* If no text is selected or right-click is outside of the selection
|
||||
* area, then bring the terminal's input below the cursor, in order to
|
||||
* trigger the event on the textarea and allow-right click paste, without
|
||||
* caring about disappearing selection.
|
||||
* @param {ClipboardEvent} ev The original paste event to be handled
|
||||
* @param {Terminal} term The terminal on which to apply the handled paste event
|
||||
*/
|
||||
export function rightClickHandler(ev: IClipboardEvent, term: ITerminal) {
|
||||
let s = document.getSelection(),
|
||||
selectedText = prepareTextForClipboard(s.toString()),
|
||||
clickIsOnSelection = false,
|
||||
x = ev.clientX,
|
||||
y = ev.clientY;
|
||||
|
||||
if (s.rangeCount) {
|
||||
let r = s.getRangeAt(0),
|
||||
cr = r.getClientRects(),
|
||||
i, rect;
|
||||
|
||||
for (i=0; i<cr.length; i++) {
|
||||
rect = cr[i];
|
||||
clickIsOnSelection = (
|
||||
(x > rect.left) && (x < rect.right) &&
|
||||
(y > rect.top) && (y < rect.bottom)
|
||||
);
|
||||
|
||||
if (clickIsOnSelection) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we clicked on selection and selection is not a single space,
|
||||
// then mark the right click as copy-only. We check for the single
|
||||
// space selection, as this can happen when clicking on an
|
||||
// and there is not much pointing in copying a single space.
|
||||
if (selectedText.match(/^\s$/) || !selectedText.length) {
|
||||
clickIsOnSelection = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bring textarea at the cursor position
|
||||
if (!clickIsOnSelection) {
|
||||
term.textarea.style.position = 'fixed';
|
||||
term.textarea.style.width = '20px';
|
||||
term.textarea.style.height = '20px';
|
||||
term.textarea.style.left = (x - 10) + 'px';
|
||||
term.textarea.style.top = (y - 10) + 'px';
|
||||
term.textarea.style.zIndex = '1000';
|
||||
term.textarea.focus();
|
||||
|
||||
// Reset the terminal textarea's styling
|
||||
setTimeout(function () {
|
||||
term.textarea.style.position = null;
|
||||
term.textarea.style.width = null;
|
||||
term.textarea.style.height = null;
|
||||
term.textarea.style.left = null;
|
||||
term.textarea.style.top = null;
|
||||
term.textarea.style.zIndex = null;
|
||||
}, 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
var assert = require('chai').assert;
|
||||
var Terminal = require('../../xterm');
|
||||
var linkify = require('../../addons/linkify/linkify');
|
||||
|
||||
describe('linkify addon', function () {
|
||||
var xterm;
|
||||
|
||||
describe('API', function () {
|
||||
it('should define Terminal.prototype.linkify', function () {
|
||||
assert.isDefined(Terminal.prototype.linkify);
|
||||
});
|
||||
it('should define Terminal.prototype.linkifyTerminalLine', function () {
|
||||
assert.isDefined(Terminal.prototype.linkifyTerminalLine);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findUrlMatchOnLine', function () {
|
||||
describe('strict regex', function () {
|
||||
it('should match when the entire text is a match', function () {
|
||||
assert.equal(linkify.findLinkMatch('http://github.com', false), 'http://github.com');
|
||||
assert.equal(linkify.findLinkMatch('http://127.0.0.1', false), 'http://127.0.0.1');
|
||||
});
|
||||
it('should match simple domains', function () {
|
||||
assert.equal(linkify.findLinkMatch('foo http://github.com bar', false), 'http://github.com');
|
||||
assert.equal(linkify.findLinkMatch('foo http://www.github.com bar', false), 'http://www.github.com');
|
||||
assert.equal(linkify.findLinkMatch('foo https://github.com bar', false), 'https://github.com');
|
||||
assert.equal(linkify.findLinkMatch('foo https://www.github.com bar', false), 'https://www.github.com');
|
||||
});
|
||||
it('should match web addresses with alpha paths', function () {
|
||||
assert.equal(linkify.findLinkMatch('foo http://github.com/a/b/c bar', false), 'http://github.com/a/b/c');
|
||||
assert.equal(linkify.findLinkMatch('foo http://www.github.com/a/b/c bar', false), 'http://www.github.com/a/b/c');
|
||||
});
|
||||
it('should not include whitespace surrounding a match', function () {
|
||||
assert.equal(linkify.findLinkMatch(' http://github.com', false), 'http://github.com');
|
||||
assert.equal(linkify.findLinkMatch('http://github.com ', false), 'http://github.com');
|
||||
assert.equal(linkify.findLinkMatch(' http://github.com ', false), 'http://github.com');
|
||||
});
|
||||
it('should match IP addresses', function () {
|
||||
assert.equal(linkify.findLinkMatch('foo http://127.0.0.1 bar', false), 'http://127.0.0.1');
|
||||
assert.equal(linkify.findLinkMatch('foo https://127.0.0.1 bar', false), 'https://127.0.0.1');
|
||||
});
|
||||
it('should match ports on both domains and IP addresses', function () {
|
||||
assert.equal(linkify.findLinkMatch('foo http://127.0.0.1:8080 bar', false), 'http://127.0.0.1:8080');
|
||||
assert.equal(linkify.findLinkMatch('foo http://www.github.com:8080 bar', false), 'http://www.github.com:8080');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
var assert = require('chai').assert;
|
||||
var Terminal = require('../../xterm');
|
||||
|
||||
describe('xterm.js addons', function() {
|
||||
it('should load addons with Terminal.loadAddon', function () {
|
||||
Terminal.loadAddon('attach');
|
||||
// Test that addon was loaded successfully
|
||||
assert.equal(typeof Terminal.prototype.attach, 'function');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
var glob = require('glob');
|
||||
var fs = require('fs');
|
||||
var pty = require('pty.js');
|
||||
var sleep = require('sleep');
|
||||
var Terminal = require('../xterm');
|
||||
|
||||
var CONSOLE_LOG = console.log;
|
||||
|
||||
// expect files need terminal at 80x25!
|
||||
var COLS = 80;
|
||||
var 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
|
||||
var primitive_pty = pty.native.open(COLS, ROWS);
|
||||
|
||||
// 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 pty_write_read(s) {
|
||||
fs.writeSync(primitive_pty.slave, s);
|
||||
sleep.usleep(10000);
|
||||
var b = Buffer(64000);
|
||||
var bytes = fs.readSync(primitive_pty.master, b, 0, 64000);
|
||||
return b.toString('utf8', 0, bytes);
|
||||
}
|
||||
|
||||
// make sure raw pty is at x=0 and has no pending data
|
||||
function pty_reset() {
|
||||
pty_write_read('\r\n');
|
||||
}
|
||||
|
||||
/* debug helpers */
|
||||
// generate colorful noisy output to compare xterm and emulator cell states
|
||||
function formatError(in_, out_, expected) {
|
||||
function addLineNumber(start, color) {
|
||||
var counter = start || 0;
|
||||
return function(s) {
|
||||
counter += 1;
|
||||
return '\x1b[33m' + (' ' + counter).slice(-2) + color + s;
|
||||
}
|
||||
}
|
||||
var line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890';
|
||||
var s = '';
|
||||
s += '\n\x1b[34m' + JSON.stringify(in_);
|
||||
s += '\n\x1b[33m ' + line80 + '\n';
|
||||
s += out_.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) {
|
||||
var result = '';
|
||||
var line_s = '';
|
||||
for (var line=0; line<term.rows; ++line) {
|
||||
line_s = '';
|
||||
for (var cell=0; cell<term.cols; ++cell) {
|
||||
line_s += term.lines[line][cell][1];
|
||||
}
|
||||
// rtrim empty cells as xterm does
|
||||
line_s = line_s.replace(/\s+$/, '');
|
||||
result += line_s;
|
||||
result += '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** tests */
|
||||
describe('xterm output comparison', function() {
|
||||
var xterm;
|
||||
|
||||
beforeEach(function () {
|
||||
xterm = new Terminal(COLS, ROWS);
|
||||
xterm.refresh = function() {};
|
||||
});
|
||||
|
||||
// omit stack trace for escape sequence files
|
||||
Error.stackTraceLimit = 0;
|
||||
var files = glob.sync('**/escape_sequence_files/*.in');
|
||||
// only successful tests for now
|
||||
var successful = [0, 2, 6, 12, 13, 18, 20, 22, 27, 28];
|
||||
console.log(files);
|
||||
for (var a in successful) {
|
||||
var i = successful[a];
|
||||
(function(filename){
|
||||
it(filename.split('/').slice(-1)[0], function () {
|
||||
pty_reset();
|
||||
var in_file = fs.readFileSync(filename, 'utf8');
|
||||
var from_pty = pty_write_read(in_file);
|
||||
// uncomment this to get log from terminal
|
||||
console.log = function(){};
|
||||
xterm.write(from_pty);
|
||||
var from_emulator = terminalToString(xterm);
|
||||
console.log = CONSOLE_LOG;
|
||||
var expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
|
||||
if (from_emulator != expected) {
|
||||
// uncomment to get noisy output
|
||||
//throw new Error(formatError(in_file, from_emulator, expected));
|
||||
throw new Error('mismatch');
|
||||
}
|
||||
});
|
||||
})(files[i]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
All 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
|
||||
@@ -0,0 +1,6 @@
|
||||
!"#$%&'()*+,-./
|
||||
0123456789:;<=>?
|
||||
@ABCDEFGHIJKLMNO
|
||||
PQRSTUVWXYZ[\]^_
|
||||
`abcdefghijklmno
|
||||
pqrstuvwxyz{|}~
|
||||
@@ -0,0 +1,25 @@
|
||||
!"#$%&'()*+,-./
|
||||
0123456789:;<=>?
|
||||
@ABCDEFGHIJKLMNO
|
||||
PQRSTUVWXYZ[\]^_
|
||||
`abcdefghijklmno
|
||||
pqrstuvwxyz{|}~
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
!
|
||||
"
|
||||
#
|
||||
$
|
||||
%
|
||||
&
|
||||
'
|
||||
(
|
||||
)
|
||||
*
|
||||
+
|
||||
,
|
||||
-
|
||||
.
|
||||
/
|
||||
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
|
||||
{
|
||||
|
|
||||
}
|
||||
~
|
||||
@@ -0,0 +1,25 @@
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
y
|
||||
z
|
||||
{
|
||||
|
|
||||
}
|
||||
~
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
abcdefghijklmnopqrstuvwxyz0123456789
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdefghijklmnopqrstuvwxyz0123456789
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
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
|
||||
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
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890a
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ab
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abc
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdef
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijk
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkl
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
r
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
rs
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
rst
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
1
|
||||
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
|
||||
@@ -0,0 +1,25 @@
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
||||
16
|
||||
17
|
||||
18
|
||||
19
|
||||
20
|
||||
21
|
||||
22
|
||||
23
|
||||
24
|
||||
25
|
||||
26
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
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
|
||||
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
|
||||
@@ -0,0 +1,25 @@
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
1␍x
|
||||
2␍x
|
||||
3␍x
|
||||
4␍x
|
||||
5␍x
|
||||
6␍x
|
||||
7␍x
|
||||
@@ -0,0 +1,25 @@
|
||||
x
|
||||
x2
|
||||
x 3
|
||||
x 4
|
||||
x 5
|
||||
x 6
|
||||
x 7
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
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
|
||||
b␍a
|
||||
@@ -0,0 +1,25 @@
|
||||
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
|
||||
|
||||
ab
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
aDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDuDvDwDxDyDzDADBDCDDDEDFDGDHDIDJDKDLDMDNDODPDQDRDSDTDUDVDWDXDYDZD0D1D2D3D4D5D6D7D8D9D0DaDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDt
|
||||
@@ -0,0 +1,25 @@
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
0 space:
|
||||
1 space:
|
||||
2 space:
|
||||
3 space:
|
||||
70 space:
|
||||
71 space:
|
||||
72 space:
|
||||
73 space:
|
||||
@@ -0,0 +1,25 @@
|
||||
0 space:
|
||||
1 space:
|
||||
2 space:
|
||||
3 space:
|
||||
70 space:
|
||||
71 space:
|
||||
72 space:
|
||||
|
||||
73 space:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
abcdefghijklmnopqrstuvwxyz!
|
||||
abc@
|
||||
#
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop$
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq%
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr^
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrs&
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdefghijklmnopqrst!vwxyz
|
||||
@bc
|
||||
#
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij$lmnop
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij%lmnopq
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
^
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
&s
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
aEabEabcEabcdEabcdeEabcdefEabcdefgEabcdefghEabcdefghiEabcdefghijEabcdefghijkEabcdefghijklEabcdefghijklmEabcdefghijklmnEabcdefghijklmnoEabcdefghijklmnopEabcdefghijklmnopqEabcdefghijklmnopqrEabcdefghijklmnopqrsEabcdefghijklmnopqrstEabcdefghijklmnopqrstuEabcdefghijklmnopqrstuvEabcdefghijklmnopqrstuvwEabcdefghijklmnopqrstuvwxEabcdefghijklmnopqrstuvwxyEabcdefghijklmnopqrstuvwxyzEabcdefghijklmnopqrstuvwxyzAEabcdefghijklmnopqrstuvwxyzABEabcdefghijklmnopqrstuvwxyzABCEabcdefghijklmnopqrstuvwxyzABCDEabcdefghijklmnopqrstuvwxyzABCDEEabcdefghijklmnopqrstuvwxyzABCDEFEabcdefghijklmnopqrstuvwxyzABCDEFGEabcdefghijklmnopqrstuvwxyzABCDEFGHEabcdefghijklmnopqrstuvwxyzABCDEFGHIEabcdefghijklmnopqrstuvwxyzABCDEFGHIJEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQREabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890EabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890aEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdeEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghiEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnoEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrsEabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrst
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890a
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890ab
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abc
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcd
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcde
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdef
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefg
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefgh
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghi
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghij
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijk
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijkl
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
r
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
rs
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
rst
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
dMeMfMg
|
||||
h
|
||||
i
|
||||
j....................................................................kMlMmMn
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
a g n
|
||||
h f m
|
||||
ie l
|
||||
j....................................................................k
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
And 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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
This should be the first line.
|
||||
This is second line.
|
||||
And the third.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
This should be the last line.
|
||||
This one should be lost.
|
||||
@@ -0,0 +1 @@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrst␍
|
||||
@@ -0,0 +1,25 @@
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqrst␍
|
||||
@@ -0,0 +1,25 @@
|
||||
8
|
||||
9
|
||||
0
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcd!Defgh
|
||||
abcd!*Defgh
|
||||
abcd[Defgh
|
||||
abcd[!Defgh
|
||||
abcd[2Defgh
|
||||
abcd[*2;Defgh
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcd!Defgh
|
||||
abcd!*Defgh
|
||||
abcd[Defgh
|
||||
abcd[!Defgh
|
||||
abcd[2Defgh
|
||||
abcd[*2;Defgh
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
abcdDefgh
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Hello[SGoodbye
|
||||
|
||||
Up[3SDown
|
||||
x
|
||||
[2S
|
||||
-----------------------------------------------------------------------------[Sx
|
||||
------------------------------------------------------------------------------[Sx
|
||||
-------------------------------------------------------------------------------[Sx
|
||||
--------------------------------------------------------------------------------[Sx
|
||||
---------------------------------------------------------------------------------[Sx
|
||||
.............................................................................[Sx
|
||||
..............................................................................[Sx
|
||||
...............................................................................[Sx
|
||||
................................................................................[Sx
|
||||
.................................................................................[Sx
|
||||
[30S
|
||||
The End.
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
The End.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
H
|
||||
I
|
||||
J
|
||||
K
|
||||
L
|
||||
M
|
||||
N
|
||||
O
|
||||
P
|
||||
Q
|
||||
R
|
||||
S
|
||||
T
|
||||
U
|
||||
V
|
||||
W
|
||||
X[3S
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e[3T
|
||||
f
|
||||
g
|
||||
h
|
||||
------------------------------------------------------------------------------[T1
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------[T2
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------[T3
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------[T4
|
||||
|
||||
|
||||
..............................................................................[T5
|
||||
|
||||
|
||||
...............................................................................[T6
|
||||
|
||||
|
||||
................................................................................[T7
|
||||
|
||||
|
||||
.................................................................................[T8
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
a
|
||||
b
|
||||
f
|
||||
g
|
||||
h 1
|
||||
|
||||
2
|
||||
|
||||
|
||||
3
|
||||
|
||||
|
||||
-4------------------------------------------------------------------------------
|
||||
|
||||
5
|
||||
|
||||
6
|
||||
|
||||
7
|
||||
|
||||
|
||||
8...............................................................................
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
abcdefg[Chijkl
|
||||
abcdefg[10Chijkl
|
||||
abcdefg[10;3Chijkl
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno[C@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop[C@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq[C@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr[C@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm[3C@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm[4C@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm[5C@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm[6C@
|
||||
[79Cx
|
||||
[80Cx
|
||||
abcd[10C
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdefg hijkl
|
||||
abcdefg hijkl
|
||||
abcdefg hijkl
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno @
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
r @
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklm @
|
||||
x
|
||||
x
|
||||
abcd
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
abcdefg[D!@
|
||||
abcdefg[10D!@
|
||||
abcdefg[2;3D!@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno[D@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnop[D@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq[D@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopqr[D@
|
||||
[Dx
|
||||
@@ -0,0 +1,25 @@
|
||||
abcdef!@
|
||||
!@cdefg
|
||||
abcde!@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmn@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmno@q
|
||||
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890abcdefghijklmnopq
|
||||
@
|
||||
x
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d[Ae[Af[Ag
|
||||
h
|
||||
i
|
||||
j....................................................................k[Al[Am[An
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[0A0[1A1[2A2[3;5A3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user