Compare commits
528 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da82b3426c | ||
|
|
5230ab6764 | ||
|
|
67685d0700 | ||
|
|
15e733f533 | ||
|
|
b2cdd55859 | ||
|
|
3daa86cbae | ||
|
|
5fd3f88e50 | ||
|
|
ae11605141 | ||
|
|
a1dbbcc1a7 | ||
|
|
3f2c25a60f | ||
|
|
682fd02be6 | ||
|
|
a20a898765 | ||
|
|
4d26f58e0d | ||
|
|
95dd60011c | ||
|
|
afcf031a68 | ||
|
|
fdf21468d3 | ||
|
|
9e45354efc | ||
|
|
ab81ddf5d4 | ||
|
|
e00698fef8 | ||
|
|
cd611a5326 | ||
|
|
4e471b5b55 | ||
|
|
45c70c9e23 | ||
|
|
0bd2cbacaa | ||
|
|
29475d7728 | ||
|
|
777cb7a0c5 | ||
|
|
553864e858 | ||
|
|
f52105bc88 | ||
|
|
057cfc7cb4 | ||
|
|
12ae8b3d50 | ||
|
|
3120c9b5ec | ||
|
|
cf0623fffa | ||
|
|
f6a29ddeeb | ||
|
|
32df3fdbe1 | ||
|
|
27e77d468f | ||
|
|
f620259bc5 | ||
|
|
529c64e103 | ||
|
|
8b0a0f6637 | ||
|
|
3c9558444c | ||
|
|
bf0ce0ba3c | ||
|
|
c8294760b1 | ||
|
|
b403cb92fb | ||
|
|
7bc383e8b6 | ||
|
|
670dbddb54 | ||
|
|
317042f8db | ||
|
|
c55f05f619 | ||
|
|
28646d978f | ||
|
|
4ee55e0126 | ||
|
|
464944844f | ||
|
|
0252c7f766 | ||
|
|
7e4475fa92 | ||
|
|
40037b6a29 | ||
|
|
c8f14d175b | ||
|
|
b2a813dc73 | ||
|
|
54e835eeac | ||
|
|
494b407a0a | ||
|
|
6a90803feb | ||
|
|
3e3df4dbb1 | ||
|
|
a0e7ab43dc | ||
|
|
c802d93189 | ||
|
|
045d922406 | ||
|
|
40b35fa20d | ||
|
|
89bdc8ce48 | ||
|
|
a369a80c24 | ||
|
|
bb180145c6 | ||
|
|
340290fa1d | ||
|
|
abf2b09ea7 | ||
|
|
a825582196 | ||
|
|
f00193e08f | ||
|
|
b0b5fc55e1 | ||
|
|
07f514d887 | ||
|
|
9ff86fb718 | ||
|
|
d1800d0960 | ||
|
|
38781d931e | ||
|
|
6c883653a2 | ||
|
|
6940936ffc | ||
|
|
0442e153a1 | ||
|
|
efed2eeafd | ||
|
|
bc4414f5b1 | ||
|
|
8f3c0f6b9b | ||
|
|
a6357e8276 | ||
|
|
6e296bfa8a | ||
|
|
7d1dc09ad0 | ||
|
|
b098afc234 | ||
|
|
74f2ac968d | ||
|
|
30bfff81d9 | ||
|
|
4f19e5c697 | ||
|
|
31ddaa1c7a | ||
|
|
29a0e6a8a3 | ||
|
|
539955ff27 | ||
|
|
2bcfd58667 | ||
|
|
1138bdd4b7 | ||
|
|
cb3e4deb73 | ||
|
|
613f05eea0 | ||
|
|
8af9b6910c | ||
|
|
48d26b2d47 | ||
|
|
8ce27ddb4b | ||
|
|
f9ffb21b6b | ||
|
|
cd1ab146bb | ||
|
|
245dc8669b | ||
|
|
d9fc1c7be4 | ||
|
|
58ded70d15 | ||
|
|
2ace90e6d5 | ||
|
|
16b3ef77d1 | ||
|
|
cefc9a9177 | ||
|
|
fdedbafb1d | ||
|
|
798340b98d | ||
|
|
3b8ec46fd2 | ||
|
|
205d1a11ce | ||
|
|
72747869a7 | ||
|
|
8b46c0deb0 | ||
|
|
7e161007ab | ||
|
|
18c34c4766 | ||
|
|
7e54fb93dd | ||
|
|
20d3fb6665 | ||
|
|
88224c3fa7 | ||
|
|
4043d8bb12 | ||
|
|
e543525faa | ||
|
|
2ec29db752 | ||
|
|
fe8a4dc9d8 | ||
|
|
9db6a90677 | ||
|
|
5cd6de495f | ||
|
|
985c433867 | ||
|
|
5108c4635c | ||
|
|
dedf9affbf | ||
|
|
1188993c0f | ||
|
|
4e534a804e | ||
|
|
6f5148648b | ||
|
|
f675e03ccc | ||
|
|
4dec490aae | ||
|
|
f8b399d7df | ||
|
|
757653c007 | ||
|
|
a2158362d9 | ||
|
|
b0ec6509f1 | ||
|
|
636be753b2 | ||
|
|
5b7598ac6b | ||
|
|
a437079307 | ||
|
|
e9f55ea0f8 | ||
|
|
455f8f3fd0 | ||
|
|
b804b3e458 | ||
|
|
155d78b399 | ||
|
|
dc4b6301c8 | ||
|
|
fc00821eba | ||
|
|
0331495382 | ||
|
|
282834caf1 | ||
|
|
ed7f8c3886 | ||
|
|
4865278dee | ||
|
|
b2e8311de6 | ||
|
|
df89129ff0 | ||
|
|
1f84c99938 | ||
|
|
40ac6f0ab6 | ||
|
|
c42ea22525 | ||
|
|
fda40d8927 | ||
|
|
bd6874e087 | ||
|
|
795fca23dc | ||
|
|
a86f97e284 | ||
|
|
dbaf49f570 | ||
|
|
ec31f82eda | ||
|
|
e2f1ff8048 | ||
|
|
58ca1978ea | ||
|
|
3257d9f265 | ||
|
|
2930403a11 | ||
|
|
f9fd0313b8 | ||
|
|
58529d347b | ||
|
|
b11bb5c385 | ||
|
|
0b0b0433b5 | ||
|
|
3b4fd003c2 | ||
|
|
53762c31fe | ||
|
|
cfc02e5e2b | ||
|
|
d02a99f0c8 | ||
|
|
f0e4548b16 | ||
|
|
e4e9a9b97f | ||
|
|
2c9623b5a7 | ||
|
|
f8f95d6023 | ||
|
|
77bd04f833 | ||
|
|
7caa9c20c2 | ||
|
|
0ca7cf4867 | ||
|
|
e6af0f60b0 | ||
|
|
bbbf42bb5a | ||
|
|
b1dee94788 | ||
|
|
1e13775bd5 | ||
|
|
2cccf7530c | ||
|
|
ee7d4c61c6 | ||
|
|
31f169e86f | ||
|
|
d6e281baf6 | ||
|
|
d21cd6c164 | ||
|
|
fb64ed2135 | ||
|
|
f8e9b9f1bf | ||
|
|
95eb681bbb | ||
|
|
91127741be | ||
|
|
960752ea53 | ||
|
|
9b731d3a58 | ||
|
|
93af721a27 | ||
|
|
7187bc121f | ||
|
|
d906dfc953 | ||
|
|
4a4643c05d | ||
|
|
dd3a8a1b6d | ||
|
|
d8c0953567 | ||
|
|
c77938efc9 | ||
|
|
082027dc87 | ||
|
|
60a415ae1c | ||
|
|
8f4a291b16 | ||
|
|
eeda79d3c3 | ||
|
|
0bb6a8c50e | ||
|
|
de8edde4a0 | ||
|
|
74d3d75a4a | ||
|
|
b98fed2f69 | ||
|
|
df0ee70e36 | ||
|
|
7cad15425e | ||
|
|
23817e1516 | ||
|
|
0fe30338a0 | ||
|
|
2fa1c0988a | ||
|
|
2db62a9528 | ||
|
|
b7996b048b | ||
|
|
6227a91c01 | ||
|
|
f1e6daf3c8 | ||
|
|
230784066c | ||
|
|
c3f6052435 | ||
|
|
97362c3980 | ||
|
|
c70000bac4 | ||
|
|
c7fc3c082f | ||
|
|
e791b87572 | ||
|
|
fb35d50f6a | ||
|
|
a856a051da | ||
|
|
03ab251587 | ||
|
|
df6b7d7341 | ||
|
|
afbeb98cfb | ||
|
|
20074a49eb | ||
|
|
d86cc2d911 | ||
|
|
98c1275d80 | ||
|
|
f6a1d98a3a | ||
|
|
85e8991664 | ||
|
|
d823e8956e | ||
|
|
8eb88937cc | ||
|
|
466a09f0f3 | ||
|
|
bf47095944 | ||
|
|
2ea40fdf9a | ||
|
|
fa30469cda | ||
|
|
9ceef041c6 | ||
|
|
f00b6fb69a | ||
|
|
4ef7566b10 | ||
|
|
ae52883b93 | ||
|
|
1e570156f9 | ||
|
|
e0dc102e5b | ||
|
|
2af865923c | ||
|
|
75d69b9f62 | ||
|
|
f0d30a90f3 | ||
|
|
ad941fadde | ||
|
|
f901d9c9b8 | ||
|
|
be09828537 | ||
|
|
142aa4583c | ||
|
|
f4f72e9db9 | ||
|
|
69127447ac | ||
|
|
3346f9229b | ||
|
|
270ae2f9f5 | ||
|
|
5eed5a4e17 | ||
|
|
7c76fd32a1 | ||
|
|
4c0b680a0b | ||
|
|
f8ddfc732d | ||
|
|
9b9e741b8c | ||
|
|
97aefe5f83 | ||
|
|
82744aa8ee | ||
|
|
eb955f8c20 | ||
|
|
968431dd46 | ||
|
|
b4a979a07e | ||
|
|
04d6a8347f | ||
|
|
76e262134e | ||
|
|
3cb89f5a14 | ||
|
|
c39df031d8 | ||
|
|
292f6a5da7 | ||
|
|
bd88b94393 | ||
|
|
9d04096e58 | ||
|
|
53c01a2353 | ||
|
|
41c66fbfcd | ||
|
|
7e24f50b66 | ||
|
|
f3ff971db9 | ||
|
|
31164434b2 | ||
|
|
fb4394b10b | ||
|
|
dfcedffc16 | ||
|
|
df4d6dde30 | ||
|
|
0e3d505e54 | ||
|
|
df9d3d9cf2 | ||
|
|
406a8b4e96 | ||
|
|
0019d3b055 | ||
|
|
35b29c98ea | ||
|
|
6ca8a2c05c | ||
|
|
7ab02c7fc7 | ||
|
|
c6ad20992d | ||
|
|
cf19ad3798 | ||
|
|
a4ec2f5c7d | ||
|
|
9e97231acf | ||
|
|
cc704b712d | ||
|
|
0fa4e0a90a | ||
|
|
b2f1961a3a | ||
|
|
ca9a9964a0 | ||
|
|
c3c51ed32b | ||
|
|
8f12ca7a5a | ||
|
|
b66ffcddcf | ||
|
|
e6500004de | ||
|
|
58873b3222 | ||
|
|
c42136d537 | ||
|
|
44ff863273 | ||
|
|
1e04775831 | ||
|
|
9f554fcdf7 | ||
|
|
7c1cd93744 | ||
|
|
e83b9e03eb | ||
|
|
7dc038efd9 | ||
|
|
cededc4d14 | ||
|
|
de0f8773b1 | ||
|
|
60d991774b | ||
|
|
ec34af8f61 | ||
|
|
33f5d3bd92 | ||
|
|
9f0d23de4d | ||
|
|
b597bdd1ec | ||
|
|
c2b1409a43 | ||
|
|
e84101b3e7 | ||
|
|
3b2acc2258 | ||
|
|
301fc915a6 | ||
|
|
b1b342a97e | ||
|
|
0d0f754aad | ||
|
|
8a147535de | ||
|
|
49d5cee91e | ||
|
|
37a3081f05 | ||
|
|
d55f5377bb | ||
|
|
902c6fb37f | ||
|
|
705c54edb6 | ||
|
|
361cde7c96 | ||
|
|
f167ea0ecc | ||
|
|
3516bdf3fa | ||
|
|
51562999c7 | ||
|
|
e7e6660272 | ||
|
|
49578da448 | ||
|
|
c95456c0e1 | ||
|
|
73ee4fa7cc | ||
|
|
5514d29950 | ||
|
|
57430b8c12 | ||
|
|
e881ce5a1b | ||
|
|
8e2d749605 | ||
|
|
60106f240a | ||
|
|
c527482ebf | ||
|
|
35785a9081 | ||
|
|
6f4b1e4071 | ||
|
|
6f4cbb3f3c | ||
|
|
7a6c94b6b9 | ||
|
|
e16ad2fd02 | ||
|
|
1d728ace69 | ||
|
|
ab7674ff73 | ||
|
|
c76b2a8101 | ||
|
|
079504a6d2 | ||
|
|
8dfd916946 | ||
|
|
8534fea664 | ||
|
|
d5fe15096b | ||
|
|
6f955236e6 | ||
|
|
5f03c3d093 | ||
|
|
2cde6e4380 | ||
|
|
26945049be | ||
|
|
ee1af44161 | ||
|
|
3af1c2751b | ||
|
|
51fc3b5f03 | ||
|
|
dbec398406 | ||
|
|
8ec3cfaacd | ||
|
|
07392d4fb8 | ||
|
|
204675c85d | ||
|
|
3435491edb | ||
|
|
39188f4ea8 | ||
|
|
ceec05260e | ||
|
|
cbb55df039 | ||
|
|
625040fc90 | ||
|
|
af6e9e2408 | ||
|
|
cf068be2d8 | ||
|
|
fa5b334dcb | ||
|
|
fcff386b92 | ||
|
|
fc003a13e7 | ||
|
|
ca6b3f4cb1 | ||
|
|
14717eb468 | ||
|
|
4dd1bb1ecb | ||
|
|
50b81d4470 | ||
|
|
af1527b276 | ||
|
|
e5d5a7d3fd | ||
|
|
d58f8b5144 | ||
|
|
18232af2a6 | ||
|
|
b50f340674 | ||
|
|
2090f1af67 | ||
|
|
9450f132c6 | ||
|
|
9ebc84f2e9 | ||
|
|
a997c5fd94 | ||
|
|
12acb663eb | ||
|
|
d5575ef6f3 | ||
|
|
90f18a8a96 | ||
|
|
bee36506e1 | ||
|
|
cb3ad1b5b5 | ||
|
|
b1d2225eee | ||
|
|
8d0c7fb5ac | ||
|
|
9e2351f5eb | ||
|
|
ab44059b8b | ||
|
|
a04087e44c | ||
|
|
9f54c23629 | ||
|
|
72a5596e50 | ||
|
|
34d8b844ae | ||
|
|
a0726a4b56 | ||
|
|
57044a3c0a | ||
|
|
dc6e501f20 | ||
|
|
e472e6ce3a | ||
|
|
17bb6ca38e | ||
|
|
df4fa13b89 | ||
|
|
53dfab7fb7 | ||
|
|
bc28395abf | ||
|
|
a117514183 | ||
|
|
58ad83878c | ||
|
|
02d1f19b5f | ||
|
|
4910600b71 | ||
|
|
ca78183171 | ||
|
|
901e565503 | ||
|
|
483157f87f | ||
|
|
dcf1994d3e | ||
|
|
c390011099 | ||
|
|
f8380ff939 | ||
|
|
f736c3316a | ||
|
|
cbd3435a07 | ||
|
|
bd96e91932 | ||
|
|
0139b2562c | ||
|
|
4c75210a4d | ||
|
|
f84504bc63 | ||
|
|
c76b3e4b26 | ||
|
|
6671c7624d | ||
|
|
ce86f5c954 | ||
|
|
c0c20581f5 | ||
|
|
f2a495c944 | ||
|
|
b8dd87c757 | ||
|
|
d065cad99e | ||
|
|
a09a75e8f2 | ||
|
|
4cd0070a1c | ||
|
|
35d7574b09 | ||
|
|
d38db74abd | ||
|
|
0c4f4b598c | ||
|
|
c6f3919cb0 | ||
|
|
3b302463f8 | ||
|
|
23ff806774 | ||
|
|
94bcf652b3 | ||
|
|
0ce93900ae | ||
|
|
9e6e6662c7 | ||
|
|
32f135d730 | ||
|
|
1af3e54bef | ||
|
|
6a02f88e4d | ||
|
|
9b75bcaada | ||
|
|
a14b8fae2a | ||
|
|
2cedf48397 | ||
|
|
c514fd5e1c | ||
|
|
5ca5e2d8cd | ||
|
|
6fbc37489f | ||
|
|
b0ac240f31 | ||
|
|
523cc4d6ab | ||
|
|
0fa6748c52 | ||
|
|
a820f1267a | ||
|
|
c577ca2305 | ||
|
|
de84e09854 | ||
|
|
682f33a790 | ||
|
|
e79917c3db | ||
|
|
a9a7f0e18c | ||
|
|
ff4bfcb773 | ||
|
|
f66bcda31b | ||
|
|
86ebeef709 | ||
|
|
f2d856767d | ||
|
|
2fa565b375 | ||
|
|
b688a909b0 | ||
|
|
06a9ef0c6c | ||
|
|
c3a172b98c | ||
|
|
aa67056785 | ||
|
|
91b0dc3915 | ||
|
|
4f545a5ba6 | ||
|
|
83ed7b8c25 | ||
|
|
1c92807a8e | ||
|
|
bc71506553 | ||
|
|
2e7d81ce24 | ||
|
|
fa4607dbf5 | ||
|
|
a538be4350 | ||
|
|
a367e84a92 | ||
|
|
1e50871599 | ||
|
|
65db2d8243 | ||
|
|
b29e06f36e | ||
|
|
f4bce78307 | ||
|
|
5299db1a00 | ||
|
|
938daad1c9 | ||
|
|
1709ee3ab7 | ||
|
|
479bfa9964 | ||
|
|
a7f5589946 | ||
|
|
6209639f2c | ||
|
|
7cd6118ce5 | ||
|
|
490d471c53 | ||
|
|
859fc7f18f | ||
|
|
1310606305 | ||
|
|
a7db50597c | ||
|
|
c506a48154 | ||
|
|
a5df24b488 | ||
|
|
e0b23efe95 | ||
|
|
608e0f52ee | ||
|
|
7e5f81f255 | ||
|
|
832c744578 | ||
|
|
b70ce07706 | ||
|
|
1c3df652de | ||
|
|
9192cbe39c | ||
|
|
54e7cbdf8f | ||
|
|
afecb83988 | ||
|
|
6ea8bece9b | ||
|
|
01a9eee991 | ||
|
|
0911173a89 | ||
|
|
42e04907ff | ||
|
|
d29c54c476 | ||
|
|
89a76c785c | ||
|
|
a7eb596d89 | ||
|
|
8e0f008841 | ||
|
|
c1e8b30877 | ||
|
|
2c6b8a481e | ||
|
|
c327865b4b | ||
|
|
53fc7392bb | ||
|
|
1734b5e465 | ||
|
|
7825b9ee9f | ||
|
|
1150d0f70f | ||
|
|
7b10dc8a48 | ||
|
|
94568bc08d | ||
|
|
fa74a6e60c | ||
|
|
48f26d799f | ||
|
|
ec40268e55 | ||
|
|
46c621175c | ||
|
|
676fbbb662 | ||
|
|
4245363cf4 | ||
|
|
ce3bdbccc2 | ||
|
|
ad3f762409 | ||
|
|
ac99a1f791 |
3
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
*.pyc
|
||||
*.o
|
||||
wsproxy
|
||||
tests/data_*.js
|
||||
utils/rebind.so
|
||||
utils/websockify
|
||||
node_modules
|
||||
|
||||
0
.gitmodules
vendored
Normal file
22
.travis.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
node_js:
|
||||
- '0.11.13'
|
||||
env:
|
||||
matrix:
|
||||
- TEST_BROWSER_NAME=PhantomJS
|
||||
- TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='Windows 7,Linux'
|
||||
- TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='Windows 7,Linux' TEST_BROWSER_VERSION='30,26'
|
||||
- TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 7' TEST_BROWSER_VERSION=10
|
||||
- TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 8.1' TEST_BROWSER_VERSION=11
|
||||
- TEST_BROWSER_NAME=safari TEST_BROWSER_OS='OS X 10.8' TEST_BROWSER_VERSION=6
|
||||
- TEST_BROWSER_NAME=safari TEST_BROWSER_OS='OS X 10.9' TEST_BROWSER_VERSION=7
|
||||
global:
|
||||
- secure: QE5GqGd2hrpQsIgd8dlv3oRUUHqZayomzzQjNXOB81VQi241uz/ru+3GtBZLB5WLZCq/Gj89vbLnR0LN4ixlmPaWv3/WJQGyDGuRD/vMnccVl+rBUP/Hh2zdYwiISIGcrywNAE+KLus/lyt/ahVgzbaRaDSzrM1HaZFT/rndGck=
|
||||
- secure: g75sdctEwj0hoLW0Y08Tdv8s5scNzplB6a9EtaJ2vJD9S/bK+AsPqbWesGv1UlrFPCWdbV7Vg61vkmoUjcmb5xhqFIjcM9TlYJoKWeOTsOmnQoSIkIq6gMF1k02+LmKInbPgIzrp3m3jluS1qaOs/EzFpDnJp9hWBiAfXa12Jxk=
|
||||
before_script: npm install -g karma-cli
|
||||
addons:
|
||||
sauce_connect: true
|
||||
54
CONTRIBUTING.md
Normal file
@@ -0,0 +1,54 @@
|
||||
How to contribute to noVNC
|
||||
==========================
|
||||
|
||||
We accept code via pull requests on GitHub. There are several guidelines that
|
||||
we expect contributors submitting code requests to follow. If you have issues
|
||||
following any of these guidelines, feel free to drop us a line by leaving a
|
||||
comment in the code request or sending us an email.
|
||||
|
||||
Contributing Guidelines
|
||||
-----------------------
|
||||
|
||||
* While we don't have an official coding style guide, please try to follow
|
||||
the general coding style of the existing code.
|
||||
** Use four spaces instead of tabs
|
||||
** prefix private variables and functions with an `_`
|
||||
|
||||
* Please try to include unit tests for your code. For instance, if you
|
||||
introduce a new encoding, add a test to `tests/test.rfb.js` under the
|
||||
"Encoding Handlers" section (basically, input a small pattern in your
|
||||
encoding and make sure the pattern gets displayed correctly). If you
|
||||
fix a bug, try to add a unit test that would have caught that bug
|
||||
(if possible -- some bugs, especially visual ones, are hard to test for).
|
||||
|
||||
* Squash your commits down in to a clean commit history. For instance, there
|
||||
should not be "cleanup" commits where you fix issues in previous commits in
|
||||
the same pull request. Before you go to commit, use `git rebase -i` to
|
||||
squash these changes into the relevant commits. For instance, a good commit
|
||||
history might look like "Added support for FOO encoding, Added support for
|
||||
BAR message, Placed Button in UI to Trigger BAR" (where each comma denotes
|
||||
a separate commit).
|
||||
|
||||
* Add both a title and description to your commit, if possible. Place more
|
||||
detail on what you did in the description.
|
||||
|
||||
Running the unit tests
|
||||
----------------------
|
||||
|
||||
There are two ways to run the unit tests. For both ways, you should first run
|
||||
`npm install` (not as root).
|
||||
|
||||
The first way to run the tests is to run `npm test`. This will run all the
|
||||
tests in the headless PhantomJS browser (which uses WebKit).
|
||||
|
||||
The second way to run the tests is using the `tests/run_from_console.js` file.
|
||||
This way is a bit more flexible, and can provide more information about what
|
||||
went wrong. To run all the tests, simply run `tests/run_from_console.js`.
|
||||
To run a specific test file, you can use the `-t path/to/test/file.js` option.
|
||||
If you wish to simply generate the HTML for the test, use the `-g` option, and
|
||||
the path to the temporary HTML file will be written to standard out. To open
|
||||
this file in your default browser automatically, pass the `-o` option as well.
|
||||
More information can be found by passing the `--help` or `-h` option.
|
||||
|
||||
|
||||
Thanks, and happy coding!
|
||||
82
LICENSE.txt
@@ -1,30 +1,84 @@
|
||||
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
|
||||
|
||||
The noVNC core library files are licensed under the MPL 2.0 (Mozilla
|
||||
Public License 2.0). The noVNC core library is composed of the
|
||||
Javascript code necessary for full noVNC operation. This includes (but
|
||||
is not limited to):
|
||||
|
||||
include/base64.js
|
||||
include/des.js
|
||||
include/display.js
|
||||
include/input.js
|
||||
include/keysym.js
|
||||
include/logo.js
|
||||
include/playback.js
|
||||
include/rfb.js
|
||||
include/ui.js
|
||||
include/util.js
|
||||
include/websock.js
|
||||
include/webutil.js
|
||||
|
||||
The HTML, CSS, font and images files that included with the noVNC
|
||||
source distibution (or repository) are not considered part of the
|
||||
noVNC core library and are licensed under more permissive licenses.
|
||||
The intent is to allow easy integration of noVNC into existing web
|
||||
sites and web applications.
|
||||
|
||||
The HTML, CSS, font and image files are licensed as follows:
|
||||
|
||||
*.html : 2-Clause BSD license
|
||||
|
||||
include/*.css : 2-Clause BSD license
|
||||
|
||||
include/Orbitron* : SIL Open Font License 1.1
|
||||
(Copyright 2009 Matt McInerney)
|
||||
|
||||
images/ : Creative Commons Attribution-ShareAlike
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
Some portions of noVNC are copyright to their individual authors.
|
||||
Please refer to the individual source files and/or to the noVNC commit
|
||||
history: https://github.com/kanaka/noVNC/commits/master
|
||||
|
||||
noVNC is licensed under the LGPL (GNU Lesser General Public License)
|
||||
version 3 with the following exceptions:
|
||||
The are several files and projects that have been incorporated into
|
||||
the noVNC core library. Here is a list of those files and the original
|
||||
licenses (all MPL 2.0 compatible):
|
||||
|
||||
include/input.js : LGPL-2 or any later version
|
||||
include/base64.js : MPL 2.0
|
||||
|
||||
include/base64.js : Dual GPL-2 or LGPL-2.1
|
||||
|
||||
incluee/des.js : Various BSD style licenses
|
||||
include/des.js : Various BSD style licenses
|
||||
|
||||
include/web-socket-js/ : New BSD license. Source code at
|
||||
http://github.com/gimite/web-socket-js
|
||||
include/chrome-app/tcp-stream.js
|
||||
: Apache 2.0 license
|
||||
|
||||
images/ : Creative Commons Attribution-ShareAlike
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
Uses of the work must be attributed
|
||||
to the noVNC project.
|
||||
utils/websockify
|
||||
utils/websocket.py : LGPL 3
|
||||
|
||||
utils/inflator.partial.js
|
||||
include/inflator.js : MIT (for pako)
|
||||
|
||||
The LGPL-3 license text is included at:
|
||||
Any other files not mentioned above are typically marked with
|
||||
a copyright/license header at the top of the file. The default noVNC
|
||||
license is MPL-2.0.
|
||||
|
||||
The following license texts are included:
|
||||
|
||||
docs/LICENSE.MPL-2.0
|
||||
docs/LICENSE.LGPL-3 and
|
||||
docs/LICENSE.GPL-3
|
||||
docs/LICENSE.OFL-1.1
|
||||
docs/LICENSE.BSD-3-Clause (New BSD)
|
||||
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
|
||||
docs/LICENSE.zlib
|
||||
docs/LICENSE.Apache-2.0
|
||||
docs/LICENSE.pako
|
||||
|
||||
Or alternatively the LGPL-3 license text may be found here:
|
||||
Or alternatively the license texts may be found here:
|
||||
|
||||
http://www.mozilla.org/MPL/2.0/
|
||||
http://www.gnu.org/licenses/lgpl.html and
|
||||
http://www.gnu.org/licenses/gpl.html
|
||||
http://scripts.sil.org/OFL
|
||||
http://en.wikipedia.org/wiki/BSD_licenses
|
||||
http://www.gzip.org/zlib/zlib_license.html
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
121
README.md
@@ -1,71 +1,91 @@
|
||||
## noVNC: HTML5 VNC Client
|
||||
|
||||
[](https://travis-ci.org/kanaka/noVNC)
|
||||
|
||||
### Description
|
||||
|
||||
noVNC is a VNC client implemented using HTML5 technologies,
|
||||
specifically Canvas and WebSockets (supports 'wss://' encryption).
|
||||
noVNC is licensed under the
|
||||
[LGPLv3](http://www.gnu.org/licenses/lgpl.html).
|
||||
noVNC is a HTML5 VNC client that runs well in any modern browser
|
||||
including mobile browsers (iPhone/iPad and Android).
|
||||
|
||||
Special thanks to [Sentry Data Systems](http://www.sentryds.com) for
|
||||
sponsoring ongoing development of this project (and for employing me).
|
||||
Many companies/projects have integrated noVNC including [Ganeti Web
|
||||
Manager](http://code.osuosl.org/projects/ganeti-webmgr),
|
||||
[OpenStack](http://www.openstack.org),
|
||||
[OpenNebula](http://opennebula.org/), and
|
||||
[LibVNCServer](http://libvncserver.sourceforge.net). See [the Projects
|
||||
and Companies wiki
|
||||
page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC)
|
||||
for a more complete list with additional info and links.
|
||||
|
||||
There are many companies/projects that have integrated noVNC into
|
||||
their products including: [Sentry Data Systems](http://www.sentryds.com), [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr), [Archipel](http://archipelproject.org), [openQRM](http://www.openqrm.com/), [OpenNode](http://www.opennodecloud.com/), [OpenStack](http://www.openstack.org), [Broadway (HTML5 GDK/GTK+ backend)](http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/), [OpenNebula](http://opennebula.org/) and [CloudSigma](http://www.cloudsigma.com/). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links.
|
||||
### News/help/contact
|
||||
|
||||
Notable commits, announcements and news are posted to
|
||||
@<a href="http://www.twitter.com/noVNC">noVNC</a>
|
||||
<a href="http://www.twitter.com/noVNC">@noVNC</a>
|
||||
|
||||
If you are a noVNC developer/integrator/user (or want to be) please
|
||||
join the <a
|
||||
href="https://groups.google.com/forum/?fromgroups#!forum/novnc">noVNC
|
||||
discussion group</a>
|
||||
|
||||
Bugs and feature requests can be submitted via [github
|
||||
issues](https://github.com/kanaka/noVNC/issues). If you are looking
|
||||
for a place to start contributing to noVNC, a good place to start
|
||||
would be the issues that are marked as
|
||||
["patchwelcome"](https://github.com/kanaka/noVNC/issues?labels=patchwelcome).
|
||||
|
||||
If you want to show appreciation for noVNC you could donate to a great
|
||||
non-profits such as: [Compassion
|
||||
International](http://www.compassion.com/), [SIL](http://www.sil.org),
|
||||
[Habitat for Humanity](http://www.habitat.org), [Electronic Frontier
|
||||
Foundation](https://www.eff.org/), [Against Malaria
|
||||
Foundation](http://www.againstmalaria.com/), [Nothing But
|
||||
Nets](http://www.nothingbutnets.net/), etc. Please tweet <a
|
||||
href="http://www.twitter.com/noVNC">@noVNC</a> if you do.
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Supports all modern browsers including mobile (iOS, Android)
|
||||
* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG
|
||||
* WebSocket SSL/TLS encryption (i.e. "wss://") support
|
||||
* 24-bit true color and 8 bit colour mapped
|
||||
* Supports desktop resize notification/pseudo-encoding
|
||||
* Local or remote cursor
|
||||
* Clipboard copy/paste
|
||||
* Clipping or scolling modes for large remote screens
|
||||
* Easy site integration and theming (3 example themes included)
|
||||
* Licensed under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/)
|
||||
|
||||
### Screenshots
|
||||
|
||||
Running in Chrome before and after connecting:
|
||||
|
||||
<img src="http://kanaka.github.com/noVNC/img/noVNC-1.jpg" width=400> <img src="http://kanaka.github.com/noVNC/img/noVNC-2.jpg" width=400>
|
||||
<img src="http://kanaka.github.com/noVNC/img/noVNC-5.png" width=400> <img src="http://kanaka.github.com/noVNC/img/noVNC-7.jpg" width=400>
|
||||
|
||||
See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>.
|
||||
|
||||
|
||||
### Browser Requirements
|
||||
|
||||
* HTML5 Canvas: Except for Internet Explorer, most
|
||||
browsers have had Canvas support for quite some time. Internet
|
||||
Explorer 9 will have Canvas support (finally).
|
||||
* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS
|
||||
Safari, Opera 11+, Internet Explorer 9+, etc.
|
||||
|
||||
* HTML5 WebSockets: For browsers that do not have builtin
|
||||
WebSockets support, the project includes
|
||||
<a href="http://github.com/gimite/web-socket-js">web-socket-js</a>,
|
||||
a WebSockets emulator using Adobe Flash.
|
||||
* HTML5 WebSockets and Typed Arrays
|
||||
|
||||
* Fast Javascript Engine: noVNC avoids using new Javascript
|
||||
functionality so it will run on older browsers, but decode and
|
||||
rendering happen in Javascript, so a slow Javascript engine will
|
||||
mean noVNC is painfully slow.
|
||||
* Fast Javascript Engine: this is not strictly a requirement, but
|
||||
without a fast Javascript engine, noVNC might be painfully slow.
|
||||
|
||||
* I maintain a more detailed list of browser compatibility <a
|
||||
href="https://github.com/kanaka/noVNC/wiki/Browser-support">here</a>.
|
||||
* See the more detailed [browser compatibility wiki page](https://github.com/kanaka/noVNC/wiki/Browser-support).
|
||||
|
||||
|
||||
### Server Requirements
|
||||
|
||||
Unless you are using a VNC server with support for WebSockets
|
||||
connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver)
|
||||
currently), you need to use a WebSockets to TCP socket proxy. There is
|
||||
a python proxy included ('websockify'). One advantage of using the
|
||||
proxy is that it has builtin support for SSL/TLS encryption (i.e.
|
||||
"wss://").
|
||||
|
||||
There a few reasons why a proxy is required:
|
||||
|
||||
1. WebSockets is not a pure socket protocol. There is an initial HTTP
|
||||
like handshake to allow easy hand-off by web servers and allow
|
||||
some origin policy exchange. Also, each WebSockets frame begins
|
||||
with 0 ('\x00') and ends with 255 ('\xff').
|
||||
|
||||
2. Javascript itself does not have the ability to handle pure byte
|
||||
arrays. The python proxy encodes the data as base64 so that the
|
||||
Javascript client can decode the data as an integer array.
|
||||
connections (such as
|
||||
[x11vnc/libvncserver](http://libvncserver.sourceforge.net/),
|
||||
[QEMU](http://www.qemu.org/), or
|
||||
[PocketVNC](http://www.pocketvnc.com/blog/?page_id=866)), you need to
|
||||
use a WebSockets to TCP socket proxy. There is a python proxy included
|
||||
('websockify').
|
||||
|
||||
|
||||
### Quick Start
|
||||
@@ -83,11 +103,30 @@ There a few reasons why a proxy is required:
|
||||
|
||||
### Other Pages
|
||||
|
||||
* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Generating an SSL
|
||||
certificate, starting a VNC server, advanced websockify usage, etc.
|
||||
* [Encrypted Connections](https://github.com/kanaka/websockify/wiki/Encrypted-Connections). How to setup websockify so that you can use encrypted connections from noVNC.
|
||||
|
||||
* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Starting a VNC server, advanced websockify usage, etc.
|
||||
|
||||
* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects.
|
||||
|
||||
* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems.
|
||||
|
||||
|
||||
### Authors/Contributors
|
||||
|
||||
* Core team:
|
||||
* [Joel Martin](https://github.com/kanaka)
|
||||
* [Samuel Mannehed](https://github.com/samhed) (Cendio)
|
||||
* [Peter Åstrand](https://github.com/astrand) (Cendio)
|
||||
* [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack)
|
||||
|
||||
* Notable contributions:
|
||||
* UI and Icons : Chris Gordon
|
||||
* Original Logo : Michael Sersen
|
||||
* tight encoding : Michael Tinglof (Mercuri.ca)
|
||||
|
||||
* Included libraries:
|
||||
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
|
||||
* base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
|
||||
* DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
|
||||
* Pako : Vitaly Puzrin (https://github.com/nodeca/pako)
|
||||
|
||||
5
debian/changelog
vendored
@@ -1,5 +0,0 @@
|
||||
novnc (0.1) maverick; urgency=low
|
||||
|
||||
* First upstream release
|
||||
|
||||
-- Joel Martin <github@martintribe.org> Tue, 05 Jul 2011 01:00:00 -0600
|
||||
1
debian/compat
vendored
@@ -1 +0,0 @@
|
||||
7
|
||||
13
debian/control
vendored
@@ -1,13 +0,0 @@
|
||||
Source: novnc
|
||||
Section: web
|
||||
Priority: optional
|
||||
Maintainer: Joel Martin <github@martintribe.org>
|
||||
Build-Depends: debhelper (>= 7.0.0~)
|
||||
Standards-Version: 3.8.3
|
||||
Homepage: https://github.com/kanaka/noVNC/
|
||||
|
||||
Package: novnc
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, python (>= 2.4)
|
||||
Description: HTML5 VNC client
|
||||
VNC client using HTML5 (WebSockets, Canvas) with encryption (wss://) support.
|
||||
36
debian/copyright
vendored
@@ -1,36 +0,0 @@
|
||||
Upstream Project: https://github.com/kanaka/noVNC/
|
||||
|
||||
--------------------- Original LICENSE.txt ---------------------------
|
||||
|
||||
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
|
||||
|
||||
Some portions of noVNC are copyright to their individual authors.
|
||||
Please refer to the individual source files and/or to the noVNC commit
|
||||
history: https://github.com/kanaka/noVNC/commits/master
|
||||
|
||||
noVNC is licensed under the LGPL (GNU Lesser General Public License)
|
||||
version 3 with the following exceptions:
|
||||
|
||||
include/input.js : LGPL-2 or any later version
|
||||
|
||||
include/base64.js : Dual GPL-2 or LGPL-2.1
|
||||
|
||||
incluee/des.js : Various BSD style licenses
|
||||
|
||||
include/web-socket-js/ : New BSD license. Source code at
|
||||
http://github.com/gimite/web-socket-js
|
||||
|
||||
images/ : Creative Commons Attribution-ShareAlike
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
Uses of the work must be attributed
|
||||
to the noVNC project.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
The LGPL-3 license text is located at:
|
||||
/usr/share/common-licenses/LGPL-3 and
|
||||
/usr/share/common-licenses/GPL-3
|
||||
|
||||
Or alternatively the LGPL-3 license text may be found here:
|
||||
http://www.gnu.org/licenses/lgpl.html and
|
||||
http://www.gnu.org/licenses/gpl.html
|
||||
27
debian/novnc.install
vendored
@@ -1,27 +0,0 @@
|
||||
vnc.html /usr/share/novnc
|
||||
vnc_auto.html /usr/share/novnc
|
||||
README.md /usr/share/doc/novnc
|
||||
LICENSE.txt /usr/share/doc/novnc
|
||||
utils/Makefile /usr/share/novnc/utils
|
||||
utils/launch.sh /usr/share/novnc/utils
|
||||
utils/websocket.py /usr/share/novnc/utils
|
||||
utils/websockify /usr/share/novnc/utils
|
||||
utils/wsproxy.py /usr/share/novnc/utils
|
||||
utils/rebind.c /usr/share/novnc/utils
|
||||
utils/rebind.so /usr/share/novnc/utils
|
||||
images/favicon.ico /usr/share/novnc
|
||||
include/base64.js /usr/share/novnc/include
|
||||
include/black.css /usr/share/novnc/include
|
||||
include/des.js /usr/share/novnc/include
|
||||
include/display.js /usr/share/novnc/include
|
||||
include/input.js /usr/share/novnc/include
|
||||
include/logo.js /usr/share/novnc/include
|
||||
include/plain.css /usr/share/novnc/include
|
||||
include/playback.js /usr/share/novnc/include
|
||||
include/rfb.js /usr/share/novnc/include
|
||||
include/ui.js /usr/share/novnc/include
|
||||
include/util.js /usr/share/novnc/include
|
||||
include/vnc.js /usr/share/novnc/include
|
||||
include/websock.js /usr/share/novnc/include
|
||||
include/webutil.js /usr/share/novnc/include
|
||||
include/web-socket-js/* /usr/share/novnc/include/web-socket-js
|
||||
14
debian/rules
vendored
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
clean:
|
||||
make -C utils clean
|
||||
dh clean
|
||||
|
||||
build:
|
||||
make -C utils rebind.so
|
||||
|
||||
%:
|
||||
dh ${@}
|
||||
202
docs/LICENSE.Apache-2.0
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
22
docs/LICENSE.BSD-2-Clause
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (c) <year>, <copyright holder>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
24
docs/LICENSE.BSD-3-Clause
Normal file
@@ -0,0 +1,24 @@
|
||||
Copyright (c) <year>, <copyright holder>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
373
docs/LICENSE.MPL-2.0
Normal file
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
91
docs/LICENSE.OFL-1.1
Normal file
@@ -0,0 +1,91 @@
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
21
docs/LICENSE.pako
Normal file
@@ -0,0 +1,21 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (C) 2014 by Vitaly Puzrin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
27
docs/LICENSE.zlib
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) <year>, <copyright holder>
|
||||
All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express
|
||||
or implied warranty. In no event will the authors be
|
||||
held liable for any damages arising from the use of
|
||||
this software.
|
||||
|
||||
Permission is granted to anyone to use this software
|
||||
for any purpose, including commercial applications,
|
||||
and to alter it and redistribute it freely, subject to
|
||||
the following restrictions:
|
||||
|
||||
1. The origin of this software must not be
|
||||
misrepresented; you must not claim that you
|
||||
wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in
|
||||
the product documentation would be appreciated
|
||||
but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked
|
||||
as such, and must not be misrepresented as
|
||||
being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from
|
||||
any source distribution.
|
||||
|
||||
53
docs/TODO
@@ -1,53 +0,0 @@
|
||||
Short Term:
|
||||
|
||||
- Test normal arrays vs TypedArrays vs imageData arrays.
|
||||
- Blog post about it.
|
||||
|
||||
- VNC performance and regression playback suite.
|
||||
- WebSockets
|
||||
- expand latency test
|
||||
- add absolute timers (every 500 packets)
|
||||
- try 1 ms delay
|
||||
- stop at 4000 packets
|
||||
- small and large packets test
|
||||
|
||||
- JavaScript
|
||||
- just base64 decode
|
||||
- everything except Canvas
|
||||
|
||||
- Full test
|
||||
- Without WebSockets
|
||||
- With replay from python tester
|
||||
- add higher-resolution multi test
|
||||
|
||||
- websockify test with echo and playback functionality
|
||||
- choosen by client test page on connect
|
||||
|
||||
|
||||
- Keyboard layout/internationalization support
|
||||
- convert keyCode into proper charCode
|
||||
|
||||
- IE 9 improvements.
|
||||
- https://github.com/gimite/web-socket-js/issues#issue/41
|
||||
- try window.atob instead of decode for better performance
|
||||
|
||||
- Status bar menu/buttons:
|
||||
- Explanatory hover text over buttons
|
||||
|
||||
- Configuration menu:
|
||||
- Tunable: speed vs. bandwidth selection
|
||||
- Tunable: CPU use versus latency.
|
||||
- Scaling
|
||||
|
||||
- Keyboard menu:
|
||||
- Ctrl Lock, Alt Lock, SysRq Lock
|
||||
- Highlight menu icon when keys are locked
|
||||
|
||||
- Clipboard button -> popup:
|
||||
- text, clear and send buttons
|
||||
|
||||
Medium Term:
|
||||
|
||||
- Viewport support
|
||||
|
||||
- Touchscreen testing/support.
|
||||
@@ -1 +1 @@
|
||||
0.1
|
||||
0.6.1
|
||||
|
||||
20
docs/notes
@@ -1,17 +1,5 @@
|
||||
Some implementation notes:
|
||||
Rebuilding inflator.js
|
||||
|
||||
There is an included flash object (web-socket-js) that is used to
|
||||
emulate websocket support on browsers without websocket support
|
||||
(currently only Chrome has WebSocket support).
|
||||
|
||||
Javascript doesn't have a bytearray type, so what you get out of
|
||||
a WebSocket object is just Javascript strings. Javascript has UTF-16
|
||||
unicode strings and anything sent through the WebSocket gets converted
|
||||
to UTF-8 and vice-versa. So, one additional (and necessary) function
|
||||
of wsproxy is base64 encoding/decoding what is sent to/from the
|
||||
browser.
|
||||
|
||||
Building web-socket-js emulator:
|
||||
|
||||
cd include/web-socket-js/flash-src
|
||||
mxmlc -static-link-runtime-shared-libraries WebSocketMain.as
|
||||
- Download pako from npm
|
||||
- Install browserify using npm
|
||||
- browserify utils/inflator.partial.js -o include/inflator.js -s inflator
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
noVNC packaging steps for Debian/Ubuntu:
|
||||
|
||||
- Update the noVNC version in docs/VERSION and add a new entry for the
|
||||
version in debian/changelog
|
||||
|
||||
- Rename the novnc source directory to match the form "novnc-VERSION".
|
||||
|
||||
- In the novnc source directory, run the packaging command:
|
||||
|
||||
debuild -I -uc -us
|
||||
|
||||
- The -I option ignores the .git directory when generating the
|
||||
source tarball.
|
||||
- the -uc and -us may be omitted in order to create a signed
|
||||
package.
|
||||
|
||||
- Alternatively, use pbuilder instead of debuild in order to build
|
||||
for other distributions and to guarantee a sanitized package.
|
||||
|
||||
- Verify the package and then commit the changes to docs/VERSION and
|
||||
debian/changelog.
|
||||
|
||||
- Upload the new package file(s).
|
||||
34
docs/release.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
- Decide a new version number X.Y.Z (follow SemVer)
|
||||
- Update version in package.json
|
||||
- Update version in docs/VERSION
|
||||
- Commit the change with a commit like "Release X.Y.Z"
|
||||
- Add a new release on GitHub called "vX.Y.Z", and populate it with
|
||||
release notes of the following form (where A.B.C is the last release):
|
||||
|
||||
Major Changes Since A.B.C
|
||||
=========================
|
||||
|
||||
*Insert warnings here about incompatibilities*
|
||||
|
||||
*Thanks to all the contributors who filed bugs, added features, and fixed bugs
|
||||
during this release :tada:*
|
||||
|
||||
App-visible Changes
|
||||
-------------------
|
||||
|
||||
- *feature* a feature which improves the app usage (#PRNUM)
|
||||
- *bugfix* a bug fix which fixes the app usage (#PRNUM)
|
||||
- *refactor* a refactor which changes the app usage (#PRNUM)
|
||||
|
||||
Library-visible Changes
|
||||
-----------------------
|
||||
|
||||
- *feature* a feature which improves the noVNC APIs (#PRNUM)
|
||||
- *bugfix* a bug fix which fixes the noVNC APIs (#PRNUM)
|
||||
- *refactor* a refactor which changes the noVNC APIs (#PRNUM)
|
||||
|
||||
App-internals Changes
|
||||
---------------------
|
||||
|
||||
- *bugfix* a bug fix with affects the internals of noVNC only (#PRNUM)
|
||||
- *refactor* a refactor which affects the internals of noVNC only (#PRNUM)
|
||||
687
images/Logo.svg
@@ -1,687 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="512"
|
||||
height="512"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
version="1.0"
|
||||
sodipodi:docname="noVNC_Logo_13.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_Logo12.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
style="display:inline">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient7584">
|
||||
<stop
|
||||
id="stop7586"
|
||||
offset="0"
|
||||
style="stop-color:#171717;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop7588"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5965">
|
||||
<stop
|
||||
style="stop-color:#030303;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5967" />
|
||||
<stop
|
||||
id="stop5973"
|
||||
offset="0.5"
|
||||
style="stop-color:#090909;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop5969" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5932">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.87450981;"
|
||||
offset="0"
|
||||
id="stop5934" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop5936" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5333">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.05;"
|
||||
offset="0"
|
||||
id="stop5335" />
|
||||
<stop
|
||||
style="stop-color:#4c4c4c;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop5337" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4694">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.03529412;"
|
||||
offset="0"
|
||||
id="stop4696" />
|
||||
<stop
|
||||
style="stop-color:#4c4c4c;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4698" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6989">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6991" />
|
||||
<stop
|
||||
style="stop-color:#0f0f0f;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6993" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6790">
|
||||
<stop
|
||||
style="stop-color:#434343;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6792" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop6794" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6736">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6738" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6740" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6720">
|
||||
<stop
|
||||
style="stop-color:#303030;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6722" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6724" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6702">
|
||||
<stop
|
||||
id="stop4688"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
<stop
|
||||
id="stop6714"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient6662">
|
||||
<stop
|
||||
style="stop-color:#010000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop6664" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop6666" />
|
||||
</linearGradient>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Send"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Send"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path4730"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
||||
transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path4721"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Lstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Lstart"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path4715"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
||||
transform="matrix(0.8,0,0,0.8,10,0)" />
|
||||
</marker>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="-24.105461 : 505.9325 : 1"
|
||||
inkscape:vp_y="6.1230318e-14 : 1000 : 0"
|
||||
inkscape:vp_z="719.98902 : 505.9325 : 1"
|
||||
inkscape:persp3d-origin="347.94178 : 330.5388 : 1"
|
||||
id="perspective10" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6720"
|
||||
id="linearGradient6742"
|
||||
x1="341.66687"
|
||||
y1="-253.40732"
|
||||
x2="470.19839"
|
||||
y2="-253.40732"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.874106,0,0,1.0453135,43.679874,11.482781)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6790"
|
||||
id="linearGradient6796"
|
||||
x1="-32.526917"
|
||||
y1="294.91821"
|
||||
x2="653.36664"
|
||||
y2="294.91821"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient7584"
|
||||
id="linearGradient6987"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.6520109,0,0,1.5543006,133.44336,136.96388)"
|
||||
x1="341.66687"
|
||||
y1="-253.40732"
|
||||
x2="470.19839"
|
||||
y2="-253.40732" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6702"
|
||||
id="linearGradient4686"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="159.54247"
|
||||
y1="443.13766"
|
||||
x2="430.90103"
|
||||
y2="443.13766" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4694"
|
||||
id="linearGradient4700"
|
||||
x1="229.03033"
|
||||
y1="461.81128"
|
||||
x2="393.28391"
|
||||
y2="461.81128"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4694"
|
||||
id="linearGradient4966"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="229.03033"
|
||||
y1="461.81128"
|
||||
x2="393.28391"
|
||||
y2="461.81128"
|
||||
spreadMethod="reflect" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5333"
|
||||
id="linearGradient5339"
|
||||
x1="271.28635"
|
||||
y1="294.12686"
|
||||
x2="490.23206"
|
||||
y2="294.12686"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5932"
|
||||
id="linearGradient5938"
|
||||
x1="-245.06445"
|
||||
y1="7.705513"
|
||||
x2="-62.328537"
|
||||
y2="7.705513"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.5245666,0,0,2.6282551,644.92825,162.55033)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5965"
|
||||
id="linearGradient5971"
|
||||
x1="159.54247"
|
||||
y1="443.13766"
|
||||
x2="430.90103"
|
||||
y2="443.13766"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
spreadMethod="pad" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6720"
|
||||
id="radialGradient7582"
|
||||
cx="295.22174"
|
||||
cy="443.13766"
|
||||
fx="295.22174"
|
||||
fy="443.13766"
|
||||
r="135.67928"
|
||||
gradientTransform="matrix(1,0,0,0.1356784,0,383.01346)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6720"
|
||||
id="radialGradient7611"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.1356784,0,383.01346)"
|
||||
cx="295.22174"
|
||||
cy="443.13766"
|
||||
fx="295.22174"
|
||||
fy="443.13766"
|
||||
r="135.67928" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4694"
|
||||
id="linearGradient7613"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
spreadMethod="reflect"
|
||||
x1="229.03033"
|
||||
y1="461.81128"
|
||||
x2="393.28391"
|
||||
y2="461.81128" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5333"
|
||||
id="linearGradient2913"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="271.28635"
|
||||
y1="294.12686"
|
||||
x2="490.23206"
|
||||
y2="294.12686"
|
||||
gradientTransform="matrix(1.0217389,0,0,1.0105488,-8.1719712,-6.8195787)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5932"
|
||||
id="linearGradient2916"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.5891218,0,0,2.7050059,653.14704,159.9845)"
|
||||
x1="-245.06445"
|
||||
y1="7.705513"
|
||||
x2="-62.328537"
|
||||
y2="7.705513" />
|
||||
<inkscape:perspective
|
||||
id="perspective2920"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
gridtolerance="10000"
|
||||
guidetolerance="10"
|
||||
objecttolerance="10"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="198.11269"
|
||||
inkscape:cy="276.37194"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="svg2"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="1026"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
showborder="false"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Screen"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="fill:#303030;fill-opacity:1;stroke:none;display:inline"
|
||||
id="rect6684"
|
||||
width="473.59906"
|
||||
height="321.92017"
|
||||
x="18.594318"
|
||||
y="19.852146"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer15"
|
||||
inkscape:label="Favicon_Box"
|
||||
style="display:none">
|
||||
<rect
|
||||
style="opacity:0;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
||||
id="rect3899"
|
||||
width="342.5874"
|
||||
height="342.62888"
|
||||
x="86.710419"
|
||||
y="14.318709"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
||||
inkscape:export-xdpi="4.1999998"
|
||||
inkscape:export-ydpi="4.1999998" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
||||
id="rect3899-5"
|
||||
width="342.5874"
|
||||
height="342.62888"
|
||||
x="86.710419"
|
||||
y="14.31871"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
||||
inkscape:export-xdpi="4.1999998"
|
||||
inkscape:export-ydpi="4.1999998" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer6"
|
||||
inkscape:label="Base"
|
||||
style="display:inline">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:url(#linearGradient5971);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path6944"
|
||||
sodipodi:cx="295.22174"
|
||||
sodipodi:cy="443.13766"
|
||||
sodipodi:rx="135.67928"
|
||||
sodipodi:ry="18.408747"
|
||||
d="m 430.90102,443.13766 a 135.67928,18.408747 0 1 1 -271.35855,0 135.67928,18.408747 0 1 1 271.35855,0 z"
|
||||
transform="matrix(0.01302703,-0.3700754,8.066322,-0.0237182,-3322.837,581.39212)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:url(#linearGradient4686);fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
||||
id="path4684"
|
||||
sodipodi:cx="295.22174"
|
||||
sodipodi:cy="443.13766"
|
||||
sodipodi:rx="135.67928"
|
||||
sodipodi:ry="18.408747"
|
||||
d="m 430.90102,443.13766 a 135.67928,18.408747 0 1 1 -271.35855,0 135.67928,18.408747 0 1 1 271.35855,0 z"
|
||||
transform="matrix(0.01219366,-0.2777519,7.5503003,-0.01780117,-2984.4223,654.88677)" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer14"
|
||||
inkscape:label="BaseHighlight"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:none;stroke:#0f0626;stroke-width:3.4000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.9254902;stroke-dasharray:none;marker-mid:none"
|
||||
d="m 113.22547,451.27721 c -6.48144,25.47482 27.77284,36.00307 33.46034,37.48107 75.48368,19.61581 142.61203,18.18957 201.82073,2.0728 19.20235,-5.22693 38.29352,-12.81118 45.54545,-22.87742 8.91645,-12.37672 -0.92272,-23.26137 -0.92272,-23.26137"
|
||||
id="path5997"
|
||||
sodipodi:nodetypes="csssc" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer10"
|
||||
inkscape:label="BaseShadow_1"
|
||||
style="display:inline">
|
||||
<g
|
||||
id="g7591"
|
||||
transform="translate(0,-2.1213203)">
|
||||
<path
|
||||
transform="matrix(0.01219366,-0.2777519,7.5503003,-0.01780117,-3095.3084,545.23419)"
|
||||
d="m 430.90102,443.13766 a 135.67928,18.408747 0 1 1 -271.35855,0 135.67928,18.408747 0 1 1 271.35855,0 z"
|
||||
sodipodi:ry="18.408747"
|
||||
sodipodi:rx="135.67928"
|
||||
sodipodi:cy="443.13766"
|
||||
sodipodi:cx="295.22174"
|
||||
id="path6716"
|
||||
style="fill:url(#radialGradient7611);fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
|
||||
sodipodi:type="arc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cscc"
|
||||
id="path4964"
|
||||
d="m 316.59702,421.17478 c 0,0 58.23544,7.94555 73.90264,26.394 24.79157,29.19259 -118.30811,67.5501 -242.96933,33.90089 213.50314,-1.39087 169.09283,-60.31728 169.09283,-60.31728"
|
||||
style="fill:url(#linearGradient7613);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer5"
|
||||
inkscape:label="Support"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:0.98453603;fill-rule:nonzero;stroke:url(#linearGradient6987);stroke-width:30;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
|
||||
id="rect6686"
|
||||
width="72.335358"
|
||||
height="30.264278"
|
||||
x="361.94818"
|
||||
y="-272.03949"
|
||||
transform="matrix(0,1,-1,0,0,0)" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer13"
|
||||
inkscape:label="ScreenShade"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="fill:url(#linearGradient2916);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="rect5930"
|
||||
width="473.12555"
|
||||
height="321.87091"
|
||||
x="18.64533"
|
||||
y="19.892525"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2913);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
|
||||
d="m 492.23643,341.65103 -222.74382,-0.21858 c 0,0 42.71861,-8.28882 90.56766,-25.15824 28.31396,-9.98224 57.59439,-24.1182 83.13673,-38.1986 45.98594,-25.35007 49.03937,-38.90668 49.03937,-38.90668 l 6e-5,102.4821 z"
|
||||
id="path5331"
|
||||
sodipodi:nodetypes="ccsscc"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer9"
|
||||
inkscape:label="noVNC_Black"
|
||||
style="display:inline">
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
style="fill:none;stroke:#000000;stroke-width:22.07362747;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 416.69139,186.74564 c -77.95059,0 -77.95059,0 -77.95059,0 l 0,73.23957 80.00192,0"
|
||||
id="path5134"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
style="fill:none;stroke:#000000;stroke-width:22.07362747;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 221.52501,259.96552 0,-74.30876 75.89924,74.67652 0,-73.6827"
|
||||
id="path5132"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401" />
|
||||
<g
|
||||
style="fill:none;stroke:#000000;stroke-width:25.32735443;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
id="g5128"
|
||||
transform="matrix(1.0637273,0,0,0.7140646,-87.846677,11.859872)"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401">
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
id="path5130"
|
||||
d="m 181.27307,243.94727 0,42.42561 69.52825,61.54333 0,-103.59063"
|
||||
style="fill:none;stroke:#000000;stroke-width:25.32735443;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
style="fill:none;stroke:#000000;stroke-width:21.4314537;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 106.33831,159.40077 0,-72.146954 73.69116,72.504004 0,-71.539108"
|
||||
id="path5136"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:21.4314537;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 219.23536,92.259872 0,71.123858 77.68903,0 -2.0092,-71.123858 c -75.6828,0 -75.67983,0 -75.67983,0 z"
|
||||
id="path5138"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_screen.png"
|
||||
inkscape:export-xdpi="121.61401"
|
||||
inkscape:export-ydpi="121.61401" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="noVNC"
|
||||
style="display:inline">
|
||||
<g
|
||||
style="fill:none;display:inline"
|
||||
id="g5142"
|
||||
transform="matrix(1.0637273,0,0,0.7140646,-87.924897,11.633929)"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
||||
inkscape:export-xdpi="4.1999998"
|
||||
inkscape:export-ydpi="4.1999998">
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
id="path5144"
|
||||
d="m 181.27307,243.94727 0,42.42561 69.52825,61.54333 0,-103.59063"
|
||||
style="fill:none;stroke:#f2eb41;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
style="fill:none;stroke:#f2eb41;stroke-width:17.29026794;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 221.44679,259.73958 0,-74.30877 75.89925,74.67651 0,-73.68269"
|
||||
id="path5146"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
||||
inkscape:export-xdpi="4.1999998"
|
||||
inkscape:export-ydpi="4.1999998" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
style="fill:none;stroke:#f2eb41;stroke-width:17.29026794;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 416.61319,186.51969 c -77.95061,0 -77.95061,0 -77.95061,0 l 0,73.23958 80.00193,0"
|
||||
id="path5148"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
||||
inkscape:export-xdpi="4.1999998"
|
||||
inkscape:export-ydpi="4.1999998" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
style="fill:none;stroke:#00df00;stroke-width:16.78725243;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 106.26238,159.1814 0,-72.146955 73.69115,72.503995 0,-71.539084"
|
||||
id="path5150"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
||||
inkscape:export-xdpi="4.1999998"
|
||||
inkscape:export-ydpi="4.1999998" />
|
||||
<path
|
||||
style="fill:none;stroke:#00df00;stroke-width:16.78725243;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
d="m 219.15941,92.040508 0,71.123862 77.68904,0 -2.00919,-71.123862 c -75.68283,0 -75.67985,0 -75.67985,0 z"
|
||||
id="path5152"
|
||||
inkscape:export-filename="/home/joelm/Downloads/noVNC_favicon.png"
|
||||
inkscape:export-xdpi="4.1999998"
|
||||
inkscape:export-ydpi="4.1999998" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer7"
|
||||
inkscape:label="noVNC_Save"
|
||||
style="display:none">
|
||||
<g
|
||||
style="display:inline"
|
||||
id="g3903"
|
||||
transform="matrix(0.9560363,0,0,0.9560363,-6.710991,-337.27245)">
|
||||
<g
|
||||
transform="matrix(1.0081199,0,0,1.0081199,-11.487266,-121.24134)"
|
||||
id="g3905"
|
||||
style="fill:none">
|
||||
<path
|
||||
style="fill:none;stroke:#00ff00;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 83.455645,199.50003 101.674295,0"
|
||||
id="path3907"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;stroke:#00ff00;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 81.687109,243.94727 99.585961,0 0,42.42561 79.06591,61.54333 0,-103.59063"
|
||||
id="path3909"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#00ff00;stroke-width:19.83890915;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="M 87.323416,283.46826 175.10772,160.81511"
|
||||
id="path3911"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<path
|
||||
id="path3913"
|
||||
d="m 289.84756,229.03542 0,-104.90947 71.93153,105.42866 0,-104.02559"
|
||||
style="fill:none;stroke:#00ff00;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
id="path3915"
|
||||
d="m 472.59255,125.66322 c -73.87564,0 -73.87564,0 -73.87564,0 l 0,103.4 75.81973,0"
|
||||
style="fill:none;stroke:#00ff00;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Bezel"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="fill:none;stroke:#040404;stroke-width:24.66699982;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
|
||||
id="rect6660"
|
||||
width="485.7326"
|
||||
height="337.33023"
|
||||
x="14.179501"
|
||||
y="14.179514" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer8"
|
||||
inkscape:label="BezelHighlight"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="fill:none;stroke:#100626;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.89803922;stroke-dasharray:none;stroke-dashoffset:0;display:inline"
|
||||
id="rect3888"
|
||||
width="485.7326"
|
||||
height="337.33023"
|
||||
x="13.819346"
|
||||
y="14.179423" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 25 KiB |
BIN
images/alt.png
Normal file
|
After Width: | Height: | Size: 339 B |
BIN
images/clipboard.png
Normal file
|
After Width: | Height: | Size: 501 B |
BIN
images/connect.png
Normal file
|
After Width: | Height: | Size: 404 B |
BIN
images/ctrl.png
Normal file
|
After Width: | Height: | Size: 354 B |
BIN
images/ctrlaltdel.png
Normal file
|
After Width: | Height: | Size: 317 B |
BIN
images/disconnect.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
images/drag.png
Normal file
|
After Width: | Height: | Size: 963 B |
BIN
images/esc.png
Normal file
|
After Width: | Height: | Size: 385 B |
|
Before Width: | Height: | Size: 23 KiB |
BIN
images/fullscreen.png
Normal file
|
After Width: | Height: | Size: 851 B |
BIN
images/keyboard.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
images/mouse_left.png
Normal file
|
After Width: | Height: | Size: 511 B |
BIN
images/mouse_middle.png
Normal file
|
After Width: | Height: | Size: 517 B |
BIN
images/mouse_none.png
Normal file
|
After Width: | Height: | Size: 497 B |
BIN
images/mouse_right.png
Normal file
|
After Width: | Height: | Size: 513 B |
BIN
images/power.png
Normal file
|
After Width: | Height: | Size: 390 B |
BIN
images/screen_320x460.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
images/screen_57x57.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 20 KiB |
BIN
images/screen_700x700.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
images/settings.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
images/tab.png
Normal file
|
After Width: | Height: | Size: 387 B |
BIN
images/toggleextrakeys.png
Normal file
|
After Width: | Height: | Size: 735 B |
BIN
include/Orbitron700.ttf
Normal file
BIN
include/Orbitron700.woff
Normal file
527
include/base.css
Normal file
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* noVNC base CSS
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
|
||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-family: Helvetica;
|
||||
/*Background image with light grey curve.*/
|
||||
background-color:#494949;
|
||||
background-repeat:no-repeat;
|
||||
background-position:right bottom;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
html {
|
||||
height:100%;
|
||||
}
|
||||
|
||||
#noVNC_controls ul {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#noVNC_controls li {
|
||||
padding-bottom:8px;
|
||||
}
|
||||
|
||||
#noVNC_setting_host {
|
||||
width:150px;
|
||||
}
|
||||
#noVNC_setting_port {
|
||||
width: 80px;
|
||||
}
|
||||
#noVNC_setting_password {
|
||||
width: 150px;
|
||||
}
|
||||
#noVNC_setting_encrypt {
|
||||
}
|
||||
#noVNC_setting_path {
|
||||
width: 100px;
|
||||
}
|
||||
#noVNC_connect_button {
|
||||
width: 110px;
|
||||
float:right;
|
||||
}
|
||||
|
||||
#noVNC_buttons {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#noVNC_view_drag_button {
|
||||
display: none;
|
||||
}
|
||||
#noVNC_sendCtrlAltDel_button {
|
||||
display: none;
|
||||
}
|
||||
#noVNC_fullscreen_button {
|
||||
display: none;
|
||||
}
|
||||
#noVNC_xvp_buttons {
|
||||
display: none;
|
||||
}
|
||||
#noVNC_mobile_buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#noVNC_extra_keys {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.noVNC_buttons_left {
|
||||
float: left;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.noVNC_buttons_right {
|
||||
float:right;
|
||||
right: 0px;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#noVNC_status {
|
||||
font-size: 12px;
|
||||
padding-top: 4px;
|
||||
height:32px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#noVNC_settings_menu {
|
||||
margin: 3px;
|
||||
text-align: left;
|
||||
}
|
||||
#noVNC_settings_menu ul {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#noVNC_settings_apply {
|
||||
float:right;
|
||||
}
|
||||
|
||||
#noVNC_container {
|
||||
display: table;
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color:#313131;
|
||||
border-bottom-right-radius: 800px 600px;
|
||||
/*border-top-left-radius: 800px 600px;*/
|
||||
}
|
||||
|
||||
#noVNC_screen {
|
||||
display: none;
|
||||
position: absolute;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
bottom: 0px;
|
||||
top: 36px; /* the height of the control bar */
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Do not set width/height for VNC_canvas or incorrect
|
||||
* scaling will occur. Canvas size depends on remote VNC
|
||||
* settings and noVNC settings. */
|
||||
#noVNC_canvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
#VNC_clipboard_clear_button {
|
||||
float:right;
|
||||
}
|
||||
#VNC_clipboard_text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#noVNC_clipboard_clear_button {
|
||||
float:right;
|
||||
}
|
||||
|
||||
/*Bubble contents divs*/
|
||||
#noVNC_settings {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:20px;
|
||||
position:fixed;
|
||||
}
|
||||
|
||||
#noVNC_controls {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:12px;
|
||||
position:fixed;
|
||||
}
|
||||
#noVNC_controls.top:after {
|
||||
right:15px;
|
||||
}
|
||||
|
||||
#noVNC_description {
|
||||
display:none;
|
||||
position:fixed;
|
||||
|
||||
margin-top:73px;
|
||||
right:20px;
|
||||
left:20px;
|
||||
padding:15px;
|
||||
color:#000;
|
||||
background:#eee; /* default background for browsers without gradient support */
|
||||
|
||||
border:2px solid #E0E0E0;
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius:10px;
|
||||
border-radius:10px;
|
||||
}
|
||||
|
||||
#noVNC_popup_status {
|
||||
display:none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
|
||||
margin:15px;
|
||||
margin-top:60px;
|
||||
padding:15px;
|
||||
width:auto;
|
||||
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
word-wrap:break-word;
|
||||
color:#fff;
|
||||
background:rgba(0,0,0,0.65);
|
||||
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius:10px;
|
||||
border-radius:10px;
|
||||
}
|
||||
|
||||
#noVNC_xvp {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:30px;
|
||||
position:fixed;
|
||||
}
|
||||
#noVNC_xvp.top:after {
|
||||
right:125px;
|
||||
}
|
||||
|
||||
#noVNC_clipboard {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:30px;
|
||||
position:fixed;
|
||||
}
|
||||
#noVNC_clipboard.top:after {
|
||||
right:85px;
|
||||
}
|
||||
|
||||
#noVNC_keyboardinput {
|
||||
width:1px;
|
||||
height:1px;
|
||||
background-color:#fff;
|
||||
color:#fff;
|
||||
border:0;
|
||||
position: relative;
|
||||
left: -40px;
|
||||
z-index: -1;
|
||||
ime-mode: disabled;
|
||||
}
|
||||
|
||||
/*
|
||||
* Advanced Styling
|
||||
*/
|
||||
|
||||
.noVNC_status_normal {
|
||||
background: #b2bdcd; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
|
||||
}
|
||||
.noVNC_status_error {
|
||||
background: #f04040; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
|
||||
}
|
||||
.noVNC_status_warn {
|
||||
background: #f0f040; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
|
||||
}
|
||||
|
||||
/* Control bar */
|
||||
#noVNC_control_bar {
|
||||
position:fixed;
|
||||
|
||||
display:block;
|
||||
height:36px;
|
||||
left:0;
|
||||
top:0;
|
||||
width:100%;
|
||||
z-index:200;
|
||||
}
|
||||
|
||||
.noVNC_status_button {
|
||||
padding: 4px 4px;
|
||||
vertical-align: middle;
|
||||
border:1px solid #869dbc;
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
background: #b2bdcd; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
|
||||
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
|
||||
}
|
||||
|
||||
.noVNC_status_button_selected {
|
||||
padding: 4px 4px;
|
||||
vertical-align: middle;
|
||||
border:1px solid #4366a9;
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
background: #779ced; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */
|
||||
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
|
||||
}
|
||||
|
||||
.noVNC_status_button:disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
|
||||
/*Settings Bubble*/
|
||||
.triangle-right {
|
||||
position:relative;
|
||||
padding:15px;
|
||||
margin:1em 0 3em;
|
||||
color:#fff;
|
||||
background:#fff; /* default background for browsers without gradient support */
|
||||
/* css3 */
|
||||
/*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698));
|
||||
background:-moz-linear-gradient(#2e88c4, #075698);
|
||||
background:-o-linear-gradient(#2e88c4, #075698);
|
||||
background:linear-gradient(#2e88c4, #075698);*/
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius:10px;
|
||||
border-radius:10px;
|
||||
color:#000;
|
||||
border:2px solid #E0E0E0;
|
||||
}
|
||||
|
||||
.triangle-right.top:after {
|
||||
border-color: transparent #E0E0E0;
|
||||
border-width: 20px 20px 0 0;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
right: 50px;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.triangle-right:after {
|
||||
content:"";
|
||||
position:absolute;
|
||||
bottom:-20px; /* value = - border-top-width - border-bottom-width */
|
||||
left:50px; /* controls horizontal position */
|
||||
border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */
|
||||
border-style:solid;
|
||||
border-color:#E0E0E0 transparent;
|
||||
/* reduce the damage in FF3.0 */
|
||||
display:block;
|
||||
width:0;
|
||||
}
|
||||
|
||||
.triangle-right.top:after {
|
||||
top:-40px; /* value = - border-top-width - border-bottom-width */
|
||||
right:50px; /* controls horizontal position */
|
||||
bottom:auto;
|
||||
left:auto;
|
||||
border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */
|
||||
border-color:transparent #E0E0E0;
|
||||
}
|
||||
|
||||
/*Default noVNC logo.*/
|
||||
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */
|
||||
@font-face {
|
||||
font-family: 'Orbitron';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('?'), url('Orbitron700.woff') format('woff'),
|
||||
url('Orbitron700.ttf') format('truetype');
|
||||
}
|
||||
|
||||
#noVNC_logo {
|
||||
margin-top: 170px;
|
||||
margin-left: 10px;
|
||||
color:yellow;
|
||||
text-align:left;
|
||||
font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
|
||||
line-height:90%;
|
||||
text-shadow:
|
||||
5px 5px 0 #000,
|
||||
-1px -1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px 1px 0 #000,
|
||||
1px 1px 0 #000;
|
||||
}
|
||||
|
||||
|
||||
#noVNC_logo span{
|
||||
color:green;
|
||||
}
|
||||
|
||||
/* ----------------------------------------
|
||||
* Media sizing
|
||||
* ----------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
.noVNC_status_button {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#noVNC_clipboard_text {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
#noVNC_logo {
|
||||
font-size: 180px;
|
||||
}
|
||||
|
||||
.noVNC_buttons_left {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.noVNC_buttons_right {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#noVNC_status {
|
||||
z-index: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
#noVNC_toggleExtraKeys_button { display: none; }
|
||||
#noVNC_toggleCtrl_button { display: inline; }
|
||||
#noVNC_toggleAlt_button { display: inline; }
|
||||
#noVNC_sendTab_button { display: inline; }
|
||||
#noVNC_sendEsc_button { display: inline; }
|
||||
|
||||
/* left-align the status text on lower resolutions */
|
||||
@media screen and (max-width: 800px){
|
||||
#noVNC_status {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
width: auto;
|
||||
float: left;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px){
|
||||
#noVNC_clipboard_text {
|
||||
width: 410px;
|
||||
}
|
||||
#noVNC_logo {
|
||||
font-size: 150px;
|
||||
}
|
||||
.noVNC_status_button {
|
||||
font-size: 10px;
|
||||
}
|
||||
.noVNC_buttons_left {
|
||||
padding-left: 0px;
|
||||
}
|
||||
.noVNC_buttons_right {
|
||||
padding-right: 0px;
|
||||
}
|
||||
/* collapse the extra keys on lower resolutions */
|
||||
#noVNC_toggleExtraKeys_button {
|
||||
display: inline;
|
||||
}
|
||||
#noVNC_toggleCtrl_button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0px;
|
||||
}
|
||||
#noVNC_toggleAlt_button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 0px;
|
||||
}
|
||||
#noVNC_sendTab_button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 0px;
|
||||
}
|
||||
#noVNC_sendEsc_button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 135px;
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 321px) and (max-width: 480px) {
|
||||
#noVNC_clipboard_text {
|
||||
width: 250px;
|
||||
}
|
||||
#noVNC_logo {
|
||||
font-size: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
.noVNC_status_button {
|
||||
font-size: 9px;
|
||||
}
|
||||
#noVNC_clipboard_text {
|
||||
width: 220px;
|
||||
}
|
||||
#noVNC_logo {
|
||||
font-size: 90px;
|
||||
}
|
||||
}
|
||||
@@ -1,147 +1,113 @@
|
||||
/*
|
||||
* Modified from:
|
||||
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
|
||||
*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla XML-RPC Client component.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Digital Creations 2, Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2000
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Martijn Pieters <mj@digicool.com> (original author)
|
||||
* Samuel Sieb <samuel@sieb.net>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
|
||||
|
||||
/*jslint white: false, bitwise: false, plusplus: false */
|
||||
/*jslint white: false */
|
||||
/*global console */
|
||||
|
||||
var Base64 = {
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||
base64Pad : '=',
|
||||
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
||||
base64Pad : '=',
|
||||
encode: function (data) {
|
||||
"use strict";
|
||||
var result = '';
|
||||
var toBase64Table = Base64.toBase64Table;
|
||||
var length = data.length;
|
||||
var lengthpad = (length % 3);
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
|
||||
encode: function (data) {
|
||||
"use strict";
|
||||
var result = '',
|
||||
chrTable = Base64.toBase64Table.split(''),
|
||||
pad = Base64.base64Pad,
|
||||
length = data.length,
|
||||
i;
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
for (i = 0; i < (length - 2); i += 3) {
|
||||
result += chrTable[data[i] >> 2];
|
||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
||||
result += chrTable[data[i+2] & 0x3f];
|
||||
}
|
||||
|
||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||
if (length%3) {
|
||||
i = length - (length%3);
|
||||
result += chrTable[data[i] >> 2];
|
||||
if ((length%3) === 2) {
|
||||
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += chrTable[(data[i+1] & 0x0f) << 2];
|
||||
result += pad;
|
||||
} else {
|
||||
result += chrTable[(data[i] & 0x03) << 4];
|
||||
result += pad + pad;
|
||||
for (var i = 0; i < (length - 2); i += 3) {
|
||||
result += toBase64Table[data[i] >> 2];
|
||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
||||
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
||||
result += toBase64Table[data[i + 2] & 0x3f];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/* Convert Base64 data to a string */
|
||||
toBinaryTable : [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 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,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
],
|
||||
|
||||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var binTable = Base64.toBinaryTable,
|
||||
pad = Base64.base64Pad,
|
||||
result, result_length, idx, i, c, padding,
|
||||
leftbits = 0, // number of bits decoded, but yet to be appended
|
||||
leftdata = 0, // bits decoded, but yet to be appended
|
||||
data_length = data.indexOf('=') - offset;
|
||||
|
||||
if (data_length < 0) { data_length = data.length - offset; }
|
||||
|
||||
/* Every four characters is 3 resulting numbers */
|
||||
result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5);
|
||||
result = new Array(result_length);
|
||||
|
||||
// Convert one by one.
|
||||
for (idx = 0, i = offset; i < data.length; i++) {
|
||||
c = binTable[data.charCodeAt(i) & 0x7f];
|
||||
padding = (data.charAt(i) === pad);
|
||||
// Skip illegal characters and whitespace
|
||||
if (c === -1) {
|
||||
console.error("Illegal character '" + data.charCodeAt(i) + "'");
|
||||
continue;
|
||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||
var j = 0;
|
||||
if (lengthpad === 2) {
|
||||
j = length - lengthpad;
|
||||
result += toBase64Table[data[j] >> 2];
|
||||
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
|
||||
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
|
||||
result += toBase64Table[64];
|
||||
} else if (lengthpad === 1) {
|
||||
j = length - lengthpad;
|
||||
result += toBase64Table[data[j] >> 2];
|
||||
result += toBase64Table[(data[j] & 0x03) << 4];
|
||||
result += toBase64Table[64];
|
||||
result += toBase64Table[64];
|
||||
}
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding) {
|
||||
result[idx++] = (leftdata >> leftbits) & 0xff;
|
||||
return result;
|
||||
},
|
||||
|
||||
/* Convert Base64 data to a string */
|
||||
/* jshint -W013 */
|
||||
toBinaryTable : [
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||
-1, 0, 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,-1, -1,-1,-1,-1,
|
||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
],
|
||||
/* jshint +W013 */
|
||||
|
||||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var toBinaryTable = Base64.toBinaryTable;
|
||||
var base64Pad = Base64.base64Pad;
|
||||
var result, result_length;
|
||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||
var leftdata = 0; // bits decoded, but yet to be appended
|
||||
var data_length = data.indexOf('=') - offset;
|
||||
|
||||
if (data_length < 0) { data_length = data.length - offset; }
|
||||
|
||||
/* Every four characters is 3 resulting numbers */
|
||||
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
||||
result = new Array(result_length);
|
||||
|
||||
// Convert one by one.
|
||||
for (var idx = 0, i = offset; i < data.length; i++) {
|
||||
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||
var padding = (data.charAt(i) === base64Pad);
|
||||
// Skip illegal characters and whitespace
|
||||
if (c === -1) {
|
||||
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collect data into leftdata, update bitcount
|
||||
leftdata = (leftdata << 6) | c;
|
||||
leftbits += 6;
|
||||
|
||||
// If we have 8 or more bits, append 8 bits to the result
|
||||
if (leftbits >= 8) {
|
||||
leftbits -= 8;
|
||||
// Append if not padding.
|
||||
if (!padding) {
|
||||
result[idx++] = (leftdata >> leftbits) & 0xff;
|
||||
}
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
leftdata &= (1 << leftbits) - 1;
|
||||
}
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits) {
|
||||
err = new Error('Corrupted base64 string');
|
||||
err.name = 'Base64-Error';
|
||||
throw err;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// If there are any bits left, the base64 string was corrupted
|
||||
if (leftbits) {
|
||||
throw {name: 'Base64-Error',
|
||||
message: 'Corrupted base64 string'};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}; /* End of Base64 namespace */
|
||||
|
||||
@@ -1,124 +1,71 @@
|
||||
body {
|
||||
margin: 0px;
|
||||
font-size: 13px;
|
||||
color: #111;
|
||||
font-family: "Helvetica";
|
||||
/*
|
||||
* noVNC black CSS
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
|
||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||
*/
|
||||
|
||||
#noVNC_keyboardinput {
|
||||
background-color:#000;
|
||||
}
|
||||
|
||||
#VNC_controls {
|
||||
background: #111;
|
||||
line-height: 1em;
|
||||
color: #FFF;
|
||||
overflow: hidden;
|
||||
padding: 4px 24px;
|
||||
.noVNC_status_normal {
|
||||
background: #4c4c4c; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
|
||||
}
|
||||
.noVNC_status_error {
|
||||
background: #f04040; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #f04040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
|
||||
}
|
||||
.noVNC_status_warn {
|
||||
background: #f0f040; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #f0f040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
|
||||
background: linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
|
||||
}
|
||||
|
||||
#VNC_controls ul {
|
||||
list-style:none;
|
||||
list-style-position: outside;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#VNC_controls li {
|
||||
margin-right: 15px;
|
||||
padding: 2px 0px;
|
||||
float: left;
|
||||
}
|
||||
#VNC_controls li input[type=text],
|
||||
#VNC_controls li input[type=password] {
|
||||
border: 2px solid #333;
|
||||
.triangle-right {
|
||||
border:2px solid #fff;
|
||||
background:#000;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
#VNC_host {
|
||||
width: 100px;
|
||||
}
|
||||
#VNC_port {
|
||||
width: 50px;
|
||||
}
|
||||
#VNC_password {
|
||||
width: 80px;
|
||||
}
|
||||
#VNC_encrypt {
|
||||
}
|
||||
#VNC_connect_button {
|
||||
width: 100px;
|
||||
.noVNC_status_button {
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
border:1px solid #4c4c4c;
|
||||
|
||||
background: #4c4c4c; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
|
||||
}
|
||||
|
||||
#VNC_status_bar td {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
#VNC_status_bar div {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin: 0px;
|
||||
padding: 1em;
|
||||
}
|
||||
.VNC_status_button {
|
||||
font-size: 10px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#VNC_status {
|
||||
text-align: center;
|
||||
}
|
||||
#VNC_settings_menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 13em;
|
||||
border: 1px solid #888;
|
||||
color: #111;
|
||||
font-weight: normal;
|
||||
background-color: #f0f2f6;
|
||||
padding: 5px; margin: 3px;
|
||||
z-index: 100; opacity: 1;
|
||||
text-align: left; white-space: normal;
|
||||
}
|
||||
#VNC_settings_menu ul {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.VNC_buttons_right {
|
||||
text-align: right;
|
||||
}
|
||||
.VNC_buttons_left {
|
||||
text-align: left;
|
||||
}
|
||||
.VNC_status_normal {
|
||||
background: #111;
|
||||
color: #fff;
|
||||
}
|
||||
.VNC_status_error {
|
||||
background: #111;
|
||||
color: #f44;
|
||||
}
|
||||
.VNC_status_warn {
|
||||
background: #111;
|
||||
color: #ff4;
|
||||
}
|
||||
|
||||
#VNC_screen {
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
background: #111;
|
||||
padding: 20px;
|
||||
margin: 0 auto;
|
||||
color: #FFF;
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
|
||||
/* This causes the width of the outer div(#screen) honor the size of the inner (#vnc) div */
|
||||
display: table;
|
||||
table-layout: auto;
|
||||
}
|
||||
#VNC_canvas {
|
||||
background: #111;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#VNC_clipboard {
|
||||
display: none;
|
||||
.noVNC_status_button_selected {
|
||||
background: #9dd53a; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */
|
||||
}
|
||||
|
||||
64
include/blue.css
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* noVNC blue CSS
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
|
||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||
*/
|
||||
|
||||
.noVNC_status_normal {
|
||||
background-color:#04073d;
|
||||
background-image: -webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
color-stop(0.54, rgb(10,15,79)),
|
||||
color-stop(0.5, rgb(4,7,61))
|
||||
);
|
||||
background-image: -moz-linear-gradient(
|
||||
center bottom,
|
||||
rgb(10,15,79) 54%,
|
||||
rgb(4,7,61) 50%
|
||||
);
|
||||
}
|
||||
.noVNC_status_error {
|
||||
background-color:#f04040;
|
||||
background-image: -webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
color-stop(0.54, rgb(240,64,64)),
|
||||
color-stop(0.5, rgb(4,7,61))
|
||||
);
|
||||
background-image: -moz-linear-gradient(
|
||||
center bottom,
|
||||
rgb(4,7,61) 54%,
|
||||
rgb(249,64,64) 50%
|
||||
);
|
||||
}
|
||||
.noVNC_status_warn {
|
||||
background-color:#f0f040;
|
||||
background-image: -webkit-gradient(
|
||||
linear,
|
||||
left bottom,
|
||||
left top,
|
||||
color-stop(0.54, rgb(240,240,64)),
|
||||
color-stop(0.5, rgb(4,7,61))
|
||||
);
|
||||
background-image: -moz-linear-gradient(
|
||||
center bottom,
|
||||
rgb(4,7,61) 54%,
|
||||
rgb(240,240,64) 50%
|
||||
);
|
||||
}
|
||||
|
||||
.triangle-right {
|
||||
border:2px solid #fff;
|
||||
background:#04073d;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
#noVNC_keyboardinput {
|
||||
background-color:#04073d;
|
||||
}
|
||||
|
||||
321
include/chrome-app/tcp-client.js
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Author: Boris Smus (smus@chromium.org)
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
|
||||
// Define some local variables here.
|
||||
var socket = chrome.socket || chrome.experimental.socket;
|
||||
var dns = chrome.experimental.dns;
|
||||
|
||||
/**
|
||||
* Creates an instance of the client
|
||||
*
|
||||
* @param {String} host The remote host to connect to
|
||||
* @param {Number} port The port to connect to at the remote host
|
||||
*/
|
||||
function TcpClient(host, port, pollInterval) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.pollInterval = pollInterval || 15;
|
||||
|
||||
// Callback functions.
|
||||
this.callbacks = {
|
||||
connect: null, // Called when socket is connected.
|
||||
disconnect: null, // Called when socket is disconnected.
|
||||
recvBuffer: null, // Called (as ArrayBuffer) when client receives data from server.
|
||||
recvString: null, // Called (as string) when client receives data from server.
|
||||
sent: null // Called when client sends data to server.
|
||||
};
|
||||
|
||||
// Socket.
|
||||
this.socketId = null;
|
||||
this.isConnected = false;
|
||||
|
||||
log('initialized tcp client');
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the TCP socket, and creates an open socket.
|
||||
*
|
||||
* @see http://developer.chrome.com/trunk/apps/socket.html#method-create
|
||||
* @param {Function} callback The function to call on connection
|
||||
*/
|
||||
TcpClient.prototype.connect = function(callback) {
|
||||
// First resolve the hostname to an IP.
|
||||
dns.resolve(this.host, function(result) {
|
||||
this.addr = result.address;
|
||||
socket.create('tcp', {}, this._onCreate.bind(this));
|
||||
|
||||
// Register connect callback.
|
||||
this.callbacks.connect = callback;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends an arraybuffer/view down the wire to the remote side
|
||||
*
|
||||
* @see http://developer.chrome.com/trunk/apps/socket.html#method-write
|
||||
* @param {String} msg The arraybuffer/view to send
|
||||
* @param {Function} callback The function to call when the message has sent
|
||||
*/
|
||||
TcpClient.prototype.sendBuffer = function(buf, callback) {
|
||||
if (buf.buffer) {
|
||||
buf = buf.buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
// Debug
|
||||
var bytes = [], u8 = new Uint8Array(buf);
|
||||
for (var i = 0; i < u8.length; i++) {
|
||||
bytes.push(u8[i]);
|
||||
}
|
||||
log("sending bytes: " + (bytes.join(',')));
|
||||
*/
|
||||
|
||||
socket.write(this.socketId, buf, this._onWriteComplete.bind(this));
|
||||
|
||||
// Register sent callback.
|
||||
this.callbacks.sent = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a string down the wire to the remote side
|
||||
*
|
||||
* @see http://developer.chrome.com/trunk/apps/socket.html#method-write
|
||||
* @param {String} msg The string to send
|
||||
* @param {Function} callback The function to call when the message has sent
|
||||
*/
|
||||
TcpClient.prototype.sendString = function(msg, callback) {
|
||||
/*
|
||||
// Debug
|
||||
log("sending string: " + msg);
|
||||
*/
|
||||
|
||||
this._stringToArrayBuffer(msg, function(arrayBuffer) {
|
||||
socket.write(this.socketId, arrayBuffer, this._onWriteComplete.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
// Register sent callback.
|
||||
this.callbacks.sent = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the callback for when a message is received
|
||||
*
|
||||
* @param {Function} callback The function to call when a message has arrived
|
||||
* @param {String} type The callback argument type: "arraybuffer" or "string"
|
||||
*/
|
||||
TcpClient.prototype.addResponseListener = function(callback, type) {
|
||||
if (typeof type === "undefined") {
|
||||
type = "arraybuffer";
|
||||
}
|
||||
// Register received callback.
|
||||
if (type === "string") {
|
||||
this.callbacks.recvString = callback;
|
||||
} else {
|
||||
this.callbacks.recvBuffer = callback;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the callback for when the socket disconnects
|
||||
*
|
||||
* @param {Function} callback The function to call when the socket disconnects
|
||||
* @param {String} type The callback argument type: "arraybuffer" or "string"
|
||||
*/
|
||||
TcpClient.prototype.addDisconnectListener = function(callback) {
|
||||
// Register disconnect callback.
|
||||
this.callbacks.disconnect = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disconnects from the remote side
|
||||
*
|
||||
* @see http://developer.chrome.com/trunk/apps/socket.html#method-disconnect
|
||||
*/
|
||||
TcpClient.prototype.disconnect = function() {
|
||||
if (this.isConnected) {
|
||||
this.isConnected = false;
|
||||
socket.disconnect(this.socketId);
|
||||
if (this.callbacks.disconnect) {
|
||||
this.callbacks.disconnect();
|
||||
}
|
||||
log('socket disconnected');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback function used for when we attempt to have Chrome
|
||||
* create a socket. If the socket is successfully created
|
||||
* we go ahead and connect to the remote side.
|
||||
*
|
||||
* @private
|
||||
* @see http://developer.chrome.com/trunk/apps/socket.html#method-connect
|
||||
* @param {Object} createInfo The socket details
|
||||
*/
|
||||
TcpClient.prototype._onCreate = function(createInfo) {
|
||||
this.socketId = createInfo.socketId;
|
||||
if (this.socketId > 0) {
|
||||
socket.connect(this.socketId, this.addr, this.port, this._onConnectComplete.bind(this));
|
||||
} else {
|
||||
error('Unable to create socket');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The callback function used for when we attempt to have Chrome
|
||||
* connect to the remote side. If a successful connection is
|
||||
* made then polling starts to check for data to read
|
||||
*
|
||||
* @private
|
||||
* @param {Number} resultCode Indicates whether the connection was successful
|
||||
*/
|
||||
TcpClient.prototype._onConnectComplete = function(resultCode) {
|
||||
// Start polling for reads.
|
||||
this.isConnected = true;
|
||||
setTimeout(this._periodicallyRead.bind(this), this.pollInterval);
|
||||
|
||||
if (this.callbacks.connect) {
|
||||
log('connect complete');
|
||||
this.callbacks.connect();
|
||||
}
|
||||
log('onConnectComplete');
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks for new data to read from the socket
|
||||
*
|
||||
* @see http://developer.chrome.com/trunk/apps/socket.html#method-read
|
||||
*/
|
||||
TcpClient.prototype._periodicallyRead = function() {
|
||||
var that = this;
|
||||
socket.getInfo(this.socketId, function (info) {
|
||||
if (info.connected) {
|
||||
setTimeout(that._periodicallyRead.bind(that), that.pollInterval);
|
||||
socket.read(that.socketId, null, that._onDataRead.bind(that));
|
||||
} else if (that.isConnected) {
|
||||
log('socket disconnect detected');
|
||||
that.disconnect();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback function for when data has been read from the socket.
|
||||
* Converts the array buffer that is read in to a string
|
||||
* and sends it on for further processing by passing it to
|
||||
* the previously assigned callback function.
|
||||
*
|
||||
* @private
|
||||
* @see TcpClient.prototype.addResponseListener
|
||||
* @param {Object} readInfo The incoming message
|
||||
*/
|
||||
TcpClient.prototype._onDataRead = function(readInfo) {
|
||||
// Call received callback if there's data in the response.
|
||||
if (readInfo.resultCode > 0) {
|
||||
log('onDataRead');
|
||||
|
||||
/*
|
||||
// Debug
|
||||
var bytes = [], u8 = new Uint8Array(readInfo.data);
|
||||
for (var i = 0; i < u8.length; i++) {
|
||||
bytes.push(u8[i]);
|
||||
}
|
||||
log("received bytes: " + (bytes.join(',')));
|
||||
*/
|
||||
|
||||
if (this.callbacks.recvBuffer) {
|
||||
// Return raw ArrayBuffer directly.
|
||||
this.callbacks.recvBuffer(readInfo.data);
|
||||
}
|
||||
if (this.callbacks.recvString) {
|
||||
// Convert ArrayBuffer to string.
|
||||
this._arrayBufferToString(readInfo.data, function(str) {
|
||||
this.callbacks.recvString(str);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// Trigger another read right away
|
||||
setTimeout(this._periodicallyRead.bind(this), 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for when data has been successfully
|
||||
* written to the socket.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} writeInfo The outgoing message
|
||||
*/
|
||||
TcpClient.prototype._onWriteComplete = function(writeInfo) {
|
||||
log('onWriteComplete');
|
||||
// Call sent callback.
|
||||
if (this.callbacks.sent) {
|
||||
this.callbacks.sent(writeInfo);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an array buffer to a string
|
||||
*
|
||||
* @private
|
||||
* @param {ArrayBuffer} buf The buffer to convert
|
||||
* @param {Function} callback The function to call when conversion is complete
|
||||
*/
|
||||
TcpClient.prototype._arrayBufferToString = function(buf, callback) {
|
||||
var bb = new Blob([new Uint8Array(buf)]);
|
||||
var f = new FileReader();
|
||||
f.onload = function(e) {
|
||||
callback(e.target.result);
|
||||
};
|
||||
f.readAsText(bb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a string to an array buffer
|
||||
*
|
||||
* @private
|
||||
* @param {String} str The string to convert
|
||||
* @param {Function} callback The function to call when conversion is complete
|
||||
*/
|
||||
TcpClient.prototype._stringToArrayBuffer = function(str, callback) {
|
||||
var bb = new Blob([str]);
|
||||
var f = new FileReader();
|
||||
f.onload = function(e) {
|
||||
callback(e.target.result);
|
||||
};
|
||||
f.readAsArrayBuffer(bb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper function for logging
|
||||
*/
|
||||
function log(msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function for error logging
|
||||
*/
|
||||
function error(msg) {
|
||||
console.error(msg);
|
||||
}
|
||||
|
||||
exports.TcpClient = TcpClient;
|
||||
|
||||
})(window);
|
||||
349
include/des.js
@@ -75,199 +75,202 @@
|
||||
* fine Java utilities: http://www.acme.com/java/
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint white: false, bitwise: false, plusplus: false */
|
||||
/* jslint white: false */
|
||||
|
||||
function DES(passwd) {
|
||||
"use strict";
|
||||
|
||||
// Tables, permutations, S-boxes, etc.
|
||||
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
||||
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
||||
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
||||
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
||||
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
||||
keys = [];
|
||||
// Tables, permutations, S-boxes, etc.
|
||||
// jshint -W013
|
||||
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
||||
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
||||
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
||||
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
||||
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
||||
keys = [];
|
||||
|
||||
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
||||
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
||||
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
||||
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
||||
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
||||
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
||||
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
||||
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
||||
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
||||
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
||||
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
||||
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
||||
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
||||
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
||||
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
||||
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
||||
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
||||
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
||||
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
||||
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
||||
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
||||
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
||||
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
||||
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
||||
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
||||
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
||||
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
||||
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
||||
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
||||
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
||||
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
||||
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
||||
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
||||
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
||||
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
||||
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
||||
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
||||
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
||||
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
||||
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
||||
// jshint -W015
|
||||
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
||||
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
||||
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
||||
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
||||
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
||||
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
||||
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
||||
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
||||
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
||||
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
||||
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
||||
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
||||
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
||||
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
||||
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
||||
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
||||
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
||||
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
||||
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
||||
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
||||
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
||||
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
||||
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
||||
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
||||
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
||||
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
||||
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
||||
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
||||
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
||||
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
||||
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
||||
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
||||
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
||||
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
||||
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
||||
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
||||
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
||||
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
||||
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
||||
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
||||
// jshint +W013,+W015
|
||||
|
||||
// Set the key.
|
||||
function setKeys(keyBlock) {
|
||||
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
||||
raw0, raw1, rawi, KnLi;
|
||||
// Set the key.
|
||||
function setKeys(keyBlock) {
|
||||
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
||||
raw0, raw1, rawi, KnLi;
|
||||
|
||||
for (j = 0, l = 56; j < 56; ++j, l-=8) {
|
||||
l += l<-5 ? 65 : l<-3 ? 31 : l<-1 ? 63 : l===27 ? 35 : 0; // PC1
|
||||
m = l & 0x7;
|
||||
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
||||
}
|
||||
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
|
||||
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
|
||||
m = l & 0x7;
|
||||
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
m = i << 1;
|
||||
n = m + 1;
|
||||
kn[m] = kn[n] = 0;
|
||||
for (o=28; o<59; o+=28) {
|
||||
for (j = o-28; j < o; ++j) {
|
||||
l = j + totrot[i];
|
||||
if (l < o) {
|
||||
pcr[j] = pc1m[l];
|
||||
} else {
|
||||
pcr[j] = pc1m[l - 28];
|
||||
for (i = 0; i < 16; ++i) {
|
||||
m = i << 1;
|
||||
n = m + 1;
|
||||
kn[m] = kn[n] = 0;
|
||||
for (o = 28; o < 59; o += 28) {
|
||||
for (j = o - 28; j < o; ++j) {
|
||||
l = j + totrot[i];
|
||||
if (l < o) {
|
||||
pcr[j] = pc1m[l];
|
||||
} else {
|
||||
pcr[j] = pc1m[l - 28];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 24; ++j) {
|
||||
if (pcr[PC2[j]] !== 0) {
|
||||
kn[m] |= 1 << (23 - j);
|
||||
}
|
||||
if (pcr[PC2[j + 24]] !== 0) {
|
||||
kn[n] |= 1 << (23 - j);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 24; ++j) {
|
||||
if (pcr[PC2[j]] !== 0) {
|
||||
kn[m] |= 1<<(23-j);
|
||||
}
|
||||
if (pcr[PC2[j + 24]] !== 0) {
|
||||
kn[n] |= 1<<(23-j);
|
||||
}
|
||||
|
||||
// cookey
|
||||
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
||||
raw0 = kn[rawi++];
|
||||
raw1 = kn[rawi++];
|
||||
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
||||
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
||||
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
||||
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
||||
++KnLi;
|
||||
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
||||
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
||||
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
||||
keys[KnLi] |= (raw1 & 0x0000003f);
|
||||
++KnLi;
|
||||
}
|
||||
}
|
||||
|
||||
// cookey
|
||||
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
||||
raw0 = kn[rawi++];
|
||||
raw1 = kn[rawi++];
|
||||
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
||||
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
||||
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
||||
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
||||
++KnLi;
|
||||
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
||||
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
||||
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
||||
keys[KnLi] |= (raw1 & 0x0000003f);
|
||||
++KnLi;
|
||||
}
|
||||
}
|
||||
// Encrypt 8 bytes of text
|
||||
function enc8(text) {
|
||||
var i = 0, b = text.slice(), fval, keysi = 0,
|
||||
l, r, x; // left, right, accumulator
|
||||
|
||||
// Encrypt 8 bytes of text
|
||||
function enc8(text) {
|
||||
var i = 0, b = text.slice(), fval, keysi = 0,
|
||||
l, r, x; // left, right, accumulator
|
||||
// Squash 8 bytes to 2 ints
|
||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
|
||||
// Squash 8 bytes to 2 ints
|
||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
||||
r ^= x;
|
||||
l ^= (x << 4);
|
||||
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
||||
r ^= x;
|
||||
l ^= (x << 16);
|
||||
x = ((r >>> 2) ^ l) & 0x33333333;
|
||||
l ^= x;
|
||||
r ^= (x << 2);
|
||||
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
||||
l ^= x;
|
||||
r ^= (x << 8);
|
||||
r = (r << 1) | ((r >>> 31) & 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 1) | ((l >>> 31) & 1);
|
||||
|
||||
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
||||
r ^= x;
|
||||
l ^= (x << 4);
|
||||
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
||||
r ^= x;
|
||||
l ^= (x << 16);
|
||||
x = ((r >>> 2) ^ l) & 0x33333333;
|
||||
l ^= x;
|
||||
r ^= (x << 2);
|
||||
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
||||
l ^= x;
|
||||
r ^= (x << 8);
|
||||
r = (r << 1) | ((r >>> 31) & 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 1) | ((l >>> 31) & 1);
|
||||
for (i = 0; i < 8; ++i) {
|
||||
x = (r << 28) | (r >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = r ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x3f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
l ^= fval;
|
||||
x = (l << 28) | (l >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = l ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x0000003f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
r ^= fval;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
x = (r << 28) | (r >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = r ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x3f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
l ^= fval;
|
||||
x = (l << 28) | (l >>> 4);
|
||||
x ^= keys[keysi++];
|
||||
fval = SP7[x & 0x3f];
|
||||
fval |= SP5[(x >>> 8) & 0x3f];
|
||||
fval |= SP3[(x >>> 16) & 0x3f];
|
||||
fval |= SP1[(x >>> 24) & 0x3f];
|
||||
x = l ^ keys[keysi++];
|
||||
fval |= SP8[x & 0x0000003f];
|
||||
fval |= SP6[(x >>> 8) & 0x3f];
|
||||
fval |= SP4[(x >>> 16) & 0x3f];
|
||||
fval |= SP2[(x >>> 24) & 0x3f];
|
||||
r ^= fval;
|
||||
r = (r << 31) | (r >>> 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 31) | (l >>> 1);
|
||||
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
||||
r ^= x;
|
||||
l ^= (x << 8);
|
||||
x = ((l >>> 2) ^ r) & 0x33333333;
|
||||
r ^= x;
|
||||
l ^= (x << 2);
|
||||
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
||||
l ^= x;
|
||||
r ^= (x << 16);
|
||||
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
||||
l ^= x;
|
||||
r ^= (x << 4);
|
||||
|
||||
// Spread ints to bytes
|
||||
x = [r, l];
|
||||
for (i = 0; i < 8; i++) {
|
||||
b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
|
||||
if (b[i] < 0) { b[i] += 256; } // unsigned
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
r = (r << 31) | (r >>> 1);
|
||||
x = (l ^ r) & 0xaaaaaaaa;
|
||||
l ^= x;
|
||||
r ^= x;
|
||||
l = (l << 31) | (l >>> 1);
|
||||
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
||||
r ^= x;
|
||||
l ^= (x << 8);
|
||||
x = ((l >>> 2) ^ r) & 0x33333333;
|
||||
r ^= x;
|
||||
l ^= (x << 2);
|
||||
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
||||
l ^= x;
|
||||
r ^= (x << 16);
|
||||
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
||||
l ^= x;
|
||||
r ^= (x << 4);
|
||||
|
||||
// Spread ints to bytes
|
||||
x = [r, l];
|
||||
for (i = 0; i < 8; i++) {
|
||||
b[i] = (x[i>>>2] >>> (8*(3 - (i%4)))) % 256;
|
||||
if (b[i] < 0) { b[i] += 256; } // unsigned
|
||||
// Encrypt 16 bytes of text using passwd as key
|
||||
function encrypt(t) {
|
||||
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Encrypt 16 bytes of text using passwd as key
|
||||
function encrypt(t) {
|
||||
return enc8(t.slice(0,8)).concat(enc8(t.slice(8,16)));
|
||||
}
|
||||
|
||||
setKeys(passwd); // Setup keys
|
||||
return {'encrypt': encrypt}; // Public interface
|
||||
setKeys(passwd); // Setup keys
|
||||
return {'encrypt': encrypt}; // Public interface
|
||||
|
||||
} // function DES
|
||||
|
||||
1414
include/display.js
2418
include/inflator.js
Normal file
2166
include/input.js
543
include/keyboard.js
Normal file
@@ -0,0 +1,543 @@
|
||||
var kbdUtil = (function() {
|
||||
"use strict";
|
||||
|
||||
function substituteCodepoint(cp) {
|
||||
// Any Unicode code points which do not have corresponding keysym entries
|
||||
// can be swapped out for another code point by adding them to this table
|
||||
var substitutions = {
|
||||
// {S,s} with comma below -> {S,s} with cedilla
|
||||
0x218 : 0x15e,
|
||||
0x219 : 0x15f,
|
||||
// {T,t} with comma below -> {T,t} with cedilla
|
||||
0x21a : 0x162,
|
||||
0x21b : 0x163
|
||||
};
|
||||
|
||||
var sub = substitutions[cp];
|
||||
return sub ? sub : cp;
|
||||
}
|
||||
|
||||
function isMac() {
|
||||
return navigator && !!(/mac/i).exec(navigator.platform);
|
||||
}
|
||||
function isWindows() {
|
||||
return navigator && !!(/win/i).exec(navigator.platform);
|
||||
}
|
||||
function isLinux() {
|
||||
return navigator && !!(/linux/i).exec(navigator.platform);
|
||||
}
|
||||
|
||||
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
||||
function hasShortcutModifier(charModifier, currentModifiers) {
|
||||
var mods = {};
|
||||
for (var key in currentModifiers) {
|
||||
if (parseInt(key) !== XK_Shift_L) {
|
||||
mods[key] = currentModifiers[key];
|
||||
}
|
||||
}
|
||||
|
||||
var sum = 0;
|
||||
for (var k in currentModifiers) {
|
||||
if (mods[k]) {
|
||||
++sum;
|
||||
}
|
||||
}
|
||||
if (hasCharModifier(charModifier, mods)) {
|
||||
return sum > charModifier.length;
|
||||
}
|
||||
else {
|
||||
return sum > 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the specified char modifier is currently down
|
||||
function hasCharModifier(charModifier, currentModifiers) {
|
||||
if (charModifier.length === 0) { return false; }
|
||||
|
||||
for (var i = 0; i < charModifier.length; ++i) {
|
||||
if (!currentModifiers[charModifier[i]]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper object tracking modifier key state
|
||||
// and generates fake key events to compensate if it gets out of sync
|
||||
function ModifierSync(charModifier) {
|
||||
if (!charModifier) {
|
||||
if (isMac()) {
|
||||
// on Mac, Option (AKA Alt) is used as a char modifier
|
||||
charModifier = [XK_Alt_L];
|
||||
}
|
||||
else if (isWindows()) {
|
||||
// on Windows, Ctrl+Alt is used as a char modifier
|
||||
charModifier = [XK_Alt_L, XK_Control_L];
|
||||
}
|
||||
else if (isLinux()) {
|
||||
// on Linux, ISO Level 3 Shift (AltGr) is used as a char modifier
|
||||
charModifier = [XK_ISO_Level3_Shift];
|
||||
}
|
||||
else {
|
||||
charModifier = [];
|
||||
}
|
||||
}
|
||||
|
||||
var state = {};
|
||||
state[XK_Control_L] = false;
|
||||
state[XK_Alt_L] = false;
|
||||
state[XK_ISO_Level3_Shift] = false;
|
||||
state[XK_Shift_L] = false;
|
||||
state[XK_Meta_L] = false;
|
||||
|
||||
function sync(evt, keysym) {
|
||||
var result = [];
|
||||
function syncKey(keysym) {
|
||||
return {keysym: keysyms.lookup(keysym), type: state[keysym] ? 'keydown' : 'keyup'};
|
||||
}
|
||||
|
||||
if (evt.ctrlKey !== undefined &&
|
||||
evt.ctrlKey !== state[XK_Control_L] && keysym !== XK_Control_L) {
|
||||
state[XK_Control_L] = evt.ctrlKey;
|
||||
result.push(syncKey(XK_Control_L));
|
||||
}
|
||||
if (evt.altKey !== undefined &&
|
||||
evt.altKey !== state[XK_Alt_L] && keysym !== XK_Alt_L) {
|
||||
state[XK_Alt_L] = evt.altKey;
|
||||
result.push(syncKey(XK_Alt_L));
|
||||
}
|
||||
if (evt.altGraphKey !== undefined &&
|
||||
evt.altGraphKey !== state[XK_ISO_Level3_Shift] && keysym !== XK_ISO_Level3_Shift) {
|
||||
state[XK_ISO_Level3_Shift] = evt.altGraphKey;
|
||||
result.push(syncKey(XK_ISO_Level3_Shift));
|
||||
}
|
||||
if (evt.shiftKey !== undefined &&
|
||||
evt.shiftKey !== state[XK_Shift_L] && keysym !== XK_Shift_L) {
|
||||
state[XK_Shift_L] = evt.shiftKey;
|
||||
result.push(syncKey(XK_Shift_L));
|
||||
}
|
||||
if (evt.metaKey !== undefined &&
|
||||
evt.metaKey !== state[XK_Meta_L] && keysym !== XK_Meta_L) {
|
||||
state[XK_Meta_L] = evt.metaKey;
|
||||
result.push(syncKey(XK_Meta_L));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function syncKeyEvent(evt, down) {
|
||||
var obj = getKeysym(evt);
|
||||
var keysym = obj ? obj.keysym : null;
|
||||
|
||||
// first, apply the event itself, if relevant
|
||||
if (keysym !== null && state[keysym] !== undefined) {
|
||||
state[keysym] = down;
|
||||
}
|
||||
return sync(evt, keysym);
|
||||
}
|
||||
|
||||
return {
|
||||
// sync on the appropriate keyboard event
|
||||
keydown: function(evt) { return syncKeyEvent(evt, true);},
|
||||
keyup: function(evt) { return syncKeyEvent(evt, false);},
|
||||
// Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway
|
||||
syncAny: function(evt) { return sync(evt);},
|
||||
|
||||
// is a shortcut modifier down?
|
||||
hasShortcutModifier: function() { return hasShortcutModifier(charModifier, state); },
|
||||
// if a char modifier is down, return the keys it consists of, otherwise return null
|
||||
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
|
||||
};
|
||||
}
|
||||
|
||||
// Get a key ID from a keyboard event
|
||||
// May be a string or an integer depending on the available properties
|
||||
function getKey(evt){
|
||||
if ('keyCode' in evt && 'key' in evt) {
|
||||
return evt.key + ':' + evt.keyCode;
|
||||
}
|
||||
else if ('keyCode' in evt) {
|
||||
return evt.keyCode;
|
||||
}
|
||||
else {
|
||||
return evt.key;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the most reliable keysym value we can get from a key event
|
||||
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
||||
function getKeysym(evt){
|
||||
var codepoint;
|
||||
if (evt.char && evt.char.length === 1) {
|
||||
codepoint = evt.char.charCodeAt();
|
||||
}
|
||||
else if (evt.charCode) {
|
||||
codepoint = evt.charCode;
|
||||
}
|
||||
else if (evt.keyCode && evt.type === 'keypress') {
|
||||
// IE10 stores the char code as keyCode, and has no other useful properties
|
||||
codepoint = evt.keyCode;
|
||||
}
|
||||
if (codepoint) {
|
||||
var res = keysyms.fromUnicode(substituteCodepoint(codepoint));
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
// we could check evt.key here.
|
||||
// Legal values are defined in http://www.w3.org/TR/DOM-Level-3-Events/#key-values-list,
|
||||
// so we "just" need to map them to keysym, but AFAIK this is only available in IE10, which also provides evt.key
|
||||
// so we don't *need* it yet
|
||||
if (evt.keyCode) {
|
||||
return keysyms.lookup(keysymFromKeyCode(evt.keyCode, evt.shiftKey));
|
||||
}
|
||||
if (evt.which) {
|
||||
return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Given a keycode, try to predict which keysym it might be.
|
||||
// If the keycode is unknown, null is returned.
|
||||
function keysymFromKeyCode(keycode, shiftPressed) {
|
||||
if (typeof(keycode) !== 'number') {
|
||||
return null;
|
||||
}
|
||||
// won't be accurate for azerty
|
||||
if (keycode >= 0x30 && keycode <= 0x39) {
|
||||
return keycode; // digit
|
||||
}
|
||||
if (keycode >= 0x41 && keycode <= 0x5a) {
|
||||
// remap to lowercase unless shift is down
|
||||
return shiftPressed ? keycode : keycode + 32; // A-Z
|
||||
}
|
||||
if (keycode >= 0x60 && keycode <= 0x69) {
|
||||
return XK_KP_0 + (keycode - 0x60); // numpad 0-9
|
||||
}
|
||||
|
||||
switch(keycode) {
|
||||
case 0x20: return XK_space;
|
||||
case 0x6a: return XK_KP_Multiply;
|
||||
case 0x6b: return XK_KP_Add;
|
||||
case 0x6c: return XK_KP_Separator;
|
||||
case 0x6d: return XK_KP_Subtract;
|
||||
case 0x6e: return XK_KP_Decimal;
|
||||
case 0x6f: return XK_KP_Divide;
|
||||
case 0xbb: return XK_plus;
|
||||
case 0xbc: return XK_comma;
|
||||
case 0xbd: return XK_minus;
|
||||
case 0xbe: return XK_period;
|
||||
}
|
||||
|
||||
return nonCharacterKey({keyCode: keycode});
|
||||
}
|
||||
|
||||
// if the key is a known non-character key (any key which doesn't generate character data)
|
||||
// return its keysym value. Otherwise return null
|
||||
function nonCharacterKey(evt) {
|
||||
// evt.key not implemented yet
|
||||
if (!evt.keyCode) { return null; }
|
||||
var keycode = evt.keyCode;
|
||||
|
||||
if (keycode >= 0x70 && keycode <= 0x87) {
|
||||
return XK_F1 + keycode - 0x70; // F1-F24
|
||||
}
|
||||
switch (keycode) {
|
||||
|
||||
case 8 : return XK_BackSpace;
|
||||
case 13 : return XK_Return;
|
||||
|
||||
case 9 : return XK_Tab;
|
||||
|
||||
case 27 : return XK_Escape;
|
||||
case 46 : return XK_Delete;
|
||||
|
||||
case 36 : return XK_Home;
|
||||
case 35 : return XK_End;
|
||||
case 33 : return XK_Page_Up;
|
||||
case 34 : return XK_Page_Down;
|
||||
case 45 : return XK_Insert;
|
||||
|
||||
case 37 : return XK_Left;
|
||||
case 38 : return XK_Up;
|
||||
case 39 : return XK_Right;
|
||||
case 40 : return XK_Down;
|
||||
|
||||
case 16 : return XK_Shift_L;
|
||||
case 17 : return XK_Control_L;
|
||||
case 18 : return XK_Alt_L; // also: Option-key on Mac
|
||||
|
||||
case 224 : return XK_Meta_L;
|
||||
case 225 : return XK_ISO_Level3_Shift; // AltGr
|
||||
case 91 : return XK_Super_L; // also: Windows-key
|
||||
case 92 : return XK_Super_R; // also: Windows-key
|
||||
case 93 : return XK_Menu; // also: Windows-Menu, Command on Mac
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
return {
|
||||
hasShortcutModifier : hasShortcutModifier,
|
||||
hasCharModifier : hasCharModifier,
|
||||
ModifierSync : ModifierSync,
|
||||
getKey : getKey,
|
||||
getKeysym : getKeysym,
|
||||
keysymFromKeyCode : keysymFromKeyCode,
|
||||
nonCharacterKey : nonCharacterKey,
|
||||
substituteCodepoint : substituteCodepoint
|
||||
};
|
||||
})();
|
||||
|
||||
// Takes a DOM keyboard event and:
|
||||
// - determines which keysym it represents
|
||||
// - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event)
|
||||
// - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down
|
||||
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
||||
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
||||
// This information is collected into an object which is passed to the next() function. (one call per event)
|
||||
function KeyEventDecoder(modifierState, next) {
|
||||
"use strict";
|
||||
function sendAll(evts) {
|
||||
for (var i = 0; i < evts.length; ++i) {
|
||||
next(evts[i]);
|
||||
}
|
||||
}
|
||||
function process(evt, type) {
|
||||
var result = {type: type};
|
||||
var keyId = kbdUtil.getKey(evt);
|
||||
if (keyId) {
|
||||
result.keyId = keyId;
|
||||
}
|
||||
|
||||
var keysym = kbdUtil.getKeysym(evt);
|
||||
|
||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
||||
// "special" keys like enter, tab or backspace don't send keypress events,
|
||||
// and some browsers don't send keypresses at all if a modifier is down
|
||||
if (keysym && (type !== 'keydown' || kbdUtil.nonCharacterKey(evt) || hasModifier)) {
|
||||
result.keysym = keysym;
|
||||
}
|
||||
|
||||
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
||||
|
||||
// Should we prevent the browser from handling the event?
|
||||
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
||||
// so only do that if we have to.
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!kbdUtil.nonCharacterKey(evt));
|
||||
|
||||
// If a char modifier is down on a keydown, we need to insert a stall,
|
||||
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
||||
var stall = type === 'keydown' && modifierState.activeCharModifier() && !kbdUtil.nonCharacterKey(evt);
|
||||
|
||||
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
||||
var active = modifierState.activeCharModifier();
|
||||
|
||||
// If we have a char modifier down, and we're able to determine a keysym reliably
|
||||
// then (a) we know to treat the modifier as a char modifier,
|
||||
// and (b) we'll have to "escape" the modifier to undo the modifier when sending the char.
|
||||
if (active && keysym) {
|
||||
var isCharModifier = false;
|
||||
for (var i = 0; i < active.length; ++i) {
|
||||
if (active[i] === keysym.keysym) {
|
||||
isCharModifier = true;
|
||||
}
|
||||
}
|
||||
if (type === 'keypress' && !isCharModifier) {
|
||||
result.escape = modifierState.activeCharModifier();
|
||||
}
|
||||
}
|
||||
|
||||
if (stall) {
|
||||
// insert a fake "stall" event
|
||||
next({type: 'stall'});
|
||||
}
|
||||
next(result);
|
||||
|
||||
return suppress;
|
||||
}
|
||||
|
||||
return {
|
||||
keydown: function(evt) {
|
||||
sendAll(modifierState.keydown(evt));
|
||||
return process(evt, 'keydown');
|
||||
},
|
||||
keypress: function(evt) {
|
||||
return process(evt, 'keypress');
|
||||
},
|
||||
keyup: function(evt) {
|
||||
sendAll(modifierState.keyup(evt));
|
||||
return process(evt, 'keyup');
|
||||
},
|
||||
syncModifiers: function(evt) {
|
||||
sendAll(modifierState.syncAny(evt));
|
||||
},
|
||||
releaseAll: function() { next({type: 'releaseall'}); }
|
||||
};
|
||||
}
|
||||
|
||||
// Combines keydown and keypress events where necessary to handle char modifiers.
|
||||
// On some OS'es, a char modifier is sometimes used as a shortcut modifier.
|
||||
// For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing
|
||||
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
|
||||
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
||||
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
||||
function VerifyCharModifier(next) {
|
||||
"use strict";
|
||||
var queue = [];
|
||||
var timer = null;
|
||||
function process() {
|
||||
if (timer) {
|
||||
return;
|
||||
}
|
||||
|
||||
var delayProcess = function () {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
process();
|
||||
};
|
||||
|
||||
while (queue.length !== 0) {
|
||||
var cur = queue[0];
|
||||
queue = queue.splice(1);
|
||||
switch (cur.type) {
|
||||
case 'stall':
|
||||
// insert a delay before processing available events.
|
||||
/* jshint loopfunc: true */
|
||||
timer = setTimeout(delayProcess, 5);
|
||||
/* jshint loopfunc: false */
|
||||
return;
|
||||
case 'keydown':
|
||||
// is the next element a keypress? Then we should merge the two
|
||||
if (queue.length !== 0 && queue[0].type === 'keypress') {
|
||||
// Firefox sends keypress even when no char is generated.
|
||||
// so, if keypress keysym is the same as we'd have guessed from keydown,
|
||||
// the modifier didn't have any effect, and should not be escaped
|
||||
if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) {
|
||||
cur.escape = queue[0].escape;
|
||||
}
|
||||
cur.keysym = queue[0].keysym;
|
||||
queue = queue.splice(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// swallow stall events, and pass all others to the next stage
|
||||
if (cur.type !== 'stall') {
|
||||
next(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
return function(evt) {
|
||||
queue.push(evt);
|
||||
process();
|
||||
};
|
||||
}
|
||||
|
||||
// Keeps track of which keys we (and the server) believe are down
|
||||
// When a keyup is received, match it against this list, to determine the corresponding keysym(s)
|
||||
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
||||
// key repeat events should be merged into a single entry.
|
||||
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
||||
function TrackKeyState(next) {
|
||||
"use strict";
|
||||
var state = [];
|
||||
|
||||
return function (evt) {
|
||||
var last = state.length !== 0 ? state[state.length-1] : null;
|
||||
|
||||
switch (evt.type) {
|
||||
case 'keydown':
|
||||
// insert a new entry if last seen key was different.
|
||||
if (!last || !evt.keyId || last.keyId !== evt.keyId) {
|
||||
last = {keyId: evt.keyId, keysyms: {}};
|
||||
state.push(last);
|
||||
}
|
||||
if (evt.keysym) {
|
||||
// make sure last event contains this keysym (a single "logical" keyevent
|
||||
// can cause multiple key events to be sent to the VNC server)
|
||||
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
||||
last.ignoreKeyPress = true;
|
||||
next(evt);
|
||||
}
|
||||
break;
|
||||
case 'keypress':
|
||||
if (!last) {
|
||||
last = {keyId: evt.keyId, keysyms: {}};
|
||||
state.push(last);
|
||||
}
|
||||
if (!evt.keysym) {
|
||||
console.log('keypress with no keysym:', evt);
|
||||
}
|
||||
|
||||
// If we didn't expect a keypress, and already sent a keydown to the VNC server
|
||||
// based on the keydown, make sure to skip this event.
|
||||
if (evt.keysym && !last.ignoreKeyPress) {
|
||||
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
||||
evt.type = 'keydown';
|
||||
next(evt);
|
||||
}
|
||||
break;
|
||||
case 'keyup':
|
||||
if (state.length === 0) {
|
||||
return;
|
||||
}
|
||||
var idx = null;
|
||||
// do we have a matching key tracked as being down?
|
||||
for (var i = 0; i !== state.length; ++i) {
|
||||
if (state[i].keyId === evt.keyId) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if we couldn't find a match (it happens), assume it was the last key pressed
|
||||
if (idx === null) {
|
||||
idx = state.length - 1;
|
||||
}
|
||||
|
||||
var item = state.splice(idx, 1)[0];
|
||||
// for each keysym tracked by this key entry, clone the current event and override the keysym
|
||||
var clone = (function(){
|
||||
function Clone(){}
|
||||
return function (obj) { Clone.prototype=obj; return new Clone(); };
|
||||
}());
|
||||
for (var key in item.keysyms) {
|
||||
var out = clone(evt);
|
||||
out.keysym = item.keysyms[key];
|
||||
next(out);
|
||||
}
|
||||
break;
|
||||
case 'releaseall':
|
||||
/* jshint shadow: true */
|
||||
for (var i = 0; i < state.length; ++i) {
|
||||
for (var key in state[i].keysyms) {
|
||||
var keysym = state[i].keysyms[key];
|
||||
next({keyId: 0, keysym: keysym, type: 'keyup'});
|
||||
}
|
||||
}
|
||||
/* jshint shadow: false */
|
||||
state = [];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
||||
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
||||
function EscapeModifiers(next) {
|
||||
"use strict";
|
||||
return function(evt) {
|
||||
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
||||
next(evt);
|
||||
return;
|
||||
}
|
||||
// undo modifiers
|
||||
for (var i = 0; i < evt.escape.length; ++i) {
|
||||
next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
||||
}
|
||||
// send the character event
|
||||
next(evt);
|
||||
// redo modifiers
|
||||
/* jshint shadow: true */
|
||||
for (var i = 0; i < evt.escape.length; ++i) {
|
||||
next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
||||
}
|
||||
/* jshint shadow: false */
|
||||
};
|
||||
}
|
||||
378
include/keysym.js
Normal file
@@ -0,0 +1,378 @@
|
||||
var XK_VoidSymbol = 0xffffff, /* Void symbol */
|
||||
|
||||
XK_BackSpace = 0xff08, /* Back space, back char */
|
||||
XK_Tab = 0xff09,
|
||||
XK_Linefeed = 0xff0a, /* Linefeed, LF */
|
||||
XK_Clear = 0xff0b,
|
||||
XK_Return = 0xff0d, /* Return, enter */
|
||||
XK_Pause = 0xff13, /* Pause, hold */
|
||||
XK_Scroll_Lock = 0xff14,
|
||||
XK_Sys_Req = 0xff15,
|
||||
XK_Escape = 0xff1b,
|
||||
XK_Delete = 0xffff, /* Delete, rubout */
|
||||
|
||||
/* Cursor control & motion */
|
||||
|
||||
XK_Home = 0xff50,
|
||||
XK_Left = 0xff51, /* Move left, left arrow */
|
||||
XK_Up = 0xff52, /* Move up, up arrow */
|
||||
XK_Right = 0xff53, /* Move right, right arrow */
|
||||
XK_Down = 0xff54, /* Move down, down arrow */
|
||||
XK_Prior = 0xff55, /* Prior, previous */
|
||||
XK_Page_Up = 0xff55,
|
||||
XK_Next = 0xff56, /* Next */
|
||||
XK_Page_Down = 0xff56,
|
||||
XK_End = 0xff57, /* EOL */
|
||||
XK_Begin = 0xff58, /* BOL */
|
||||
|
||||
|
||||
/* Misc functions */
|
||||
|
||||
XK_Select = 0xff60, /* Select, mark */
|
||||
XK_Print = 0xff61,
|
||||
XK_Execute = 0xff62, /* Execute, run, do */
|
||||
XK_Insert = 0xff63, /* Insert, insert here */
|
||||
XK_Undo = 0xff65,
|
||||
XK_Redo = 0xff66, /* Redo, again */
|
||||
XK_Menu = 0xff67,
|
||||
XK_Find = 0xff68, /* Find, search */
|
||||
XK_Cancel = 0xff69, /* Cancel, stop, abort, exit */
|
||||
XK_Help = 0xff6a, /* Help */
|
||||
XK_Break = 0xff6b,
|
||||
XK_Mode_switch = 0xff7e, /* Character set switch */
|
||||
XK_script_switch = 0xff7e, /* Alias for mode_switch */
|
||||
XK_Num_Lock = 0xff7f,
|
||||
|
||||
/* Keypad functions, keypad numbers cleverly chosen to map to ASCII */
|
||||
|
||||
XK_KP_Space = 0xff80, /* Space */
|
||||
XK_KP_Tab = 0xff89,
|
||||
XK_KP_Enter = 0xff8d, /* Enter */
|
||||
XK_KP_F1 = 0xff91, /* PF1, KP_A, ... */
|
||||
XK_KP_F2 = 0xff92,
|
||||
XK_KP_F3 = 0xff93,
|
||||
XK_KP_F4 = 0xff94,
|
||||
XK_KP_Home = 0xff95,
|
||||
XK_KP_Left = 0xff96,
|
||||
XK_KP_Up = 0xff97,
|
||||
XK_KP_Right = 0xff98,
|
||||
XK_KP_Down = 0xff99,
|
||||
XK_KP_Prior = 0xff9a,
|
||||
XK_KP_Page_Up = 0xff9a,
|
||||
XK_KP_Next = 0xff9b,
|
||||
XK_KP_Page_Down = 0xff9b,
|
||||
XK_KP_End = 0xff9c,
|
||||
XK_KP_Begin = 0xff9d,
|
||||
XK_KP_Insert = 0xff9e,
|
||||
XK_KP_Delete = 0xff9f,
|
||||
XK_KP_Equal = 0xffbd, /* Equals */
|
||||
XK_KP_Multiply = 0xffaa,
|
||||
XK_KP_Add = 0xffab,
|
||||
XK_KP_Separator = 0xffac, /* Separator, often comma */
|
||||
XK_KP_Subtract = 0xffad,
|
||||
XK_KP_Decimal = 0xffae,
|
||||
XK_KP_Divide = 0xffaf,
|
||||
|
||||
XK_KP_0 = 0xffb0,
|
||||
XK_KP_1 = 0xffb1,
|
||||
XK_KP_2 = 0xffb2,
|
||||
XK_KP_3 = 0xffb3,
|
||||
XK_KP_4 = 0xffb4,
|
||||
XK_KP_5 = 0xffb5,
|
||||
XK_KP_6 = 0xffb6,
|
||||
XK_KP_7 = 0xffb7,
|
||||
XK_KP_8 = 0xffb8,
|
||||
XK_KP_9 = 0xffb9,
|
||||
|
||||
/*
|
||||
* Auxiliary functions; note the duplicate definitions for left and right
|
||||
* function keys; Sun keyboards and a few other manufacturers have such
|
||||
* function key groups on the left and/or right sides of the keyboard.
|
||||
* We've not found a keyboard with more than 35 function keys total.
|
||||
*/
|
||||
|
||||
XK_F1 = 0xffbe,
|
||||
XK_F2 = 0xffbf,
|
||||
XK_F3 = 0xffc0,
|
||||
XK_F4 = 0xffc1,
|
||||
XK_F5 = 0xffc2,
|
||||
XK_F6 = 0xffc3,
|
||||
XK_F7 = 0xffc4,
|
||||
XK_F8 = 0xffc5,
|
||||
XK_F9 = 0xffc6,
|
||||
XK_F10 = 0xffc7,
|
||||
XK_F11 = 0xffc8,
|
||||
XK_L1 = 0xffc8,
|
||||
XK_F12 = 0xffc9,
|
||||
XK_L2 = 0xffc9,
|
||||
XK_F13 = 0xffca,
|
||||
XK_L3 = 0xffca,
|
||||
XK_F14 = 0xffcb,
|
||||
XK_L4 = 0xffcb,
|
||||
XK_F15 = 0xffcc,
|
||||
XK_L5 = 0xffcc,
|
||||
XK_F16 = 0xffcd,
|
||||
XK_L6 = 0xffcd,
|
||||
XK_F17 = 0xffce,
|
||||
XK_L7 = 0xffce,
|
||||
XK_F18 = 0xffcf,
|
||||
XK_L8 = 0xffcf,
|
||||
XK_F19 = 0xffd0,
|
||||
XK_L9 = 0xffd0,
|
||||
XK_F20 = 0xffd1,
|
||||
XK_L10 = 0xffd1,
|
||||
XK_F21 = 0xffd2,
|
||||
XK_R1 = 0xffd2,
|
||||
XK_F22 = 0xffd3,
|
||||
XK_R2 = 0xffd3,
|
||||
XK_F23 = 0xffd4,
|
||||
XK_R3 = 0xffd4,
|
||||
XK_F24 = 0xffd5,
|
||||
XK_R4 = 0xffd5,
|
||||
XK_F25 = 0xffd6,
|
||||
XK_R5 = 0xffd6,
|
||||
XK_F26 = 0xffd7,
|
||||
XK_R6 = 0xffd7,
|
||||
XK_F27 = 0xffd8,
|
||||
XK_R7 = 0xffd8,
|
||||
XK_F28 = 0xffd9,
|
||||
XK_R8 = 0xffd9,
|
||||
XK_F29 = 0xffda,
|
||||
XK_R9 = 0xffda,
|
||||
XK_F30 = 0xffdb,
|
||||
XK_R10 = 0xffdb,
|
||||
XK_F31 = 0xffdc,
|
||||
XK_R11 = 0xffdc,
|
||||
XK_F32 = 0xffdd,
|
||||
XK_R12 = 0xffdd,
|
||||
XK_F33 = 0xffde,
|
||||
XK_R13 = 0xffde,
|
||||
XK_F34 = 0xffdf,
|
||||
XK_R14 = 0xffdf,
|
||||
XK_F35 = 0xffe0,
|
||||
XK_R15 = 0xffe0,
|
||||
|
||||
/* Modifiers */
|
||||
|
||||
XK_Shift_L = 0xffe1, /* Left shift */
|
||||
XK_Shift_R = 0xffe2, /* Right shift */
|
||||
XK_Control_L = 0xffe3, /* Left control */
|
||||
XK_Control_R = 0xffe4, /* Right control */
|
||||
XK_Caps_Lock = 0xffe5, /* Caps lock */
|
||||
XK_Shift_Lock = 0xffe6, /* Shift lock */
|
||||
|
||||
XK_Meta_L = 0xffe7, /* Left meta */
|
||||
XK_Meta_R = 0xffe8, /* Right meta */
|
||||
XK_Alt_L = 0xffe9, /* Left alt */
|
||||
XK_Alt_R = 0xffea, /* Right alt */
|
||||
XK_Super_L = 0xffeb, /* Left super */
|
||||
XK_Super_R = 0xffec, /* Right super */
|
||||
XK_Hyper_L = 0xffed, /* Left hyper */
|
||||
XK_Hyper_R = 0xffee, /* Right hyper */
|
||||
|
||||
XK_ISO_Level3_Shift = 0xfe03, /* AltGr */
|
||||
|
||||
/*
|
||||
* Latin 1
|
||||
* (ISO/IEC 8859-1 = Unicode U+0020..U+00FF)
|
||||
* Byte 3 = 0
|
||||
*/
|
||||
|
||||
XK_space = 0x0020, /* U+0020 SPACE */
|
||||
XK_exclam = 0x0021, /* U+0021 EXCLAMATION MARK */
|
||||
XK_quotedbl = 0x0022, /* U+0022 QUOTATION MARK */
|
||||
XK_numbersign = 0x0023, /* U+0023 NUMBER SIGN */
|
||||
XK_dollar = 0x0024, /* U+0024 DOLLAR SIGN */
|
||||
XK_percent = 0x0025, /* U+0025 PERCENT SIGN */
|
||||
XK_ampersand = 0x0026, /* U+0026 AMPERSAND */
|
||||
XK_apostrophe = 0x0027, /* U+0027 APOSTROPHE */
|
||||
XK_quoteright = 0x0027, /* deprecated */
|
||||
XK_parenleft = 0x0028, /* U+0028 LEFT PARENTHESIS */
|
||||
XK_parenright = 0x0029, /* U+0029 RIGHT PARENTHESIS */
|
||||
XK_asterisk = 0x002a, /* U+002A ASTERISK */
|
||||
XK_plus = 0x002b, /* U+002B PLUS SIGN */
|
||||
XK_comma = 0x002c, /* U+002C COMMA */
|
||||
XK_minus = 0x002d, /* U+002D HYPHEN-MINUS */
|
||||
XK_period = 0x002e, /* U+002E FULL STOP */
|
||||
XK_slash = 0x002f, /* U+002F SOLIDUS */
|
||||
XK_0 = 0x0030, /* U+0030 DIGIT ZERO */
|
||||
XK_1 = 0x0031, /* U+0031 DIGIT ONE */
|
||||
XK_2 = 0x0032, /* U+0032 DIGIT TWO */
|
||||
XK_3 = 0x0033, /* U+0033 DIGIT THREE */
|
||||
XK_4 = 0x0034, /* U+0034 DIGIT FOUR */
|
||||
XK_5 = 0x0035, /* U+0035 DIGIT FIVE */
|
||||
XK_6 = 0x0036, /* U+0036 DIGIT SIX */
|
||||
XK_7 = 0x0037, /* U+0037 DIGIT SEVEN */
|
||||
XK_8 = 0x0038, /* U+0038 DIGIT EIGHT */
|
||||
XK_9 = 0x0039, /* U+0039 DIGIT NINE */
|
||||
XK_colon = 0x003a, /* U+003A COLON */
|
||||
XK_semicolon = 0x003b, /* U+003B SEMICOLON */
|
||||
XK_less = 0x003c, /* U+003C LESS-THAN SIGN */
|
||||
XK_equal = 0x003d, /* U+003D EQUALS SIGN */
|
||||
XK_greater = 0x003e, /* U+003E GREATER-THAN SIGN */
|
||||
XK_question = 0x003f, /* U+003F QUESTION MARK */
|
||||
XK_at = 0x0040, /* U+0040 COMMERCIAL AT */
|
||||
XK_A = 0x0041, /* U+0041 LATIN CAPITAL LETTER A */
|
||||
XK_B = 0x0042, /* U+0042 LATIN CAPITAL LETTER B */
|
||||
XK_C = 0x0043, /* U+0043 LATIN CAPITAL LETTER C */
|
||||
XK_D = 0x0044, /* U+0044 LATIN CAPITAL LETTER D */
|
||||
XK_E = 0x0045, /* U+0045 LATIN CAPITAL LETTER E */
|
||||
XK_F = 0x0046, /* U+0046 LATIN CAPITAL LETTER F */
|
||||
XK_G = 0x0047, /* U+0047 LATIN CAPITAL LETTER G */
|
||||
XK_H = 0x0048, /* U+0048 LATIN CAPITAL LETTER H */
|
||||
XK_I = 0x0049, /* U+0049 LATIN CAPITAL LETTER I */
|
||||
XK_J = 0x004a, /* U+004A LATIN CAPITAL LETTER J */
|
||||
XK_K = 0x004b, /* U+004B LATIN CAPITAL LETTER K */
|
||||
XK_L = 0x004c, /* U+004C LATIN CAPITAL LETTER L */
|
||||
XK_M = 0x004d, /* U+004D LATIN CAPITAL LETTER M */
|
||||
XK_N = 0x004e, /* U+004E LATIN CAPITAL LETTER N */
|
||||
XK_O = 0x004f, /* U+004F LATIN CAPITAL LETTER O */
|
||||
XK_P = 0x0050, /* U+0050 LATIN CAPITAL LETTER P */
|
||||
XK_Q = 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */
|
||||
XK_R = 0x0052, /* U+0052 LATIN CAPITAL LETTER R */
|
||||
XK_S = 0x0053, /* U+0053 LATIN CAPITAL LETTER S */
|
||||
XK_T = 0x0054, /* U+0054 LATIN CAPITAL LETTER T */
|
||||
XK_U = 0x0055, /* U+0055 LATIN CAPITAL LETTER U */
|
||||
XK_V = 0x0056, /* U+0056 LATIN CAPITAL LETTER V */
|
||||
XK_W = 0x0057, /* U+0057 LATIN CAPITAL LETTER W */
|
||||
XK_X = 0x0058, /* U+0058 LATIN CAPITAL LETTER X */
|
||||
XK_Y = 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */
|
||||
XK_Z = 0x005a, /* U+005A LATIN CAPITAL LETTER Z */
|
||||
XK_bracketleft = 0x005b, /* U+005B LEFT SQUARE BRACKET */
|
||||
XK_backslash = 0x005c, /* U+005C REVERSE SOLIDUS */
|
||||
XK_bracketright = 0x005d, /* U+005D RIGHT SQUARE BRACKET */
|
||||
XK_asciicircum = 0x005e, /* U+005E CIRCUMFLEX ACCENT */
|
||||
XK_underscore = 0x005f, /* U+005F LOW LINE */
|
||||
XK_grave = 0x0060, /* U+0060 GRAVE ACCENT */
|
||||
XK_quoteleft = 0x0060, /* deprecated */
|
||||
XK_a = 0x0061, /* U+0061 LATIN SMALL LETTER A */
|
||||
XK_b = 0x0062, /* U+0062 LATIN SMALL LETTER B */
|
||||
XK_c = 0x0063, /* U+0063 LATIN SMALL LETTER C */
|
||||
XK_d = 0x0064, /* U+0064 LATIN SMALL LETTER D */
|
||||
XK_e = 0x0065, /* U+0065 LATIN SMALL LETTER E */
|
||||
XK_f = 0x0066, /* U+0066 LATIN SMALL LETTER F */
|
||||
XK_g = 0x0067, /* U+0067 LATIN SMALL LETTER G */
|
||||
XK_h = 0x0068, /* U+0068 LATIN SMALL LETTER H */
|
||||
XK_i = 0x0069, /* U+0069 LATIN SMALL LETTER I */
|
||||
XK_j = 0x006a, /* U+006A LATIN SMALL LETTER J */
|
||||
XK_k = 0x006b, /* U+006B LATIN SMALL LETTER K */
|
||||
XK_l = 0x006c, /* U+006C LATIN SMALL LETTER L */
|
||||
XK_m = 0x006d, /* U+006D LATIN SMALL LETTER M */
|
||||
XK_n = 0x006e, /* U+006E LATIN SMALL LETTER N */
|
||||
XK_o = 0x006f, /* U+006F LATIN SMALL LETTER O */
|
||||
XK_p = 0x0070, /* U+0070 LATIN SMALL LETTER P */
|
||||
XK_q = 0x0071, /* U+0071 LATIN SMALL LETTER Q */
|
||||
XK_r = 0x0072, /* U+0072 LATIN SMALL LETTER R */
|
||||
XK_s = 0x0073, /* U+0073 LATIN SMALL LETTER S */
|
||||
XK_t = 0x0074, /* U+0074 LATIN SMALL LETTER T */
|
||||
XK_u = 0x0075, /* U+0075 LATIN SMALL LETTER U */
|
||||
XK_v = 0x0076, /* U+0076 LATIN SMALL LETTER V */
|
||||
XK_w = 0x0077, /* U+0077 LATIN SMALL LETTER W */
|
||||
XK_x = 0x0078, /* U+0078 LATIN SMALL LETTER X */
|
||||
XK_y = 0x0079, /* U+0079 LATIN SMALL LETTER Y */
|
||||
XK_z = 0x007a, /* U+007A LATIN SMALL LETTER Z */
|
||||
XK_braceleft = 0x007b, /* U+007B LEFT CURLY BRACKET */
|
||||
XK_bar = 0x007c, /* U+007C VERTICAL LINE */
|
||||
XK_braceright = 0x007d, /* U+007D RIGHT CURLY BRACKET */
|
||||
XK_asciitilde = 0x007e, /* U+007E TILDE */
|
||||
|
||||
XK_nobreakspace = 0x00a0, /* U+00A0 NO-BREAK SPACE */
|
||||
XK_exclamdown = 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */
|
||||
XK_cent = 0x00a2, /* U+00A2 CENT SIGN */
|
||||
XK_sterling = 0x00a3, /* U+00A3 POUND SIGN */
|
||||
XK_currency = 0x00a4, /* U+00A4 CURRENCY SIGN */
|
||||
XK_yen = 0x00a5, /* U+00A5 YEN SIGN */
|
||||
XK_brokenbar = 0x00a6, /* U+00A6 BROKEN BAR */
|
||||
XK_section = 0x00a7, /* U+00A7 SECTION SIGN */
|
||||
XK_diaeresis = 0x00a8, /* U+00A8 DIAERESIS */
|
||||
XK_copyright = 0x00a9, /* U+00A9 COPYRIGHT SIGN */
|
||||
XK_ordfeminine = 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */
|
||||
XK_guillemotleft = 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
|
||||
XK_notsign = 0x00ac, /* U+00AC NOT SIGN */
|
||||
XK_hyphen = 0x00ad, /* U+00AD SOFT HYPHEN */
|
||||
XK_registered = 0x00ae, /* U+00AE REGISTERED SIGN */
|
||||
XK_macron = 0x00af, /* U+00AF MACRON */
|
||||
XK_degree = 0x00b0, /* U+00B0 DEGREE SIGN */
|
||||
XK_plusminus = 0x00b1, /* U+00B1 PLUS-MINUS SIGN */
|
||||
XK_twosuperior = 0x00b2, /* U+00B2 SUPERSCRIPT TWO */
|
||||
XK_threesuperior = 0x00b3, /* U+00B3 SUPERSCRIPT THREE */
|
||||
XK_acute = 0x00b4, /* U+00B4 ACUTE ACCENT */
|
||||
XK_mu = 0x00b5, /* U+00B5 MICRO SIGN */
|
||||
XK_paragraph = 0x00b6, /* U+00B6 PILCROW SIGN */
|
||||
XK_periodcentered = 0x00b7, /* U+00B7 MIDDLE DOT */
|
||||
XK_cedilla = 0x00b8, /* U+00B8 CEDILLA */
|
||||
XK_onesuperior = 0x00b9, /* U+00B9 SUPERSCRIPT ONE */
|
||||
XK_masculine = 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */
|
||||
XK_guillemotright = 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
|
||||
XK_onequarter = 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */
|
||||
XK_onehalf = 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */
|
||||
XK_threequarters = 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */
|
||||
XK_questiondown = 0x00bf, /* U+00BF INVERTED QUESTION MARK */
|
||||
XK_Agrave = 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */
|
||||
XK_Aacute = 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */
|
||||
XK_Acircumflex = 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
|
||||
XK_Atilde = 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */
|
||||
XK_Adiaeresis = 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */
|
||||
XK_Aring = 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */
|
||||
XK_AE = 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */
|
||||
XK_Ccedilla = 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */
|
||||
XK_Egrave = 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */
|
||||
XK_Eacute = 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
|
||||
XK_Ecircumflex = 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
|
||||
XK_Ediaeresis = 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */
|
||||
XK_Igrave = 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */
|
||||
XK_Iacute = 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */
|
||||
XK_Icircumflex = 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
|
||||
XK_Idiaeresis = 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */
|
||||
XK_ETH = 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */
|
||||
XK_Eth = 0x00d0, /* deprecated */
|
||||
XK_Ntilde = 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */
|
||||
XK_Ograve = 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */
|
||||
XK_Oacute = 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */
|
||||
XK_Ocircumflex = 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
|
||||
XK_Otilde = 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */
|
||||
XK_Odiaeresis = 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */
|
||||
XK_multiply = 0x00d7, /* U+00D7 MULTIPLICATION SIGN */
|
||||
XK_Oslash = 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
|
||||
XK_Ooblique = 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
|
||||
XK_Ugrave = 0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */
|
||||
XK_Uacute = 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */
|
||||
XK_Ucircumflex = 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
|
||||
XK_Udiaeresis = 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */
|
||||
XK_Yacute = 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */
|
||||
XK_THORN = 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */
|
||||
XK_Thorn = 0x00de, /* deprecated */
|
||||
XK_ssharp = 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */
|
||||
XK_agrave = 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */
|
||||
XK_aacute = 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
|
||||
XK_acircumflex = 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */
|
||||
XK_atilde = 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */
|
||||
XK_adiaeresis = 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */
|
||||
XK_aring = 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */
|
||||
XK_ae = 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */
|
||||
XK_ccedilla = 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */
|
||||
XK_egrave = 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */
|
||||
XK_eacute = 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
|
||||
XK_ecircumflex = 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */
|
||||
XK_ediaeresis = 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */
|
||||
XK_igrave = 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */
|
||||
XK_iacute = 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */
|
||||
XK_icircumflex = 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */
|
||||
XK_idiaeresis = 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */
|
||||
XK_eth = 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */
|
||||
XK_ntilde = 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */
|
||||
XK_ograve = 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */
|
||||
XK_oacute = 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */
|
||||
XK_ocircumflex = 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */
|
||||
XK_otilde = 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */
|
||||
XK_odiaeresis = 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */
|
||||
XK_division = 0x00f7, /* U+00F7 DIVISION SIGN */
|
||||
XK_oslash = 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
|
||||
XK_ooblique = 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
|
||||
XK_ugrave = 0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */
|
||||
XK_uacute = 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */
|
||||
XK_ucircumflex = 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */
|
||||
XK_udiaeresis = 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */
|
||||
XK_yacute = 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
|
||||
XK_thorn = 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
|
||||
XK_ydiaeresis = 0x00ff; /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
|
||||
15
include/keysymdef.js
Normal file
@@ -1,97 +0,0 @@
|
||||
#VNC_controls {
|
||||
overflow: hidden;
|
||||
}
|
||||
#VNC_controls ul {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#VNC_controls li {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
#VNC_host {
|
||||
width: 100px;
|
||||
}
|
||||
#VNC_port {
|
||||
width: 50px;
|
||||
}
|
||||
#VNC_password {
|
||||
width: 80px;
|
||||
}
|
||||
#VNC_encrypt {
|
||||
}
|
||||
#VNC_connectTimeout {
|
||||
width: 30px;
|
||||
}
|
||||
#VNC_connect_button {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
#VNC_status_bar td {
|
||||
margin-top: 15px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
#VNC_status_bar div {
|
||||
font-size: 12px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
.VNC_status_button {
|
||||
font-size: 10px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#VNC_status {
|
||||
text-align: center;
|
||||
}
|
||||
#VNC_settings_menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
width: 12em;
|
||||
border: 1px solid #888;
|
||||
background-color: #f0f2f6;
|
||||
padding: 5px; margin: 3px;
|
||||
z-index: 100; opacity: 1;
|
||||
text-align: left; white-space: normal;
|
||||
}
|
||||
#VNC_settings_menu ul {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.VNC_buttons_right {
|
||||
text-align: right;
|
||||
}
|
||||
.VNC_buttons_left {
|
||||
text-align: left;
|
||||
}
|
||||
.VNC_status_normal {
|
||||
background: #eee;
|
||||
}
|
||||
.VNC_status_error {
|
||||
background: #f44;
|
||||
}
|
||||
.VNC_status_warn {
|
||||
background: #ff4;
|
||||
}
|
||||
|
||||
/* Do not set width/height for VNC_screen or VNC_canvas or incorrect
|
||||
* scaling will occur. Canvas resizes to remote VNC settings */
|
||||
#VNC_screen {
|
||||
text-align: center;
|
||||
display: table;
|
||||
}
|
||||
#VNC_canvas {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
#VNC_clipboard_clear_button {
|
||||
}
|
||||
#VNC_clipboard_text {
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2011 Joel Martin
|
||||
* Licensed under LGPL-3 (see LICENSE.LGPL-3)
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -12,21 +12,39 @@ var rfb, mode, test_state, frame_idx, frame_length,
|
||||
iteration, iterations, istart_time,
|
||||
|
||||
// Pre-declarations for jslint
|
||||
send_array, next_iteration, queue_next_packet, do_packet;
|
||||
send_array, next_iteration, queue_next_packet, do_packet, enable_test_mode;
|
||||
|
||||
// Override send_array
|
||||
send_array = function (arr) {
|
||||
// Stub out send_array
|
||||
};
|
||||
|
||||
enable_test_mode = function () {
|
||||
rfb._sock._mode = VNC_frame_encoding;
|
||||
rfb._sock.send = send_array;
|
||||
rfb._sock.close = function () {};
|
||||
rfb._sock.flush = function () {};
|
||||
rfb._checkEvents = function () {};
|
||||
rfb.connect = function (host, port, password, path) {
|
||||
this._rfb_host = host;
|
||||
this._rfb_port = port;
|
||||
this._rfb_password = (password !== undefined) ? password : "";
|
||||
this._rfb_path = (path !== undefined) ? path : "";
|
||||
this._sock.init('binary', 'ws');
|
||||
this._updateState('ProtocolVersion', "Starting VNC handshake");
|
||||
};
|
||||
};
|
||||
|
||||
next_iteration = function () {
|
||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
||||
'onUpdateState': updateState});
|
||||
enable_test_mode();
|
||||
|
||||
if (iteration === 0) {
|
||||
frame_length = VNC_frame_data.length;
|
||||
test_state = 'running';
|
||||
} else {
|
||||
rfb.disconnect();
|
||||
}
|
||||
|
||||
|
||||
if (test_state !== 'running') { return; }
|
||||
|
||||
iteration += 1;
|
||||
@@ -79,10 +97,22 @@ queue_next_packet = function () {
|
||||
}
|
||||
};
|
||||
|
||||
var bytes_processed = 0;
|
||||
|
||||
do_packet = function () {
|
||||
//Util.Debug("Processing frame: " + frame_idx);
|
||||
var frame = VNC_frame_data[frame_idx];
|
||||
rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)});
|
||||
var frame = VNC_frame_data[frame_idx],
|
||||
start = frame.indexOf('{', 1) + 1;
|
||||
bytes_processed += frame.length - start;
|
||||
if (VNC_frame_encoding === 'binary') {
|
||||
var u8 = new Uint8Array(frame.length - start);
|
||||
for (var i = 0; i < frame.length - start; i++) {
|
||||
u8[i] = frame.charCodeAt(start + i);
|
||||
}
|
||||
rfb._sock._recv_message({'data' : u8});
|
||||
} else {
|
||||
rfb._sock._recv_message({'data' : frame.slice(start)});
|
||||
}
|
||||
frame_idx += 1;
|
||||
|
||||
queue_next_packet();
|
||||
|
||||
3547
include/rfb.js
1664
include/ui.js
625
include/util.js
@@ -1,14 +1,13 @@
|
||||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2011 Joel Martin
|
||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint bitwise: false, white: false */
|
||||
/*global window, console, document, navigator, ActiveXObject */
|
||||
/* jshint white: false, nonstandard: true */
|
||||
/*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
|
||||
|
||||
// Globals defined here
|
||||
var Util = {};
|
||||
@@ -18,22 +17,163 @@ var Util = {};
|
||||
* Make arrays quack
|
||||
*/
|
||||
|
||||
Array.prototype.push8 = function (num) {
|
||||
this.push(num & 0xFF);
|
||||
var addFunc = function (cl, name, func) {
|
||||
if (!cl.prototype[name]) {
|
||||
Object.defineProperty(cl.prototype, name, { enumerable: false, value: func });
|
||||
}
|
||||
};
|
||||
|
||||
Array.prototype.push16 = function (num) {
|
||||
addFunc(Array, 'push8', function (num) {
|
||||
"use strict";
|
||||
this.push(num & 0xFF);
|
||||
});
|
||||
|
||||
addFunc(Array, 'push16', function (num) {
|
||||
"use strict";
|
||||
this.push((num >> 8) & 0xFF,
|
||||
(num ) & 0xFF );
|
||||
};
|
||||
Array.prototype.push32 = function (num) {
|
||||
num & 0xFF);
|
||||
});
|
||||
|
||||
addFunc(Array, 'push32', function (num) {
|
||||
"use strict";
|
||||
this.push((num >> 24) & 0xFF,
|
||||
(num >> 16) & 0xFF,
|
||||
(num >> 8) & 0xFF,
|
||||
(num ) & 0xFF );
|
||||
};
|
||||
num & 0xFF);
|
||||
});
|
||||
|
||||
/*
|
||||
// IE does not support map (even in IE9)
|
||||
//This prototype is provided by the Mozilla foundation and
|
||||
//is distributed under the MIT license.
|
||||
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
|
||||
addFunc(Array, 'map', function (fun /*, thisp*/) {
|
||||
"use strict";
|
||||
var len = this.length;
|
||||
if (typeof fun != "function") {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
var res = new Array(len);
|
||||
var thisp = arguments[1];
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (i in this) {
|
||||
res[i] = fun.call(thisp, this[i], i, this);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
// IE <9 does not support indexOf
|
||||
//This prototype is provided by the Mozilla foundation and
|
||||
//is distributed under the MIT license.
|
||||
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
|
||||
addFunc(Array, 'indexOf', function (elt /*, from*/) {
|
||||
"use strict";
|
||||
var len = this.length >>> 0;
|
||||
|
||||
var from = Number(arguments[1]) || 0;
|
||||
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
|
||||
if (from < 0) {
|
||||
from += len;
|
||||
}
|
||||
|
||||
for (; from < len; from++) {
|
||||
if (from in this &&
|
||||
this[from] === elt) {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
});
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
|
||||
if (!Object.keys) {
|
||||
Object.keys = (function () {
|
||||
'use strict';
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty,
|
||||
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
|
||||
dontEnums = [
|
||||
'toString',
|
||||
'toLocaleString',
|
||||
'valueOf',
|
||||
'hasOwnProperty',
|
||||
'isPrototypeOf',
|
||||
'propertyIsEnumerable',
|
||||
'constructor'
|
||||
],
|
||||
dontEnumsLength = dontEnums.length;
|
||||
|
||||
return function (obj) {
|
||||
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
|
||||
throw new TypeError('Object.keys called on non-object');
|
||||
}
|
||||
|
||||
var result = [], prop, i;
|
||||
|
||||
for (prop in obj) {
|
||||
if (hasOwnProperty.call(obj, prop)) {
|
||||
result.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDontEnumBug) {
|
||||
for (i = 0; i < dontEnumsLength; i++) {
|
||||
if (hasOwnProperty.call(obj, dontEnums[i])) {
|
||||
result.push(dontEnums[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
// PhantomJS 1.x doesn't support bind,
|
||||
// so leave this in until PhantomJS 2.0 is released
|
||||
//This prototype is provided by the Mozilla foundation and
|
||||
//is distributed under the MIT license.
|
||||
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license
|
||||
addFunc(Function, 'bind', function (oThis) {
|
||||
if (typeof this !== "function") {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError("Function.prototype.bind - " +
|
||||
"what is trying to be bound is not callable");
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function () {},
|
||||
fBound = function () {
|
||||
return fToBind.apply(this instanceof fNOP && oThis ? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
});
|
||||
|
||||
//
|
||||
// requestAnimationFrame shim with setTimeout fallback
|
||||
//
|
||||
|
||||
window.requestAnimFrame = (function () {
|
||||
"use strict";
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function (callback) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
})();
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in Util
|
||||
* ------------------------------------------------------
|
||||
@@ -45,6 +185,7 @@ Array.prototype.push32 = function (num) {
|
||||
|
||||
Util._log_level = 'warn';
|
||||
Util.init_logging = function (level) {
|
||||
"use strict";
|
||||
if (typeof level === 'undefined') {
|
||||
level = Util._log_level;
|
||||
} else {
|
||||
@@ -55,26 +196,34 @@ Util.init_logging = function (level) {
|
||||
window.console = {
|
||||
'log' : window.opera.postError,
|
||||
'warn' : window.opera.postError,
|
||||
'error': window.opera.postError };
|
||||
'error': window.opera.postError
|
||||
};
|
||||
} else {
|
||||
window.console = {
|
||||
'log' : function(m) {},
|
||||
'warn' : function(m) {},
|
||||
'error': function(m) {}};
|
||||
'log' : function (m) {},
|
||||
'warn' : function (m) {},
|
||||
'error': function (m) {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
|
||||
/* jshint -W086 */
|
||||
switch (level) {
|
||||
case 'debug': Util.Debug = function (msg) { console.log(msg); };
|
||||
case 'info': Util.Info = function (msg) { console.log(msg); };
|
||||
case 'warn': Util.Warn = function (msg) { console.warn(msg); };
|
||||
case 'error': Util.Error = function (msg) { console.error(msg); };
|
||||
case 'debug':
|
||||
Util.Debug = function (msg) { console.log(msg); };
|
||||
case 'info':
|
||||
Util.Info = function (msg) { console.log(msg); };
|
||||
case 'warn':
|
||||
Util.Warn = function (msg) { console.warn(msg); };
|
||||
case 'error':
|
||||
Util.Error = function (msg) { console.error(msg); };
|
||||
case 'none':
|
||||
break;
|
||||
default:
|
||||
throw("invalid logging type '" + level + "'");
|
||||
throw new Error("invalid logging type '" + level + "'");
|
||||
}
|
||||
/* jshint +W086 */
|
||||
};
|
||||
Util.get_logging = function () {
|
||||
return Util._log_level;
|
||||
@@ -82,108 +231,226 @@ Util.get_logging = function () {
|
||||
// Initialize logging level
|
||||
Util.init_logging();
|
||||
|
||||
Util.make_property = function (proto, name, mode, type) {
|
||||
"use strict";
|
||||
|
||||
// Set configuration default for Crockford style function namespaces
|
||||
Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
|
||||
var getter, setter;
|
||||
|
||||
// Default getter function
|
||||
getter = function (idx) {
|
||||
if ((type in {'arr':1, 'array':1}) &&
|
||||
(typeof idx !== 'undefined')) {
|
||||
return cfg[v][idx];
|
||||
} else {
|
||||
return cfg[v];
|
||||
}
|
||||
};
|
||||
|
||||
// Default setter function
|
||||
setter = function (val, idx) {
|
||||
if (type in {'boolean':1, 'bool':1}) {
|
||||
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
|
||||
val = false;
|
||||
var getter;
|
||||
if (type === 'arr') {
|
||||
getter = function (idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
return this['_' + name][idx];
|
||||
} else {
|
||||
val = true;
|
||||
return this['_' + name];
|
||||
}
|
||||
} else if (type in {'integer':1, 'int':1}) {
|
||||
val = parseInt(val, 10);
|
||||
} else if (type === 'func') {
|
||||
if (!val) {
|
||||
val = function () {};
|
||||
}
|
||||
}
|
||||
if (typeof idx !== 'undefined') {
|
||||
cfg[v][idx] = val;
|
||||
} else {
|
||||
cfg[v] = val;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the description
|
||||
api[v + '_description'] = desc;
|
||||
|
||||
// Set the getter function
|
||||
if (typeof api['get_' + v] === 'undefined') {
|
||||
api['get_' + v] = getter;
|
||||
}
|
||||
|
||||
// Set the setter function with extra sanity checks
|
||||
if (typeof api['set_' + v] === 'undefined') {
|
||||
api['set_' + v] = function (val, idx) {
|
||||
if (mode in {'RO':1, 'ro':1}) {
|
||||
throw(v + " is read-only");
|
||||
} else if ((mode in {'WO':1, 'wo':1}) &&
|
||||
(typeof cfg[v] !== 'undefined')) {
|
||||
throw(v + " can only be set once");
|
||||
}
|
||||
setter(val, idx);
|
||||
};
|
||||
} else {
|
||||
getter = function () {
|
||||
return this['_' + name];
|
||||
};
|
||||
}
|
||||
|
||||
// Set the default value
|
||||
if (typeof defaults[v] !== 'undefined') {
|
||||
defval = defaults[v];
|
||||
} else if ((type in {'arr':1, 'array':1}) &&
|
||||
(! (defval instanceof Array))) {
|
||||
defval = [];
|
||||
var make_setter = function (process_val) {
|
||||
if (process_val) {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = process_val(val);
|
||||
} else {
|
||||
this['_' + name] = process_val(val);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = val;
|
||||
} else {
|
||||
this['_' + name] = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var setter;
|
||||
if (type === 'bool') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else if (type === 'int') {
|
||||
setter = make_setter(function (val) { return parseInt(val, 10); });
|
||||
} else if (type === 'float') {
|
||||
setter = make_setter(parseFloat);
|
||||
} else if (type === 'str') {
|
||||
setter = make_setter(String);
|
||||
} else if (type === 'func') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val) {
|
||||
return function () {};
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
} else if (type === 'arr' || type === 'dom' || type == 'raw') {
|
||||
setter = make_setter();
|
||||
} else {
|
||||
throw new Error('Unknown property type ' + type); // some sanity checking
|
||||
}
|
||||
// Coerce existing setting to the right type
|
||||
//Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]);
|
||||
setter(defval);
|
||||
|
||||
// set the getter
|
||||
if (typeof proto['get_' + name] === 'undefined') {
|
||||
proto['get_' + name] = getter;
|
||||
}
|
||||
|
||||
// set the setter if needed
|
||||
if (typeof proto['set_' + name] === 'undefined') {
|
||||
if (mode === 'rw') {
|
||||
proto['set_' + name] = setter;
|
||||
} else if (mode === 'wo') {
|
||||
proto['set_' + name] = function (val, idx) {
|
||||
if (typeof this['_' + name] !== 'undefined') {
|
||||
throw new Error(name + " can only be set once");
|
||||
}
|
||||
setter.call(this, val, idx);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// make a special setter that we can use in set defaults
|
||||
proto['_raw_set_' + name] = function (val, idx) {
|
||||
setter.call(this, val, idx);
|
||||
//delete this['_init_set_' + name]; // remove it after use
|
||||
};
|
||||
};
|
||||
|
||||
// Set group of configuration defaults
|
||||
Util.conf_defaults = function(cfg, api, defaults, arr) {
|
||||
var i;
|
||||
for (i = 0; i < arr.length; i++) {
|
||||
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
|
||||
arr[i][2], arr[i][3], arr[i][4]);
|
||||
Util.make_properties = function (constructor, arr) {
|
||||
"use strict";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Util.set_defaults = function (obj, conf, defaults) {
|
||||
var defaults_keys = Object.keys(defaults);
|
||||
var conf_keys = Object.keys(conf);
|
||||
var keys_obj = {};
|
||||
var i;
|
||||
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
|
||||
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
|
||||
var keys = Object.keys(keys_obj);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
var setter = obj['_raw_set_' + keys[i]];
|
||||
if (!setter) {
|
||||
Util.Warn('Invalid property ' + keys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keys[i] in conf) {
|
||||
setter.call(obj, conf[keys[i]]);
|
||||
} else {
|
||||
setter.call(obj, defaults[keys[i]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode from UTF-8
|
||||
*/
|
||||
Util.decodeUTF8 = function (utf8string) {
|
||||
"use strict";
|
||||
return decodeURIComponent(escape(utf8string));
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Cross-browser routines
|
||||
*/
|
||||
|
||||
// Get DOM element position on page
|
||||
Util.getPosition = function (obj) {
|
||||
var x = 0, y = 0;
|
||||
if (obj.offsetParent) {
|
||||
do {
|
||||
x += obj.offsetLeft;
|
||||
y += obj.offsetTop;
|
||||
obj = obj.offsetParent;
|
||||
} while (obj);
|
||||
}
|
||||
return {'x': x, 'y': y};
|
||||
|
||||
// Dynamically load scripts without using document.write()
|
||||
// Reference: http://unixpapa.com/js/dyna.html
|
||||
//
|
||||
// Handles the case where load_scripts is invoked from a script that
|
||||
// itself is loaded via load_scripts. Once all scripts are loaded the
|
||||
// window.onscriptsloaded handler is called (if set).
|
||||
Util.get_include_uri = function () {
|
||||
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
|
||||
};
|
||||
Util._loading_scripts = [];
|
||||
Util._pending_scripts = [];
|
||||
Util.load_scripts = function (files) {
|
||||
"use strict";
|
||||
var head = document.getElementsByTagName('head')[0], script,
|
||||
ls = Util._loading_scripts, ps = Util._pending_scripts;
|
||||
|
||||
var loadFunc = function (e) {
|
||||
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
|
||||
ls[0].readyState === 'complete')) {
|
||||
// For IE, append the script to trigger execution
|
||||
var s = ls.shift();
|
||||
//console.log("loaded script: " + s.src);
|
||||
head.appendChild(s);
|
||||
}
|
||||
if (!this.readyState ||
|
||||
(Util.Engine.presto && this.readyState === 'loaded') ||
|
||||
this.readyState === 'complete') {
|
||||
if (ps.indexOf(this) >= 0) {
|
||||
this.onload = this.onreadystatechange = null;
|
||||
//console.log("completed script: " + this.src);
|
||||
ps.splice(ps.indexOf(this), 1);
|
||||
|
||||
// Call window.onscriptsload after last script loads
|
||||
if (ps.length === 0 && window.onscriptsload) {
|
||||
window.onscriptsload();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = Util.get_include_uri() + files[f];
|
||||
//console.log("loading script: " + script.src);
|
||||
script.onload = script.onreadystatechange = loadFunc;
|
||||
// In-order script execution tricks
|
||||
if (Util.Engine.trident) {
|
||||
// For IE wait until readyState is 'loaded' before
|
||||
// appending it which will trigger execution
|
||||
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
|
||||
ls.push(script);
|
||||
} else {
|
||||
// For webkit and firefox set async=false and append now
|
||||
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
|
||||
script.async = false;
|
||||
head.appendChild(script);
|
||||
}
|
||||
ps.push(script);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Util.getPosition = function(obj) {
|
||||
"use strict";
|
||||
// NB(sross): the Mozilla developer reference seems to indicate that
|
||||
// getBoundingClientRect includes border and padding, so the canvas
|
||||
// style should NOT include either.
|
||||
var objPosition = obj.getBoundingClientRect();
|
||||
return {'x': objPosition.left + window.pageXOffset, 'y': objPosition.top + window.pageYOffset,
|
||||
'width': objPosition.width, 'height': objPosition.height};
|
||||
};
|
||||
|
||||
|
||||
// Get mouse event position in DOM element
|
||||
Util.getEventPosition = function (e, obj, scale) {
|
||||
"use strict";
|
||||
var evt, docX, docY, pos;
|
||||
//if (!e) evt = window.event;
|
||||
evt = (e ? e : window.event);
|
||||
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
|
||||
if (evt.pageX || evt.pageY) {
|
||||
docX = evt.pageX;
|
||||
docY = evt.pageY;
|
||||
@@ -197,36 +464,43 @@ Util.getEventPosition = function (e, obj, scale) {
|
||||
if (typeof scale === "undefined") {
|
||||
scale = 1;
|
||||
}
|
||||
return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale};
|
||||
var realx = docX - pos.x;
|
||||
var realy = docY - pos.y;
|
||||
var x = Math.max(Math.min(realx, pos.width - 1), 0);
|
||||
var y = Math.max(Math.min(realy, pos.height - 1), 0);
|
||||
return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale};
|
||||
};
|
||||
|
||||
|
||||
// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
|
||||
Util.addEvent = function (obj, evType, fn){
|
||||
if (obj.attachEvent){
|
||||
var r = obj.attachEvent("on"+evType, fn);
|
||||
Util.addEvent = function (obj, evType, fn) {
|
||||
"use strict";
|
||||
if (obj.attachEvent) {
|
||||
var r = obj.attachEvent("on" + evType, fn);
|
||||
return r;
|
||||
} else if (obj.addEventListener){
|
||||
obj.addEventListener(evType, fn, false);
|
||||
} else if (obj.addEventListener) {
|
||||
obj.addEventListener(evType, fn, false);
|
||||
return true;
|
||||
} else {
|
||||
throw("Handler could not be attached");
|
||||
throw new Error("Handler could not be attached");
|
||||
}
|
||||
};
|
||||
|
||||
Util.removeEvent = function(obj, evType, fn){
|
||||
if (obj.detachEvent){
|
||||
var r = obj.detachEvent("on"+evType, fn);
|
||||
Util.removeEvent = function (obj, evType, fn) {
|
||||
"use strict";
|
||||
if (obj.detachEvent) {
|
||||
var r = obj.detachEvent("on" + evType, fn);
|
||||
return r;
|
||||
} else if (obj.removeEventListener){
|
||||
} else if (obj.removeEventListener) {
|
||||
obj.removeEventListener(evType, fn, false);
|
||||
return true;
|
||||
} else {
|
||||
throw("Handler could not be removed");
|
||||
throw new Error("Handler could not be removed");
|
||||
}
|
||||
};
|
||||
|
||||
Util.stopEvent = function(e) {
|
||||
Util.stopEvent = function (e) {
|
||||
"use strict";
|
||||
if (e.stopPropagation) { e.stopPropagation(); }
|
||||
else { e.cancelBubble = true; }
|
||||
|
||||
@@ -234,42 +508,115 @@ Util.stopEvent = function(e) {
|
||||
else { e.returnValue = false; }
|
||||
};
|
||||
|
||||
Util._cursor_uris_supported = null;
|
||||
|
||||
Util.browserSupportsCursorURIs = function () {
|
||||
if (Util._cursor_uris_supported === null) {
|
||||
try {
|
||||
var target = document.createElement('canvas');
|
||||
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
||||
|
||||
if (target.style.cursor) {
|
||||
Util.Info("Data URI scheme cursor supported");
|
||||
Util._cursor_uris_supported = true;
|
||||
} else {
|
||||
Util.Warn("Data URI scheme cursor not supported");
|
||||
Util._cursor_uris_supported = false;
|
||||
}
|
||||
} catch (exc) {
|
||||
Util.Error("Data URI scheme cursor test exception: " + exc);
|
||||
Util._cursor_uris_supported = false;
|
||||
}
|
||||
}
|
||||
|
||||
return Util._cursor_uris_supported;
|
||||
};
|
||||
|
||||
// Set browser engine versions. Based on mootools.
|
||||
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
||||
|
||||
Util.Engine = {
|
||||
'presto': (function() {
|
||||
return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'trident': (function() {
|
||||
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
|
||||
'webkit': (function() {
|
||||
try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
||||
//'webkit': (function() {
|
||||
// return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
|
||||
'gecko': (function() {
|
||||
return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
|
||||
};
|
||||
if (Util.Engine.webkit) {
|
||||
// Extract actual webkit version if available
|
||||
Util.Engine.webkit = (function(v) {
|
||||
var re = new RegExp('WebKit/([0-9\.]*) ');
|
||||
v = (navigator.userAgent.match(re) || ['', v])[1];
|
||||
return parseFloat(v, 10);
|
||||
})(Util.Engine.webkit);
|
||||
}
|
||||
(function () {
|
||||
"use strict";
|
||||
// 'presto': (function () { return (!window.opera) ? false : true; }()),
|
||||
var detectPresto = function () {
|
||||
return !!window.opera;
|
||||
};
|
||||
|
||||
Util.Flash = (function(){
|
||||
// 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
|
||||
var detectTrident = function () {
|
||||
if (!window.ActiveXObject) {
|
||||
return false;
|
||||
} else {
|
||||
if (window.XMLHttpRequest) {
|
||||
return (document.querySelectorAll) ? 6 : 5;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
||||
var detectInitialWebkit = function () {
|
||||
try {
|
||||
if (navigator.taintEnabled) {
|
||||
return false;
|
||||
} else {
|
||||
if (Util.Features.xpath) {
|
||||
return (Util.Features.query) ? 525 : 420;
|
||||
} else {
|
||||
return 419;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var detectActualWebkit = function (initial_ver) {
|
||||
var re = /WebKit\/([0-9\.]*) /;
|
||||
var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1];
|
||||
return parseFloat(str_ver, 10);
|
||||
};
|
||||
|
||||
// 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
|
||||
var detectGecko = function () {
|
||||
/* jshint -W041 */
|
||||
if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
|
||||
return false;
|
||||
} else {
|
||||
return (document.getElementsByClassName) ? 19 : 18;
|
||||
}
|
||||
/* jshint +W041 */
|
||||
};
|
||||
|
||||
Util.Engine = {
|
||||
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||
//'presto': (function() {
|
||||
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'presto': detectPresto(),
|
||||
'trident': detectTrident(),
|
||||
'webkit': detectInitialWebkit(),
|
||||
'gecko': detectGecko(),
|
||||
};
|
||||
|
||||
if (Util.Engine.webkit) {
|
||||
// Extract actual webkit version if available
|
||||
Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit);
|
||||
}
|
||||
})();
|
||||
|
||||
Util.Flash = (function () {
|
||||
"use strict";
|
||||
var v, version;
|
||||
try {
|
||||
v = navigator.plugins['Shockwave Flash'].description;
|
||||
} catch(err1) {
|
||||
} catch (err1) {
|
||||
try {
|
||||
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
||||
} catch(err2) {
|
||||
} catch (err2) {
|
||||
v = '0 r0';
|
||||
}
|
||||
}
|
||||
version = v.match(/\d+/g);
|
||||
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
|
||||
}());
|
||||
}());
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2011 Joel Martin
|
||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*jslint evil: true */
|
||||
/*global window, document, INCLUDE_URI */
|
||||
|
||||
/*
|
||||
* Load supporting scripts
|
||||
*/
|
||||
function get_INCLUDE_URI() {
|
||||
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
|
||||
}
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var extra = "", start, end;
|
||||
|
||||
start = "<script src='" + get_INCLUDE_URI();
|
||||
end = "'><\/script>";
|
||||
|
||||
// Uncomment to activate firebug lite
|
||||
//extra += "<script src='http://getfirebug.com/releases/lite/1.2/" +
|
||||
// "firebug-lite-compressed.js'><\/script>";
|
||||
|
||||
extra += start + "util.js" + end;
|
||||
extra += start + "webutil.js" + end;
|
||||
extra += start + "logo.js" + end;
|
||||
extra += start + "base64.js" + end;
|
||||
extra += start + "websock.js" + end;
|
||||
extra += start + "des.js" + end;
|
||||
extra += start + "input.js" + end;
|
||||
extra += start + "display.js" + end;
|
||||
extra += start + "rfb.js" + end;
|
||||
|
||||
document.write(extra);
|
||||
}());
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
* How to try
|
||||
|
||||
Assuming you have Web server (e.g. Apache) running at http://example.com/ .
|
||||
|
||||
- Download web_socket.rb from:
|
||||
http://github.com/gimite/web-socket-ruby/tree/master
|
||||
- Run sample Web Socket server (echo server) in example.com with: (#1)
|
||||
$ ruby web-socket-ruby/samples/echo_server.rb example.com 10081
|
||||
- If your server already provides socket policy file at port 843, modify the file to allow access to port 10081. Otherwise you can skip this step. See below for details.
|
||||
- Publish the web-socket-js directory with your Web server (e.g. put it in ~/public_html).
|
||||
- Change ws://localhost:10081 to ws://example.com:10081 in sample.html.
|
||||
- Open sample.html in your browser.
|
||||
- After "onopen" is shown, input something, click [Send] and confirm echo back.
|
||||
|
||||
#1: First argument of echo_server.rb means that it accepts Web Socket connection from HTML pages in example.com.
|
||||
|
||||
|
||||
* Troubleshooting
|
||||
|
||||
If it doesn't work, try these:
|
||||
|
||||
1. Try Chrome and Firefox 3.x.
|
||||
- It doesn't work on Chrome:
|
||||
-- It's likely an issue of your code or the server. Debug your code as usual e.g. using console.log.
|
||||
- It works on Chrome but it doesn't work on Firefox:
|
||||
-- It's likely an issue of web-socket-js specific configuration (e.g. 3 and 4 below).
|
||||
- It works on both Chrome and Firefox, but it doesn't work on your browser:
|
||||
-- Check "Supported environment" section below. Your browser may not be supported by web-socket-js.
|
||||
|
||||
2. Add this line before your code:
|
||||
WEB_SOCKET_DEBUG = true;
|
||||
and use Developer Tools (Chrome/Safari) or Firebug (Firefox) to see if console.log outputs any errors.
|
||||
|
||||
3. Make sure you do NOT open your HTML page as local file e.g. file:///.../sample.html. web-socket-js doesn't work on local file. Open it via Web server e.g. http:///.../sample.html.
|
||||
|
||||
4. If you are NOT using web-socket-ruby as your WebSocket server, you need to place Flash socket policy file on your server. See "Flash socket policy file" section below for details.
|
||||
|
||||
5. Check if sample.html bundled with web-socket-js works.
|
||||
|
||||
6. Make sure the port used for WebSocket (10081 in example above) is not blocked by your server/client's firewall.
|
||||
|
||||
7. Install debugger version of Flash Player available here to see Flash errors:
|
||||
http://www.adobe.com/support/flashplayer/downloads.html
|
||||
|
||||
|
||||
* Supported environments
|
||||
|
||||
It should work on:
|
||||
- Google Chrome 4 or later (just uses native implementation)
|
||||
- Firefox 3.x, Internet Explorer 8 + Flash Player 9 or later
|
||||
|
||||
It may or may not work on other browsers such as Safari, Opera or IE 6. Patch for these browsers are appreciated, but I will not work on fixing issues specific to these browsers by myself.
|
||||
|
||||
|
||||
* Flash socket policy file
|
||||
|
||||
This implementation uses Flash's socket, which means that your server must provide Flash socket policy file to declare the server accepts connections from Flash.
|
||||
|
||||
If you use web-socket-ruby available at
|
||||
http://github.com/gimite/web-socket-ruby/tree/master
|
||||
, you don't need anything special, because web-socket-ruby handles Flash socket policy file request. But if you already provide socket policy file at port 843, you need to modify the file to allow access to Web Socket port, because it precedes what web-socket-ruby provides.
|
||||
|
||||
If you use other Web Socket server implementation, you need to provide socket policy file yourself. See
|
||||
http://www.lightsphere.com/dev/articles/flash_socket_policy.html
|
||||
for details and sample script to run socket policy file server. node.js implementation is available here:
|
||||
http://github.com/LearnBoost/Socket.IO-node/blob/master/lib/socket.io/transports/flashsocket.js
|
||||
|
||||
Actually, it's still better to provide socket policy file at port 843 even if you use web-socket-ruby. Flash always try to connect to port 843 first, so providing the file at port 843 makes startup faster.
|
||||
|
||||
|
||||
* Cookie considerations
|
||||
|
||||
Cookie is sent if Web Socket host is the same as the origin of JavaScript. Otherwise it is not sent, because I don't know way to send right Cookie (which is Cookie of the host of Web Socket, I heard).
|
||||
|
||||
Note that it's technically possible that client sends arbitrary string as Cookie and any other headers (by modifying this library for example) once you place Flash socket policy file in your server. So don't trust Cookie and other headers if you allow connection from untrusted origin.
|
||||
|
||||
|
||||
* Proxy considerations
|
||||
|
||||
The WebSocket spec (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol) specifies instructions for User Agents to support proxied connections by implementing the HTTP CONNECT method.
|
||||
|
||||
The AS3 Socket class doesn't implement this mechanism, which renders it useless for the scenarios where the user trying to open a socket is behind a proxy.
|
||||
|
||||
The class RFC2817Socket (by Christian Cantrell) effectively lets us implement this, as long as the proxy settings are known and provided by the interface that instantiates the WebSocket. As such, if you want to support proxied conncetions, you'll have to supply this information to the WebSocket constructor when Flash is being used. One way to go about it would be to ask the user for proxy settings information if the initial connection fails.
|
||||
|
||||
|
||||
* How to host HTML file and SWF file in different domains
|
||||
|
||||
By default, HTML file and SWF file must be in the same domain. You can follow steps below to allow hosting them in different domain.
|
||||
|
||||
WARNING: If you use the method below, HTML files in ANY domains can send arbitrary TCP data to your WebSocket server, regardless of configuration in Flash socket policy file. Arbitrary TCP data means that they can even fake request headers including Origin and Cookie.
|
||||
|
||||
- Unzip WebSocketMainInsecure.zip to extract WebSocketMainInsecure.swf.
|
||||
- Put WebSocketMainInsecure.swf on your server, instead of WebSocketMain.swf.
|
||||
- In JavaScript, set WEB_SOCKET_SWF_LOCATION to URL of your WebSocketMainInsecure.swf.
|
||||
|
||||
|
||||
* How to build WebSocketMain.swf
|
||||
|
||||
Install Flex 4 SDK:
|
||||
http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4
|
||||
|
||||
$ cd flash-src
|
||||
$ ./build.sh
|
||||
|
||||
|
||||
* License
|
||||
|
||||
New BSD License.
|
||||
@@ -1,341 +0,0 @@
|
||||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||
// License: New BSD License
|
||||
// Reference: http://dev.w3.org/html5/websockets/
|
||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
|
||||
|
||||
(function() {
|
||||
|
||||
if (window.WebSocket) return;
|
||||
|
||||
var console = window.console;
|
||||
if (!console || !console.log || !console.error) {
|
||||
console = {log: function(){ }, error: function(){ }};
|
||||
}
|
||||
|
||||
if (!swfobject.hasFlashPlayerVersion("10.0.0")) {
|
||||
console.error("Flash Player >= 10.0.0 is required.");
|
||||
return;
|
||||
}
|
||||
if (location.protocol == "file:") {
|
||||
console.error(
|
||||
"WARNING: web-socket-js doesn't work in file:///... URL " +
|
||||
"unless you set Flash Security Settings properly. " +
|
||||
"Open the page via Web server i.e. http://...");
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents a faux web socket.
|
||||
* @param {string} url
|
||||
* @param {string} protocol
|
||||
* @param {string} proxyHost
|
||||
* @param {int} proxyPort
|
||||
* @param {string} headers
|
||||
*/
|
||||
WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
|
||||
var self = this;
|
||||
self.__id = WebSocket.__nextId++;
|
||||
WebSocket.__instances[self.__id] = self;
|
||||
self.readyState = WebSocket.CONNECTING;
|
||||
self.bufferedAmount = 0;
|
||||
self.__events = {};
|
||||
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
|
||||
// Otherwise, when onopen fires immediately, onopen is called before it is set.
|
||||
setTimeout(function() {
|
||||
WebSocket.__addTask(function() {
|
||||
WebSocket.__flash.create(
|
||||
self.__id, url, protocol, proxyHost || null, proxyPort || 0, headers || null);
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send data to the web socket.
|
||||
* @param {string} data The data to send to the socket.
|
||||
* @return {boolean} True for success, false for failure.
|
||||
*/
|
||||
WebSocket.prototype.send = function(data) {
|
||||
if (this.readyState == WebSocket.CONNECTING) {
|
||||
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
|
||||
}
|
||||
// We use encodeURIComponent() here, because FABridge doesn't work if
|
||||
// the argument includes some characters. We don't use escape() here
|
||||
// because of this:
|
||||
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
|
||||
// But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
|
||||
// preserve all Unicode characters either e.g. "\uffff" in Firefox.
|
||||
// Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
|
||||
// additional testing.
|
||||
var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
|
||||
if (result < 0) { // success
|
||||
return true;
|
||||
} else {
|
||||
this.bufferedAmount += result;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Close this web socket gracefully.
|
||||
*/
|
||||
WebSocket.prototype.close = function() {
|
||||
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
|
||||
return;
|
||||
}
|
||||
this.readyState = WebSocket.CLOSING;
|
||||
WebSocket.__flash.close(this.__id);
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {function} listener
|
||||
* @param {boolean} useCapture
|
||||
* @return void
|
||||
*/
|
||||
WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
|
||||
if (!(type in this.__events)) {
|
||||
this.__events[type] = [];
|
||||
}
|
||||
this.__events[type].push(listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {function} listener
|
||||
* @param {boolean} useCapture
|
||||
* @return void
|
||||
*/
|
||||
WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
|
||||
if (!(type in this.__events)) return;
|
||||
var events = this.__events[type];
|
||||
for (var i = events.length - 1; i >= 0; --i) {
|
||||
if (events[i] === listener) {
|
||||
events.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
|
||||
*
|
||||
* @param {Event} event
|
||||
* @return void
|
||||
*/
|
||||
WebSocket.prototype.dispatchEvent = function(event) {
|
||||
var events = this.__events[event.type] || [];
|
||||
for (var i = 0; i < events.length; ++i) {
|
||||
events[i](event);
|
||||
}
|
||||
var handler = this["on" + event.type];
|
||||
if (handler) handler(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles an event from Flash.
|
||||
* @param {Object} flashEvent
|
||||
*/
|
||||
WebSocket.prototype.__handleEvent = function(flashEvent) {
|
||||
if ("readyState" in flashEvent) {
|
||||
this.readyState = flashEvent.readyState;
|
||||
}
|
||||
|
||||
var jsEvent;
|
||||
if (flashEvent.type == "open" || flashEvent.type == "error") {
|
||||
jsEvent = this.__createSimpleEvent(flashEvent.type);
|
||||
} else if (flashEvent.type == "close") {
|
||||
// TODO implement jsEvent.wasClean
|
||||
jsEvent = this.__createSimpleEvent("close");
|
||||
} else if (flashEvent.type == "message") {
|
||||
var data = decodeURIComponent(flashEvent.message);
|
||||
jsEvent = this.__createMessageEvent("message", data);
|
||||
} else {
|
||||
throw "unknown event type: " + flashEvent.type;
|
||||
}
|
||||
|
||||
this.dispatchEvent(jsEvent);
|
||||
};
|
||||
|
||||
WebSocket.prototype.__createSimpleEvent = function(type) {
|
||||
if (document.createEvent && window.Event) {
|
||||
var event = document.createEvent("Event");
|
||||
event.initEvent(type, false, false);
|
||||
return event;
|
||||
} else {
|
||||
return {type: type, bubbles: false, cancelable: false};
|
||||
}
|
||||
};
|
||||
|
||||
WebSocket.prototype.__createMessageEvent = function(type, data) {
|
||||
if (document.createEvent && window.MessageEvent && !window.opera) {
|
||||
var event = document.createEvent("MessageEvent");
|
||||
event.initMessageEvent("message", false, false, data, null, null, window, null);
|
||||
return event;
|
||||
} else {
|
||||
// IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
|
||||
return {type: type, data: data, bubbles: false, cancelable: false};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Define the WebSocket readyState enumeration.
|
||||
*/
|
||||
WebSocket.CONNECTING = 0;
|
||||
WebSocket.OPEN = 1;
|
||||
WebSocket.CLOSING = 2;
|
||||
WebSocket.CLOSED = 3;
|
||||
|
||||
WebSocket.__flash = null;
|
||||
WebSocket.__instances = {};
|
||||
WebSocket.__tasks = [];
|
||||
WebSocket.__nextId = 0;
|
||||
|
||||
/**
|
||||
* Load a new flash security policy file.
|
||||
* @param {string} url
|
||||
*/
|
||||
WebSocket.loadFlashPolicyFile = function(url){
|
||||
WebSocket.__addTask(function() {
|
||||
WebSocket.__flash.loadManualPolicyFile(url);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
|
||||
*/
|
||||
WebSocket.__initialize = function() {
|
||||
if (WebSocket.__flash) return;
|
||||
|
||||
if (WebSocket.__swfLocation) {
|
||||
// For backword compatibility.
|
||||
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
|
||||
}
|
||||
if (!window.WEB_SOCKET_SWF_LOCATION) {
|
||||
console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
|
||||
return;
|
||||
}
|
||||
var container = document.createElement("div");
|
||||
container.id = "webSocketContainer";
|
||||
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
|
||||
// Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
|
||||
// But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
|
||||
// Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
|
||||
// the best we can do as far as we know now.
|
||||
container.style.position = "absolute";
|
||||
if (WebSocket.__isFlashLite()) {
|
||||
container.style.left = "0px";
|
||||
container.style.top = "0px";
|
||||
} else {
|
||||
container.style.left = "-100px";
|
||||
container.style.top = "-100px";
|
||||
}
|
||||
var holder = document.createElement("div");
|
||||
holder.id = "webSocketFlash";
|
||||
container.appendChild(holder);
|
||||
document.body.appendChild(container);
|
||||
// See this article for hasPriority:
|
||||
// http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
|
||||
swfobject.embedSWF(
|
||||
WEB_SOCKET_SWF_LOCATION,
|
||||
"webSocketFlash",
|
||||
"1" /* width */,
|
||||
"1" /* height */,
|
||||
"10.0.0" /* SWF version */,
|
||||
null,
|
||||
null,
|
||||
{hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
|
||||
null,
|
||||
function(e) {
|
||||
if (!e.success) {
|
||||
console.error("[WebSocket] swfobject.embedSWF failed");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Flash to notify JS that it's fully loaded and ready
|
||||
* for communication.
|
||||
*/
|
||||
WebSocket.__onFlashInitialized = function() {
|
||||
// We need to set a timeout here to avoid round-trip calls
|
||||
// to flash during the initialization process.
|
||||
setTimeout(function() {
|
||||
WebSocket.__flash = document.getElementById("webSocketFlash");
|
||||
WebSocket.__flash.setCallerUrl(location.href);
|
||||
WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
|
||||
for (var i = 0; i < WebSocket.__tasks.length; ++i) {
|
||||
WebSocket.__tasks[i]();
|
||||
}
|
||||
WebSocket.__tasks = [];
|
||||
}, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by Flash to notify WebSockets events are fired.
|
||||
*/
|
||||
WebSocket.__onFlashEvent = function() {
|
||||
setTimeout(function() {
|
||||
try {
|
||||
// Gets events using receiveEvents() instead of getting it from event object
|
||||
// of Flash event. This is to make sure to keep message order.
|
||||
// It seems sometimes Flash events don't arrive in the same order as they are sent.
|
||||
var events = WebSocket.__flash.receiveEvents();
|
||||
for (var i = 0; i < events.length; ++i) {
|
||||
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}, 0);
|
||||
return true;
|
||||
};
|
||||
|
||||
// Called by Flash.
|
||||
WebSocket.__log = function(message) {
|
||||
console.log(decodeURIComponent(message));
|
||||
};
|
||||
|
||||
// Called by Flash.
|
||||
WebSocket.__error = function(message) {
|
||||
console.error(decodeURIComponent(message));
|
||||
};
|
||||
|
||||
WebSocket.__addTask = function(task) {
|
||||
if (WebSocket.__flash) {
|
||||
task();
|
||||
} else {
|
||||
WebSocket.__tasks.push(task);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test if the browser is running flash lite.
|
||||
* @return {boolean} True if flash lite is running, false otherwise.
|
||||
*/
|
||||
WebSocket.__isFlashLite = function() {
|
||||
if (!window.navigator || !window.navigator.mimeTypes) {
|
||||
return false;
|
||||
}
|
||||
var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
|
||||
if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
|
||||
return false;
|
||||
}
|
||||
return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
|
||||
};
|
||||
|
||||
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener("load", function(){
|
||||
WebSocket.__initialize();
|
||||
}, false);
|
||||
} else {
|
||||
window.attachEvent("onload", function(){
|
||||
WebSocket.__initialize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Websock: high-performance binary WebSockets
|
||||
* Copyright (C) 2011 Joel Martin
|
||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* Websock is similar to the standard WebSocket object but Websock
|
||||
* enables communication with raw TCP sockets (i.e. the binary stream)
|
||||
@@ -14,331 +14,427 @@
|
||||
* read binary data off of the receive queue.
|
||||
*/
|
||||
|
||||
/*jslint browser: true, bitwise: true */
|
||||
/*global Util*/
|
||||
|
||||
|
||||
// Load Flash WebSocket emulator if needed
|
||||
|
||||
if (window.WebSocket) {
|
||||
// To force WebSocket emulator even when native WebSocket available
|
||||
//window.WEB_SOCKET_FORCE_FLASH = true;
|
||||
// To enable WebSocket emulator debug:
|
||||
//window.WEB_SOCKET_DEBUG=1;
|
||||
|
||||
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||
Websock_native = true;
|
||||
} else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
|
||||
Websock_native = true;
|
||||
window.WebSocket = window.MozWebSocket;
|
||||
} else {
|
||||
/* no builtin WebSocket so load web_socket.js */
|
||||
|
||||
Websock_native = false;
|
||||
(function () {
|
||||
function get_INCLUDE_URI() {
|
||||
return (typeof INCLUDE_URI !== "undefined") ?
|
||||
INCLUDE_URI : "include/";
|
||||
}
|
||||
|
||||
var start = "<script src='" + get_INCLUDE_URI(),
|
||||
end = "'><\/script>", extra = "";
|
||||
|
||||
WEB_SOCKET_SWF_LOCATION = get_INCLUDE_URI() +
|
||||
"web-socket-js/WebSocketMain.swf";
|
||||
if (Util.Engine.trident) {
|
||||
Util.Debug("Forcing uncached load of WebSocketMain.swf");
|
||||
WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
|
||||
}
|
||||
extra += start + "web-socket-js/swfobject.js" + end;
|
||||
extra += start + "web-socket-js/web_socket.js" + end;
|
||||
document.write(extra);
|
||||
}());
|
||||
}
|
||||
|
||||
|
||||
function Websock() {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
var api = {}, // Public API
|
||||
websocket = null, // WebSocket object
|
||||
rQ = [], // Receive queue
|
||||
rQi = 0, // Receive queue index
|
||||
rQmax = 10000, // Max receive queue size before compacting
|
||||
sQ = [], // Send queue
|
||||
this._websocket = null; // WebSocket object
|
||||
|
||||
eventHandlers = {
|
||||
'message' : function() {},
|
||||
'open' : function() {},
|
||||
'close' : function() {},
|
||||
'error' : function() {}
|
||||
},
|
||||
this._rQi = 0; // Receive queue index
|
||||
this._rQlen = 0; // Next write position in the receive queue
|
||||
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
|
||||
this._rQmax = this._rQbufferSize / 8;
|
||||
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
|
||||
this._rQ = null; // Receive queue
|
||||
|
||||
test_mode = false;
|
||||
this._sQbufferSize = 1024 * 10; // 10 KiB
|
||||
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
|
||||
this._sQlen = 0;
|
||||
this._sQ = null; // Send queue
|
||||
|
||||
this._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64'
|
||||
this.maxBufferedAmount = 200;
|
||||
|
||||
//
|
||||
// Queue public functions
|
||||
//
|
||||
|
||||
function get_sQ() {
|
||||
return sQ;
|
||||
this._eventHandlers = {
|
||||
'message': function () {},
|
||||
'open': function () {},
|
||||
'close': function () {},
|
||||
'error': function () {}
|
||||
};
|
||||
}
|
||||
|
||||
function get_rQ() {
|
||||
return rQ;
|
||||
}
|
||||
function get_rQi() {
|
||||
return rQi;
|
||||
}
|
||||
function set_rQi(val) {
|
||||
rQi = val;
|
||||
};
|
||||
(function () {
|
||||
"use strict";
|
||||
// this has performance issues in some versions Chromium, and
|
||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
||||
// at the moment. It may be valuable to turn it on in the future.
|
||||
var ENABLE_COPYWITHIN = false;
|
||||
|
||||
function rQlen() {
|
||||
return rQ.length - rQi;
|
||||
}
|
||||
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||
|
||||
function rQpeek8() {
|
||||
return (rQ[rQi] );
|
||||
}
|
||||
function rQshift8() {
|
||||
return (rQ[rQi++] );
|
||||
}
|
||||
function rQunshift8(num) {
|
||||
if (rQi === 0) {
|
||||
rQ.unshift(num);
|
||||
} else {
|
||||
rQi -= 1;
|
||||
rQ[rQi] = num;
|
||||
}
|
||||
var typedArrayToString = (function () {
|
||||
// This is only for PhantomJS, which doesn't like apply-ing
|
||||
// with Typed Arrays
|
||||
try {
|
||||
var arr = new Uint8Array([1, 2, 3]);
|
||||
String.fromCharCode.apply(null, arr);
|
||||
return function (a) { return String.fromCharCode.apply(null, a); };
|
||||
} catch (ex) {
|
||||
return function (a) {
|
||||
return String.fromCharCode.apply(
|
||||
null, Array.prototype.slice.call(a));
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
}
|
||||
function rQshift16() {
|
||||
return (rQ[rQi++] << 8) +
|
||||
(rQ[rQi++] );
|
||||
}
|
||||
function rQshift32() {
|
||||
return (rQ[rQi++] << 24) +
|
||||
(rQ[rQi++] << 16) +
|
||||
(rQ[rQi++] << 8) +
|
||||
(rQ[rQi++] );
|
||||
}
|
||||
function rQshiftStr(len) {
|
||||
var arr = rQ.slice(rQi, rQi + len);
|
||||
rQi += len;
|
||||
return arr.map(function (num) {
|
||||
return String.fromCharCode(num); } ).join('');
|
||||
Websock.prototype = {
|
||||
// Getters and Setters
|
||||
get_sQ: function () {
|
||||
return this._sQ;
|
||||
},
|
||||
|
||||
}
|
||||
function rQshiftBytes(len) {
|
||||
rQi += len;
|
||||
return rQ.slice(rQi-len, rQi);
|
||||
}
|
||||
get_rQ: function () {
|
||||
return this._rQ;
|
||||
},
|
||||
|
||||
function rQslice(start, end) {
|
||||
if (end) {
|
||||
return rQ.slice(rQi + start, rQi + end);
|
||||
} else {
|
||||
return rQ.slice(rQi + start);
|
||||
}
|
||||
}
|
||||
get_rQi: function () {
|
||||
return this._rQi;
|
||||
},
|
||||
|
||||
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
||||
// to be available in the receive queue. Return true if we need to
|
||||
// wait (and possibly print a debug message), otherwise false.
|
||||
function rQwait(msg, num, goback) {
|
||||
var rQlen = rQ.length - rQi; // Skip rQlen() function call
|
||||
if (rQlen < num) {
|
||||
if (goback) {
|
||||
if (rQi < goback) {
|
||||
throw("rQwait cannot backup " + goback + " bytes");
|
||||
set_rQi: function (val) {
|
||||
this._rQi = val;
|
||||
},
|
||||
|
||||
// Receive Queue
|
||||
rQlen: function () {
|
||||
return this._rQlen - this._rQi;
|
||||
},
|
||||
|
||||
rQpeek8: function () {
|
||||
return this._rQ[this._rQi];
|
||||
},
|
||||
|
||||
rQshift8: function () {
|
||||
return this._rQ[this._rQi++];
|
||||
},
|
||||
|
||||
rQskip8: function () {
|
||||
this._rQi++;
|
||||
},
|
||||
|
||||
rQskipBytes: function (num) {
|
||||
this._rQi += num;
|
||||
},
|
||||
|
||||
// TODO(directxman12): test performance with these vs a DataView
|
||||
rQshift16: function () {
|
||||
return (this._rQ[this._rQi++] << 8) +
|
||||
this._rQ[this._rQi++];
|
||||
},
|
||||
|
||||
rQshift32: function () {
|
||||
return (this._rQ[this._rQi++] << 24) +
|
||||
(this._rQ[this._rQi++] << 16) +
|
||||
(this._rQ[this._rQi++] << 8) +
|
||||
this._rQ[this._rQi++];
|
||||
},
|
||||
|
||||
rQshiftStr: function (len) {
|
||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
||||
var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
|
||||
this._rQi += len;
|
||||
return typedArrayToString(arr);
|
||||
},
|
||||
|
||||
rQshiftBytes: function (len) {
|
||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
||||
this._rQi += len;
|
||||
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
|
||||
},
|
||||
|
||||
rQshiftTo: function (target, len) {
|
||||
if (len === undefined) { len = this.rQlen(); }
|
||||
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
|
||||
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
|
||||
this._rQi += len;
|
||||
},
|
||||
|
||||
rQwhole: function () {
|
||||
return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
|
||||
},
|
||||
|
||||
rQslice: function (start, end) {
|
||||
if (end) {
|
||||
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
|
||||
} else {
|
||||
return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
|
||||
}
|
||||
rQi -= goback;
|
||||
}
|
||||
//Util.Debug(" waiting for " + (num-rQlen) +
|
||||
// " " + msg + " byte(s)");
|
||||
return true; // true means need more data
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// Private utility routines
|
||||
//
|
||||
|
||||
function encode_message() {
|
||||
/* base64 encode */
|
||||
return Base64.encode(sQ);
|
||||
}
|
||||
|
||||
function decode_message(data) {
|
||||
//Util.Debug(">> decode_message: " + data);
|
||||
/* base64 decode */
|
||||
rQ = rQ.concat(Base64.decode(data, 0));
|
||||
//Util.Debug(">> decode_message, rQ: " + rQ);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Public Send functions
|
||||
//
|
||||
|
||||
function flush() {
|
||||
if (websocket.bufferedAmount !== 0) {
|
||||
Util.Debug("bufferedAmount: " + websocket.bufferedAmount);
|
||||
}
|
||||
if (websocket.bufferedAmount < api.maxBufferedAmount) {
|
||||
//Util.Debug("arr: " + arr);
|
||||
//Util.Debug("sQ: " + sQ);
|
||||
if (sQ.length > 0) {
|
||||
websocket.send(encode_message(sQ));
|
||||
sQ = [];
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
Util.Info("Delaying send, bufferedAmount: " +
|
||||
websocket.bufferedAmount);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// overridable for testing
|
||||
function send(arr) {
|
||||
//Util.Debug(">> send_array: " + arr);
|
||||
sQ = sQ.concat(arr);
|
||||
return flush();
|
||||
}
|
||||
|
||||
function send_string(str) {
|
||||
//Util.Debug(">> send_string: " + str);
|
||||
api.send(str.split('').map(
|
||||
function (chr) { return chr.charCodeAt(0); } ) );
|
||||
}
|
||||
|
||||
//
|
||||
// Other public functions
|
||||
|
||||
function recv_message(e) {
|
||||
//Util.Debug(">> recv_message: " + e.data.length);
|
||||
|
||||
try {
|
||||
decode_message(e.data);
|
||||
if (rQlen() > 0) {
|
||||
eventHandlers.message();
|
||||
// Compact the receive queue
|
||||
if (rQ.length > rQmax) {
|
||||
//Util.Debug("Compacting receive queue");
|
||||
rQ = rQ.slice(rQi);
|
||||
rQi = 0;
|
||||
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
||||
// to be available in the receive queue. Return true if we need to
|
||||
// wait (and possibly print a debug message), otherwise false.
|
||||
rQwait: function (msg, num, goback) {
|
||||
var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
|
||||
if (rQlen < num) {
|
||||
if (goback) {
|
||||
if (this._rQi < goback) {
|
||||
throw new Error("rQwait cannot backup " + goback + " bytes");
|
||||
}
|
||||
this._rQi -= goback;
|
||||
}
|
||||
return true; // true means need more data
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Send Queue
|
||||
|
||||
flush: function () {
|
||||
if (this._websocket.bufferedAmount !== 0) {
|
||||
Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
||||
}
|
||||
|
||||
if (this._websocket.bufferedAmount < this.maxBufferedAmount) {
|
||||
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
||||
this._websocket.send(this._encode_message());
|
||||
this._sQlen = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Util.Info("Delaying send, bufferedAmount: " +
|
||||
this._websocket.bufferedAmount);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
send: function (arr) {
|
||||
this._sQ.set(arr, this._sQlen);
|
||||
this._sQlen += arr.length;
|
||||
return this.flush();
|
||||
},
|
||||
|
||||
send_string: function (str) {
|
||||
this.send(str.split('').map(function (chr) {
|
||||
return chr.charCodeAt(0);
|
||||
}));
|
||||
},
|
||||
|
||||
// Event Handlers
|
||||
off: function (evt) {
|
||||
this._eventHandlers[evt] = function () {};
|
||||
},
|
||||
|
||||
on: function (evt, handler) {
|
||||
this._eventHandlers[evt] = handler;
|
||||
},
|
||||
|
||||
_allocate_buffers: function () {
|
||||
this._rQ = new Uint8Array(this._rQbufferSize);
|
||||
this._sQ = new Uint8Array(this._sQbufferSize);
|
||||
},
|
||||
|
||||
init: function (protocols, ws_schema) {
|
||||
this._allocate_buffers();
|
||||
this._rQi = 0;
|
||||
this._websocket = null;
|
||||
|
||||
// Check for full typed array support
|
||||
var bt = false;
|
||||
if (('Uint8Array' in window) &&
|
||||
('set' in Uint8Array.prototype)) {
|
||||
bt = true;
|
||||
}
|
||||
|
||||
// Check for full binary type support in WebSockets
|
||||
// Inspired by:
|
||||
// https://github.com/Modernizr/Modernizr/issues/370
|
||||
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/websockets/binary.js
|
||||
var wsbt = false;
|
||||
try {
|
||||
if (bt && ('binaryType' in WebSocket.prototype ||
|
||||
!!(new WebSocket(ws_schema + '://.').binaryType))) {
|
||||
Util.Info("Detected binaryType support in WebSockets");
|
||||
wsbt = true;
|
||||
}
|
||||
} catch (exc) {
|
||||
// Just ignore failed test localhost connection
|
||||
}
|
||||
|
||||
// Default protocols if not specified
|
||||
if (typeof(protocols) === "undefined") {
|
||||
protocols = 'binary';
|
||||
}
|
||||
|
||||
if (Array.isArray(protocols) && protocols.indexOf('binary') > -1) {
|
||||
protocols = 'binary';
|
||||
}
|
||||
|
||||
if (!wsbt) {
|
||||
throw new Error("noVNC no longer supports base64 WebSockets. " +
|
||||
"Please use a browser which supports binary WebSockets.");
|
||||
}
|
||||
|
||||
if (protocols != 'binary') {
|
||||
throw new Error("noVNC no longer supports base64 WebSockets. Please " +
|
||||
"use the binary subprotocol instead.");
|
||||
}
|
||||
|
||||
return protocols;
|
||||
},
|
||||
|
||||
open: function (uri, protocols) {
|
||||
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
|
||||
protocols = this.init(protocols, ws_schema);
|
||||
|
||||
this._websocket = new WebSocket(uri, protocols);
|
||||
|
||||
if (protocols.indexOf('binary') >= 0) {
|
||||
this._websocket.binaryType = 'arraybuffer';
|
||||
}
|
||||
|
||||
this._websocket.onmessage = this._recv_message.bind(this);
|
||||
this._websocket.onopen = (function () {
|
||||
Util.Debug('>> WebSock.onopen');
|
||||
if (this._websocket.protocol) {
|
||||
this._mode = this._websocket.protocol;
|
||||
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||
} else {
|
||||
this._mode = 'binary';
|
||||
Util.Error('Server select no sub-protocol!: ' + this._websocket.protocol);
|
||||
}
|
||||
|
||||
if (this._mode != 'binary') {
|
||||
throw new Error("noVNC no longer supports base64 WebSockets. Please " +
|
||||
"use the binary subprotocol instead.");
|
||||
|
||||
}
|
||||
|
||||
this._eventHandlers.open();
|
||||
Util.Debug("<< WebSock.onopen");
|
||||
}).bind(this);
|
||||
this._websocket.onclose = (function (e) {
|
||||
Util.Debug(">> WebSock.onclose");
|
||||
this._eventHandlers.close(e);
|
||||
Util.Debug("<< WebSock.onclose");
|
||||
}).bind(this);
|
||||
this._websocket.onerror = (function (e) {
|
||||
Util.Debug(">> WebSock.onerror: " + e);
|
||||
this._eventHandlers.error(e);
|
||||
Util.Debug("<< WebSock.onerror: " + e);
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
close: function () {
|
||||
if (this._websocket) {
|
||||
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
||||
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
||||
Util.Info("Closing WebSocket connection");
|
||||
this._websocket.close();
|
||||
}
|
||||
|
||||
this._websocket.onmessage = function (e) { return; };
|
||||
}
|
||||
},
|
||||
|
||||
// private methods
|
||||
_encode_message: function () {
|
||||
// Put in a binary arraybuffer
|
||||
// according to the spec, you can send ArrayBufferViews with the send method
|
||||
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
|
||||
},
|
||||
|
||||
_expand_compact_rQ: function (min_fit) {
|
||||
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
|
||||
if (resizeNeeded) {
|
||||
if (!min_fit) {
|
||||
// just double the size if we need to do compaction
|
||||
this._rQbufferSize *= 2;
|
||||
} else {
|
||||
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
|
||||
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
|
||||
}
|
||||
}
|
||||
|
||||
// we don't want to grow unboundedly
|
||||
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
|
||||
this._rQbufferSize = MAX_RQ_GROW_SIZE;
|
||||
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
|
||||
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
|
||||
}
|
||||
}
|
||||
|
||||
if (resizeNeeded) {
|
||||
var old_rQbuffer = this._rQ.buffer;
|
||||
this._rQmax = this._rQbufferSize / 8;
|
||||
this._rQ = new Uint8Array(this._rQbufferSize);
|
||||
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
|
||||
} else {
|
||||
if (ENABLE_COPYWITHIN) {
|
||||
this._rQ.copyWithin(0, this._rQi);
|
||||
} else {
|
||||
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
|
||||
}
|
||||
}
|
||||
|
||||
this._rQlen = this._rQlen - this._rQi;
|
||||
this._rQi = 0;
|
||||
},
|
||||
|
||||
_decode_message: function (data) {
|
||||
// push arraybuffer values onto the end
|
||||
var u8 = new Uint8Array(data);
|
||||
if (u8.length > this._rQbufferSize - this._rQlen) {
|
||||
this._expand_compact_rQ(u8.length);
|
||||
}
|
||||
this._rQ.set(u8, this._rQlen);
|
||||
this._rQlen += u8.length;
|
||||
},
|
||||
|
||||
_recv_message: function (e) {
|
||||
try {
|
||||
this._decode_message(e.data);
|
||||
if (this.rQlen() > 0) {
|
||||
this._eventHandlers.message();
|
||||
// Compact the receive queue
|
||||
if (this._rQlen == this._rQi) {
|
||||
this._rQlen = 0;
|
||||
this._rQi = 0;
|
||||
} else if (this._rQlen > this._rQmax) {
|
||||
this._expand_compact_rQ();
|
||||
}
|
||||
} else {
|
||||
Util.Debug("Ignoring empty message");
|
||||
}
|
||||
} catch (exc) {
|
||||
var exception_str = "";
|
||||
if (exc.name) {
|
||||
exception_str += "\n name: " + exc.name + "\n";
|
||||
exception_str += " message: " + exc.message + "\n";
|
||||
}
|
||||
|
||||
if (typeof exc.description !== 'undefined') {
|
||||
exception_str += " description: " + exc.description + "\n";
|
||||
}
|
||||
|
||||
if (typeof exc.stack !== 'undefined') {
|
||||
exception_str += exc.stack;
|
||||
}
|
||||
|
||||
if (exception_str.length > 0) {
|
||||
Util.Error("recv_message, caught exception: " + exception_str);
|
||||
} else {
|
||||
Util.Error("recv_message, caught exception: " + exc);
|
||||
}
|
||||
|
||||
if (typeof exc.name !== 'undefined') {
|
||||
this._eventHandlers.error(exc.name + ": " + exc.message);
|
||||
} else {
|
||||
this._eventHandlers.error(exc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Util.Debug("Ignoring empty message");
|
||||
}
|
||||
} catch (exc) {
|
||||
if (typeof exc.stack !== 'undefined') {
|
||||
Util.Warn("recv_message, caught exception: " + exc.stack);
|
||||
} else if (typeof exc.description !== 'undefined') {
|
||||
Util.Warn("recv_message, caught exception: " + exc.description);
|
||||
} else {
|
||||
Util.Warn("recv_message, caught exception:" + exc);
|
||||
}
|
||||
if (typeof exc.name !== 'undefined') {
|
||||
eventHandlers.error(exc.name + ": " + exc.message);
|
||||
} else {
|
||||
eventHandlers.error(exc);
|
||||
}
|
||||
}
|
||||
//Util.Debug("<< recv_message");
|
||||
}
|
||||
|
||||
|
||||
// Set event handlers
|
||||
function on(evt, handler) {
|
||||
eventHandlers[evt] = handler;
|
||||
}
|
||||
|
||||
function init() {
|
||||
rQ = [];
|
||||
rQi = 0;
|
||||
sQ = [];
|
||||
websocket = null;
|
||||
}
|
||||
|
||||
function open(uri) {
|
||||
init();
|
||||
|
||||
if (test_mode) {
|
||||
websocket = {};
|
||||
} else {
|
||||
websocket = new WebSocket(uri, 'base64');
|
||||
// TODO: future native binary support
|
||||
//websocket = new WebSocket(uri, ['binary', 'base64']);
|
||||
}
|
||||
|
||||
websocket.onmessage = recv_message;
|
||||
websocket.onopen = function() {
|
||||
Util.Debug(">> WebSock.onopen");
|
||||
if (websocket.protocol) {
|
||||
Util.Info("Server chose sub-protocol: " + websocket.protocol);
|
||||
}
|
||||
eventHandlers.open();
|
||||
Util.Debug("<< WebSock.onopen");
|
||||
};
|
||||
websocket.onclose = function(e) {
|
||||
Util.Debug(">> WebSock.onclose");
|
||||
eventHandlers.close(e);
|
||||
Util.Debug("<< WebSock.onclose");
|
||||
};
|
||||
websocket.onerror = function(e) {
|
||||
Util.Debug(">> WebSock.onerror: " + e);
|
||||
eventHandlers.error(e);
|
||||
Util.Debug("<< WebSock.onerror");
|
||||
};
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (websocket) {
|
||||
if ((websocket.readyState === WebSocket.OPEN) ||
|
||||
(websocket.readyState === WebSocket.CONNECTING)) {
|
||||
Util.Info("Closing WebSocket connection");
|
||||
websocket.close();
|
||||
}
|
||||
websocket.onmessage = function (e) { return; };
|
||||
}
|
||||
}
|
||||
|
||||
// Override internal functions for testing
|
||||
// Takes a send function, returns reference to recv function
|
||||
function testMode(override_send) {
|
||||
test_mode = true;
|
||||
api.send = override_send;
|
||||
api.close = function () {};
|
||||
return recv_message;
|
||||
}
|
||||
|
||||
function constructor() {
|
||||
// Configuration settings
|
||||
api.maxBufferedAmount = 200;
|
||||
|
||||
// Direct access to send and receive queues
|
||||
api.get_sQ = get_sQ;
|
||||
api.get_rQ = get_rQ;
|
||||
api.get_rQi = get_rQi;
|
||||
api.set_rQi = set_rQi;
|
||||
|
||||
// Routines to read from the receive queue
|
||||
api.rQlen = rQlen;
|
||||
api.rQpeek8 = rQpeek8;
|
||||
api.rQshift8 = rQshift8;
|
||||
api.rQunshift8 = rQunshift8;
|
||||
api.rQshift16 = rQshift16;
|
||||
api.rQshift32 = rQshift32;
|
||||
api.rQshiftStr = rQshiftStr;
|
||||
api.rQshiftBytes = rQshiftBytes;
|
||||
api.rQslice = rQslice;
|
||||
api.rQwait = rQwait;
|
||||
|
||||
api.flush = flush;
|
||||
api.send = send;
|
||||
api.send_string = send_string;
|
||||
|
||||
api.on = on;
|
||||
api.init = init;
|
||||
api.open = open;
|
||||
api.close = close;
|
||||
api.testMode = testMode;
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
return constructor();
|
||||
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2011 Joel Martin
|
||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2013 NTT corp.
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint bitwise: false, white: false */
|
||||
/*global window, document */
|
||||
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
||||
/*global Util, window, document */
|
||||
|
||||
// Globals defined here
|
||||
var WebUtil = {}, $D;
|
||||
@@ -17,7 +17,7 @@ var WebUtil = {}, $D;
|
||||
* Simple DOM selector by ID
|
||||
*/
|
||||
if (!window.$D) {
|
||||
$D = function (id) {
|
||||
window.$D = function (id) {
|
||||
if (document.getElementById) {
|
||||
return document.getElementById(id);
|
||||
} else if (document.all) {
|
||||
@@ -30,43 +30,47 @@ if (!window.$D) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in WebUtil
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
|
||||
// init log level reading the logging HTTP param
|
||||
WebUtil.init_logging = function() {
|
||||
Util._log_level = (document.location.href.match(
|
||||
/logging=([A-Za-z0-9\._\-]*)/) ||
|
||||
['', Util._log_level])[1];
|
||||
|
||||
Util.init_logging()
|
||||
}
|
||||
WebUtil.init_logging();
|
||||
WebUtil.init_logging = function (level) {
|
||||
"use strict";
|
||||
if (typeof level !== "undefined") {
|
||||
Util._log_level = level;
|
||||
} else {
|
||||
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
||||
Util._log_level = (param || ['', Util._log_level])[1];
|
||||
}
|
||||
Util.init_logging();
|
||||
};
|
||||
|
||||
|
||||
WebUtil.dirObj = function (obj, depth, parent) {
|
||||
var i, msg = "", val = "";
|
||||
if (! depth) { depth=2; }
|
||||
if (! parent) { parent= ""; }
|
||||
"use strict";
|
||||
if (! depth) { depth = 2; }
|
||||
if (! parent) { parent = ""; }
|
||||
|
||||
// Print the properties of the passed-in object
|
||||
for (i in obj) {
|
||||
if ((depth > 1) && (typeof obj[i] === "object")) {
|
||||
// Print the properties of the passed-in object
|
||||
var msg = "";
|
||||
for (var i in obj) {
|
||||
if ((depth > 1) && (typeof obj[i] === "object")) {
|
||||
// Recurse attributes that are objects
|
||||
msg += WebUtil.dirObj(obj[i], depth-1, parent + "." + i);
|
||||
msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
|
||||
} else {
|
||||
//val = new String(obj[i]).replace("\n", " ");
|
||||
var val = "";
|
||||
if (typeof(obj[i]) === "undefined") {
|
||||
val = "undefined";
|
||||
} else {
|
||||
val = obj[i].toString().replace("\n", " ");
|
||||
}
|
||||
if (val.length > 30) {
|
||||
val = val.substr(0,30) + "...";
|
||||
}
|
||||
val = val.substr(0, 30) + "...";
|
||||
}
|
||||
msg += parent + "." + i + ": " + val + "\n";
|
||||
}
|
||||
}
|
||||
@@ -74,51 +78,159 @@ WebUtil.dirObj = function (obj, depth, parent) {
|
||||
};
|
||||
|
||||
// Read a query string variable
|
||||
WebUtil.getQueryVar = function(name, defVal) {
|
||||
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
|
||||
WebUtil.getQueryVar = function (name, defVal) {
|
||||
"use strict";
|
||||
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||
match = document.location.href.match(re);
|
||||
if (typeof defVal === 'undefined') { defVal = null; }
|
||||
return (document.location.href.match(re) || ['',defVal])[1];
|
||||
if (match) {
|
||||
return decodeURIComponent(match[1]);
|
||||
} else {
|
||||
return defVal;
|
||||
}
|
||||
};
|
||||
|
||||
// Read a hash fragment variable
|
||||
WebUtil.getHashVar = function (name, defVal) {
|
||||
"use strict";
|
||||
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
||||
match = document.location.hash.match(re);
|
||||
if (typeof defVal === 'undefined') { defVal = null; }
|
||||
if (match) {
|
||||
return decodeURIComponent(match[1]);
|
||||
} else {
|
||||
return defVal;
|
||||
}
|
||||
};
|
||||
|
||||
// Read a variable from the fragment or the query string
|
||||
// Fragment takes precedence
|
||||
WebUtil.getConfigVar = function (name, defVal) {
|
||||
"use strict";
|
||||
var val = WebUtil.getHashVar(name);
|
||||
if (val === null) {
|
||||
val = WebUtil.getQueryVar(name, defVal);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
/*
|
||||
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
|
||||
*/
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.createCookie = function(name,value,days) {
|
||||
WebUtil.createCookie = function (name, value, days) {
|
||||
"use strict";
|
||||
var date, expires;
|
||||
if (days) {
|
||||
date = new Date();
|
||||
date.setTime(date.getTime()+(days*24*60*60*1000));
|
||||
expires = "; expires="+date.toGMTString();
|
||||
}
|
||||
else {
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toGMTString();
|
||||
} else {
|
||||
expires = "";
|
||||
}
|
||||
document.cookie = name+"="+value+expires+"; path=/";
|
||||
|
||||
var secure;
|
||||
if (document.location.protocol === "https:") {
|
||||
secure = "; secure";
|
||||
} else {
|
||||
secure = "";
|
||||
}
|
||||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||
};
|
||||
|
||||
WebUtil.readCookie = function(name, defaultValue) {
|
||||
var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
|
||||
for(i=0; i < ca.length; i += 1) {
|
||||
c = ca[i];
|
||||
while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
|
||||
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
|
||||
WebUtil.readCookie = function (name, defaultValue) {
|
||||
"use strict";
|
||||
var nameEQ = name + "=",
|
||||
ca = document.cookie.split(';');
|
||||
|
||||
for (var i = 0; i < ca.length; i += 1) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
|
||||
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
|
||||
}
|
||||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||
};
|
||||
|
||||
WebUtil.eraseCookie = function(name) {
|
||||
WebUtil.createCookie(name,"",-1);
|
||||
WebUtil.eraseCookie = function (name) {
|
||||
"use strict";
|
||||
WebUtil.createCookie(name, "", -1);
|
||||
};
|
||||
|
||||
/*
|
||||
* Setting handling.
|
||||
*/
|
||||
|
||||
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
||||
"use strict";
|
||||
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.get(function (cfg) {
|
||||
WebUtil.settings = cfg;
|
||||
console.log(WebUtil.settings);
|
||||
if (callback) {
|
||||
callback.apply(this, callbackArgs);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// No-op
|
||||
if (callback) {
|
||||
callback.apply(this, callbackArgs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.writeSetting = function (name, value) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
//console.log("writeSetting:", name, value);
|
||||
if (WebUtil.settings[name] !== value) {
|
||||
WebUtil.settings[name] = value;
|
||||
window.chrome.storage.sync.set(WebUtil.settings);
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
WebUtil.readSetting = function (name, defaultValue) {
|
||||
"use strict";
|
||||
var value;
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
value = WebUtil.settings[name];
|
||||
} else {
|
||||
value = localStorage.getItem(name);
|
||||
}
|
||||
if (typeof value === "undefined") {
|
||||
value = null;
|
||||
}
|
||||
if (value === null && typeof defaultValue !== undefined) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
WebUtil.eraseSetting = function (name) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.remove(name);
|
||||
delete WebUtil.settings[name];
|
||||
} else {
|
||||
localStorage.removeItem(name);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Alternate stylesheet selection
|
||||
*/
|
||||
WebUtil.getStylesheets = function() { var i, links, sheets = [];
|
||||
links = document.getElementsByTagName("link");
|
||||
for (i = 0; i < links.length; i += 1) {
|
||||
WebUtil.getStylesheets = function () {
|
||||
"use strict";
|
||||
var links = document.getElementsByTagName("link");
|
||||
var sheets = [];
|
||||
|
||||
for (var i = 0; i < links.length; i += 1) {
|
||||
if (links[i].title &&
|
||||
links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
|
||||
sheets.push(links[i]);
|
||||
@@ -129,14 +241,16 @@ WebUtil.getStylesheets = function() { var i, links, sheets = [];
|
||||
|
||||
// No sheet means try and use value from cookie, null sheet used to
|
||||
// clear all alternates.
|
||||
WebUtil.selectStylesheet = function(sheet) {
|
||||
var i, link, sheets = WebUtil.getStylesheets();
|
||||
WebUtil.selectStylesheet = function (sheet) {
|
||||
"use strict";
|
||||
if (typeof sheet === 'undefined') {
|
||||
sheet = 'default';
|
||||
}
|
||||
for (i=0; i < sheets.length; i += 1) {
|
||||
link = sheets[i];
|
||||
if (link.title === sheet) {
|
||||
|
||||
var sheets = WebUtil.getStylesheets();
|
||||
for (var i = 0; i < sheets.length; i += 1) {
|
||||
var link = sheets[i];
|
||||
if (link.title === sheet) {
|
||||
Util.Debug("Using stylesheet " + sheet);
|
||||
link.disabled = false;
|
||||
} else {
|
||||
@@ -146,3 +260,33 @@ WebUtil.selectStylesheet = function(sheet) {
|
||||
}
|
||||
return sheet;
|
||||
};
|
||||
|
||||
WebUtil.injectParamIfMissing = function (path, param, value) {
|
||||
// force pretend that we're dealing with a relative path
|
||||
// (assume that we wanted an extra if we pass one in)
|
||||
path = "/" + path;
|
||||
|
||||
var elem = document.createElement('a');
|
||||
elem.href = path;
|
||||
|
||||
var param_eq = encodeURIComponent(param) + "=";
|
||||
var query;
|
||||
if (elem.search) {
|
||||
query = elem.search.slice(1).split('&');
|
||||
} else {
|
||||
query = [];
|
||||
}
|
||||
|
||||
if (!query.some(function (v) { return v.startsWith(param_eq); })) {
|
||||
query.push(param_eq + encodeURIComponent(value));
|
||||
elem.search = "?" + query.join("&");
|
||||
}
|
||||
|
||||
// some browsers (e.g. IE11) may occasionally omit the leading slash
|
||||
// in the elem.pathname string. Handle that case gracefully.
|
||||
if (elem.pathname.charAt(0) == "/") {
|
||||
return elem.pathname.slice(1) + elem.search + elem.hash;
|
||||
} else {
|
||||
return elem.pathname + elem.search + elem.hash;
|
||||
}
|
||||
};
|
||||
|
||||
197
karma.conf.js
Normal file
@@ -0,0 +1,197 @@
|
||||
// Karma configuration
|
||||
|
||||
module.exports = function(config) {
|
||||
/*var customLaunchers = {
|
||||
sl_chrome_win7: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
|
||||
sl_firefox30_linux: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '30',
|
||||
platform: 'Linux'
|
||||
},
|
||||
|
||||
sl_firefox26_linux: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: 26,
|
||||
platform: 'Linux'
|
||||
},
|
||||
|
||||
sl_windows7_ie10: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 7',
|
||||
version: '10'
|
||||
},
|
||||
|
||||
sl_windows81_ie11: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
|
||||
sl_osxmavericks_safari7: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
},
|
||||
|
||||
sl_osxmtnlion_safari6: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.8',
|
||||
version: '6'
|
||||
}
|
||||
};*/
|
||||
|
||||
var customLaunchers = {};
|
||||
var browsers = [];
|
||||
var useSauce = false;
|
||||
|
||||
if (process.env.SAUCE_USERNAME && process.env.SAUCE_ACCESS_KEY) {
|
||||
useSauce = true;
|
||||
}
|
||||
|
||||
if (useSauce && process.env.TEST_BROWSER_NAME && process.env.TEST_BROWSER_NAME != 'PhantomJS') {
|
||||
var names = process.env.TEST_BROWSER_NAME.split(',');
|
||||
var platforms = process.env.TEST_BROWSER_OS.split(',');
|
||||
var versions = [];
|
||||
if (process.env.TEST_BROWSER_VERSION) {
|
||||
versions = process.env.TEST_BROWSER_VERSION.split(',');
|
||||
} else {
|
||||
versions = [null];
|
||||
}
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
for (var j = 0; j < platforms.length; j++) {
|
||||
for (var k = 0; k < versions.length; k++) {
|
||||
var launcher_name = 'sl_' + platforms[j].replace(/[^a-zA-Z0-9]/g, '') + '_' + names[i];
|
||||
if (versions[k]) {
|
||||
launcher_name += '_' + versions[k];
|
||||
}
|
||||
|
||||
customLaunchers[launcher_name] = {
|
||||
base: 'SauceLabs',
|
||||
browserName: names[i],
|
||||
platform: platforms[j],
|
||||
};
|
||||
|
||||
if (versions[i]) {
|
||||
customLaunchers[launcher_name].version = versions[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
browsers = Object.keys(customLaunchers);
|
||||
} else {
|
||||
useSauce = false;
|
||||
browsers = ['PhantomJS'];
|
||||
}
|
||||
|
||||
var my_conf = {
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['mocha', 'sinon', 'chai', 'sinon-chai'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser (loaded in order)
|
||||
files: [
|
||||
'tests/fake.*.js',
|
||||
'tests/assertions.js',
|
||||
'include/util.js', // load first to avoid issues, since methods are called immediately
|
||||
//'../include/*.js',
|
||||
'include/base64.js',
|
||||
'include/keysym.js',
|
||||
'include/keysymdef.js',
|
||||
'include/keyboard.js',
|
||||
'include/input.js',
|
||||
'include/websock.js',
|
||||
'include/rfb.js',
|
||||
'include/des.js',
|
||||
'include/display.js',
|
||||
'include/inflator.js',
|
||||
'tests/test.*.js'
|
||||
],
|
||||
|
||||
client: {
|
||||
mocha: {
|
||||
'ui': 'bdd'
|
||||
}
|
||||
},
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
'../include/playback.js',
|
||||
'../include/ui.js'
|
||||
],
|
||||
|
||||
customLaunchers: customLaunchers,
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: browsers,
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['mocha', 'saucelabs'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
|
||||
// Increase timeout in case connection is slow/we run more browsers than possible
|
||||
// (we currently get 3 for free, and we try to run 7, so it can take a while)
|
||||
captureTimeout: 240000,
|
||||
|
||||
// similarly to above
|
||||
browserNoActivityTimeout: 100000,
|
||||
};
|
||||
|
||||
if (useSauce) {
|
||||
my_conf.captureTimeout = 0; // use SL timeout
|
||||
my_conf.sauceLabs = {
|
||||
testName: 'noVNC Tests (all)',
|
||||
startConnect: false,
|
||||
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
|
||||
};
|
||||
}
|
||||
|
||||
config.set(my_conf);
|
||||
};
|
||||
50
package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "noVNC",
|
||||
"version": "0.6.1",
|
||||
"description": "An HTML5 VNC client",
|
||||
"main": "karma.conf.js",
|
||||
"directories": {
|
||||
"doc": "docs",
|
||||
"test": "tests"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "PATH=$PATH:node_modules/karma/bin karma start karma.conf.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kanaka/noVNC.git"
|
||||
},
|
||||
"author": "Joel Martin <github@martintribe.org> (https://github.com/kanaka)",
|
||||
"contributors": [
|
||||
"Solly Ross <sross@redhat.com> (https://github.com/directxman12)",
|
||||
"Peter Åstrand <astrand@cendio.se> (https://github.com/astrand)",
|
||||
"Samuel Mannehed <samuel@cendio.se> (https://github.com/samhed)"
|
||||
],
|
||||
"license": "MPL 2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/kanaka/noVNC/issues"
|
||||
},
|
||||
"homepage": "https://github.com/kanaka/noVNC",
|
||||
"devDependencies": {
|
||||
"ansi": "^0.3.0",
|
||||
"casperjs": "^1.1.0-beta3",
|
||||
"chai": "^2.1.0",
|
||||
"commander": "^2.6.0",
|
||||
"karma": "^0.12.31",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-mocha": "^0.1.10",
|
||||
"karma-mocha-reporter": "^1.0.0",
|
||||
"karma-phantomjs-launcher": "^0.1.4",
|
||||
"karma-sauce-launcher": "^0.2.10",
|
||||
"karma-sinon": "^1.0.4",
|
||||
"karma-sinon-chai-latest": "^0.1.0",
|
||||
"mocha": "^2.1.0",
|
||||
"open": "^0.0.5",
|
||||
"phantom": "^0.7.2",
|
||||
"phantomjs": "^1.9.15",
|
||||
"sinon": "^1.12.2",
|
||||
"sinon-chai": "^2.7.0",
|
||||
"spooky": "^0.2.5",
|
||||
"temp": "^0.8.1"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Javascript binary array performance tests
|
||||
* Copyright (C) 2011 Joel Martin
|
||||
* Licensed under LGPL-3 (see LICENSE.txt)
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
var ctx, i, j, randlist,
|
||||
|
||||
98
tests/assertions.js
Normal file
@@ -0,0 +1,98 @@
|
||||
// some useful assertions for noVNC
|
||||
chai.use(function (_chai, utils) {
|
||||
_chai.Assertion.addMethod('displayed', function (target_data) {
|
||||
var obj = this._obj;
|
||||
var data_cl = obj._drawCtx.getImageData(0, 0, obj._viewportLoc.w, obj._viewportLoc.h).data;
|
||||
// NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that
|
||||
var data = new Uint8Array(data_cl);
|
||||
var same = true;
|
||||
var len = data_cl.length;
|
||||
if (len != target_data.length) {
|
||||
same = false;
|
||||
} else {
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (data[i] != target_data[i]) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!same) {
|
||||
console.log("expected data: %o, actual data: %o", target_data, data);
|
||||
}
|
||||
this.assert(same,
|
||||
"expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}",
|
||||
"expected #{this} not to have displayed the image #{act}",
|
||||
target_data,
|
||||
data);
|
||||
});
|
||||
|
||||
_chai.Assertion.addMethod('sent', function (target_data) {
|
||||
var obj = this._obj;
|
||||
obj.inspect = function () {
|
||||
var res = { _websocket: obj._websocket, rQi: obj._rQi, _rQ: new Uint8Array(obj._rQ.buffer, 0, obj._rQlen),
|
||||
_sQ: new Uint8Array(obj._sQ.buffer, 0, obj._sQlen) };
|
||||
res.prototype = obj;
|
||||
return res;
|
||||
};
|
||||
var data = obj._websocket._get_sent_data();
|
||||
var same = true;
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
if (data[i] != target_data[i]) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!same) {
|
||||
console.log("expected data: %o, actual data: %o", target_data, data);
|
||||
}
|
||||
this.assert(same,
|
||||
"expected #{this} to have sent the data #{exp}, but it actually sent #{act}",
|
||||
"expected #{this} not to have sent the data #{act}",
|
||||
Array.prototype.slice.call(target_data),
|
||||
Array.prototype.slice.call(data));
|
||||
});
|
||||
|
||||
_chai.Assertion.addProperty('array', function () {
|
||||
utils.flag(this, 'array', true);
|
||||
});
|
||||
|
||||
_chai.Assertion.overwriteMethod('equal', function (_super) {
|
||||
return function assertArrayEqual(target) {
|
||||
if (utils.flag(this, 'array')) {
|
||||
var obj = this._obj;
|
||||
|
||||
var i;
|
||||
var same = true;
|
||||
|
||||
if (utils.flag(this, 'deep')) {
|
||||
for (i = 0; i < obj.length; i++) {
|
||||
if (!utils.eql(obj[i], target[i])) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.assert(same,
|
||||
"expected #{this} to have elements deeply equal to #{exp}",
|
||||
"expected #{this} not to have elements deeply equal to #{exp}",
|
||||
Array.prototype.slice.call(target));
|
||||
} else {
|
||||
for (i = 0; i < obj.length; i++) {
|
||||
if (obj[i] != target[i]) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.assert(same,
|
||||
"expected #{this} to have elements equal to #{exp}",
|
||||
"expected #{this} not to have elements equal to #{exp}",
|
||||
Array.prototype.slice.call(target));
|
||||
}
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,7 @@
|
||||
<script src="../include/util.js"></script>
|
||||
<script src="../include/webutil.js"></script>
|
||||
<script src="../include/base64.js"></script>
|
||||
<script src="../include/canvas.js"></script>
|
||||
<script src="../include/display.js"></script>
|
||||
<script src="face.png.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<script>
|
||||
var msg_cnt = 0;
|
||||
var start_width = 300, start_height = 100;
|
||||
var display, start_width = 300, start_height = 100;
|
||||
var iterations;
|
||||
|
||||
function message(str) {
|
||||
@@ -48,12 +48,12 @@
|
||||
}
|
||||
|
||||
function test_functions () {
|
||||
var img, x, y, w, h, ctx = canvas.getContext();
|
||||
w = canvas.get_width();
|
||||
h = canvas.get_height();
|
||||
canvas.fillRect(0, 0, w, h, [240,240,240]);
|
||||
var img, x, y, w, h, ctx = display.get_context();
|
||||
w = display.get_width();
|
||||
h = display.get_height();
|
||||
display.fillRect(0, 0, w, h, [240,240,240]);
|
||||
|
||||
canvas.blitStringImage("data:image/png;base64," + face64, 150, 10);
|
||||
display.blitStringImage("data:image/png;base64," + face64, 150, 10);
|
||||
|
||||
var himg = new Image();
|
||||
himg.onload = function () {
|
||||
@@ -70,14 +70,14 @@
|
||||
data[(y*50 + x)*4 + 3] = 255;
|
||||
}
|
||||
}
|
||||
canvas.blitImage(30, 10, 50, 50, data, 0);
|
||||
display.blitImage(30, 10, 50, 50, data, 0);
|
||||
|
||||
img = canvas.getTile(5,5,16,16,[0,128,128]);
|
||||
canvas.putTile(img);
|
||||
img = display.getTile(5,5,16,16,[0,128,128]);
|
||||
display.putTile(img);
|
||||
|
||||
img = canvas.getTile(90,15,16,16,[0,0,0]);
|
||||
canvas.setSubTile(img, 0,0,16,16,[128,128,0]);
|
||||
canvas.putTile(img);
|
||||
img = display.getTile(90,15,16,16,[0,0,0]);
|
||||
display.setSubTile(img, 0,0,16,16,[128,128,0]);
|
||||
display.putTile(img);
|
||||
}
|
||||
|
||||
function begin () {
|
||||
@@ -90,7 +90,7 @@
|
||||
function start_delayed () {
|
||||
var ret;
|
||||
|
||||
ret = canvas.set_prefer_js(true);
|
||||
ret = display.set_prefer_js(true);
|
||||
if (ret) {
|
||||
message("Running test: prefer Javascript ops");
|
||||
var time1 = run_test();
|
||||
@@ -100,14 +100,14 @@
|
||||
message("Could not run: prefer Javascript ops");
|
||||
}
|
||||
|
||||
canvas.set_prefer_js(false);
|
||||
display.set_prefer_js(false);
|
||||
message("Running test: prefer Canvas ops");
|
||||
var time2 = run_test();
|
||||
message("prefer Canvas ops: " + time2 + "ms total, " +
|
||||
(time2 / iterations) + "ms per frame");
|
||||
|
||||
if (Util.get_logging() !== 'debug') {
|
||||
canvas.resize(start_width, start_height, true);
|
||||
display.resize(start_width, start_height, true);
|
||||
test_functions();
|
||||
}
|
||||
$D('startButton').disabled = false;
|
||||
@@ -118,7 +118,7 @@
|
||||
var width, height;
|
||||
width = $D('width').value;
|
||||
height = $D('height').value;
|
||||
canvas.resize(width, height);
|
||||
display.resize(width, height);
|
||||
var color, start_time = (new Date()).getTime(), w, h;
|
||||
for (var i=0; i < iterations; i++) {
|
||||
color = [128, 128, (255 / iterations) * i, 0];
|
||||
@@ -126,9 +126,9 @@
|
||||
for (var y=0; y < height; y = y + 16) {
|
||||
w = Math.min(16, width - x);
|
||||
h = Math.min(16, height - y);
|
||||
var tile = canvas.getTile(x, y, w, h, color);
|
||||
canvas.setSubTile(tile, 0, 0, w, h, color);
|
||||
canvas.putTile(tile);
|
||||
var tile = display.getTile(x, y, w, h, color);
|
||||
display.setSubTile(tile, 0, 0, w, h, color);
|
||||
display.putTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,8 +139,8 @@
|
||||
window.onload = function() {
|
||||
message("in onload");
|
||||
$D('iterations').value = 10;
|
||||
canvas = new Canvas({'target' : $D('canvas')});
|
||||
canvas.resize(start_width, start_height, true);
|
||||
display = new Display({'target' : $D('canvas')});
|
||||
display.resize(start_width, start_height, true);
|
||||
message("Canvas initialized");
|
||||
test_functions();
|
||||
}
|
||||
|
||||
91
tests/fake.websocket.js
Normal file
@@ -0,0 +1,91 @@
|
||||
var FakeWebSocket;
|
||||
|
||||
(function () {
|
||||
// PhantomJS can't create Event objects directly, so we need to use this
|
||||
function make_event(name, props) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent(name, true, true);
|
||||
if (props) {
|
||||
for (var prop in props) {
|
||||
evt[prop] = props[prop];
|
||||
}
|
||||
}
|
||||
return evt;
|
||||
}
|
||||
|
||||
FakeWebSocket = function (uri, protocols) {
|
||||
this.url = uri;
|
||||
this.binaryType = "arraybuffer";
|
||||
this.extensions = "";
|
||||
|
||||
if (!protocols || typeof protocols === 'string') {
|
||||
this.protocol = protocols;
|
||||
} else {
|
||||
this.protocol = protocols[0];
|
||||
}
|
||||
|
||||
this._send_queue = new Uint8Array(20000);
|
||||
|
||||
this.readyState = FakeWebSocket.CONNECTING;
|
||||
this.bufferedAmount = 0;
|
||||
|
||||
this.__is_fake = true;
|
||||
};
|
||||
|
||||
FakeWebSocket.prototype = {
|
||||
close: function (code, reason) {
|
||||
this.readyState = FakeWebSocket.CLOSED;
|
||||
if (this.onclose) {
|
||||
this.onclose(make_event("close", { 'code': code, 'reason': reason, 'wasClean': true }));
|
||||
}
|
||||
},
|
||||
|
||||
send: function (data) {
|
||||
if (this.protocol == 'base64') {
|
||||
data = Base64.decode(data);
|
||||
} else {
|
||||
data = new Uint8Array(data);
|
||||
}
|
||||
this._send_queue.set(data, this.bufferedAmount);
|
||||
this.bufferedAmount += data.length;
|
||||
},
|
||||
|
||||
_get_sent_data: function () {
|
||||
var res = new Uint8Array(this._send_queue.buffer, 0, this.bufferedAmount);
|
||||
this.bufferedAmount = 0;
|
||||
return res;
|
||||
},
|
||||
|
||||
_open: function (data) {
|
||||
this.readyState = FakeWebSocket.OPEN;
|
||||
if (this.onopen) {
|
||||
this.onopen(make_event('open'));
|
||||
}
|
||||
},
|
||||
|
||||
_receive_data: function (data) {
|
||||
this.onmessage(make_event("message", { 'data': data }));
|
||||
}
|
||||
};
|
||||
|
||||
FakeWebSocket.OPEN = WebSocket.OPEN;
|
||||
FakeWebSocket.CONNECTING = WebSocket.CONNECTING;
|
||||
FakeWebSocket.CLOSING = WebSocket.CLOSING;
|
||||
FakeWebSocket.CLOSED = WebSocket.CLOSED;
|
||||
|
||||
FakeWebSocket.__is_fake = true;
|
||||
|
||||
FakeWebSocket.replace = function () {
|
||||
if (!WebSocket.__is_fake) {
|
||||
var real_version = WebSocket;
|
||||
WebSocket = FakeWebSocket;
|
||||
FakeWebSocket.__real_version = real_version;
|
||||
}
|
||||
};
|
||||
|
||||
FakeWebSocket.restore = function () {
|
||||
if (WebSocket.__is_fake) {
|
||||
WebSocket = WebSocket.__real_version;
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -4,7 +4,11 @@
|
||||
<body>
|
||||
<br><br>
|
||||
|
||||
Canvas:<br>
|
||||
Canvas:
|
||||
<span id="button-selection" style="display: none;">
|
||||
<input id="button1" type="button" value="L"><input id="button2" type="button" value="M"><input id="button4" type="button" value="R">
|
||||
</span>
|
||||
<br>
|
||||
<canvas id="canvas" width="640" height="20"
|
||||
style="border-style: dotted; border-width: 1px;">
|
||||
Canvas not supported.
|
||||
@@ -22,12 +26,15 @@
|
||||
<script src="../include/util.js"></script>
|
||||
<script src="../include/webutil.js"></script>
|
||||
<script src="../include/base64.js"></script>
|
||||
<script src="../include/keysym.js"></script>
|
||||
<script src="../include/keysymdef.js"></script>
|
||||
<script src="../include/keyboard.js"></script>
|
||||
<script src="../include/input.js"></script>
|
||||
<script src="../include/canvas.js"></script>
|
||||
<script src="../include/display.js"></script>
|
||||
<script>
|
||||
var msg_cnt = 0;
|
||||
var width = 400, height = 200;
|
||||
var iterations;
|
||||
var msg_cnt = 0, iterations,
|
||||
width = 400, height = 200,
|
||||
canvas, keyboard, mouse;
|
||||
|
||||
var newline = "\n";
|
||||
if (Util.Engine.trident) {
|
||||
@@ -53,25 +60,73 @@
|
||||
//console.log(msg);
|
||||
}
|
||||
|
||||
function keyPress(keysym, down, e) {
|
||||
function rfbKeyPress(keysym, down) {
|
||||
var d = down ? "down" : " up ";
|
||||
msg = "keyPress " + d + " keysym: " + keysym +
|
||||
var key = keysyms.lookup(keysym);
|
||||
var msg = "RFB keypress " + d + " keysym: " + keysym;
|
||||
if (key && key.keyname) {
|
||||
msg += " key name: " + key.keyname;
|
||||
}
|
||||
message(msg);
|
||||
}
|
||||
function rawKey(e) {
|
||||
msg = "raw key event " + e.type +
|
||||
" (key: " + e.keyCode + ", char: " + e.charCode +
|
||||
", which: " + e.which +")";
|
||||
message(msg);
|
||||
}
|
||||
|
||||
function selectButton(num) {
|
||||
var b, blist = [1,2,4];
|
||||
|
||||
if (typeof num === 'undefined') {
|
||||
// Show the default
|
||||
num = mouse.get_touchButton();
|
||||
} else if (num === mouse.get_touchButton()) {
|
||||
// Set all buttons off (no clicks)
|
||||
mouse.set_touchButton(0);
|
||||
num = 0;
|
||||
} else {
|
||||
// Turn on one button
|
||||
mouse.set_touchButton(num);
|
||||
}
|
||||
|
||||
for (b = 0; b < blist.length; b++) {
|
||||
if (blist[b] === num) {
|
||||
$D('button' + blist[b]).style.backgroundColor = "black";
|
||||
$D('button' + blist[b]).style.color = "lightgray";
|
||||
} else {
|
||||
$D('button' + blist[b]).style.backgroundColor = "";
|
||||
$D('button' + blist[b]).style.color = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
var canvas = new Canvas({'target' : $D('canvas')});
|
||||
canvas = new Display({'target' : $D('canvas')});
|
||||
keyboard = new Keyboard({'target': document,
|
||||
'keyPress': keyPress});
|
||||
'onKeyPress': rfbKeyPress});
|
||||
Util.addEvent(document, 'keypress', rawKey);
|
||||
Util.addEvent(document, 'keydown', rawKey);
|
||||
Util.addEvent(document, 'keyup', rawKey);
|
||||
mouse = new Mouse({'target': $D('canvas'),
|
||||
'mouseButton': mouseButton,
|
||||
'mouseMove': mouseMove});
|
||||
'onMouseButton': mouseButton,
|
||||
'onMouseMove': mouseMove});
|
||||
|
||||
canvas.resize(width, height, true);
|
||||
keyboard.grab();
|
||||
mouse.grab();
|
||||
message("Canvas initialized");
|
||||
message("Display initialized");
|
||||
|
||||
if ('ontouchstart' in document.documentElement) {
|
||||
message("Touch device detected");
|
||||
$D('button-selection').style.display = "inline";
|
||||
$D('button1').onclick = function(){ selectButton(1) };
|
||||
$D('button2').onclick = function(){ selectButton(2) };
|
||||
$D('button4').onclick = function(){ selectButton(4) };
|
||||
selectButton();
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
29
tests/keyboard-tests.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mocha Tests</title>
|
||||
<link rel="stylesheet" href="node_modules/mocha/mocha.css" />
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
To run tests
|
||||
cd .../noVNC/tests
|
||||
npm install chai mocha
|
||||
open keyboard-tests.html in a browser
|
||||
-->
|
||||
<div id="mocha"></div>
|
||||
<script src="node_modules/chai/chai.js"></script>
|
||||
<script src="node_modules/mocha/mocha.js"></script>
|
||||
<script>mocha.setup('bdd')</script>
|
||||
<script src="../include/keysymdef.js"></script>
|
||||
<script src="../include/keyboard.js"></script>
|
||||
<script src="test.keyboard.js"></script>
|
||||
<script src="test.helper.js"></script>
|
||||
<script>
|
||||
mocha.checkLeaks();
|
||||
mocha.globals(['navigator']);
|
||||
mocha.run();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
114
tests/run_from_console.casper.js
Normal file
@@ -0,0 +1,114 @@
|
||||
var Spooky = require('spooky');
|
||||
var path = require('path');
|
||||
|
||||
var phantom_path = require('phantomjs').path;
|
||||
var casper_path = path.resolve(__dirname, '../node_modules/casperjs/bin/casperjs');
|
||||
process.env.PHANTOMJS_EXECUTABLE = phantom_path;
|
||||
var casper_opts = {
|
||||
child: {
|
||||
transport: 'http',
|
||||
command: casper_path
|
||||
},
|
||||
casper: {
|
||||
logLevel: 'debug',
|
||||
verbose: true
|
||||
}
|
||||
};
|
||||
|
||||
var provide_emitter = function(file_paths, debug_port) {
|
||||
if (debug_port) {
|
||||
casper_opts.child['remote-debugger-port'] = debug_port;
|
||||
var debug_url = ('https://localhost:' + debug_port +
|
||||
'/webkit/inspector/inspector.html?page=');
|
||||
console.info('[remote-debugger] Navigate to ' + debug_url + '1 and ' +
|
||||
'run `__run();` in the console to continue loading.' +
|
||||
'\n[remote-debugger] Navigate to ' + debug_url + '2 to ' +
|
||||
'view the actual page source.\n' +
|
||||
'[remote-debugger] Use the `debugger;` statement to ' +
|
||||
'trigger an initial breakpoint.');
|
||||
}
|
||||
|
||||
var spooky = new Spooky(casper_opts, function(err) {
|
||||
if (err) {
|
||||
if (err.stack) console.warn(err.stack);
|
||||
else console.warn(err);
|
||||
return;
|
||||
}
|
||||
spooky.start('about:blank');
|
||||
|
||||
file_paths.forEach(function(file_path, path_ind) {
|
||||
spooky.thenOpen('file://'+file_path);
|
||||
spooky.waitFor(function() {
|
||||
return this.getGlobal('__mocha_done') === true;
|
||||
},
|
||||
[{ path_ind: path_ind }, function() {
|
||||
var res_json = {
|
||||
file_ind: path_ind
|
||||
};
|
||||
|
||||
res_json.num_tests = this.evaluate(function() { return document.querySelectorAll('li.test').length; });
|
||||
res_json.num_passes = this.evaluate(function() { return document.querySelectorAll('li.test.pass').length; });
|
||||
res_json.num_fails = this.evaluate(function() { return document.querySelectorAll('li.test.fail').length; });
|
||||
res_json.num_slow = this.evaluate(function() { return document.querySelectorAll('li.test.pass:not(.fast):not(.pending)').length; });
|
||||
res_json.num_skipped = this.evaluate(function () { return document.querySelectorAll('li.test.pending').length; });
|
||||
res_json.duration = this.evaluate(function() { return document.querySelector('li.duration em').textContent; });
|
||||
|
||||
res_json.suites = this.evaluate(function() {
|
||||
var traverse_node = function(elem) {
|
||||
var res;
|
||||
if (elem.classList.contains('suite')) {
|
||||
res = {
|
||||
type: 'suite',
|
||||
name: elem.querySelector('h1').textContent,
|
||||
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0,
|
||||
};
|
||||
|
||||
var child_elems = elem.querySelector('ul').children;
|
||||
res.children = Array.prototype.map.call(child_elems, traverse_node);
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
var h2_content = elem.querySelector('h2').childNodes;
|
||||
res = {
|
||||
type: 'test',
|
||||
text: h2_content[0].textContent,
|
||||
};
|
||||
|
||||
if (elem.classList.contains('pass')) {
|
||||
res.pass = true;
|
||||
if (elem.classList.contains('pending')) {
|
||||
res.slow = false;
|
||||
res.skipped = true;
|
||||
}
|
||||
else {
|
||||
res.slow = !elem.classList.contains('fast');
|
||||
res.skipped = false;
|
||||
res.duration = h2_content[1].textContent;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.error = elem.querySelector('pre.error').textContent;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
var top_suites = document.querySelectorAll('#mocha-report > li.suite');
|
||||
return Array.prototype.map.call(top_suites, traverse_node);
|
||||
});
|
||||
|
||||
res_json.replay = this.evaluate(function() { return document.querySelector('a.replay').textContent; });
|
||||
|
||||
this.emit('test_ready', res_json);
|
||||
}]);
|
||||
});
|
||||
spooky.run();
|
||||
});
|
||||
|
||||
return spooky;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
provide_emitter: provide_emitter,
|
||||
name: 'SpookyJS (CapserJS on PhantomJS)'
|
||||
};
|
||||
361
tests/run_from_console.js
Executable file
@@ -0,0 +1,361 @@
|
||||
#!/usr/bin/env node
|
||||
var ansi = require('ansi');
|
||||
var program = require('commander');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
var make_list = function(val) {
|
||||
return val.split(',');
|
||||
};
|
||||
|
||||
program
|
||||
.option('-t, --tests <testlist>', 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list', make_list, [])
|
||||
.option('-a, --print-all', 'Print all tests, not just the failures')
|
||||
.option('--disable-color', 'Explicitly disable color')
|
||||
.option('-c, --color', 'Explicitly enable color (default is to use color when not outputting to a pipe)')
|
||||
.option('-i, --auto-inject <includefiles>', 'Treat the test list as a set of mocha JS files, and automatically generate HTML files with which to test test. \'includefiles\' should be a comma-separated list of paths to javascript files to include in each of the generated HTML files', make_list, null)
|
||||
.option('-p, --provider <name>', 'Use the given provider (defaults to "casper"). Currently, may be "casper" or "zombie"', 'casper')
|
||||
.option('-g, --generate-html', 'Instead of running the tests, just return the path to the generated HTML file, then wait for user interaction to exit (should be used with .js tests).')
|
||||
.option('-o, --open-in-browser', 'Open the generated HTML files in a web browser using the "open" module (must be used with the "-g"/"--generate-html" option).')
|
||||
.option('--output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with .js tests)')
|
||||
.option('-d, --debug', 'Show debug output (the "console" event) from the provider')
|
||||
.option('-r, --relative', 'Use relative paths in the generated HTML file')
|
||||
.option('--debugger <port>', 'Enable the remote debugger for CasperJS')
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.tests.length === 0) {
|
||||
program.tests = fs.readdirSync(__dirname).filter(function(f) { return (/^test\.(\w|\.|-)+\.js$/).test(f); });
|
||||
program.tests = program.tests.map(function (f) { return path.resolve(__dirname, f); }); // add full paths in
|
||||
console.log('using files %s', program.tests);
|
||||
}
|
||||
|
||||
var file_paths = [];
|
||||
|
||||
var all_js = program.tests.reduce(function(a,e) { return a && e.slice(-3) == '.js'; }, true);
|
||||
|
||||
var get_path = function (/* arguments */) {
|
||||
if (program.relative) {
|
||||
return path.join.apply(null, arguments);
|
||||
} else {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift(__dirname, '..');
|
||||
return path.resolve.apply(null, args);
|
||||
}
|
||||
};
|
||||
|
||||
var get_path_cwd = function (/* arguments */) {
|
||||
if (program.relative) {
|
||||
var part_path = path.join.apply(null, arguments);
|
||||
return path.relative(path.join(__dirname, '..'), path.resolve(process.cwd(), part_path));
|
||||
} else {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift(process.cwd());
|
||||
return path.resolve.apply(null, args);
|
||||
}
|
||||
};
|
||||
|
||||
if (all_js && !program.autoInject) {
|
||||
var all_modules = {};
|
||||
|
||||
// uses the first instance of the string 'requires local modules: '
|
||||
program.tests.forEach(function (testname) {
|
||||
var full_path = path.resolve(process.cwd(), testname);
|
||||
var content = fs.readFileSync(full_path).toString();
|
||||
var ind = content.indexOf('requires local modules: ');
|
||||
if (ind > -1) {
|
||||
ind += 'requires local modules: '.length;
|
||||
var eol = content.indexOf('\n', ind);
|
||||
var modules = content.slice(ind, eol).split(/,\s*/);
|
||||
modules.forEach(function (mod) {
|
||||
all_modules[get_path('include/', mod) + '.js'] = 1;
|
||||
});
|
||||
}
|
||||
|
||||
var fakes_ind = content.indexOf('requires test modules: ');
|
||||
if (fakes_ind > -1) {
|
||||
fakes_ind += 'requires test modules: '.length;
|
||||
var fakes_eol = content.indexOf('\n', fakes_ind);
|
||||
var fakes_modules = content.slice(fakes_ind, fakes_eol).split(/,\s*/);
|
||||
fakes_modules.forEach(function (mod) {
|
||||
all_modules[get_path('tests/', mod) + '.js'] = 1;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
program.autoInject = Object.keys(all_modules);
|
||||
}
|
||||
|
||||
if (program.autoInject) {
|
||||
var temp = require('temp');
|
||||
temp.track();
|
||||
|
||||
var template = {
|
||||
header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + get_path('node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>",
|
||||
script_tag: function(p) { return "<script src='" + p + "'></script>"; },
|
||||
footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__']);\nmocha.run(function () { window.__mocha_done = true; });\n</script>\n</body>\n</html>"
|
||||
};
|
||||
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/chai/chai.js'));
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/mocha/mocha.js'));
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/sinon/pkg/sinon.js'));
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||
template.header += "\n<script>mocha.setup('bdd');</script>";
|
||||
|
||||
|
||||
template.header = program.autoInject.reduce(function(acc, sn) {
|
||||
return acc + "\n" + template.script_tag(get_path_cwd(sn));
|
||||
}, template.header);
|
||||
|
||||
file_paths = program.tests.map(function(jsn, ind) {
|
||||
var templ = template.header;
|
||||
templ += "\n";
|
||||
templ += template.script_tag(get_path_cwd(jsn));
|
||||
templ += template.footer;
|
||||
|
||||
var tempfile = temp.openSync({ prefix: 'novnc-zombie-inject-', suffix: '-file_num-'+ind+'.html' });
|
||||
fs.writeSync(tempfile.fd, templ);
|
||||
fs.closeSync(tempfile.fd);
|
||||
return tempfile.path;
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
file_paths = program.tests.map(function(fn) {
|
||||
return path.resolve(process.cwd(), fn);
|
||||
});
|
||||
}
|
||||
|
||||
var use_ansi = false;
|
||||
if (program.color) use_ansi = true;
|
||||
else if (program.disableColor) use_ansi = false;
|
||||
else if (process.stdout.isTTY) use_ansi = true;
|
||||
|
||||
var cursor = ansi(process.stdout, { enabled: use_ansi });
|
||||
|
||||
if (program.outputHtml) {
|
||||
file_paths.forEach(function(path, path_ind) {
|
||||
fs.readFile(path, function(err, data) {
|
||||
if (err) {
|
||||
console.warn(error.stack);
|
||||
return;
|
||||
}
|
||||
|
||||
if (use_ansi) {
|
||||
cursor
|
||||
.bold()
|
||||
.write(program.tests[path_ind])
|
||||
.reset()
|
||||
.write("\n")
|
||||
.write(Array(program.tests[path_ind].length+1).join('='))
|
||||
.write("\n\n");
|
||||
}
|
||||
|
||||
cursor
|
||||
.write(data)
|
||||
.write("\n\n");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (program.generateHtml) {
|
||||
var open_browser;
|
||||
if (program.openInBrowser) {
|
||||
open_browser = require('open');
|
||||
}
|
||||
|
||||
file_paths.forEach(function(path, path_ind) {
|
||||
cursor
|
||||
.bold()
|
||||
.write(program.tests[path_ind])
|
||||
.write(": ")
|
||||
.reset()
|
||||
.write(path)
|
||||
.write("\n");
|
||||
|
||||
if (program.openInBrowser) {
|
||||
open_browser(path);
|
||||
}
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (program.generateHtml) {
|
||||
process.stdin.resume(); // pause until C-c
|
||||
process.on('SIGINT', function() {
|
||||
process.stdin.pause(); // exit
|
||||
});
|
||||
}
|
||||
|
||||
if (!program.outputHtml && !program.generateHtml) {
|
||||
var failure_count = 0;
|
||||
|
||||
var prov = require(path.resolve(__dirname, 'run_from_console.'+program.provider+'.js'));
|
||||
|
||||
cursor
|
||||
.write("Running tests ")
|
||||
.bold()
|
||||
.write(program.tests.join(', '))
|
||||
.reset()
|
||||
.grey()
|
||||
.write(' using provider '+prov.name)
|
||||
.reset()
|
||||
.write("\n");
|
||||
//console.log("Running tests %s using provider %s", program.tests.join(', '), prov.name);
|
||||
|
||||
var provider = prov.provide_emitter(file_paths, program.debugger);
|
||||
provider.on('test_ready', function(test_json) {
|
||||
console.log('');
|
||||
|
||||
filename = program.tests[test_json.file_ind];
|
||||
|
||||
cursor.bold();
|
||||
console.log('Results for %s:', filename);
|
||||
console.log(Array('Results for :'.length+filename.length+1).join('='));
|
||||
cursor.reset();
|
||||
|
||||
console.log('');
|
||||
|
||||
cursor
|
||||
.write(''+test_json.num_tests+' tests run, ')
|
||||
.green()
|
||||
.write(''+test_json.num_passes+' passed');
|
||||
if (test_json.num_slow > 0) {
|
||||
cursor
|
||||
.reset()
|
||||
.write(' (');
|
||||
cursor
|
||||
.yellow()
|
||||
.write(''+test_json.num_slow+' slow')
|
||||
.reset()
|
||||
.write(')');
|
||||
}
|
||||
cursor
|
||||
.reset()
|
||||
.write(', ');
|
||||
cursor
|
||||
.red()
|
||||
.write(''+test_json.num_fails+' failed');
|
||||
if (test_json.num_skipped > 0) {
|
||||
cursor
|
||||
.reset()
|
||||
.write(', ')
|
||||
.grey()
|
||||
.write(''+test_json.num_skipped+' skipped');
|
||||
}
|
||||
cursor
|
||||
.reset()
|
||||
.write(' -- duration: '+test_json.duration+"s\n");
|
||||
|
||||
console.log('');
|
||||
|
||||
if (test_json.num_fails > 0 || program.printAll) {
|
||||
var extract_error_lines = function (err) {
|
||||
// the split is to avoid a weird thing where in PhantomJS where we get a stack trace too
|
||||
var err_lines = err.split('\n');
|
||||
if (err_lines.length == 1) {
|
||||
return err_lines[0];
|
||||
} else {
|
||||
var ind;
|
||||
for (ind = 0; ind < err_lines.length; ind++) {
|
||||
var at_ind = err_lines[ind].trim().indexOf('at ');
|
||||
if (at_ind === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err_lines.slice(0, ind).join('\n');
|
||||
}
|
||||
};
|
||||
|
||||
var traverse_tree = function(indentation, node) {
|
||||
if (node.type == 'suite') {
|
||||
if (!node.has_subfailures && !program.printAll) return;
|
||||
|
||||
if (indentation === 0) {
|
||||
cursor.bold();
|
||||
console.log(node.name);
|
||||
console.log(Array(node.name.length+1).join('-'));
|
||||
cursor.reset();
|
||||
}
|
||||
else {
|
||||
cursor
|
||||
.write(Array(indentation+3).join('#'))
|
||||
.bold()
|
||||
.write(' '+node.name+' ')
|
||||
.reset()
|
||||
.write(Array(indentation+3).join('#'))
|
||||
.write("\n");
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
traverse_tree(indentation+1, node.children[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!node.pass) {
|
||||
cursor.magenta();
|
||||
console.log('- failed: '+node.text+test_json.replay);
|
||||
cursor.red();
|
||||
console.log(' '+extract_error_lines(node.error));
|
||||
cursor.reset();
|
||||
console.log('');
|
||||
}
|
||||
else if (program.printAll) {
|
||||
if (node.skipped) {
|
||||
cursor
|
||||
.grey()
|
||||
.write('- skipped: '+node.text);
|
||||
}
|
||||
else {
|
||||
if (node.slow) cursor.yellow();
|
||||
else cursor.green();
|
||||
|
||||
cursor
|
||||
.write('- pass: '+node.text)
|
||||
.grey()
|
||||
.write(' ('+node.duration+') ');
|
||||
}
|
||||
/*if (node.slow) cursor.yellow();
|
||||
else cursor.green();*/
|
||||
cursor
|
||||
//.write(test_json.replay)
|
||||
.reset()
|
||||
.write("\n");
|
||||
console.log('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < test_json.suites.length; i++) {
|
||||
traverse_tree(0, test_json.suites[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (test_json.num_fails === 0) {
|
||||
cursor.fg.green();
|
||||
console.log('all tests passed :-)');
|
||||
cursor.reset();
|
||||
}
|
||||
});
|
||||
|
||||
if (program.debug) {
|
||||
provider.on('console', function(line) {
|
||||
// log to stderr
|
||||
console.error(line);
|
||||
});
|
||||
}
|
||||
|
||||
provider.on('error', function(line) {
|
||||
// log to stderr
|
||||
console.error('ERROR: ' + line);
|
||||
});
|
||||
|
||||
/*gprom.finally(function(ph) {
|
||||
ph.exit();
|
||||
// exit with a status code that actually gives information
|
||||
if (program.exitWithFailureCount) process.exit(failure_count);
|
||||
});*/
|
||||
}
|
||||
82
tests/run_from_console.zombie.js
Normal file
@@ -0,0 +1,82 @@
|
||||
var Browser = require('zombie');
|
||||
var path = require('path');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Q = require('q');
|
||||
|
||||
var provide_emitter = function(file_paths) {
|
||||
var emitter = new EventEmitter();
|
||||
|
||||
file_paths.reduce(function(prom, file_path, path_ind) {
|
||||
return prom.then(function(browser) {
|
||||
browser.visit('file://'+file_path, function() {
|
||||
if (browser.error) throw new Error(browser.errors);
|
||||
|
||||
var res_json = {};
|
||||
res_json.file_ind = path_ind;
|
||||
|
||||
res_json.num_tests = browser.querySelectorAll('li.test').length;
|
||||
res_json.num_fails = browser.querySelectorAll('li.test.fail').length;
|
||||
res_json.num_passes = browser.querySelectorAll('li.test.pass').length;
|
||||
res_json.num_slow = browser.querySelectorAll('li.test.pass:not(.fast)').length;
|
||||
res_json.num_skipped = browser.querySelectorAll('li.test.pending').length;
|
||||
res_json.duration = browser.text('li.duration em');
|
||||
|
||||
var traverse_node = function(elem) {
|
||||
var classList = elem.className.split(' ');
|
||||
var res;
|
||||
if (classList.indexOf('suite') > -1) {
|
||||
res = {
|
||||
type: 'suite',
|
||||
name: elem.querySelector('h1').textContent,
|
||||
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0
|
||||
};
|
||||
|
||||
var child_elems = elem.querySelector('ul').children;
|
||||
res.children = Array.prototype.map.call(child_elems, traverse_node);
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
var h2_content = elem.querySelector('h2').childNodes;
|
||||
res = {
|
||||
type: 'test',
|
||||
text: h2_content[0].textContent
|
||||
};
|
||||
|
||||
if (classList.indexOf('pass') > -1) {
|
||||
res.pass = true;
|
||||
if (classList.indexOf('pending') > -1) {
|
||||
res.slow = false;
|
||||
res.skipped = true;
|
||||
}
|
||||
else {
|
||||
res.slow = classList.indexOf('fast') < 0;
|
||||
res.skipped = false;
|
||||
res.duration = h2_content[1].textContent;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.error = elem.querySelector('pre.error').textContent;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
var top_suites = browser.querySelectorAll('#mocha-report > li.suite');
|
||||
res_json.suites = Array.prototype.map.call(top_suites, traverse_node);
|
||||
res_json.replay = browser.querySelector('a.replay').textContent;
|
||||
|
||||
emitter.emit('test_ready', res_json);
|
||||
});
|
||||
|
||||
return new Browser();
|
||||
});
|
||||
}, Q(new Browser()));
|
||||
|
||||
return emitter;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
provide_emitter: provide_emitter,
|
||||
name: 'ZombieJS'
|
||||
};
|
||||
33
tests/test.base64.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// requires local modules: base64
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Base64 Tools', function() {
|
||||
"use strict";
|
||||
|
||||
var BIN_ARR = new Array(256);
|
||||
for (var i = 0; i < 256; i++) {
|
||||
BIN_ARR[i] = i;
|
||||
}
|
||||
|
||||
var B64_STR = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==";
|
||||
|
||||
|
||||
describe('encode', function() {
|
||||
it('should encode a binary string into Base64', function() {
|
||||
var encoded = Base64.encode(BIN_ARR);
|
||||
expect(encoded).to.equal(B64_STR);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decode', function() {
|
||||
it('should decode a Base64 string into a normal string', function() {
|
||||
var decoded = Base64.decode(B64_STR);
|
||||
expect(decoded).to.deep.equal(BIN_ARR);
|
||||
});
|
||||
|
||||
it('should throw an error if we have extra characters at the end of the string', function() {
|
||||
expect(function () { Base64.decode(B64_STR+'abcdef'); }).to.throw(Error);
|
||||
});
|
||||
});
|
||||
});
|
||||
461
tests/test.display.js
Normal file
@@ -0,0 +1,461 @@
|
||||
// requires local modules: util, base64, display
|
||||
// requires test modules: assertions
|
||||
/* jshint expr: true */
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Display/Canvas Helper', function () {
|
||||
var checked_data = [
|
||||
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
||||
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
||||
0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
|
||||
0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
|
||||
];
|
||||
checked_data = new Uint8Array(checked_data);
|
||||
|
||||
var basic_data = [0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255];
|
||||
basic_data = new Uint8Array(basic_data);
|
||||
|
||||
function make_image_canvas (input_data) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = 4;
|
||||
canvas.height = 4;
|
||||
var ctx = canvas.getContext('2d');
|
||||
var data = ctx.createImageData(4, 4);
|
||||
for (var i = 0; i < checked_data.length; i++) { data.data[i] = input_data[i]; }
|
||||
ctx.putImageData(data, 0, 0);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
describe('checking for cursor uri support', function () {
|
||||
beforeEach(function () {
|
||||
this._old_browser_supports_cursor_uris = Util.browserSupportsCursorURIs;
|
||||
});
|
||||
|
||||
it('should disable cursor URIs if there is no support', function () {
|
||||
Util.browserSupportsCursorURIs = function () { return false; };
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
|
||||
expect(display._cursor_uri).to.be.false;
|
||||
});
|
||||
|
||||
it('should enable cursor URIs if there is support', function () {
|
||||
Util.browserSupportsCursorURIs = function () { return true; };
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
|
||||
expect(display._cursor_uri).to.be.true;
|
||||
});
|
||||
|
||||
it('respect the cursor_uri option if there is support', function () {
|
||||
Util.browserSupportsCursorURIs = function () { return false; };
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false, cursor_uri: false });
|
||||
expect(display._cursor_uri).to.be.false;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Util.browserSupportsCursorURIs = this._old_browser_supports_cursor_uris;
|
||||
});
|
||||
});
|
||||
|
||||
describe('viewport handling', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display.resize(5, 5);
|
||||
display.viewportChangeSize(3, 3);
|
||||
display.viewportChangePos(1, 1);
|
||||
display.getCleanDirtyReset();
|
||||
});
|
||||
|
||||
it('should take viewport location into consideration when drawing images', function () {
|
||||
display.set_width(4);
|
||||
display.set_height(4);
|
||||
display.viewportChangeSize(2, 2);
|
||||
display.drawImage(make_image_canvas(basic_data), 1, 1);
|
||||
|
||||
var expected = new Uint8Array(16);
|
||||
var i;
|
||||
for (i = 0; i < 8; i++) { expected[i] = basic_data[i]; }
|
||||
for (i = 8; i < 16; i++) { expected[i] = 0; }
|
||||
expect(display).to.have.displayed(expected);
|
||||
});
|
||||
|
||||
it('should redraw the left side when shifted left', function () {
|
||||
display.viewportChangePos(-1, 0);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 2, h: 3 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 1, w: 2, h: 3 });
|
||||
});
|
||||
|
||||
it('should redraw the right side when shifted right', function () {
|
||||
display.viewportChangePos(1, 0);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 2, y: 1, w: 2, h: 3 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 4, y: 1, w: 1, h: 3 });
|
||||
});
|
||||
|
||||
it('should redraw the top part when shifted up', function () {
|
||||
display.viewportChangePos(0, -1);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 1, w: 3, h: 2 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 0, w: 3, h: 1 });
|
||||
});
|
||||
|
||||
it('should redraw the bottom part when shifted down', function () {
|
||||
display.viewportChangePos(0, 1);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 2 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 1, y: 4, w: 3, h: 1 });
|
||||
});
|
||||
|
||||
it('should reset the entire viewport to being clean after calculating the clean/dirty boxes', function () {
|
||||
display.viewportChangePos(0, 1);
|
||||
var cdr1 = display.getCleanDirtyReset();
|
||||
var cdr2 = display.getCleanDirtyReset();
|
||||
expect(cdr1).to.not.deep.equal(cdr2);
|
||||
expect(cdr2.cleanBox).to.deep.equal({ x: 1, y: 2, w: 3, h: 3 });
|
||||
expect(cdr2.dirtyBoxes).to.be.empty;
|
||||
});
|
||||
|
||||
it('should simply mark the whole display area as dirty if not using viewports', function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: false });
|
||||
display.resize(5, 5);
|
||||
var cdr = display.getCleanDirtyReset();
|
||||
expect(cdr.cleanBox).to.deep.equal({ x: 0, y: 0, w: 0, h: 0 });
|
||||
expect(cdr.dirtyBoxes).to.have.length(1);
|
||||
expect(cdr.dirtyBoxes[0]).to.deep.equal({ x: 0, y: 0, w: 5, h: 5 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('clipping', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display.resize(4, 3);
|
||||
});
|
||||
|
||||
it('should report true when no max-size and framebuffer > viewport', function () {
|
||||
display.viewportChangeSize(2,2);
|
||||
var clipping = display.clippingDisplay();
|
||||
expect(clipping).to.be.true;
|
||||
});
|
||||
|
||||
it('should report false when no max-size and framebuffer = viewport', function () {
|
||||
var clipping = display.clippingDisplay();
|
||||
expect(clipping).to.be.false;
|
||||
});
|
||||
|
||||
it('should report true when viewport > max-size and framebuffer > viewport', function () {
|
||||
display.viewportChangeSize(2,2);
|
||||
display.set_maxWidth(1);
|
||||
display.set_maxHeight(2);
|
||||
var clipping = display.clippingDisplay();
|
||||
expect(clipping).to.be.true;
|
||||
});
|
||||
|
||||
it('should report true when viewport > max-size and framebuffer = viewport', function () {
|
||||
display.set_maxWidth(1);
|
||||
display.set_maxHeight(2);
|
||||
var clipping = display.clippingDisplay();
|
||||
expect(clipping).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('resizing', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display.resize(4, 3);
|
||||
});
|
||||
|
||||
it('should change the size of the logical canvas', function () {
|
||||
display.resize(5, 7);
|
||||
expect(display._fb_width).to.equal(5);
|
||||
expect(display._fb_height).to.equal(7);
|
||||
});
|
||||
|
||||
it('should update the viewport dimensions', function () {
|
||||
sinon.spy(display, 'viewportChangeSize');
|
||||
display.resize(2, 2);
|
||||
expect(display.viewportChangeSize).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('rescaling', function () {
|
||||
var display;
|
||||
var canvas;
|
||||
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display.resize(4, 3);
|
||||
canvas = display.get_target();
|
||||
document.body.appendChild(canvas);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
document.body.removeChild(canvas);
|
||||
});
|
||||
|
||||
it('should not change the bitmap size of the canvas', function () {
|
||||
display.set_scale(0.5);
|
||||
expect(canvas.width).to.equal(4);
|
||||
expect(canvas.height).to.equal(3);
|
||||
});
|
||||
|
||||
it('should change the effective rendered size of the canvas', function () {
|
||||
display.set_scale(0.5);
|
||||
expect(canvas.clientWidth).to.equal(2);
|
||||
expect(canvas.clientHeight).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('autoscaling', function () {
|
||||
var display;
|
||||
var canvas;
|
||||
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false, viewport: true });
|
||||
display.resize(4, 3);
|
||||
canvas = display.get_target();
|
||||
document.body.appendChild(canvas);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
document.body.removeChild(canvas);
|
||||
});
|
||||
|
||||
it('should preserve aspect ratio while autoscaling', function () {
|
||||
display.autoscale(16, 9);
|
||||
expect(canvas.clientWidth / canvas.clientHeight).to.equal(4 / 3);
|
||||
});
|
||||
|
||||
it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
|
||||
expect(display.autoscale(9, 16)).to.equal(9 / 4);
|
||||
expect(canvas.clientWidth).to.equal(9);
|
||||
expect(canvas.clientHeight).to.equal(7); // round 9 / (4 / 3)
|
||||
});
|
||||
|
||||
it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
|
||||
expect(display.autoscale(16, 9)).to.equal(3); // 9 / 3
|
||||
expect(canvas.clientWidth).to.equal(12); // 16 * (4 / 3)
|
||||
expect(canvas.clientHeight).to.equal(9);
|
||||
|
||||
});
|
||||
|
||||
it('should not change the bitmap size of the canvas', function () {
|
||||
display.autoscale(16, 9);
|
||||
expect(canvas.width).to.equal(4);
|
||||
expect(canvas.height).to.equal(3);
|
||||
});
|
||||
|
||||
it('should not upscale when downscaleOnly is true', function () {
|
||||
expect(display.autoscale(2, 2, true)).to.equal(0.5);
|
||||
expect(canvas.clientWidth).to.equal(2);
|
||||
expect(canvas.clientHeight).to.equal(2);
|
||||
|
||||
expect(display.autoscale(16, 9, true)).to.equal(1.0);
|
||||
expect(canvas.clientWidth).to.equal(4);
|
||||
expect(canvas.clientHeight).to.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('drawing', function () {
|
||||
|
||||
// TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
|
||||
// basic cases
|
||||
function drawing_tests (pref_js) {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: pref_js });
|
||||
display.resize(4, 4);
|
||||
});
|
||||
|
||||
it('should clear the screen on #clear without a logo set', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
|
||||
display._logo = null;
|
||||
display.clear();
|
||||
display.resize(4, 4);
|
||||
var empty = [];
|
||||
for (var i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
|
||||
expect(display).to.have.displayed(new Uint8Array(empty));
|
||||
});
|
||||
|
||||
it('should draw the logo on #clear with a logo set', function (done) {
|
||||
display._logo = { width: 4, height: 4, data: make_image_canvas(checked_data).toDataURL() };
|
||||
display._drawCtx._act_drawImg = display._drawCtx.drawImage;
|
||||
display._drawCtx.drawImage = function (img, x, y) {
|
||||
this._act_drawImg(img, x, y);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
done();
|
||||
};
|
||||
display.clear();
|
||||
expect(display._fb_width).to.equal(4);
|
||||
expect(display._fb_height).to.equal(4);
|
||||
});
|
||||
|
||||
it('should support filling a rectangle with particular color via #fillRect', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support copying an portion of the canvas via #copyImage', function () {
|
||||
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
|
||||
display.copyImage(0, 0, 2, 2, 2, 2);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing tile data with a background color and sub tiles', function () {
|
||||
display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
|
||||
display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
|
||||
display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
|
||||
display.finishTile();
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing BGRX blit images with true color via #blitImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 4] = checked_data[i * 4 + 2];
|
||||
data[i * 4 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 4 + 2] = checked_data[i * 4];
|
||||
data[i * 4 + 3] = checked_data[i * 4 + 3];
|
||||
}
|
||||
display.blitImage(0, 0, 4, 4, data, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
|
||||
var data = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
data[i * 3] = checked_data[i * 4];
|
||||
data[i * 3 + 1] = checked_data[i * 4 + 1];
|
||||
data[i * 3 + 2] = checked_data[i * 4 + 2];
|
||||
}
|
||||
display.blitRgbImage(0, 0, 4, 4, data, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing blit images from a data URL via #blitStringImage', function (done) {
|
||||
var img_url = make_image_canvas(checked_data).toDataURL();
|
||||
display._drawCtx._act_drawImg = display._drawCtx.drawImage;
|
||||
display._drawCtx.drawImage = function (img, x, y) {
|
||||
this._act_drawImg(img, x, y);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
done();
|
||||
};
|
||||
display.blitStringImage(img_url, 0, 0);
|
||||
});
|
||||
|
||||
it('should support drawing solid colors with color maps', function () {
|
||||
display._true_color = false;
|
||||
display.set_colourMap({ 0: [0xff, 0, 0], 1: [0, 0xff, 0] });
|
||||
display.fillRect(0, 0, 4, 4, 1);
|
||||
display.fillRect(0, 0, 2, 2, 0);
|
||||
display.fillRect(2, 2, 2, 2, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing blit images with color maps', function () {
|
||||
display._true_color = false;
|
||||
display.set_colourMap({ 1: [0xff, 0, 0], 0: [0, 0xff, 0] });
|
||||
var data = [1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1].map(function (elem) { return [elem]; });
|
||||
display.blitImage(0, 0, 4, 4, data, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
|
||||
it('should support drawing an image object via #drawImage', function () {
|
||||
var img = make_image_canvas(checked_data);
|
||||
display.drawImage(img, 0, 0);
|
||||
expect(display).to.have.displayed(checked_data);
|
||||
});
|
||||
}
|
||||
|
||||
describe('(prefering native methods)', function () { drawing_tests.call(this, false); });
|
||||
describe('(prefering JavaScript)', function () { drawing_tests.call(this, true); });
|
||||
});
|
||||
|
||||
describe('the render queue processor', function () {
|
||||
var display;
|
||||
beforeEach(function () {
|
||||
display = new Display({ target: document.createElement('canvas'), prefer_js: false });
|
||||
display.resize(4, 4);
|
||||
sinon.spy(display, '_scan_renderQ');
|
||||
this.old_requestAnimFrame = window.requestAnimFrame;
|
||||
window.requestAnimFrame = function (cb) {
|
||||
this.next_frame_cb = cb;
|
||||
}.bind(this);
|
||||
this.next_frame = function () { this.next_frame_cb(); };
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
window.requestAnimFrame = this.old_requestAnimFrame;
|
||||
});
|
||||
|
||||
it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
|
||||
display.renderQ_push({ type: 'noop' }); // does nothing
|
||||
expect(display._scan_renderQ).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
|
||||
display._renderQ.length = 2;
|
||||
display.renderQ_push({ type: 'noop' });
|
||||
expect(display._scan_renderQ).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
|
||||
var img = { complete: false };
|
||||
display._renderQ = [{ type: 'img', x: 3, y: 4, img: img },
|
||||
{ type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
|
||||
display.drawImage = sinon.spy();
|
||||
display.fillRect = sinon.spy();
|
||||
|
||||
display._scan_renderQ();
|
||||
expect(display.drawImage).to.not.have.been.called;
|
||||
expect(display.fillRect).to.not.have.been.called;
|
||||
|
||||
display._renderQ[0].img.complete = true;
|
||||
this.next_frame();
|
||||
expect(display.drawImage).to.have.been.calledOnce;
|
||||
expect(display.fillRect).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should draw a blit image on type "blit"', function () {
|
||||
display.blitImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
|
||||
expect(display.blitImage).to.have.been.calledOnce;
|
||||
expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
|
||||
});
|
||||
|
||||
it('should draw a blit RGB image on type "blitRgb"', function () {
|
||||
display.blitRgbImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
|
||||
expect(display.blitRgbImage).to.have.been.calledOnce;
|
||||
expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
|
||||
});
|
||||
|
||||
it('should copy a region on type "copy"', function () {
|
||||
display.copyImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 });
|
||||
expect(display.copyImage).to.have.been.calledOnce;
|
||||
expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6);
|
||||
});
|
||||
|
||||
it('should fill a rect with a given color on type "fill"', function () {
|
||||
display.fillRect = sinon.spy();
|
||||
display.renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]});
|
||||
expect(display.fillRect).to.have.been.calledOnce;
|
||||
expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]);
|
||||
});
|
||||
|
||||
it('should draw an image from an image object on type "img" (if complete)', function () {
|
||||
display.drawImage = sinon.spy();
|
||||
display.renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } });
|
||||
expect(display.drawImage).to.have.been.calledOnce;
|
||||
expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
|
||||
});
|
||||
});
|
||||
});
|
||||
262
tests/test.helper.js
Normal file
@@ -0,0 +1,262 @@
|
||||
// requires local modules: keysym, keysymdef, keyboard
|
||||
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Helpers', function() {
|
||||
"use strict";
|
||||
describe('keysymFromKeyCode', function() {
|
||||
it('should map known keycodes to keysyms', function() {
|
||||
expect(kbdUtil.keysymFromKeyCode(0x41, false), 'a').to.be.equal(0x61);
|
||||
expect(kbdUtil.keysymFromKeyCode(0x41, true), 'A').to.be.equal(0x41);
|
||||
expect(kbdUtil.keysymFromKeyCode(0xd, false), 'enter').to.be.equal(0xFF0D);
|
||||
expect(kbdUtil.keysymFromKeyCode(0x11, false), 'ctrl').to.be.equal(0xFFE3);
|
||||
expect(kbdUtil.keysymFromKeyCode(0x12, false), 'alt').to.be.equal(0xFFE9);
|
||||
expect(kbdUtil.keysymFromKeyCode(0xe1, false), 'altgr').to.be.equal(0xFE03);
|
||||
expect(kbdUtil.keysymFromKeyCode(0x1b, false), 'esc').to.be.equal(0xFF1B);
|
||||
expect(kbdUtil.keysymFromKeyCode(0x26, false), 'up').to.be.equal(0xFF52);
|
||||
});
|
||||
it('should return null for unknown keycodes', function() {
|
||||
expect(kbdUtil.keysymFromKeyCode(0xc0, false), 'DK æ').to.be.null;
|
||||
expect(kbdUtil.keysymFromKeyCode(0xde, false), 'DK ø').to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe('keysyms.fromUnicode', function() {
|
||||
it('should map ASCII characters to keysyms', function() {
|
||||
expect(keysyms.fromUnicode('a'.charCodeAt())).to.have.property('keysym', 0x61);
|
||||
expect(keysyms.fromUnicode('A'.charCodeAt())).to.have.property('keysym', 0x41);
|
||||
});
|
||||
it('should map Latin-1 characters to keysyms', function() {
|
||||
expect(keysyms.fromUnicode('ø'.charCodeAt())).to.have.property('keysym', 0xf8);
|
||||
|
||||
expect(keysyms.fromUnicode('é'.charCodeAt())).to.have.property('keysym', 0xe9);
|
||||
});
|
||||
it('should map characters that are in Windows-1252 but not in Latin-1 to keysyms', function() {
|
||||
expect(keysyms.fromUnicode('Š'.charCodeAt())).to.have.property('keysym', 0x01a9);
|
||||
});
|
||||
it('should map characters which aren\'t in Latin1 *or* Windows-1252 to keysyms', function() {
|
||||
expect(keysyms.fromUnicode('ŵ'.charCodeAt())).to.have.property('keysym', 0x1000175);
|
||||
});
|
||||
it('should return undefined for unknown codepoints', function() {
|
||||
expect(keysyms.fromUnicode('\n'.charCodeAt())).to.be.undefined;
|
||||
expect(keysyms.fromUnicode('\u1F686'.charCodeAt())).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('substituteCodepoint', function() {
|
||||
it('should replace characters which don\'t have a keysym', function() {
|
||||
expect(kbdUtil.substituteCodepoint('Ș'.charCodeAt())).to.equal('Ş'.charCodeAt());
|
||||
expect(kbdUtil.substituteCodepoint('ș'.charCodeAt())).to.equal('ş'.charCodeAt());
|
||||
expect(kbdUtil.substituteCodepoint('Ț'.charCodeAt())).to.equal('Ţ'.charCodeAt());
|
||||
expect(kbdUtil.substituteCodepoint('ț'.charCodeAt())).to.equal('ţ'.charCodeAt());
|
||||
});
|
||||
it('should pass other characters through unchanged', function() {
|
||||
expect(kbdUtil.substituteCodepoint('T'.charCodeAt())).to.equal('T'.charCodeAt());
|
||||
});
|
||||
});
|
||||
|
||||
describe('nonCharacterKey', function() {
|
||||
it('should recognize the right keys', function() {
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 0xd}), 'enter').to.be.defined;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 0x08}), 'backspace').to.be.defined;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 0x09}), 'tab').to.be.defined;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 0x10}), 'shift').to.be.defined;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 0x11}), 'ctrl').to.be.defined;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 0x12}), 'alt').to.be.defined;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 0xe0}), 'meta').to.be.defined;
|
||||
});
|
||||
it('should not recognize character keys', function() {
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: 'A'}), 'A').to.be.null;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: '1'}), '1').to.be.null;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: '.'}), '.').to.be.null;
|
||||
expect(kbdUtil.nonCharacterKey({keyCode: ' '}), 'space').to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe('getKeysym', function() {
|
||||
it('should prefer char', function() {
|
||||
expect(kbdUtil.getKeysym({char : 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x61);
|
||||
});
|
||||
it('should use charCode if no char', function() {
|
||||
expect(kbdUtil.getKeysym({char : '', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
|
||||
expect(kbdUtil.getKeysym({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
|
||||
expect(kbdUtil.getKeysym({char : 'hello', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
|
||||
});
|
||||
it('should use keyCode if no charCode', function() {
|
||||
expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: false})).to.have.property('keysym', 0x62);
|
||||
expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: true})).to.have.property('keysym', 0x42);
|
||||
});
|
||||
it('should use which if no keyCode', function() {
|
||||
expect(kbdUtil.getKeysym({which: 0x43, shiftKey: false})).to.have.property('keysym', 0x63);
|
||||
expect(kbdUtil.getKeysym({which: 0x43, shiftKey: true})).to.have.property('keysym', 0x43);
|
||||
});
|
||||
it('should substitute where applicable', function() {
|
||||
expect(kbdUtil.getKeysym({char : 'Ș'})).to.have.property('keysym', 0x1aa);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Modifier Sync', function() { // return a list of fake events necessary to fix modifier state
|
||||
describe('Toggle all modifiers', function() {
|
||||
var sync = kbdUtil.ModifierSync();
|
||||
it ('should do nothing if all modifiers are up as expected', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
altGraphKey: false,
|
||||
shiftKey: false,
|
||||
metaKey: false})
|
||||
).to.have.lengthOf(0);
|
||||
});
|
||||
it ('should synthesize events if all keys are unexpectedly down', function() {
|
||||
var result = sync.keydown({
|
||||
keyCode: 0x41,
|
||||
ctrlKey: true,
|
||||
altKey: true,
|
||||
altGraphKey: true,
|
||||
shiftKey: true,
|
||||
metaKey: true
|
||||
});
|
||||
expect(result).to.have.lengthOf(5);
|
||||
var keysyms = {};
|
||||
for (var i = 0; i < result.length; ++i) {
|
||||
keysyms[result[i].keysym] = (result[i].type == 'keydown');
|
||||
}
|
||||
expect(keysyms[0xffe3]);
|
||||
expect(keysyms[0xffe9]);
|
||||
expect(keysyms[0xfe03]);
|
||||
expect(keysyms[0xffe1]);
|
||||
expect(keysyms[0xffe7]);
|
||||
});
|
||||
it ('should do nothing if all modifiers are down as expected', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
ctrlKey: true,
|
||||
altKey: true,
|
||||
altGraphKey: true,
|
||||
shiftKey: true,
|
||||
metaKey: true
|
||||
})).to.have.lengthOf(0);
|
||||
});
|
||||
});
|
||||
describe('Toggle Ctrl', function() {
|
||||
var sync = kbdUtil.ModifierSync();
|
||||
it('should sync if modifier is suddenly down', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
ctrlKey: true,
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keydown'}]);
|
||||
});
|
||||
it('should sync if modifier is suddenly up', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
ctrlKey: false
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keyup'}]);
|
||||
});
|
||||
});
|
||||
describe('Toggle Alt', function() {
|
||||
var sync = kbdUtil.ModifierSync();
|
||||
it('should sync if modifier is suddenly down', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
altKey: true,
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
|
||||
});
|
||||
it('should sync if modifier is suddenly up', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
altKey: false
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keyup'}]);
|
||||
});
|
||||
});
|
||||
describe('Toggle AltGr', function() {
|
||||
var sync = kbdUtil.ModifierSync();
|
||||
it('should sync if modifier is suddenly down', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
altGraphKey: true,
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keydown'}]);
|
||||
});
|
||||
it('should sync if modifier is suddenly up', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
altGraphKey: false
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keyup'}]);
|
||||
});
|
||||
});
|
||||
describe('Toggle Shift', function() {
|
||||
var sync = kbdUtil.ModifierSync();
|
||||
it('should sync if modifier is suddenly down', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
shiftKey: true,
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keydown'}]);
|
||||
});
|
||||
it('should sync if modifier is suddenly up', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
shiftKey: false
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keyup'}]);
|
||||
});
|
||||
});
|
||||
describe('Toggle Meta', function() {
|
||||
var sync = kbdUtil.ModifierSync();
|
||||
it('should sync if modifier is suddenly down', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
metaKey: true,
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keydown'}]);
|
||||
});
|
||||
it('should sync if modifier is suddenly up', function() {
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
metaKey: false
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keyup'}]);
|
||||
});
|
||||
});
|
||||
describe('Modifier keyevents', function() {
|
||||
it('should not sync a modifier on its own events', function() {
|
||||
expect(kbdUtil.ModifierSync().keydown({
|
||||
keyCode: 0x11,
|
||||
ctrlKey: false
|
||||
})).to.be.deep.equal([]);
|
||||
expect(kbdUtil.ModifierSync().keydown({
|
||||
keyCode: 0x11,
|
||||
ctrlKey: true
|
||||
}), 'B').to.be.deep.equal([]);
|
||||
})
|
||||
it('should update state on modifier keyevents', function() {
|
||||
var sync = kbdUtil.ModifierSync();
|
||||
sync.keydown({
|
||||
keyCode: 0x11,
|
||||
});
|
||||
expect(sync.keydown({
|
||||
keyCode: 0x41,
|
||||
ctrlKey: true,
|
||||
})).to.be.deep.equal([]);
|
||||
});
|
||||
it('should sync other modifiers on ctrl events', function() {
|
||||
expect(kbdUtil.ModifierSync().keydown({
|
||||
keyCode: 0x11,
|
||||
altKey: true
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
|
||||
})
|
||||
});
|
||||
describe('sync modifiers on non-key events', function() {
|
||||
it('should generate sync events when receiving non-keyboard events', function() {
|
||||
expect(kbdUtil.ModifierSync().syncAny({
|
||||
altKey: true
|
||||
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
|
||||
});
|
||||
});
|
||||
describe('do not treat shift as a modifier key', function() {
|
||||
it('should not treat shift as a shortcut modifier', function() {
|
||||
expect(kbdUtil.hasShortcutModifier([], {0xffe1 : true})).to.be.false;
|
||||
});
|
||||
it('should not treat shift as a char modifier', function() {
|
||||
expect(kbdUtil.hasCharModifier([], {0xffe1 : true})).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
842
tests/test.keyboard.js
Normal file
@@ -0,0 +1,842 @@
|
||||
// requires local modules: input, keyboard, keysymdef
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
/* jshint newcap: false, expr: true */
|
||||
describe('Key Event Pipeline Stages', function() {
|
||||
"use strict";
|
||||
describe('Decode Keyboard Events', function() {
|
||||
it('should pass events to the next stage', function(done) {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.an.object;
|
||||
done();
|
||||
}).keydown({keyCode: 0x41});
|
||||
});
|
||||
it('should pass the right keysym through', function(done) {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt.keysym).to.be.deep.equal(keysyms.lookup(0x61));
|
||||
done();
|
||||
}).keypress({keyCode: 0x41});
|
||||
});
|
||||
it('should pass the right keyid through', function(done) {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.have.property('keyId', 0x41);
|
||||
done();
|
||||
}).keydown({keyCode: 0x41});
|
||||
});
|
||||
it('should not sync modifiers on a keypress', function() {
|
||||
// Firefox provides unreliable modifier state on keypress events
|
||||
var count = 0;
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
++count;
|
||||
}).keypress({keyCode: 0x41, ctrlKey: true});
|
||||
expect(count).to.be.equal(1);
|
||||
});
|
||||
it('should sync modifiers if necessary', function(done) {
|
||||
var count = 0;
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
switch (count) {
|
||||
case 0: // fake a ctrl keydown
|
||||
expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xffe3), type: 'keydown'});
|
||||
++count;
|
||||
break;
|
||||
case 1:
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown', keysym: keysyms.lookup(0x61)});
|
||||
done();
|
||||
break;
|
||||
}
|
||||
}).keydown({keyCode: 0x41, ctrlKey: true});
|
||||
});
|
||||
it('should forward keydown events with the right type', function(done) {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
|
||||
done();
|
||||
}).keydown({keyCode: 0x41});
|
||||
});
|
||||
it('should forward keyup events with the right type', function(done) {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'});
|
||||
done();
|
||||
}).keyup({keyCode: 0x41});
|
||||
});
|
||||
it('should forward keypress events with the right type', function(done) {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'});
|
||||
done();
|
||||
}).keypress({keyCode: 0x41});
|
||||
});
|
||||
it('should generate stalls if a char modifier is down while a key is pressed', function(done) {
|
||||
var count = 0;
|
||||
KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
|
||||
switch (count) {
|
||||
case 0: // fake altgr
|
||||
expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xfe03), type: 'keydown'});
|
||||
++count;
|
||||
break;
|
||||
case 1: // stall before processing the 'a' keydown
|
||||
expect(evt).to.be.deep.equal({type: 'stall'});
|
||||
++count;
|
||||
break;
|
||||
case 2: // 'a'
|
||||
expect(evt).to.be.deep.equal({
|
||||
type: 'keydown',
|
||||
keyId: 0x41,
|
||||
keysym: keysyms.lookup(0x61)
|
||||
});
|
||||
|
||||
done();
|
||||
break;
|
||||
}
|
||||
}).keydown({keyCode: 0x41, altGraphKey: true});
|
||||
|
||||
});
|
||||
describe('suppress the right events at the right time', function() {
|
||||
it('should suppress anything while a shortcut modifier is down', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
|
||||
|
||||
obj.keydown({keyCode: 0x11}); // press ctrl
|
||||
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.true;
|
||||
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.true;
|
||||
expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.true;
|
||||
expect(obj.keydown({keyCode: 0x3c})).to.be.true; // < key on DK Windows
|
||||
expect(obj.keydown({keyCode: 0xde})).to.be.true; // Ø key on DK
|
||||
});
|
||||
it('should suppress non-character keys', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
|
||||
|
||||
expect(obj.keydown({keyCode: 0x08}), 'a').to.be.true;
|
||||
expect(obj.keydown({keyCode: 0x09}), 'b').to.be.true;
|
||||
expect(obj.keydown({keyCode: 0x11}), 'd').to.be.true;
|
||||
expect(obj.keydown({keyCode: 0x12}), 'e').to.be.true;
|
||||
});
|
||||
it('should not suppress shift', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
|
||||
|
||||
expect(obj.keydown({keyCode: 0x10}), 'd').to.be.false;
|
||||
});
|
||||
it('should generate event for shift keydown', function() {
|
||||
var called = false;
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.have.property('keysym');
|
||||
called = true;
|
||||
}).keydown({keyCode: 0x10});
|
||||
expect(called).to.be.true;
|
||||
});
|
||||
it('should not suppress character keys', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
|
||||
|
||||
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
|
||||
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
|
||||
expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false;
|
||||
expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows
|
||||
expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK
|
||||
});
|
||||
it('should not suppress if a char modifier is down', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {});
|
||||
|
||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
||||
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
|
||||
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
|
||||
expect(obj.keydown({keyCode: '1'.charCodeAt()})).to.be.false;
|
||||
expect(obj.keydown({keyCode: 0x3c})).to.be.false; // < key on DK Windows
|
||||
expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK
|
||||
});
|
||||
});
|
||||
describe('Keypress and keyup events', function() {
|
||||
it('should always suppress event propagation', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
|
||||
|
||||
expect(obj.keypress({keyCode: 'A'.charCodeAt()})).to.be.true;
|
||||
expect(obj.keypress({keyCode: 0x3c})).to.be.true; // < key on DK Windows
|
||||
expect(obj.keypress({keyCode: 0x11})).to.be.true;
|
||||
|
||||
expect(obj.keyup({keyCode: 'A'.charCodeAt()})).to.be.true;
|
||||
expect(obj.keyup({keyCode: 0x3c})).to.be.true; // < key on DK Windows
|
||||
expect(obj.keyup({keyCode: 0x11})).to.be.true;
|
||||
});
|
||||
it('should never generate stalls', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt.type).to.not.be.equal('stall');
|
||||
});
|
||||
|
||||
obj.keypress({keyCode: 'A'.charCodeAt()});
|
||||
obj.keypress({keyCode: 0x3c});
|
||||
obj.keypress({keyCode: 0x11});
|
||||
|
||||
obj.keyup({keyCode: 'A'.charCodeAt()});
|
||||
obj.keyup({keyCode: 0x3c});
|
||||
obj.keyup({keyCode: 0x11});
|
||||
});
|
||||
});
|
||||
describe('mark events if a char modifier is down', function() {
|
||||
it('should not mark modifiers on a keydown event', function() {
|
||||
var times_called = 0;
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
|
||||
switch (times_called++) {
|
||||
case 0: //altgr
|
||||
break;
|
||||
case 1: // 'a'
|
||||
expect(evt).to.not.have.property('escape');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
||||
obj.keydown({keyCode: 'A'.charCodeAt()});
|
||||
});
|
||||
|
||||
it('should indicate on events if a single-key char modifier is down', function(done) {
|
||||
var times_called = 0;
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
|
||||
switch (times_called++) {
|
||||
case 0: //altgr
|
||||
break;
|
||||
case 1: // 'a'
|
||||
expect(evt).to.be.deep.equal({
|
||||
type: 'keypress',
|
||||
keyId: 'A'.charCodeAt(),
|
||||
keysym: keysyms.lookup('a'.charCodeAt()),
|
||||
escape: [0xfe03]
|
||||
});
|
||||
done();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
||||
obj.keypress({keyCode: 'A'.charCodeAt()});
|
||||
});
|
||||
it('should indicate on events if a multi-key char modifier is down', function(done) {
|
||||
var times_called = 0;
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xffe9, 0xffe3]), function(evt) {
|
||||
switch (times_called++) {
|
||||
case 0: //ctrl
|
||||
break;
|
||||
case 1: //alt
|
||||
break;
|
||||
case 2: // 'a'
|
||||
expect(evt).to.be.deep.equal({
|
||||
type: 'keypress',
|
||||
keyId: 'A'.charCodeAt(),
|
||||
keysym: keysyms.lookup('a'.charCodeAt()),
|
||||
escape: [0xffe9, 0xffe3]
|
||||
});
|
||||
done();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
obj.keydown({keyCode: 0x11}); // press ctrl
|
||||
obj.keydown({keyCode: 0x12}); // press alt
|
||||
obj.keypress({keyCode: 'A'.charCodeAt()});
|
||||
});
|
||||
it('should not consider a char modifier to be down on the modifier key itself', function() {
|
||||
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
|
||||
expect(evt).to.not.have.property('escape');
|
||||
});
|
||||
|
||||
obj.keydown({keyCode: 0xe1}); // press altgr
|
||||
|
||||
});
|
||||
});
|
||||
describe('add/remove keysym', function() {
|
||||
it('should remove keysym from keydown if a char key and no modifier', function() {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
|
||||
}).keydown({keyCode: 0x41});
|
||||
});
|
||||
it('should not remove keysym from keydown if a shortcut modifier is down', function() {
|
||||
var times_called = 0;
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
switch (times_called++) {
|
||||
case 1:
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'});
|
||||
break;
|
||||
}
|
||||
}).keydown({keyCode: 0x41, ctrlKey: true});
|
||||
expect(times_called).to.be.equal(2);
|
||||
});
|
||||
it('should not remove keysym from keydown if a char modifier is down', function() {
|
||||
var times_called = 0;
|
||||
KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
|
||||
switch (times_called++) {
|
||||
case 2:
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'});
|
||||
break;
|
||||
}
|
||||
}).keydown({keyCode: 0x41, altGraphKey: true});
|
||||
expect(times_called).to.be.equal(3);
|
||||
});
|
||||
it('should not remove keysym from keydown if key is noncharacter', function() {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt, 'bacobjpace').to.be.deep.equal({keyId: 0x09, keysym: keysyms.lookup(0xff09), type: 'keydown'});
|
||||
}).keydown({keyCode: 0x09});
|
||||
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt, 'ctrl').to.be.deep.equal({keyId: 0x11, keysym: keysyms.lookup(0xffe3), type: 'keydown'});
|
||||
}).keydown({keyCode: 0x11});
|
||||
});
|
||||
it('should never remove keysym from keypress', function() {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'});
|
||||
}).keypress({keyCode: 0x41});
|
||||
});
|
||||
it('should never remove keysym from keyup', function() {
|
||||
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
|
||||
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'});
|
||||
}).keyup({keyCode: 0x41});
|
||||
});
|
||||
});
|
||||
// on keypress, keyup(?), always set keysym
|
||||
// on keydown, only do it if we don't expect a keypress: if noncharacter OR modifier is down
|
||||
});
|
||||
|
||||
describe('Verify that char modifiers are active', function() {
|
||||
it('should pass keydown events through if there is no stall', function(done) {
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
done();
|
||||
})({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
});
|
||||
it('should pass keyup events through if there is no stall', function(done) {
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
expect(evt).to.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
done();
|
||||
})({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
});
|
||||
it('should pass keypress events through if there is no stall', function(done) {
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
expect(evt).to.deep.equal({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
done();
|
||||
})({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
});
|
||||
it('should not pass stall events through', function(done){
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
// should only be called once, for the keydown
|
||||
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
done();
|
||||
});
|
||||
|
||||
obj({type: 'stall'});
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
});
|
||||
it('should merge keydown and keypress events if they come after a stall', function(done) {
|
||||
var next_called = false;
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
// should only be called once, for the keydown
|
||||
expect(next_called).to.be.false;
|
||||
next_called = true;
|
||||
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x44)});
|
||||
done();
|
||||
});
|
||||
|
||||
obj({type: 'stall'});
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)});
|
||||
expect(next_called).to.be.false;
|
||||
});
|
||||
it('should preserve modifier attribute when merging if keysyms differ', function(done) {
|
||||
var next_called = false;
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
// should only be called once, for the keydown
|
||||
expect(next_called).to.be.false;
|
||||
next_called = true;
|
||||
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x44), escape: [0xffe3]});
|
||||
done();
|
||||
});
|
||||
|
||||
obj({type: 'stall'});
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44), escape: [0xffe3]});
|
||||
expect(next_called).to.be.false;
|
||||
});
|
||||
it('should not preserve modifier attribute when merging if keysyms are the same', function() {
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
expect(evt).to.not.have.property('escape');
|
||||
});
|
||||
|
||||
obj({type: 'stall'});
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x42), escape: [0xffe3]});
|
||||
});
|
||||
it('should not merge keydown and keypress events if there is no stall', function(done) {
|
||||
var times_called = 0;
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
switch(times_called) {
|
||||
case 0:
|
||||
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
break;
|
||||
case 1:
|
||||
expect(evt).to.deep.equal({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)});
|
||||
done();
|
||||
break;
|
||||
}
|
||||
|
||||
++times_called;
|
||||
});
|
||||
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
obj({type: 'keypress', keyId: 0x43, keysym: keysyms.lookup(0x44)});
|
||||
});
|
||||
it('should not merge keydown and keypress events if separated by another event', function(done) {
|
||||
var times_called = 0;
|
||||
var obj = VerifyCharModifier(function(evt){
|
||||
switch(times_called) {
|
||||
case 0:
|
||||
expect(evt,1).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
break;
|
||||
case 1:
|
||||
expect(evt,2).to.deep.equal({type: 'keyup', keyId: 0x43, keysym: keysyms.lookup(0x44)});
|
||||
break;
|
||||
case 2:
|
||||
expect(evt,3).to.deep.equal({type: 'keypress', keyId: 0x45, keysym: keysyms.lookup(0x46)});
|
||||
done();
|
||||
break;
|
||||
}
|
||||
|
||||
++times_called;
|
||||
});
|
||||
|
||||
obj({type: 'stall'});
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
obj({type: 'keyup', keyId: 0x43, keysym: keysyms.lookup(0x44)});
|
||||
obj({type: 'keypress', keyId: 0x45, keysym: keysyms.lookup(0x46)});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Track Key State', function() {
|
||||
it('should do nothing on keyup events if no keys are down', function() {
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
expect(true).to.be.false;
|
||||
});
|
||||
obj({type: 'keyup', keyId: 0x41});
|
||||
});
|
||||
it('should insert into the queue on keydown if no keys are down', function() {
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
}
|
||||
elem = null;
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keyup', keyId: 0x41};
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
expect(times_called).to.be.equal(2);
|
||||
});
|
||||
it('should insert into the queue on keypress if no keys are down', function() {
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
}
|
||||
elem = null;
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keyup', keyId: 0x41};
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
expect(times_called).to.be.equal(2);
|
||||
});
|
||||
it('should add keysym to last key entry if keyId matches', function() {
|
||||
// this implies that a single keyup will release both keysyms
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
elem = null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x43)};
|
||||
keysymsdown[keysyms.lookup(0x43).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keyup', keyId: 0x41};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(4);
|
||||
});
|
||||
it('should create new key entry if keyId matches and keysym does not', function() {
|
||||
// this implies that a single keyup will release both keysyms
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
elem = null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x43)};
|
||||
keysymsdown[keysyms.lookup(0x43).keysym] = true;
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(2);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keyup', keyId: 0};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(3);
|
||||
elem = {type: 'keyup', keyId: 0};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(4);
|
||||
});
|
||||
it('should merge key entry if keyIds are zero and keysyms match', function() {
|
||||
// this implies that a single keyup will release both keysyms
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
elem = null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(2);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keyup', keyId: 0};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(3);
|
||||
});
|
||||
it('should add keysym as separate entry if keyId does not match last event', function() {
|
||||
// this implies that separate keyups are required
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
elem = null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keypress', keyId: 0x42, keysym: keysyms.lookup(0x43)};
|
||||
keysymsdown[keysyms.lookup(0x43).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keyup', keyId: 0x41};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(4);
|
||||
elem = {type: 'keyup', keyId: 0x42};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(4);
|
||||
});
|
||||
it('should add keysym as separate entry if keyId does not match last event and first is zero', function() {
|
||||
// this implies that separate keyups are required
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
elem = null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x43)};
|
||||
keysymsdown[keysyms.lookup(0x43).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
expect(times_called).to.be.equal(2);
|
||||
elem = {type: 'keyup', keyId: 0};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(3);
|
||||
elem = {type: 'keyup', keyId: 0x42};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(4);
|
||||
});
|
||||
it('should add keysym as separate entry if keyId does not match last event and second is zero', function() {
|
||||
// this implies that a separate keyups are required
|
||||
var times_called = 0;
|
||||
var elem = null;
|
||||
var keysymsdown = {};
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
if (elem.type == 'keyup') {
|
||||
expect(evt).to.have.property('keysym');
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
delete keysymsdown[evt.keysym.keysym];
|
||||
}
|
||||
else {
|
||||
expect(evt).to.be.deep.equal(elem);
|
||||
expect (keysymsdown[evt.keysym.keysym]).to.not.be.undefined;
|
||||
elem = null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)};
|
||||
keysymsdown[keysyms.lookup(0x42).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x43)};
|
||||
keysymsdown[keysyms.lookup(0x43).keysym] = true;
|
||||
obj(elem);
|
||||
expect(elem).to.be.null;
|
||||
elem = {type: 'keyup', keyId: 0x41};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(3);
|
||||
elem = {type: 'keyup', keyId: 0};
|
||||
obj(elem);
|
||||
expect(times_called).to.be.equal(4);
|
||||
});
|
||||
it('should pop matching key event on keyup', function() {
|
||||
var times_called = 0;
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
switch (times_called++) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
expect(evt.type).to.be.equal('keydown');
|
||||
break;
|
||||
case 3:
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x42, keysym: keysyms.lookup(0x62)});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x61)});
|
||||
obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x62)});
|
||||
obj({type: 'keydown', keyId: 0x43, keysym: keysyms.lookup(0x63)});
|
||||
obj({type: 'keyup', keyId: 0x42});
|
||||
expect(times_called).to.equal(4);
|
||||
});
|
||||
it('should pop the first zero keyevent on keyup with zero keyId', function() {
|
||||
var times_called = 0;
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
switch (times_called++) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
expect(evt.type).to.be.equal('keydown');
|
||||
break;
|
||||
case 3:
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x61)});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
obj({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x61)});
|
||||
obj({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0x62)});
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x63)});
|
||||
obj({type: 'keyup', keyId: 0x0});
|
||||
expect(times_called).to.equal(4);
|
||||
});
|
||||
it('should pop the last keyevents keysym if no match is found for keyId', function() {
|
||||
var times_called = 0;
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
switch (times_called++) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
expect(evt.type).to.be.equal('keydown');
|
||||
break;
|
||||
case 3:
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x44, keysym: keysyms.lookup(0x63)});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x61)});
|
||||
obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x62)});
|
||||
obj({type: 'keydown', keyId: 0x43, keysym: keysyms.lookup(0x63)});
|
||||
obj({type: 'keyup', keyId: 0x44});
|
||||
expect(times_called).to.equal(4);
|
||||
});
|
||||
describe('Firefox sends keypress even when keydown is suppressed', function() {
|
||||
it('should discard the keypress', function() {
|
||||
var times_called = 0;
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
expect(times_called).to.be.equal(0);
|
||||
++times_called;
|
||||
});
|
||||
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
expect(times_called).to.be.equal(1);
|
||||
obj({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x43)});
|
||||
});
|
||||
});
|
||||
describe('releaseAll', function() {
|
||||
it('should do nothing if no keys have been pressed', function() {
|
||||
var times_called = 0;
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
++times_called;
|
||||
});
|
||||
obj({type: 'releaseall'});
|
||||
expect(times_called).to.be.equal(0);
|
||||
});
|
||||
it('should release the keys that have been pressed', function() {
|
||||
var times_called = 0;
|
||||
var obj = TrackKeyState(function(evt) {
|
||||
switch (times_called++) {
|
||||
case 2:
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x41)});
|
||||
break;
|
||||
case 3:
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x42)});
|
||||
break;
|
||||
}
|
||||
});
|
||||
obj({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
|
||||
obj({type: 'keydown', keyId: 0x42, keysym: keysyms.lookup(0x42)});
|
||||
expect(times_called).to.be.equal(2);
|
||||
obj({type: 'releaseall'});
|
||||
expect(times_called).to.be.equal(4);
|
||||
obj({type: 'releaseall'});
|
||||
expect(times_called).to.be.equal(4);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Escape Modifiers', function() {
|
||||
describe('Keydown', function() {
|
||||
it('should pass through when a char modifier is not down', function() {
|
||||
var times_called = 0;
|
||||
EscapeModifiers(function(evt) {
|
||||
expect(times_called).to.be.equal(0);
|
||||
++times_called;
|
||||
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
})({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
expect(times_called).to.be.equal(1);
|
||||
});
|
||||
it('should generate fake undo/redo events when a char modifier is down', function() {
|
||||
var times_called = 0;
|
||||
EscapeModifiers(function(evt) {
|
||||
switch(times_called++) {
|
||||
case 0:
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0xffe9)});
|
||||
break;
|
||||
case 1:
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0xffe3)});
|
||||
break;
|
||||
case 2:
|
||||
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xffe9, 0xffe3]});
|
||||
break;
|
||||
case 3:
|
||||
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0xffe9)});
|
||||
break;
|
||||
case 4:
|
||||
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: keysyms.lookup(0xffe3)});
|
||||
break;
|
||||
}
|
||||
})({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xffe9, 0xffe3]});
|
||||
expect(times_called).to.be.equal(5);
|
||||
});
|
||||
});
|
||||
describe('Keyup', function() {
|
||||
it('should pass through when a char modifier is down', function() {
|
||||
var times_called = 0;
|
||||
EscapeModifiers(function(evt) {
|
||||
expect(times_called).to.be.equal(0);
|
||||
++times_called;
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xfe03]});
|
||||
})({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xfe03]});
|
||||
expect(times_called).to.be.equal(1);
|
||||
});
|
||||
it('should pass through when a char modifier is not down', function() {
|
||||
var times_called = 0;
|
||||
EscapeModifiers(function(evt) {
|
||||
expect(times_called).to.be.equal(0);
|
||||
++times_called;
|
||||
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
})({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42)});
|
||||
expect(times_called).to.be.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
1929
tests/test.rfb.js
Normal file
105
tests/test.util.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// requires local modules: util
|
||||
/* jshint expr: true */
|
||||
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Utils', function() {
|
||||
"use strict";
|
||||
|
||||
describe('Array instance methods', function () {
|
||||
describe('push8', function () {
|
||||
it('should push a byte on to the array', function () {
|
||||
var arr = [1];
|
||||
arr.push8(128);
|
||||
expect(arr).to.deep.equal([1, 128]);
|
||||
});
|
||||
|
||||
it('should only use the least significant byte of any number passed in', function () {
|
||||
var arr = [1];
|
||||
arr.push8(0xABCD);
|
||||
expect(arr).to.deep.equal([1, 0xCD]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('push16', function () {
|
||||
it('should push two bytes on to the array', function () {
|
||||
var arr = [1];
|
||||
arr.push16(0xABCD);
|
||||
expect(arr).to.deep.equal([1, 0xAB, 0xCD]);
|
||||
});
|
||||
|
||||
it('should only use the two least significant bytes of any number passed in', function () {
|
||||
var arr = [1];
|
||||
arr.push16(0xABCDEF);
|
||||
expect(arr).to.deep.equal([1, 0xCD, 0xEF]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('push32', function () {
|
||||
it('should push four bytes on to the array', function () {
|
||||
var arr = [1];
|
||||
arr.push32(0xABCDEF12);
|
||||
expect(arr).to.deep.equal([1, 0xAB, 0xCD, 0xEF, 0x12]);
|
||||
});
|
||||
|
||||
it('should only use the four least significant bytes of any number passed in', function () {
|
||||
var arr = [1];
|
||||
arr.push32(0xABCDEF1234);
|
||||
expect(arr).to.deep.equal([1, 0xCD, 0xEF, 0x12, 0x34]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('logging functions', function () {
|
||||
beforeEach(function () {
|
||||
sinon.spy(console, 'log');
|
||||
sinon.spy(console, 'warn');
|
||||
sinon.spy(console, 'error');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
console.log.restore();
|
||||
console.warn.restore();
|
||||
console.error.restore();
|
||||
});
|
||||
|
||||
it('should use noop for levels lower than the min level', function () {
|
||||
Util.init_logging('warn');
|
||||
Util.Debug('hi');
|
||||
Util.Info('hello');
|
||||
expect(console.log).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('should use console.log for Debug and Info', function () {
|
||||
Util.init_logging('debug');
|
||||
Util.Debug('dbg');
|
||||
Util.Info('inf');
|
||||
expect(console.log).to.have.been.calledWith('dbg');
|
||||
expect(console.log).to.have.been.calledWith('inf');
|
||||
});
|
||||
|
||||
it('should use console.warn for Warn', function () {
|
||||
Util.init_logging('warn');
|
||||
Util.Warn('wrn');
|
||||
expect(console.warn).to.have.been.called;
|
||||
expect(console.warn).to.have.been.calledWith('wrn');
|
||||
});
|
||||
|
||||
it('should use console.error for Error', function () {
|
||||
Util.init_logging('error');
|
||||
Util.Error('err');
|
||||
expect(console.error).to.have.been.called;
|
||||
expect(console.error).to.have.been.calledWith('err');
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(directxman12): test the conf_default and conf_defaults methods
|
||||
// TODO(directxman12): test decodeUTF8
|
||||
// TODO(directxman12): test the event methods (addEvent, removeEvent, stopEvent)
|
||||
// TODO(directxman12): figure out a good way to test getPosition and getEventPosition
|
||||
// TODO(directxman12): figure out how to test the browser detection functions properly
|
||||
// (we can't really test them against the browsers, except for Gecko
|
||||
// via PhantomJS, the default test driver)
|
||||
// TODO(directxman12): figure out how to test Util.Flash
|
||||
});
|
||||
457
tests/test.websock.js
Normal file
@@ -0,0 +1,457 @@
|
||||
// requires local modules: websock, util
|
||||
// requires test modules: fake.websocket, assertions
|
||||
/* jshint expr: true */
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
describe('Websock', function() {
|
||||
"use strict";
|
||||
|
||||
describe('Queue methods', function () {
|
||||
var sock;
|
||||
var RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
// skip init
|
||||
sock._allocate_buffers();
|
||||
sock._rQ.set(RQ_TEMPLATE);
|
||||
sock._rQlen = RQ_TEMPLATE.length;
|
||||
});
|
||||
describe('rQlen', function () {
|
||||
it('should return the length of the receive queue', function () {
|
||||
sock.set_rQi(0);
|
||||
|
||||
expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length);
|
||||
});
|
||||
|
||||
it("should return the proper length if we read some from the receive queue", function () {
|
||||
sock.set_rQi(1);
|
||||
|
||||
expect(sock.rQlen()).to.equal(RQ_TEMPLATE.length - 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQpeek8', function () {
|
||||
it('should peek at the next byte without poping it off the queue', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var peek = sock.rQpeek8();
|
||||
expect(sock.rQpeek8()).to.equal(peek);
|
||||
expect(sock.rQlen()).to.equal(bef_len);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshift8', function () {
|
||||
it('should pop a single byte from the receive queue', function () {
|
||||
var peek = sock.rQpeek8();
|
||||
var bef_len = sock.rQlen();
|
||||
expect(sock.rQshift8()).to.equal(peek);
|
||||
expect(sock.rQlen()).to.equal(bef_len - 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshift16', function () {
|
||||
it('should pop two bytes from the receive queue and return a single number', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1];
|
||||
expect(sock.rQshift16()).to.equal(expected);
|
||||
expect(sock.rQlen()).to.equal(bef_len - 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshift32', function () {
|
||||
it('should pop four bytes from the receive queue and return a single number', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var expected = (RQ_TEMPLATE[0] << 24) +
|
||||
(RQ_TEMPLATE[1] << 16) +
|
||||
(RQ_TEMPLATE[2] << 8) +
|
||||
RQ_TEMPLATE[3];
|
||||
expect(sock.rQshift32()).to.equal(expected);
|
||||
expect(sock.rQlen()).to.equal(bef_len - 4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshiftStr', function () {
|
||||
it('should shift the given number of bytes off of the receive queue and return a string', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var bef_rQi = sock.get_rQi();
|
||||
var shifted = sock.rQshiftStr(3);
|
||||
expect(shifted).to.be.a('string');
|
||||
expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3))));
|
||||
expect(sock.rQlen()).to.equal(bef_len - 3);
|
||||
});
|
||||
|
||||
it('should shift the entire rest of the queue off if no length is given', function () {
|
||||
sock.rQshiftStr();
|
||||
expect(sock.rQlen()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQshiftBytes', function () {
|
||||
it('should shift the given number of bytes of the receive queue and return an array', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
var bef_rQi = sock.get_rQi();
|
||||
var shifted = sock.rQshiftBytes(3);
|
||||
expect(shifted).to.be.an.instanceof(Uint8Array);
|
||||
expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3));
|
||||
expect(sock.rQlen()).to.equal(bef_len - 3);
|
||||
});
|
||||
|
||||
it('should shift the entire rest of the queue off if no length is given', function () {
|
||||
sock.rQshiftBytes();
|
||||
expect(sock.rQlen()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQslice', function () {
|
||||
beforeEach(function () {
|
||||
sock.set_rQi(0);
|
||||
});
|
||||
|
||||
it('should not modify the receive queue', function () {
|
||||
var bef_len = sock.rQlen();
|
||||
sock.rQslice(0, 2);
|
||||
expect(sock.rQlen()).to.equal(bef_len);
|
||||
});
|
||||
|
||||
it('should return an array containing the given slice of the receive queue', function () {
|
||||
var sl = sock.rQslice(0, 2);
|
||||
expect(sl).to.be.an.instanceof(Uint8Array);
|
||||
expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2));
|
||||
});
|
||||
|
||||
it('should use the rest of the receive queue if no end is given', function () {
|
||||
var sl = sock.rQslice(1);
|
||||
expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
|
||||
expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1));
|
||||
});
|
||||
|
||||
it('should take the current rQi in to account', function () {
|
||||
sock.set_rQi(1);
|
||||
expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
|
||||
});
|
||||
});
|
||||
|
||||
describe('rQwait', function () {
|
||||
beforeEach(function () {
|
||||
sock.set_rQi(0);
|
||||
});
|
||||
|
||||
it('should return true if there are not enough bytes in the receive queue', function () {
|
||||
expect(sock.rQwait('hi', RQ_TEMPLATE.length + 1)).to.be.true;
|
||||
});
|
||||
|
||||
it('should return false if there are enough bytes in the receive queue', function () {
|
||||
expect(sock.rQwait('hi', RQ_TEMPLATE.length)).to.be.false;
|
||||
});
|
||||
|
||||
it('should return true and reduce rQi by "goback" if there are not enough bytes', function () {
|
||||
sock.set_rQi(5);
|
||||
expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
|
||||
expect(sock.get_rQi()).to.equal(1);
|
||||
});
|
||||
|
||||
it('should raise an error if we try to go back more than possible', function () {
|
||||
sock.set_rQi(5);
|
||||
expect(function () { sock.rQwait('hi', RQ_TEMPLATE.length, 6); }).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should not reduce rQi if there are enough bytes', function () {
|
||||
sock.set_rQi(5);
|
||||
sock.rQwait('hi', 1, 6);
|
||||
expect(sock.get_rQi()).to.equal(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('flush', function () {
|
||||
beforeEach(function () {
|
||||
sock._websocket = {
|
||||
send: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
it('should actually send on the websocket if the websocket does not have too much buffered', function () {
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 8;
|
||||
sock._websocket.readyState = WebSocket.OPEN
|
||||
sock._sQ = new Uint8Array([1, 2, 3]);
|
||||
sock._sQlen = 3;
|
||||
var encoded = sock._encode_message();
|
||||
|
||||
sock.flush();
|
||||
expect(sock._websocket.send).to.have.been.calledOnce;
|
||||
expect(sock._websocket.send).to.have.been.calledWith(encoded);
|
||||
});
|
||||
|
||||
it('should return true if the websocket did not have too much buffered', function () {
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 8;
|
||||
|
||||
expect(sock.flush()).to.be.true;
|
||||
});
|
||||
|
||||
it('should not call send if we do not have anything queued up', function () {
|
||||
sock._sQlen = 0;
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 8;
|
||||
|
||||
sock.flush();
|
||||
|
||||
expect(sock._websocket.send).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should not send and return false if the websocket has too much buffered', function () {
|
||||
sock.maxBufferedAmount = 10;
|
||||
sock._websocket.bufferedAmount = 12;
|
||||
|
||||
expect(sock.flush()).to.be.false;
|
||||
expect(sock._websocket.send).to.not.have.been.called;
|
||||
});
|
||||
});
|
||||
|
||||
describe('send', function () {
|
||||
beforeEach(function () {
|
||||
sock.flush = sinon.spy();
|
||||
});
|
||||
|
||||
it('should add to the send queue', function () {
|
||||
sock.send([1, 2, 3]);
|
||||
var sq = sock.get_sQ();
|
||||
expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
|
||||
});
|
||||
|
||||
it('should call flush', function () {
|
||||
sock.send([1, 2, 3]);
|
||||
expect(sock.flush).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('send_string', function () {
|
||||
beforeEach(function () {
|
||||
sock.send = sinon.spy();
|
||||
});
|
||||
|
||||
it('should call send after converting the string to an array', function () {
|
||||
sock.send_string("\x01\x02\x03");
|
||||
expect(sock.send).to.have.been.calledWith([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('lifecycle methods', function () {
|
||||
var old_WS;
|
||||
before(function () {
|
||||
old_WS = WebSocket;
|
||||
});
|
||||
|
||||
var sock;
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
WebSocket = sinon.spy();
|
||||
WebSocket.OPEN = old_WS.OPEN;
|
||||
WebSocket.CONNECTING = old_WS.CONNECTING;
|
||||
WebSocket.CLOSING = old_WS.CLOSING;
|
||||
WebSocket.CLOSED = old_WS.CLOSED;
|
||||
|
||||
WebSocket.prototype.binaryType = 'arraybuffer';
|
||||
});
|
||||
|
||||
describe('opening', function () {
|
||||
it('should pick the correct protocols if none are given' , function () {
|
||||
|
||||
});
|
||||
|
||||
it('should open the actual websocket', function () {
|
||||
sock.open('ws://localhost:8675', 'binary');
|
||||
expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
|
||||
});
|
||||
|
||||
it('should fail if we specify a protocol besides binary', function () {
|
||||
expect(function () { sock.open('ws:///', 'base64'); }).to.throw(Error);
|
||||
});
|
||||
|
||||
// it('should initialize the event handlers')?
|
||||
});
|
||||
|
||||
describe('closing', function () {
|
||||
beforeEach(function () {
|
||||
sock.open('ws://');
|
||||
sock._websocket.close = sinon.spy();
|
||||
});
|
||||
|
||||
it('should close the actual websocket if it is open', function () {
|
||||
sock._websocket.readyState = WebSocket.OPEN;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should close the actual websocket if it is connecting', function () {
|
||||
sock._websocket.readyState = WebSocket.CONNECTING;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should not try to close the actual websocket if closing', function () {
|
||||
sock._websocket.readyState = WebSocket.CLOSING;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should not try to close the actual websocket if closed', function () {
|
||||
sock._websocket.readyState = WebSocket.CLOSED;
|
||||
sock.close();
|
||||
expect(sock._websocket.close).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should reset onmessage to not call _recv_message', function () {
|
||||
sinon.spy(sock, '_recv_message');
|
||||
sock.close();
|
||||
sock._websocket.onmessage(null);
|
||||
try {
|
||||
expect(sock._recv_message).not.to.have.been.called;
|
||||
} finally {
|
||||
sock._recv_message.restore();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('event handlers', function () {
|
||||
beforeEach(function () {
|
||||
sock._recv_message = sinon.spy();
|
||||
sock.on('open', sinon.spy());
|
||||
sock.on('close', sinon.spy());
|
||||
sock.on('error', sinon.spy());
|
||||
sock.open('ws://');
|
||||
});
|
||||
|
||||
it('should call _recv_message on a message', function () {
|
||||
sock._websocket.onmessage(null);
|
||||
expect(sock._recv_message).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should fail if a protocol besides binary is requested', function () {
|
||||
sock._websocket.protocol = 'base64';
|
||||
expect(sock._websocket.onopen).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should assume binary if no protocol was available on opening', function () {
|
||||
sock._websocket.protocol = null;
|
||||
sock._websocket.onopen();
|
||||
expect(sock._mode).to.equal('binary');
|
||||
});
|
||||
|
||||
it('should call the open event handler on opening', function () {
|
||||
sock._websocket.onopen();
|
||||
expect(sock._eventHandlers.open).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should call the close event handler on closing', function () {
|
||||
sock._websocket.onclose();
|
||||
expect(sock._eventHandlers.close).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should call the error event handler on error', function () {
|
||||
sock._websocket.onerror();
|
||||
expect(sock._eventHandlers.error).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
WebSocket = old_WS;
|
||||
});
|
||||
});
|
||||
|
||||
describe('WebSocket Receiving', function () {
|
||||
var sock;
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
sock._allocate_buffers();
|
||||
});
|
||||
|
||||
it('should support adding binary Uint8Array data to the receive queue', function () {
|
||||
var msg = { data: new Uint8Array([1, 2, 3]) };
|
||||
sock._mode = 'binary';
|
||||
sock._recv_message(msg);
|
||||
expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
|
||||
});
|
||||
|
||||
it('should call the message event handler if present', function () {
|
||||
sock._eventHandlers.message = sinon.spy();
|
||||
var msg = { data: new Uint8Array([1, 2, 3]).buffer };
|
||||
sock._mode = 'binary';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._eventHandlers.message).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('should not call the message event handler if there is nothing in the receive queue', function () {
|
||||
sock._eventHandlers.message = sinon.spy();
|
||||
var msg = { data: new Uint8Array([]).buffer };
|
||||
sock._mode = 'binary';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._eventHandlers.message).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it('should compact the receive queue', function () {
|
||||
// NB(sross): while this is an internal implementation detail, it's important to
|
||||
// test, otherwise the receive queue could become very large very quickly
|
||||
sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
|
||||
sock._rQlen = 6;
|
||||
sock.set_rQi(6);
|
||||
sock._rQmax = 3;
|
||||
var msg = { data: new Uint8Array([1, 2, 3]).buffer };
|
||||
sock._mode = 'binary';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._rQlen).to.equal(3);
|
||||
expect(sock.get_rQi()).to.equal(0);
|
||||
});
|
||||
|
||||
it('should automatically resize the receive queue if the incoming message is too large', function () {
|
||||
sock._rQ = new Uint8Array(20);
|
||||
sock._rQlen = 0;
|
||||
sock.set_rQi(0);
|
||||
sock._rQbufferSize = 20;
|
||||
sock._rQmax = 2;
|
||||
var msg = { data: new Uint8Array(30).buffer };
|
||||
sock._mode = 'binary';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._rQlen).to.equal(30);
|
||||
expect(sock.get_rQi()).to.equal(0);
|
||||
expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen
|
||||
});
|
||||
|
||||
it('should call the error event handler on an exception', function () {
|
||||
sock._eventHandlers.error = sinon.spy();
|
||||
sock._eventHandlers.message = sinon.stub().throws();
|
||||
var msg = { data: new Uint8Array([1, 2, 3]).buffer };
|
||||
sock._mode = 'binary';
|
||||
sock._recv_message(msg);
|
||||
expect(sock._eventHandlers.error).to.have.been.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Data encoding', function () {
|
||||
before(function () { FakeWebSocket.replace(); });
|
||||
after(function () { FakeWebSocket.restore(); });
|
||||
|
||||
describe('as binary data', function () {
|
||||
var sock;
|
||||
beforeEach(function () {
|
||||
sock = new Websock();
|
||||
sock.open('ws://', 'binary');
|
||||
sock._websocket._open();
|
||||
});
|
||||
|
||||
it('should only send the send queue up to the send queue length', function () {
|
||||
sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
|
||||
sock._sQlen = 3;
|
||||
var res = sock._encode_message();
|
||||
expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
|
||||
});
|
||||
|
||||
it('should properly pass the encoded data off to the actual WebSocket', function () {
|
||||
sock.send([1, 2, 3]);
|
||||
expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
43
tests/viewport.css
Normal file
@@ -0,0 +1,43 @@
|
||||
html,body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.flex-layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: box;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-box;
|
||||
|
||||
box-orient: vertical;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-ms-box-orient: vertical;
|
||||
|
||||
box-align: stretch;
|
||||
-webkit-box-align: stretch;
|
||||
-moz-box-align: stretch;
|
||||
-ms-box-align: stretch;
|
||||
}
|
||||
.flex-box {
|
||||
box-flex: 1;
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-ms-box-flex: 1;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.canvas {
|
||||
position: absolute;
|
||||
border-style: dotted;
|
||||
border-width: 1px;
|
||||
}
|
||||
203
tests/viewport.html
Normal file
@@ -0,0 +1,203 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Viewport Test</title>
|
||||
<link rel="stylesheet" href="viewport.css">
|
||||
<!--
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
-->
|
||||
<meta name=viewport content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
</head>
|
||||
<body>
|
||||
<div class="flex-layout">
|
||||
<div>
|
||||
Canvas:
|
||||
<input id="move-selector" type="button" value="Move"
|
||||
onclick="toggleMove();">
|
||||
<br>
|
||||
</div>
|
||||
<div class="container flex-box">
|
||||
<canvas id="canvas" class="canvas">
|
||||
Canvas not supported.
|
||||
</canvas>
|
||||
<br>
|
||||
</div>
|
||||
<div>
|
||||
<br>
|
||||
Results:<br>
|
||||
<textarea id="messages" style="font-size: 9;" cols=80 rows=8></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<!--
|
||||
<script type='text/javascript'
|
||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||
-->
|
||||
<script src="../include/util.js"></script>
|
||||
<script src="../include/webutil.js"></script>
|
||||
<script src="../include/base64.js"></script>
|
||||
<script src="../include/keysymdef.js"></script>
|
||||
<script src="../include/keyboard.js"></script>
|
||||
<script src="../include/input.js"></script>
|
||||
<script src="../include/display.js"></script>
|
||||
<script>
|
||||
var msg_cnt = 0, iterations,
|
||||
penDown = false, doMove = false,
|
||||
inMove = false, lastPos = {},
|
||||
padW = 0, padH = 0,
|
||||
display, ctx, keyboard, mouse;
|
||||
|
||||
var newline = "\n";
|
||||
if (Util.Engine.trident) {
|
||||
var newline = "<br>\n";
|
||||
}
|
||||
|
||||
function message(str) {
|
||||
console.log(str);
|
||||
cell = $D('messages');
|
||||
cell.innerHTML += msg_cnt + ": " + str + newline;
|
||||
cell.scrollTop = cell.scrollHeight;
|
||||
msg_cnt++;
|
||||
}
|
||||
|
||||
function mouseButton(x, y, down, bmask) {
|
||||
//msg = 'mouse x,y: ' + x + ',' + y + ' down: ' + down;
|
||||
//msg += ' bmask: ' + bmask;
|
||||
//message(msg);
|
||||
|
||||
if (doMove) {
|
||||
if (down && !inMove) {
|
||||
inMove = true;
|
||||
lastPos = {'x': x, 'y': y};
|
||||
} else if (!down && inMove) {
|
||||
inMove = false;
|
||||
//dirtyRedraw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (down && ! penDown) {
|
||||
penDown = true;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
} else if (!down && penDown) {
|
||||
penDown = false;
|
||||
ctx.closePath();
|
||||
}
|
||||
}
|
||||
|
||||
function mouseMove(x, y) {
|
||||
var deltaX, deltaY;
|
||||
|
||||
if (inMove) {
|
||||
//deltaX = x - lastPos.x; // drag viewport
|
||||
deltaX = lastPos.x - x; // drag frame buffer
|
||||
//deltaY = y - lastPos.y; // drag viewport
|
||||
deltaY = lastPos.y - y; // drag frame buffer
|
||||
lastPos = {'x': x, 'y': y};
|
||||
|
||||
display.viewportChangePos(deltaX, deltaY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (penDown) {
|
||||
ctx.lineTo(x, y);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function dirtyRedraw() {
|
||||
if (inMove) {
|
||||
// Wait for user to stop moving viewport
|
||||
return;
|
||||
}
|
||||
|
||||
var d = display.getCleanDirtyReset();
|
||||
|
||||
for (i = 0; i < d.dirtyBoxes.length; i++) {
|
||||
//showBox(d.dirtyBoxes[i], "dirty[" + i + "]: ");
|
||||
drawArea(d.dirtyBoxes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function drawArea(b) {
|
||||
var data = [], pixel, x, y;
|
||||
|
||||
//message("draw "+b.x+","+b.y+" ("+b.w+","+b.h+")");
|
||||
|
||||
for (var i = 0; i < b.w; i++) {
|
||||
x = b.x + i;
|
||||
for (var j = 0; j < b.h; j++) {
|
||||
y = b.y + j;
|
||||
pixel = (j * b.w * 4 + i * 4);
|
||||
data[pixel + 0] = ((x * y) / 13) % 256;
|
||||
data[pixel + 1] = ((x * y) + 392) % 256;
|
||||
data[pixel + 2] = ((x + y) + 256) % 256;
|
||||
data[pixel + 3] = 255;
|
||||
}
|
||||
}
|
||||
//message("i: " + i + ", j: " + j + ", pixel: " + pixel);
|
||||
display.blitImage(b.x, b.y, b.w, b.h, data, 0);
|
||||
}
|
||||
|
||||
function toggleMove() {
|
||||
if (doMove) {
|
||||
doMove = false;
|
||||
$D('move-selector').style.backgroundColor = "";
|
||||
$D('move-selector').style.color = "";
|
||||
} else {
|
||||
doMove = true;
|
||||
$D('move-selector').style.backgroundColor = "black";
|
||||
$D('move-selector').style.color = "lightgray";
|
||||
}
|
||||
}
|
||||
|
||||
function detectPad() {
|
||||
var c = $D('canvas'), p = c.parentNode;
|
||||
c.width = 10;
|
||||
c.height = 10;
|
||||
padW = c.offsetWidth - 10;
|
||||
padH = c.offsetHeight - 10;
|
||||
message("padW: " + padW + ", padH: " + padH);
|
||||
}
|
||||
|
||||
function doResize() {
|
||||
var p = $D('canvas').parentNode;
|
||||
message("doResize1: [" + (p.offsetWidth - padW) +
|
||||
"," + (p.offsetHeight - padH) + "]");
|
||||
display.viewportChangeSize(p.offsetWidth - padW, p.offsetHeight - padH);
|
||||
/*
|
||||
var pos, new_w, new_h;pos
|
||||
pos = Util.getPosition($D('canvas').parentNode);
|
||||
new_w = window.innerWidth - pos.x;
|
||||
new_h = window.innerHeight - pos.y;
|
||||
display.viewportChangeSize(new_w, new_h);
|
||||
*/
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
detectPad();
|
||||
|
||||
display = new Display({'target': $D('canvas')});
|
||||
display.resize(1600, 1024);
|
||||
display.set_viewport(true);
|
||||
ctx = display.get_context();
|
||||
|
||||
mouse = new Mouse({'target': $D('canvas'),
|
||||
'onMouseButton': mouseButton,
|
||||
'onMouseMove': mouseMove});
|
||||
mouse.grab();
|
||||
|
||||
|
||||
Util.addEvent(window, 'resize', doResize);
|
||||
// Shrink viewport for first resize call so that the
|
||||
// scrollbars are disabled
|
||||
display.viewportChangeSize(10, 10);
|
||||
setTimeout(doResize, 1);
|
||||
setInterval(dirtyRedraw, 50);
|
||||
|
||||
message("Display initialized");
|
||||
};
|
||||
</script>
|
||||
</html>
|
||||
@@ -2,7 +2,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>VNC Performance Benchmark</title>
|
||||
<link rel="stylesheet" href="../include/plain.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -38,12 +37,25 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var INCLUDE_URI= "../include/";
|
||||
// TODO: Data file should override
|
||||
var VNC_frame_encoding = "binary";
|
||||
</script>
|
||||
<script src="../include/vnc.js"></script>
|
||||
<script src="../include/playback.js"></script>
|
||||
<script src="data/multi.js"></script>
|
||||
<script src="../include/util.js"></script>
|
||||
<script src="../include/webutil.js"></script>
|
||||
|
||||
<script>
|
||||
var fname = WebUtil.getQueryVar('data', null);
|
||||
if (fname) {
|
||||
msg("Loading " + fname);
|
||||
|
||||
// Load supporting scripts
|
||||
Util.load_scripts(["base64.js", "websock.js", "des.js", "keysym.js",
|
||||
"keysymdef.js", "keyboard.js", "input.js", "display.js",
|
||||
"rfb.js", "playback.js", "inflator.js", fname]);
|
||||
} else {
|
||||
msg("Must specifiy data=FOO.js in query string.");
|
||||
}
|
||||
|
||||
var start_time, VNC_frame_data, pass, passes, encIdx,
|
||||
encOrder = ['raw', 'rre', 'hextile', 'tightpng', 'copyrect'],
|
||||
encTot = {}, encMin = {}, encMax = {},
|
||||
@@ -126,7 +138,9 @@
|
||||
pass += 1;
|
||||
if (pass > passes) {
|
||||
// We are finished
|
||||
rfb.get_canvas().stop(); // Shut-off event interception
|
||||
// Shut-off event interception
|
||||
rfb.get_mouse().ungrab();
|
||||
rfb.get_keyboard().ungrab();
|
||||
$D('startButton').disabled = false;
|
||||
$D('startButton').value = "Start";
|
||||
finish_passes();
|
||||
@@ -186,16 +200,14 @@
|
||||
" (min/avg/max)");
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
window.onscriptsload = function() {
|
||||
var i, enc;
|
||||
dbgmsg("Frame lengths:");
|
||||
for (i = 0; i < encOrder.length; i++) {
|
||||
enc = encOrder[i];
|
||||
dbgmsg(" " + enc + ": " + VNC_frame_data_multi[enc].length);
|
||||
}
|
||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
||||
'updateState': updateState});
|
||||
rfb.testMode(send_array);
|
||||
$D('startButton').disabled = false;
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>VNC Playback</title>
|
||||
<link rel="stylesheet" href="../include/plain.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -40,9 +39,11 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var INCLUDE_URI= "../include/";
|
||||
// TODO: Data file should override
|
||||
var VNC_frame_encoding = "binary";
|
||||
</script>
|
||||
<script src="../include/vnc.js"></script>
|
||||
<script src="../include/playback.js"></script>
|
||||
<script src="../include/util.js"></script>
|
||||
<script src="../include/webutil.js"></script>
|
||||
|
||||
<script>
|
||||
var fname, start_time;
|
||||
@@ -55,10 +56,13 @@
|
||||
}
|
||||
|
||||
fname = WebUtil.getQueryVar('data', null);
|
||||
|
||||
if (fname) {
|
||||
message("Loading " + fname);
|
||||
document.write('<script src="' + fname + '"><\/script>');
|
||||
// Load supporting scripts
|
||||
Util.load_scripts(["base64.js", "websock.js", "des.js", "keysym.js",
|
||||
"keysymdef.js", "keyboard.js", "input.js", "display.js",
|
||||
"rfb.js", "playback.js", "inflator.js", fname]);
|
||||
|
||||
} else {
|
||||
message("Must specify data=FOO in query string.");
|
||||
}
|
||||
@@ -71,7 +75,6 @@
|
||||
test_state = 'failed';
|
||||
break;
|
||||
case 'loaded':
|
||||
$D('startButton').disabled = false;
|
||||
break;
|
||||
}
|
||||
if (typeof msg !== 'undefined') {
|
||||
@@ -95,7 +98,8 @@
|
||||
mode = 'realtime';
|
||||
}
|
||||
|
||||
recv_message = rfb.testMode(send_array);
|
||||
//recv_message = rfb.testMode(send_array, VNC_frame_encoding);
|
||||
|
||||
next_iteration();
|
||||
}
|
||||
|
||||
@@ -115,7 +119,7 @@
|
||||
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
window.onscriptsload = function () {
|
||||
iterations = WebUtil.getQueryVar('iterations', 3);
|
||||
$D('iterations').value = iterations;
|
||||
mode = WebUtil.getQueryVar('mode', 3);
|
||||
@@ -126,9 +130,8 @@
|
||||
}
|
||||
if (fname) {
|
||||
message("VNC_frame_data.length: " + VNC_frame_data.length);
|
||||
rfb = new RFB({'target': $D('VNC_canvas'),
|
||||
'updateState': updateState});
|
||||
}
|
||||
$D('startButton').disabled = false;
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
TARGETS=rebind.so
|
||||
CFLAGS += -fPIC
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
rebind.so: rebind.o
|
||||
$(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -o $@
|
||||
|
||||
clean:
|
||||
rm -f rebind.o rebind.so
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
## WebSockets Proxy
|
||||
## WebSockets Proxy/Bridge
|
||||
|
||||
wsproxy has become [websockify](https://github.com/kanaka/websockify).
|
||||
A copy of the python version of websockify (named wsproxy.py) is kept
|
||||
here for ease of use. The other versions of websockify (C, Node.js)
|
||||
and the associated test programs have been moved to
|
||||
[websockify](https://github.com/kanaka/websockify).
|
||||
Websockify has been forked out into its own project. `launch.sh` wil
|
||||
automatically download it here if it is not already present and not
|
||||
installed as system-wide.
|
||||
|
||||
For more detailed description and usage information please refer to
|
||||
the [websockify README](https://github.com/kanaka/websockify/blob/master/README.md).
|
||||
|
||||
The other versions of websockify (C, Node.js) and the associated test
|
||||
programs have been moved to
|
||||
[websockify](https://github.com/kanaka/websockify). Websockify was
|
||||
formerly named wsproxy.
|
||||
|
||||
|
||||
17
utils/b64-to-binary.pl
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env perl
|
||||
use MIME::Base64;
|
||||
|
||||
for (<>) {
|
||||
unless (/^'([{}])(\d+)\1(.+?)',$/) {
|
||||
print;
|
||||
next;
|
||||
}
|
||||
|
||||
my ($dir, $amt, $b64) = ($1, $2, $3);
|
||||
|
||||
my $decoded = MIME::Base64::decode($b64) or die "Could not base64-decode line `$_`";
|
||||
|
||||
my $decoded_escaped = join "", map { "\\x$_" } unpack("(H2)*", $decoded);
|
||||
|
||||
print "'${dir}${amt}${dir}${decoded_escaped}',\n";
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# Convert image to Javascript compatible base64 Data URI
|
||||
# Copyright 2011 Joel Martin
|
||||
# Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
||||
# Licensed under MPL 2.0 (see docs/LICENSE.MPL-2.0)
|
||||
#
|
||||
|
||||
import sys, base64
|
||||
|
||||