From 0c04c5ec3b0979bb9e9712baddcfcbfebdb86084 Mon Sep 17 00:00:00 2001 From: Doro Wu Date: Sat, 25 Feb 2017 12:55:24 +0800 Subject: [PATCH] feat: upgrade noVNC --- Dockerfile | 2 +- noVNC/.gitlastcommit | 1 + noVNC/.gitmodules | 3 - noVNC/.npmignore | 20 + noVNC/.travis.yml | 16 +- noVNC/LICENSE.txt | 51 +- noVNC/README.md | 128 +- noVNC/app/images/alt.svg | 92 + noVNC/app/images/clipboard.svg | 106 + noVNC/app/images/connect.svg | 96 + noVNC/app/images/ctrl.svg | 96 + noVNC/app/images/ctrlaltdel.svg | 100 + noVNC/app/images/disconnect.svg | 94 + noVNC/app/images/drag.svg | 76 + noVNC/app/images/error.svg | 81 + noVNC/app/images/esc.svg | 92 + noVNC/app/images/expander.svg | 69 + noVNC/app/images/fullscreen.svg | 93 + noVNC/app/images/handle.svg | 82 + noVNC/app/images/handle_bg.svg | 172 + noVNC/app/images/icons/Makefile | 42 + noVNC/app/images/icons/novnc-120x120.png | Bin 0 -> 4028 bytes noVNC/app/images/icons/novnc-144x144.png | Bin 0 -> 4582 bytes noVNC/app/images/icons/novnc-152x152.png | Bin 0 -> 5216 bytes noVNC/app/images/icons/novnc-16x16.png | Bin 0 -> 675 bytes noVNC/app/images/icons/novnc-192x192.png | Bin 0 -> 5787 bytes noVNC/app/images/icons/novnc-24x24.png | Bin 0 -> 1000 bytes noVNC/app/images/icons/novnc-32x32.png | Bin 0 -> 1064 bytes noVNC/app/images/icons/novnc-48x48.png | Bin 0 -> 1397 bytes noVNC/app/images/icons/novnc-60x60.png | Bin 0 -> 1932 bytes noVNC/app/images/icons/novnc-64x64.png | Bin 0 -> 1946 bytes noVNC/app/images/icons/novnc-72x72.png | Bin 0 -> 2699 bytes noVNC/app/images/icons/novnc-76x76.png | Bin 0 -> 2874 bytes noVNC/app/images/icons/novnc-96x96.png | Bin 0 -> 2351 bytes noVNC/app/images/icons/novnc-icon-sm.svg | 163 + noVNC/app/images/icons/novnc-icon.svg | 163 + noVNC/app/images/info.svg | 81 + noVNC/app/images/keyboard.svg | 88 + noVNC/app/images/mouse_left.svg | 92 + noVNC/app/images/mouse_middle.svg | 92 + noVNC/app/images/mouse_none.svg | 92 + noVNC/app/images/mouse_right.svg | 92 + noVNC/app/images/power.svg | 87 + noVNC/app/images/settings.svg | 76 + noVNC/app/images/tab.svg | 86 + noVNC/app/images/toggleextrakeys.svg | 90 + noVNC/app/images/warning.svg | 81 + noVNC/app/locale/de.js | 18 + noVNC/app/locale/el.js | 74 + noVNC/app/locale/nl.js | 18 + noVNC/app/locale/sv.js | 77 + noVNC/app/sounds/CREDITS | 4 + noVNC/app/sounds/bell.mp3 | Bin 0 -> 4531 bytes noVNC/app/sounds/bell.oga | Bin 0 -> 8495 bytes noVNC/{include => app/styles}/Orbitron700.ttf | Bin .../{include => app/styles}/Orbitron700.woff | Bin noVNC/app/styles/auto.css | 89 + noVNC/app/styles/base.css | 876 + noVNC/app/ui.js | 1761 ++ noVNC/{include => app}/webutil.js | 164 +- noVNC/{include => core}/base64.js | 4 +- noVNC/{include => core}/des.js | 12 +- noVNC/core/display.js | 872 + noVNC/core/inflator.js | 2453 +++ noVNC/core/inflator.mod.js | 40 + .../input.js => core/input/devices.js} | 231 +- noVNC/core/input/keysym.js | 382 + noVNC/{include => core/input}/keysymdef.js | 11 +- .../keyboard.js => core/input/util.js} | 310 +- noVNC/core/input/xtscancodes.js | 151 + noVNC/{include => core}/rfb.js | 1849 +- noVNC/core/util.js | 621 + noVNC/{include => core}/websock.js | 284 +- noVNC/custom.css | 116 - noVNC/docs/LICENSE.pako | 21 + noVNC/docs/VERSION | 2 +- noVNC/docs/notes | 20 +- noVNC/docs/release.txt | 43 +- noVNC/favicon.ico | 1 - .../.@__thumb/defaultscreen_320x460.png | Bin 10699 -> 0 bytes .../.@__thumb/defaultscreen_700x700.png | Bin 13610 -> 0 bytes noVNC/images/.@__thumb/s100screen_320x460.png | Bin 4271 -> 0 bytes noVNC/images/.@__thumb/s100screen_700x700.png | Bin 3277 -> 0 bytes noVNC/images/.@__thumb/s800screen_320x460.png | Bin 10699 -> 0 bytes noVNC/images/.@__thumb/s800screen_700x700.png | Bin 17519 -> 0 bytes noVNC/images/alt.png | Bin 339 -> 0 bytes noVNC/images/clipboard.png | Bin 501 -> 0 bytes noVNC/images/connect.png | Bin 404 -> 0 bytes noVNC/images/ctrl.png | Bin 354 -> 0 bytes noVNC/images/ctrlaltdel.png | Bin 317 -> 0 bytes noVNC/images/disconnect.png | Bin 1378 -> 0 bytes noVNC/images/drag.png | Bin 963 -> 0 bytes noVNC/images/esc.png | Bin 385 -> 0 bytes noVNC/images/favicon.ico | Bin 1150 -> 0 bytes noVNC/images/favicon.png | Bin 453 -> 0 bytes noVNC/images/keyboard.png | Bin 1283 -> 0 bytes noVNC/images/mouse_left.png | Bin 511 -> 0 bytes noVNC/images/mouse_middle.png | Bin 517 -> 0 bytes noVNC/images/mouse_none.png | Bin 497 -> 0 bytes noVNC/images/mouse_right.png | Bin 513 -> 0 bytes noVNC/images/not_scale.png | Bin 401 -> 0 bytes noVNC/images/pause.png | Bin 2824 -> 0 bytes noVNC/images/pop_less.png | Bin 4660 -> 0 bytes noVNC/images/pop_less_hover.png | Bin 4660 -> 0 bytes noVNC/images/pop_more.png | Bin 4673 -> 0 bytes noVNC/images/pop_more_hover.png | Bin 4673 -> 0 bytes noVNC/images/power.png | Bin 390 -> 0 bytes noVNC/images/resume.png | Bin 2963 -> 0 bytes noVNC/images/scale.png | Bin 1254 -> 0 bytes noVNC/images/screen_320x460.png | Bin 12778 -> 0 bytes noVNC/images/screen_57x57.png | Bin 1807 -> 0 bytes noVNC/images/screen_700x700.png | Bin 17930 -> 0 bytes noVNC/images/settings.png | Bin 2495 -> 0 bytes noVNC/images/showextrakeys.png | Bin 735 -> 0 bytes noVNC/images/tab.png | Bin 387 -> 0 bytes noVNC/images/topbackground.png | Bin 2835 -> 0 bytes noVNC/include/base.css | 519 - noVNC/include/black.css | 71 - noVNC/include/blue.css | 64 - noVNC/include/chrome-app/tcp-client.js | 321 - noVNC/include/display.js | 793 - .../include/font-awesome/css/font-awesome.css | 66 - .../include/font-awesome/font/FontAwesome.otf | Bin 62856 -> 0 bytes .../font-awesome/font/fontawesome-webfont.eot | Bin 38205 -> 0 bytes .../font-awesome/font/fontawesome-webfont.svg | 414 - .../font-awesome/font/fontawesome-webfont.ttf | Bin 80652 -> 0 bytes .../font/fontawesome-webfont.woff | Bin 44432 -> 0 bytes noVNC/include/jsunzip.js | 676 - noVNC/include/keysym.js | 378 - noVNC/include/logo.js | 1 - noVNC/include/multiKeyCode.js | 38 - noVNC/include/playback.js | 102 - noVNC/include/ui.js | 1110 -- noVNC/include/util.js | 631 - noVNC/include/web-socket-js/README.txt | 109 - noVNC/include/web-socket-js/WebSocketMain.swf | Bin 177139 -> 0 bytes noVNC/include/web-socket-js/swfobject.js | 4 - noVNC/include/web-socket-js/web_socket.js | 391 - noVNC/index.html | 18 - noVNC/karma.conf.js | 29 +- noVNC/media/css/bootstrap-responsive.css | 1090 -- noVNC/media/css/bootstrap.css | 7241 -------- noVNC/media/css/jquery-ui.css | 1177 -- noVNC/media/css/jquery.alerts.css | 92 - noVNC/media/css/jquery.treeview.css | 84 - noVNC/media/css/radio-checkbox-chrome.css | 72 - noVNC/media/css/radio-checkbox.css | 95 - noVNC/media/docs/QNAP-Enable_VT-x_SOP.pdf | Bin 562832 -> 0 bytes noVNC/media/img/.@__thumb/defaultbtn.gif | Bin 89792 -> 0 bytes .../defaultglyphicons-halflings-green.png | Bin 537 -> 0 bytes .../defaultglyphicons-halflings-white.png | Bin 531 -> 0 bytes .../.@__thumb/defaultglyphicons-halflings.png | Bin 4371 -> 0 bytes noVNC/media/img/.@__thumb/s100btn.gif | Bin 89792 -> 0 bytes .../s100glyphicons-halflings-green.png | Bin 377 -> 0 bytes .../s100glyphicons-halflings-white.png | Bin 373 -> 0 bytes .../.@__thumb/s100glyphicons-halflings.png | Bin 17626 -> 0 bytes noVNC/media/img/.@__thumb/s800btn.gif | Bin 89792 -> 0 bytes .../s800glyphicons-halflings-green.png | Bin 537 -> 0 bytes .../s800glyphicons-halflings-white.png | Bin 531 -> 0 bytes .../.@__thumb/s800glyphicons-halflings.png | Bin 4371 -> 0 bytes noVNC/media/img/_bullet_arrow_down.png | Bin 232 -> 0 bytes noVNC/media/img/_bullet_arrow_up.png | Bin 209 -> 0 bytes noVNC/media/img/account/account.png | Bin 3048 -> 0 bytes noVNC/media/img/account/account_over.png | Bin 3040 -> 0 bytes noVNC/media/img/account/cancel.png | Bin 2970 -> 0 bytes noVNC/media/img/account/checker.png | Bin 3379 -> 0 bytes noVNC/media/img/account/permission.png | Bin 2979 -> 0 bytes noVNC/media/img/account/plus.png | Bin 2948 -> 0 bytes .../img/addSSLHint/FF/.@__thumb/defaulten.jpg | Bin 51249 -> 0 bytes .../img/addSSLHint/FF/.@__thumb/s100en.jpg | Bin 5189 -> 0 bytes .../img/addSSLHint/FF/.@__thumb/s800en.jpg | Bin 86558 -> 0 bytes noVNC/media/img/addSSLHint/FF/en.jpg | Bin 102211 -> 0 bytes noVNC/media/img/arrowbtns.png | Bin 3505 -> 0 bytes noVNC/media/img/bar/Thumbs.db | Bin 5632 -> 0 bytes noVNC/media/img/bar/bblue.png | Bin 2833 -> 0 bytes noVNC/media/img/bar/blue.png | Bin 2832 -> 0 bytes noVNC/media/img/bar/green.png | Bin 2839 -> 0 bytes noVNC/media/img/bar/orange.png | Bin 2837 -> 0 bytes noVNC/media/img/btn.gif | Bin 3319 -> 0 bytes noVNC/media/img/cent_snapshot.jpg | 1 - noVNC/media/img/clone.png | Bin 1034 -> 0 bytes noVNC/media/img/close.png | Bin 2995 -> 0 bytes noVNC/media/img/error.png | Bin 3109 -> 0 bytes noVNC/media/img/error/err_bg.jpg | Bin 1277 -> 0 bytes noVNC/media/img/error/forbidden.png | Bin 7682 -> 0 bytes noVNC/media/img/eye_h.png | Bin 2987 -> 0 bytes noVNC/media/img/eye_l.png | Bin 2966 -> 0 bytes noVNC/media/img/eye_m.png | Bin 3027 -> 0 bytes noVNC/media/img/eye_u.png | Bin 3003 -> 0 bytes noVNC/media/img/favicon.ico | Bin 4215 -> 0 bytes .../media/img/glyphicons-halflings-green.png | Bin 4797 -> 0 bytes .../media/img/glyphicons-halflings-white.png | Bin 4352 -> 0 bytes noVNC/media/img/glyphicons-halflings.png | Bin 4352 -> 0 bytes noVNC/media/img/green_light.gif | Bin 1382 -> 0 bytes noVNC/media/img/grey_light.gif | Bin 1345 -> 0 bytes noVNC/media/img/grid-split.gif | Bin 817 -> 0 bytes noVNC/media/img/icon-info.png | Bin 4515 -> 0 bytes noVNC/media/img/icon-question-b.png | Bin 6581 -> 0 bytes noVNC/media/img/icon_connect.png | Bin 712 -> 0 bytes noVNC/media/img/icon_disconnect.png | Bin 685 -> 0 bytes noVNC/media/img/icons.png | Bin 4665 -> 0 bytes noVNC/media/img/icons_green.png | Bin 2868 -> 0 bytes noVNC/media/img/icons_over.png | Bin 4787 -> 0 bytes noVNC/media/img/icons_yellow.png | Bin 2865 -> 0 bytes noVNC/media/img/line.png | Bin 214 -> 0 bytes noVNC/media/img/list_.png | Bin 2849 -> 0 bytes noVNC/media/img/list_hover.png | Bin 2839 -> 0 bytes noVNC/media/img/loading.gif | Bin 3236 -> 0 bytes .../img/login/.@__thumb/defaultmiddleBg.png | Bin 61013 -> 0 bytes .../img/login/.@__thumb/s100middleBg.png | Bin 9529 -> 0 bytes .../img/login/.@__thumb/s800middleBg.png | Bin 176938 -> 0 bytes noVNC/media/img/login/BgImg.png | Bin 704 -> 0 bytes noVNC/media/img/login/checkBox.png | Bin 429 -> 0 bytes noVNC/media/img/login/dot.png | Bin 1152 -> 0 bytes noVNC/media/img/login/goMore.png | Bin 1173 -> 0 bytes noVNC/media/img/login/loginBtn.png | Bin 430 -> 0 bytes noVNC/media/img/login/loginEnter.png | Bin 691 -> 0 bytes noVNC/media/img/login/loginEnterMobile.png | Bin 768 -> 0 bytes noVNC/media/img/login/logo.png | Bin 1905 -> 0 bytes noVNC/media/img/login/middleBg.png | Bin 130510 -> 0 bytes noVNC/media/img/login/title.png | Bin 4042 -> 0 bytes .../.@__thumb/defaultbitnami_0.jpg | Bin 49939 -> 0 bytes .../.@__thumb/defaultbitnami_1.jpg | Bin 52694 -> 0 bytes .../.@__thumb/defaultbitnami_2.jpg | Bin 33169 -> 0 bytes .../.@__thumb/defaultbitnami_3.jpg | Bin 71436 -> 0 bytes .../.@__thumb/defaultbitnami_4.jpg | Bin 67495 -> 0 bytes .../.@__thumb/defaultbitnami_5.jpg | Bin 13851 -> 0 bytes .../.@__thumb/defaultbitnami_6.jpg | Bin 17068 -> 0 bytes .../.@__thumb/defaultbitnami_7.jpg | Bin 37529 -> 0 bytes .../marketplace/.@__thumb/defaultvmware_0.jpg | Bin 57999 -> 0 bytes .../marketplace/.@__thumb/defaultvmware_1.jpg | Bin 35552 -> 0 bytes .../marketplace/.@__thumb/defaultvmware_2.jpg | Bin 65527 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_0.jpg | Bin 5631 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_1.jpg | Bin 5501 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_2.jpg | Bin 4414 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_3.jpg | Bin 6027 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_4.jpg | Bin 6336 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_5.jpg | Bin 2166 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_6.jpg | Bin 2754 -> 0 bytes .../marketplace/.@__thumb/s100bitnami_7.jpg | Bin 4093 -> 0 bytes .../marketplace/.@__thumb/s100vmware_0.jpg | Bin 6442 -> 0 bytes .../marketplace/.@__thumb/s100vmware_1.jpg | Bin 3806 -> 0 bytes .../marketplace/.@__thumb/s100vmware_2.jpg | Bin 7211 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_0.jpg | Bin 109747 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_1.jpg | Bin 102177 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_2.jpg | Bin 86180 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_3.jpg | Bin 139858 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_4.jpg | Bin 67641 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_5.jpg | Bin 36047 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_6.jpg | Bin 17090 -> 0 bytes .../marketplace/.@__thumb/s800bitnami_7.jpg | Bin 89973 -> 0 bytes .../marketplace/.@__thumb/s800vmware_0.jpg | Bin 155930 -> 0 bytes .../marketplace/.@__thumb/s800vmware_1.jpg | Bin 114133 -> 0 bytes .../marketplace/.@__thumb/s800vmware_2.jpg | Bin 71053 -> 0 bytes noVNC/media/img/marketplace/bitnami_0.jpg | Bin 81019 -> 0 bytes noVNC/media/img/marketplace/bitnami_1.jpg | Bin 69161 -> 0 bytes noVNC/media/img/marketplace/bitnami_2.jpg | Bin 87689 -> 0 bytes noVNC/media/img/marketplace/bitnami_3.jpg | Bin 98121 -> 0 bytes noVNC/media/img/marketplace/bitnami_4.jpg | Bin 68497 -> 0 bytes noVNC/media/img/marketplace/bitnami_5.jpg | Bin 24574 -> 0 bytes noVNC/media/img/marketplace/bitnami_6.jpg | Bin 19638 -> 0 bytes noVNC/media/img/marketplace/bitnami_7.jpg | Bin 61233 -> 0 bytes noVNC/media/img/marketplace/close.png | Bin 3241 -> 0 bytes noVNC/media/img/marketplace/goto.png | Bin 3307 -> 0 bytes noVNC/media/img/marketplace/page_circle.png | Bin 3983 -> 0 bytes noVNC/media/img/marketplace/vmware_0.jpg | Bin 114370 -> 0 bytes noVNC/media/img/marketplace/vmware_1.jpg | Bin 96852 -> 0 bytes noVNC/media/img/marketplace/vmware_2.jpg | Bin 53621 -> 0 bytes noVNC/media/img/minus.jpg | Bin 7758 -> 0 bytes noVNC/media/img/nas_default/item-over.gif | Bin 850 -> 0 bytes noVNC/media/img/nas_default/menu.gif | Bin 834 -> 0 bytes noVNC/media/img/ok.png | Bin 3083 -> 0 bytes noVNC/media/img/open.png | Bin 3004 -> 0 bytes noVNC/media/img/open2.png | Bin 2954 -> 0 bytes noVNC/media/img/page-first-disabled.gif | Bin 925 -> 0 bytes noVNC/media/img/page-first.gif | Bin 925 -> 0 bytes noVNC/media/img/page-last-disabled.gif | Bin 923 -> 0 bytes noVNC/media/img/page-last.gif | Bin 923 -> 0 bytes noVNC/media/img/page-next-disabled.gif | Bin 875 -> 0 bytes noVNC/media/img/page-next.gif | Bin 875 -> 0 bytes noVNC/media/img/page-prev-disabled.gif | Bin 879 -> 0 bytes noVNC/media/img/page-prev.gif | Bin 879 -> 0 bytes noVNC/media/img/page-refresh.gif | Bin 977 -> 0 bytes noVNC/media/img/plus.jpg | Bin 7782 -> 0 bytes noVNC/media/img/qnap-logo-b.png | Bin 839 -> 0 bytes noVNC/media/img/question.png | Bin 2964 -> 0 bytes noVNC/media/img/refresh.png | Bin 602 -> 0 bytes noVNC/media/img/refresh2.png | Bin 3497 -> 0 bytes noVNC/media/img/remove2.png | Bin 2996 -> 0 bytes .../img/slider/ui-bg_flat_0_aaaaaa_40x100.png | Bin 212 -> 0 bytes .../slider/ui-bg_flat_75_ffffff_40x100.png | Bin 208 -> 0 bytes .../slider/ui-bg_glass_55_fbf9ee_1x400.png | Bin 335 -> 0 bytes .../slider/ui-bg_glass_65_ffffff_1x400.png | Bin 207 -> 0 bytes .../slider/ui-bg_glass_75_dadada_1x400.png | Bin 262 -> 0 bytes .../slider/ui-bg_glass_75_e6e6e6_1x400.png | Bin 262 -> 0 bytes .../slider/ui-bg_glass_95_fef1ec_1x400.png | Bin 332 -> 0 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 280 -> 0 bytes noVNC/media/img/switch_off.png | Bin 3501 -> 0 bytes noVNC/media/img/switch_on.png | Bin 3439 -> 0 bytes noVNC/media/img/text-bg.png | Bin 203 -> 0 bytes noVNC/media/img/title_bg.png | Bin 1167 -> 0 bytes noVNC/media/img/tree_root.png | Bin 3500 -> 0 bytes .../.@__thumb/defaulttreeview-black-line.gif | Bin 33396 -> 0 bytes .../defaulttreeview-default-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/defaulttreeview-gray-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/defaulttreeview-red-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s100treeview-black-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s100treeview-default-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s100treeview-gray-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s100treeview-red-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s800treeview-black-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s800treeview-default-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s800treeview-gray-line.gif | Bin 33396 -> 0 bytes .../.@__thumb/s800treeview-red-line.gif | Bin 33396 -> 0 bytes noVNC/media/img/treeview/Thumbs.db | Bin 4608 -> 0 bytes noVNC/media/img/treeview/file.gif | Bin 913 -> 0 bytes noVNC/media/img/treeview/folder-closed.gif | Bin 900 -> 0 bytes noVNC/media/img/treeview/folder-expanded.png | Bin 3029 -> 0 bytes noVNC/media/img/treeview/folder.gif | Bin 904 -> 0 bytes noVNC/media/img/treeview/folder.png | Bin 3128 -> 0 bytes noVNC/media/img/treeview/minus.gif | Bin 837 -> 0 bytes noVNC/media/img/treeview/nas.png | Bin 3006 -> 0 bytes noVNC/media/img/treeview/plus.gif | Bin 841 -> 0 bytes noVNC/media/img/treeview/tree_arrows.png | Bin 3178 -> 0 bytes .../img/treeview/treeview-black-line.gif | Bin 1877 -> 0 bytes noVNC/media/img/treeview/treeview-black.gif | Bin 1216 -> 0 bytes .../img/treeview/treeview-default-line.gif | Bin 1993 -> 0 bytes noVNC/media/img/treeview/treeview-default.gif | Bin 1222 -> 0 bytes .../img/treeview/treeview-famfamfam-line.gif | Bin 807 -> 0 bytes .../media/img/treeview/treeview-famfamfam.gif | Bin 1280 -> 0 bytes .../media/img/treeview/treeview-gray-line.gif | Bin 1877 -> 0 bytes noVNC/media/img/treeview/treeview-gray.gif | Bin 1230 -> 0 bytes .../media/img/treeview/treeview-red-line.gif | Bin 1877 -> 0 bytes noVNC/media/img/treeview/treeview-red.gif | Bin 1230 -> 0 bytes noVNC/media/img/ubuntu_snapshot.jpg | 1 - noVNC/media/img/vnc/auto_login.png | Bin 3152 -> 0 bytes noVNC/media/img/vnc/auto_logout.png | Bin 2948 -> 0 bytes noVNC/media/img/vnc/autofit.png | Bin 1254 -> 0 bytes noVNC/media/img/vnc/c_a_d.png | Bin 2866 -> 0 bytes noVNC/media/img/vnc/cancel.png | Bin 2977 -> 0 bytes noVNC/media/img/vnc/com.png | Bin 2891 -> 0 bytes noVNC/media/img/vnc/destroy.png | Bin 2823 -> 0 bytes noVNC/media/img/vnc/exit_full.png | Bin 2878 -> 0 bytes noVNC/media/img/vnc/full_screen.png | Bin 2908 -> 0 bytes noVNC/media/img/vnc/install.png | Bin 3407 -> 0 bytes noVNC/media/img/vnc/logout.png | Bin 3275 -> 0 bytes noVNC/media/img/vnc/reset.png | Bin 3083 -> 0 bytes noVNC/media/img/vnc/restore.png | Bin 401 -> 0 bytes noVNC/media/img/vnc/resume.png | Bin 2963 -> 0 bytes noVNC/media/img/vnc/shutdown.png | Bin 3096 -> 0 bytes noVNC/media/img/vnc/snapshot.png | Bin 2910 -> 0 bytes noVNC/media/img/vnc/suspend.png | Bin 2824 -> 0 bytes noVNC/media/img/wait.gif | Bin 1100 -> 0 bytes noVNC/media/img/wizard/Thumbs.db | Bin 35840 -> 0 bytes noVNC/media/img/wizard/circle.png | Bin 2679 -> 0 bytes noVNC/media/img/wizard/icon.png | Bin 11907 -> 0 bytes noVNC/media/img/wizard/n1.png | Bin 330 -> 0 bytes noVNC/media/img/wizard/n2.png | Bin 390 -> 0 bytes noVNC/media/img/wizard/window.png | Bin 20947 -> 0 bytes noVNC/media/java/tightvnc-jviewer-nossh.jar | Bin 266436 -> 0 bytes noVNC/media/java/vncviewer.jar | Bin 344518 -> 0 bytes noVNC/media/js/QFlot.js | 121 - noVNC/media/js/QFolderTree.js | 227 - noVNC/media/js/QSpingEdit.js | 197 - noVNC/media/js/QUtility.js | 1182 -- noVNC/media/js/ZeroClipboard.swf | Bin 1071 -> 0 bytes noVNC/media/js/bootstrap-alert.js | 94 - noVNC/media/js/bootstrap-button.js | 100 - noVNC/media/js/bootstrap-carousel.js | 161 - noVNC/media/js/bootstrap-collapse.js | 138 - noVNC/media/js/bootstrap-dropdown.js | 92 - noVNC/media/js/bootstrap-file-input.js | 113 - noVNC/media/js/bootstrap-modal.js | 211 - noVNC/media/js/bootstrap-popover.js | 95 - noVNC/media/js/bootstrap-scrollspy.js | 125 - noVNC/media/js/bootstrap-tab.js | 130 - noVNC/media/js/bootstrap-tooltip.js | 270 - noVNC/media/js/bootstrap-transition.js | 60 - noVNC/media/js/bootstrap-typeahead.js | 271 - noVNC/media/js/bootstrap.js | 1726 -- noVNC/media/js/bootstrap.min.js | 6 - noVNC/media/js/excanvas.min.js | 1 - noVNC/media/js/get_sid.js | 186 - noVNC/media/js/jquery-ui.js | 15009 ---------------- noVNC/media/js/jquery.alerts.js | 281 - noVNC/media/js/jquery.flot.min.js | 6 - noVNC/media/js/jquery.fullscreen-min.js | 10 - noVNC/media/js/jquery.js | 9252 ---------- noVNC/media/js/jquery.treeview.js | 251 - noVNC/media/js/jquery.zclip.js | 497 - noVNC/media/js/multiLanguage.js | 29 - noVNC/media/js/scroll_fixed.js | 16 - noVNC/media/js/templates/importvm.js | 48 - noVNC/media/js/templates/vm.js | 143 - noVNC/media/js/templates/wizard.js | 29 - noVNC/novnc.html | 1111 -- noVNC/package.json | 49 +- noVNC/po/Makefile | 34 + noVNC/po/de.po | 58 + noVNC/po/el.po | 293 + noVNC/po/nl.po | 58 + noVNC/po/noVNC.pot | 291 + noVNC/po/po2js | 56 + noVNC/po/sv.po | 293 + noVNC/po/xgettext-html | 117 + noVNC/tests/arrays.html | 39 - noVNC/tests/arrays.js | 375 - noVNC/tests/assertions.js | 89 +- noVNC/tests/base64.html | 91 - noVNC/tests/base64.js | 12 - noVNC/tests/browser.js | 134 - noVNC/tests/canvas.html | 148 - noVNC/tests/cursor.html | 135 - noVNC/tests/face.png | Bin 2303 -> 0 bytes noVNC/tests/face.png.js | 1 - noVNC/tests/fake.websocket.js | 9 +- noVNC/tests/input.html | 51 +- noVNC/tests/keyboard-tests.html | 29 - noVNC/tests/playback.js | 198 + noVNC/tests/run_from_console.casper.js | 16 +- noVNC/tests/run_from_console.js | 28 +- noVNC/tests/stats.js | 53 - noVNC/tests/test.display.js | 311 +- noVNC/tests/test.helper.js | 108 +- noVNC/tests/test.keyboard.js | 114 +- noVNC/tests/test.rfb.js | 1029 +- noVNC/tests/test.util.js | 125 +- noVNC/tests/test.websock.js | 167 +- noVNC/tests/viewport.css | 43 - noVNC/tests/viewport.html | 203 - noVNC/tests/vnc_perf.html | 76 +- noVNC/tests/vnc_playback.html | 70 +- noVNC/utils/b64-to-binary.pl | 17 + noVNC/utils/json2graph.py | 2 +- noVNC/utils/launch.sh | 23 +- noVNC/utils/make-module-transform.js | 25 + noVNC/utils/parse.js | 8 +- noVNC/utils/use_require.js | 121 + noVNC/utils/websockify/.travis.yml | 10 + noVNC/utils/websockify/CHANGES.txt | 28 + noVNC/utils/websockify/LICENSE.txt | 2 - noVNC/utils/websockify/README.md | 54 +- noVNC/utils/websockify/docs/notes | 18 - noVNC/utils/websockify/docs/websockify.1 | 5 +- noVNC/utils/websockify/include/base64.js | 114 - noVNC/utils/websockify/include/util.js | 15 - .../include/web-socket-js/README.txt | 109 - .../include/web-socket-js/WebSocketMain.swf | Bin 177139 -> 0 bytes .../include/web-socket-js/swfobject.js | 4 - .../include/web-socket-js/web_socket.js | 391 - noVNC/utils/websockify/include/websock.js | 682 +- noVNC/utils/websockify/other/js/package.json | 5 +- noVNC/utils/websockify/other/js/websockify.js | 35 +- noVNC/utils/websockify/other/websocket.c | 24 +- noVNC/utils/websockify/other/websocket.rb | 107 +- noVNC/utils/websockify/run | 2 +- noVNC/utils/websockify/setup.py | 2 +- noVNC/utils/websockify/test-requirements.txt | 2 + noVNC/utils/websockify/tests/b64_vs_utf8.py | 29 - noVNC/utils/websockify/tests/base64.html | 91 - noVNC/utils/websockify/tests/base64.js | 12 - noVNC/utils/websockify/tests/echo.html | 1 - noVNC/utils/websockify/tests/echo.py | 8 +- noVNC/utils/websockify/tests/echo_client.py | 70 + noVNC/utils/websockify/tests/latency.html | 36 +- noVNC/utils/websockify/tests/load.html | 19 - noVNC/utils/websockify/tests/load.py | 20 +- noVNC/utils/websockify/tests/plain_echo.html | 20 +- noVNC/utils/websockify/tests/simple.html | 1 - .../websockify/tests/test_auth_plugins.py | 28 + .../utils/websockify/tests/test_websocket.py | 341 +- .../websockify/tests/test_websocketproxy.py | 191 +- .../websockify/tests/test_websockifyserver.py | 347 + noVNC/utils/websockify/tests/utf8-list.py | 19 - noVNC/utils/websockify/{tests => }/tox.ini | 11 +- .../websockify/websockify/auth_plugins.py | 83 + .../websockify/websockify/token_plugins.py | 9 +- .../utils/websockify/websockify/websocket.py | 1691 +- .../websockify/websockify/websocketproxy.py | 153 +- .../websockify/websockify/websocketserver.py | 95 + .../websockify/websockify/websockifyserver.py | 797 + noVNC/utils/websockify/wsirc.html | 1 - noVNC/utils/websockify/wstelnet.html | 1 - noVNC/vnc.html | 422 +- noVNC/vnc_auto.html | 468 +- .../connect/node_modules/qs/.gitmodules | 6 - .../node_modules/mkdirp/.gitignore.orig | 2 - .../node_modules/mkdirp/.gitignore.rej | 5 - web/lightop/__init__.py | 2 +- 489 files changed, 18241 insertions(+), 54521 deletions(-) create mode 100644 noVNC/.gitlastcommit delete mode 100644 noVNC/.gitmodules create mode 100644 noVNC/.npmignore create mode 100644 noVNC/app/images/alt.svg create mode 100644 noVNC/app/images/clipboard.svg create mode 100644 noVNC/app/images/connect.svg create mode 100644 noVNC/app/images/ctrl.svg create mode 100644 noVNC/app/images/ctrlaltdel.svg create mode 100644 noVNC/app/images/disconnect.svg create mode 100644 noVNC/app/images/drag.svg create mode 100644 noVNC/app/images/error.svg create mode 100644 noVNC/app/images/esc.svg create mode 100644 noVNC/app/images/expander.svg create mode 100644 noVNC/app/images/fullscreen.svg create mode 100644 noVNC/app/images/handle.svg create mode 100644 noVNC/app/images/handle_bg.svg create mode 100644 noVNC/app/images/icons/Makefile create mode 100644 noVNC/app/images/icons/novnc-120x120.png create mode 100644 noVNC/app/images/icons/novnc-144x144.png create mode 100644 noVNC/app/images/icons/novnc-152x152.png create mode 100644 noVNC/app/images/icons/novnc-16x16.png create mode 100644 noVNC/app/images/icons/novnc-192x192.png create mode 100644 noVNC/app/images/icons/novnc-24x24.png create mode 100644 noVNC/app/images/icons/novnc-32x32.png create mode 100644 noVNC/app/images/icons/novnc-48x48.png create mode 100644 noVNC/app/images/icons/novnc-60x60.png create mode 100644 noVNC/app/images/icons/novnc-64x64.png create mode 100644 noVNC/app/images/icons/novnc-72x72.png create mode 100644 noVNC/app/images/icons/novnc-76x76.png create mode 100644 noVNC/app/images/icons/novnc-96x96.png create mode 100644 noVNC/app/images/icons/novnc-icon-sm.svg create mode 100644 noVNC/app/images/icons/novnc-icon.svg create mode 100644 noVNC/app/images/info.svg create mode 100644 noVNC/app/images/keyboard.svg create mode 100644 noVNC/app/images/mouse_left.svg create mode 100644 noVNC/app/images/mouse_middle.svg create mode 100644 noVNC/app/images/mouse_none.svg create mode 100644 noVNC/app/images/mouse_right.svg create mode 100644 noVNC/app/images/power.svg create mode 100644 noVNC/app/images/settings.svg create mode 100644 noVNC/app/images/tab.svg create mode 100644 noVNC/app/images/toggleextrakeys.svg create mode 100644 noVNC/app/images/warning.svg create mode 100644 noVNC/app/locale/de.js create mode 100644 noVNC/app/locale/el.js create mode 100644 noVNC/app/locale/nl.js create mode 100644 noVNC/app/locale/sv.js create mode 100644 noVNC/app/sounds/CREDITS create mode 100644 noVNC/app/sounds/bell.mp3 create mode 100644 noVNC/app/sounds/bell.oga rename noVNC/{include => app/styles}/Orbitron700.ttf (100%) rename noVNC/{include => app/styles}/Orbitron700.woff (100%) create mode 100644 noVNC/app/styles/auto.css create mode 100644 noVNC/app/styles/base.css create mode 100644 noVNC/app/ui.js rename noVNC/{include => app}/webutil.js (53%) rename noVNC/{include => core}/base64.js (99%) rename noVNC/{include => core}/des.js (99%) create mode 100644 noVNC/core/display.js create mode 100644 noVNC/core/inflator.js create mode 100644 noVNC/core/inflator.mod.js rename noVNC/{include/input.js => core/input/devices.js} (57%) create mode 100644 noVNC/core/input/keysym.js rename noVNC/{include => core/input}/keysymdef.js (98%) rename noVNC/{include/keyboard.js => core/input/util.js} (66%) create mode 100644 noVNC/core/input/xtscancodes.js rename noVNC/{include => core}/rfb.js (51%) create mode 100644 noVNC/core/util.js rename noVNC/{include => core}/websock.js (53%) delete mode 100644 noVNC/custom.css create mode 100644 noVNC/docs/LICENSE.pako delete mode 120000 noVNC/favicon.ico delete mode 100755 noVNC/images/.@__thumb/defaultscreen_320x460.png delete mode 100755 noVNC/images/.@__thumb/defaultscreen_700x700.png delete mode 100755 noVNC/images/.@__thumb/s100screen_320x460.png delete mode 100755 noVNC/images/.@__thumb/s100screen_700x700.png delete mode 100755 noVNC/images/.@__thumb/s800screen_320x460.png delete mode 100755 noVNC/images/.@__thumb/s800screen_700x700.png delete mode 100644 noVNC/images/alt.png delete mode 100644 noVNC/images/clipboard.png delete mode 100644 noVNC/images/connect.png delete mode 100644 noVNC/images/ctrl.png delete mode 100644 noVNC/images/ctrlaltdel.png delete mode 100644 noVNC/images/disconnect.png delete mode 100644 noVNC/images/drag.png delete mode 100644 noVNC/images/esc.png delete mode 100644 noVNC/images/favicon.ico delete mode 100644 noVNC/images/favicon.png delete mode 100644 noVNC/images/keyboard.png delete mode 100644 noVNC/images/mouse_left.png delete mode 100644 noVNC/images/mouse_middle.png delete mode 100644 noVNC/images/mouse_none.png delete mode 100644 noVNC/images/mouse_right.png delete mode 100644 noVNC/images/not_scale.png delete mode 100644 noVNC/images/pause.png delete mode 100755 noVNC/images/pop_less.png delete mode 100755 noVNC/images/pop_less_hover.png delete mode 100755 noVNC/images/pop_more.png delete mode 100755 noVNC/images/pop_more_hover.png delete mode 100644 noVNC/images/power.png delete mode 100644 noVNC/images/resume.png delete mode 100644 noVNC/images/scale.png delete mode 100644 noVNC/images/screen_320x460.png delete mode 100644 noVNC/images/screen_57x57.png delete mode 100644 noVNC/images/screen_700x700.png delete mode 100644 noVNC/images/settings.png delete mode 100644 noVNC/images/showextrakeys.png delete mode 100644 noVNC/images/tab.png delete mode 100755 noVNC/images/topbackground.png delete mode 100644 noVNC/include/base.css delete mode 100644 noVNC/include/black.css delete mode 100644 noVNC/include/blue.css delete mode 100644 noVNC/include/chrome-app/tcp-client.js delete mode 100644 noVNC/include/display.js delete mode 100755 noVNC/include/font-awesome/css/font-awesome.css delete mode 100755 noVNC/include/font-awesome/font/FontAwesome.otf delete mode 100755 noVNC/include/font-awesome/font/fontawesome-webfont.eot delete mode 100755 noVNC/include/font-awesome/font/fontawesome-webfont.svg delete mode 100755 noVNC/include/font-awesome/font/fontawesome-webfont.ttf delete mode 100755 noVNC/include/font-awesome/font/fontawesome-webfont.woff delete mode 100755 noVNC/include/jsunzip.js delete mode 100644 noVNC/include/keysym.js delete mode 100644 noVNC/include/logo.js delete mode 100755 noVNC/include/multiKeyCode.js delete mode 100644 noVNC/include/playback.js delete mode 100644 noVNC/include/ui.js delete mode 100644 noVNC/include/util.js delete mode 100644 noVNC/include/web-socket-js/README.txt delete mode 100644 noVNC/include/web-socket-js/WebSocketMain.swf delete mode 100644 noVNC/include/web-socket-js/swfobject.js delete mode 100644 noVNC/include/web-socket-js/web_socket.js delete mode 100644 noVNC/index.html delete mode 100755 noVNC/media/css/bootstrap-responsive.css delete mode 100755 noVNC/media/css/bootstrap.css delete mode 100755 noVNC/media/css/jquery-ui.css delete mode 100755 noVNC/media/css/jquery.alerts.css delete mode 100755 noVNC/media/css/jquery.treeview.css delete mode 100755 noVNC/media/css/radio-checkbox-chrome.css delete mode 100755 noVNC/media/css/radio-checkbox.css delete mode 100755 noVNC/media/docs/QNAP-Enable_VT-x_SOP.pdf delete mode 100755 noVNC/media/img/.@__thumb/defaultbtn.gif delete mode 100755 noVNC/media/img/.@__thumb/defaultglyphicons-halflings-green.png delete mode 100755 noVNC/media/img/.@__thumb/defaultglyphicons-halflings-white.png delete mode 100755 noVNC/media/img/.@__thumb/defaultglyphicons-halflings.png delete mode 100755 noVNC/media/img/.@__thumb/s100btn.gif delete mode 100755 noVNC/media/img/.@__thumb/s100glyphicons-halflings-green.png delete mode 100755 noVNC/media/img/.@__thumb/s100glyphicons-halflings-white.png delete mode 100755 noVNC/media/img/.@__thumb/s100glyphicons-halflings.png delete mode 100755 noVNC/media/img/.@__thumb/s800btn.gif delete mode 100755 noVNC/media/img/.@__thumb/s800glyphicons-halflings-green.png delete mode 100755 noVNC/media/img/.@__thumb/s800glyphicons-halflings-white.png delete mode 100755 noVNC/media/img/.@__thumb/s800glyphicons-halflings.png delete mode 100755 noVNC/media/img/_bullet_arrow_down.png delete mode 100755 noVNC/media/img/_bullet_arrow_up.png delete mode 100755 noVNC/media/img/account/account.png delete mode 100755 noVNC/media/img/account/account_over.png delete mode 100755 noVNC/media/img/account/cancel.png delete mode 100755 noVNC/media/img/account/checker.png delete mode 100755 noVNC/media/img/account/permission.png delete mode 100755 noVNC/media/img/account/plus.png delete mode 100755 noVNC/media/img/addSSLHint/FF/.@__thumb/defaulten.jpg delete mode 100755 noVNC/media/img/addSSLHint/FF/.@__thumb/s100en.jpg delete mode 100755 noVNC/media/img/addSSLHint/FF/.@__thumb/s800en.jpg delete mode 100755 noVNC/media/img/addSSLHint/FF/en.jpg delete mode 100755 noVNC/media/img/arrowbtns.png delete mode 100755 noVNC/media/img/bar/Thumbs.db delete mode 100755 noVNC/media/img/bar/bblue.png delete mode 100755 noVNC/media/img/bar/blue.png delete mode 100755 noVNC/media/img/bar/green.png delete mode 100755 noVNC/media/img/bar/orange.png delete mode 100755 noVNC/media/img/btn.gif delete mode 120000 noVNC/media/img/cent_snapshot.jpg delete mode 100755 noVNC/media/img/clone.png delete mode 100755 noVNC/media/img/close.png delete mode 100755 noVNC/media/img/error.png delete mode 100755 noVNC/media/img/error/err_bg.jpg delete mode 100755 noVNC/media/img/error/forbidden.png delete mode 100755 noVNC/media/img/eye_h.png delete mode 100755 noVNC/media/img/eye_l.png delete mode 100755 noVNC/media/img/eye_m.png delete mode 100755 noVNC/media/img/eye_u.png delete mode 100755 noVNC/media/img/favicon.ico delete mode 100755 noVNC/media/img/glyphicons-halflings-green.png delete mode 100755 noVNC/media/img/glyphicons-halflings-white.png delete mode 100755 noVNC/media/img/glyphicons-halflings.png delete mode 100755 noVNC/media/img/green_light.gif delete mode 100755 noVNC/media/img/grey_light.gif delete mode 100755 noVNC/media/img/grid-split.gif delete mode 100755 noVNC/media/img/icon-info.png delete mode 100755 noVNC/media/img/icon-question-b.png delete mode 100755 noVNC/media/img/icon_connect.png delete mode 100755 noVNC/media/img/icon_disconnect.png delete mode 100755 noVNC/media/img/icons.png delete mode 100755 noVNC/media/img/icons_green.png delete mode 100755 noVNC/media/img/icons_over.png delete mode 100755 noVNC/media/img/icons_yellow.png delete mode 100755 noVNC/media/img/line.png delete mode 100755 noVNC/media/img/list_.png delete mode 100755 noVNC/media/img/list_hover.png delete mode 100755 noVNC/media/img/loading.gif delete mode 100755 noVNC/media/img/login/.@__thumb/defaultmiddleBg.png delete mode 100755 noVNC/media/img/login/.@__thumb/s100middleBg.png delete mode 100755 noVNC/media/img/login/.@__thumb/s800middleBg.png delete mode 100755 noVNC/media/img/login/BgImg.png delete mode 100755 noVNC/media/img/login/checkBox.png delete mode 100755 noVNC/media/img/login/dot.png delete mode 100755 noVNC/media/img/login/goMore.png delete mode 100755 noVNC/media/img/login/loginBtn.png delete mode 100755 noVNC/media/img/login/loginEnter.png delete mode 100755 noVNC/media/img/login/loginEnterMobile.png delete mode 100755 noVNC/media/img/login/logo.png delete mode 100755 noVNC/media/img/login/middleBg.png delete mode 100755 noVNC/media/img/login/title.png delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_0.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_1.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_2.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_3.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_4.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_5.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_6.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultbitnami_7.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultvmware_0.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultvmware_1.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/defaultvmware_2.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_0.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_1.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_2.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_3.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_4.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_5.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_6.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100bitnami_7.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100vmware_0.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100vmware_1.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s100vmware_2.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_0.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_1.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_2.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_3.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_4.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_5.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_6.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800bitnami_7.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800vmware_0.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800vmware_1.jpg delete mode 100755 noVNC/media/img/marketplace/.@__thumb/s800vmware_2.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_0.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_1.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_2.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_3.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_4.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_5.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_6.jpg delete mode 100755 noVNC/media/img/marketplace/bitnami_7.jpg delete mode 100755 noVNC/media/img/marketplace/close.png delete mode 100755 noVNC/media/img/marketplace/goto.png delete mode 100755 noVNC/media/img/marketplace/page_circle.png delete mode 100755 noVNC/media/img/marketplace/vmware_0.jpg delete mode 100755 noVNC/media/img/marketplace/vmware_1.jpg delete mode 100755 noVNC/media/img/marketplace/vmware_2.jpg delete mode 100755 noVNC/media/img/minus.jpg delete mode 100755 noVNC/media/img/nas_default/item-over.gif delete mode 100755 noVNC/media/img/nas_default/menu.gif delete mode 100755 noVNC/media/img/ok.png delete mode 100755 noVNC/media/img/open.png delete mode 100755 noVNC/media/img/open2.png delete mode 100755 noVNC/media/img/page-first-disabled.gif delete mode 100755 noVNC/media/img/page-first.gif delete mode 100755 noVNC/media/img/page-last-disabled.gif delete mode 100755 noVNC/media/img/page-last.gif delete mode 100755 noVNC/media/img/page-next-disabled.gif delete mode 100755 noVNC/media/img/page-next.gif delete mode 100755 noVNC/media/img/page-prev-disabled.gif delete mode 100755 noVNC/media/img/page-prev.gif delete mode 100755 noVNC/media/img/page-refresh.gif delete mode 100755 noVNC/media/img/plus.jpg delete mode 100755 noVNC/media/img/qnap-logo-b.png delete mode 100755 noVNC/media/img/question.png delete mode 100755 noVNC/media/img/refresh.png delete mode 100755 noVNC/media/img/refresh2.png delete mode 100755 noVNC/media/img/remove2.png delete mode 100755 noVNC/media/img/slider/ui-bg_flat_0_aaaaaa_40x100.png delete mode 100755 noVNC/media/img/slider/ui-bg_flat_75_ffffff_40x100.png delete mode 100755 noVNC/media/img/slider/ui-bg_glass_55_fbf9ee_1x400.png delete mode 100755 noVNC/media/img/slider/ui-bg_glass_65_ffffff_1x400.png delete mode 100755 noVNC/media/img/slider/ui-bg_glass_75_dadada_1x400.png delete mode 100755 noVNC/media/img/slider/ui-bg_glass_75_e6e6e6_1x400.png delete mode 100755 noVNC/media/img/slider/ui-bg_glass_95_fef1ec_1x400.png delete mode 100755 noVNC/media/img/slider/ui-bg_highlight-soft_75_cccccc_1x100.png delete mode 100755 noVNC/media/img/switch_off.png delete mode 100755 noVNC/media/img/switch_on.png delete mode 100755 noVNC/media/img/text-bg.png delete mode 100755 noVNC/media/img/title_bg.png delete mode 100755 noVNC/media/img/tree_root.png delete mode 100755 noVNC/media/img/treeview/.@__thumb/defaulttreeview-black-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/defaulttreeview-default-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/defaulttreeview-gray-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/defaulttreeview-red-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s100treeview-black-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s100treeview-default-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s100treeview-gray-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s100treeview-red-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s800treeview-black-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s800treeview-default-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s800treeview-gray-line.gif delete mode 100755 noVNC/media/img/treeview/.@__thumb/s800treeview-red-line.gif delete mode 100755 noVNC/media/img/treeview/Thumbs.db delete mode 100755 noVNC/media/img/treeview/file.gif delete mode 100755 noVNC/media/img/treeview/folder-closed.gif delete mode 100755 noVNC/media/img/treeview/folder-expanded.png delete mode 100755 noVNC/media/img/treeview/folder.gif delete mode 100755 noVNC/media/img/treeview/folder.png delete mode 100755 noVNC/media/img/treeview/minus.gif delete mode 100755 noVNC/media/img/treeview/nas.png delete mode 100755 noVNC/media/img/treeview/plus.gif delete mode 100755 noVNC/media/img/treeview/tree_arrows.png delete mode 100755 noVNC/media/img/treeview/treeview-black-line.gif delete mode 100755 noVNC/media/img/treeview/treeview-black.gif delete mode 100755 noVNC/media/img/treeview/treeview-default-line.gif delete mode 100755 noVNC/media/img/treeview/treeview-default.gif delete mode 100755 noVNC/media/img/treeview/treeview-famfamfam-line.gif delete mode 100755 noVNC/media/img/treeview/treeview-famfamfam.gif delete mode 100755 noVNC/media/img/treeview/treeview-gray-line.gif delete mode 100755 noVNC/media/img/treeview/treeview-gray.gif delete mode 100755 noVNC/media/img/treeview/treeview-red-line.gif delete mode 100755 noVNC/media/img/treeview/treeview-red.gif delete mode 120000 noVNC/media/img/ubuntu_snapshot.jpg delete mode 100755 noVNC/media/img/vnc/auto_login.png delete mode 100755 noVNC/media/img/vnc/auto_logout.png delete mode 100755 noVNC/media/img/vnc/autofit.png delete mode 100755 noVNC/media/img/vnc/c_a_d.png delete mode 100755 noVNC/media/img/vnc/cancel.png delete mode 100755 noVNC/media/img/vnc/com.png delete mode 100755 noVNC/media/img/vnc/destroy.png delete mode 100755 noVNC/media/img/vnc/exit_full.png delete mode 100755 noVNC/media/img/vnc/full_screen.png delete mode 100755 noVNC/media/img/vnc/install.png delete mode 100755 noVNC/media/img/vnc/logout.png delete mode 100755 noVNC/media/img/vnc/reset.png delete mode 100755 noVNC/media/img/vnc/restore.png delete mode 100755 noVNC/media/img/vnc/resume.png delete mode 100755 noVNC/media/img/vnc/shutdown.png delete mode 100755 noVNC/media/img/vnc/snapshot.png delete mode 100755 noVNC/media/img/vnc/suspend.png delete mode 100755 noVNC/media/img/wait.gif delete mode 100755 noVNC/media/img/wizard/Thumbs.db delete mode 100755 noVNC/media/img/wizard/circle.png delete mode 100755 noVNC/media/img/wizard/icon.png delete mode 100755 noVNC/media/img/wizard/n1.png delete mode 100755 noVNC/media/img/wizard/n2.png delete mode 100755 noVNC/media/img/wizard/window.png delete mode 100755 noVNC/media/java/tightvnc-jviewer-nossh.jar delete mode 100755 noVNC/media/java/vncviewer.jar delete mode 100755 noVNC/media/js/QFlot.js delete mode 100755 noVNC/media/js/QFolderTree.js delete mode 100755 noVNC/media/js/QSpingEdit.js delete mode 100755 noVNC/media/js/QUtility.js delete mode 100755 noVNC/media/js/ZeroClipboard.swf delete mode 100755 noVNC/media/js/bootstrap-alert.js delete mode 100755 noVNC/media/js/bootstrap-button.js delete mode 100755 noVNC/media/js/bootstrap-carousel.js delete mode 100755 noVNC/media/js/bootstrap-collapse.js delete mode 100755 noVNC/media/js/bootstrap-dropdown.js delete mode 100755 noVNC/media/js/bootstrap-file-input.js delete mode 100755 noVNC/media/js/bootstrap-modal.js delete mode 100755 noVNC/media/js/bootstrap-popover.js delete mode 100755 noVNC/media/js/bootstrap-scrollspy.js delete mode 100755 noVNC/media/js/bootstrap-tab.js delete mode 100755 noVNC/media/js/bootstrap-tooltip.js delete mode 100755 noVNC/media/js/bootstrap-transition.js delete mode 100755 noVNC/media/js/bootstrap-typeahead.js delete mode 100755 noVNC/media/js/bootstrap.js delete mode 100755 noVNC/media/js/bootstrap.min.js delete mode 100755 noVNC/media/js/excanvas.min.js delete mode 100755 noVNC/media/js/get_sid.js delete mode 100755 noVNC/media/js/jquery-ui.js delete mode 100755 noVNC/media/js/jquery.alerts.js delete mode 100755 noVNC/media/js/jquery.flot.min.js delete mode 100755 noVNC/media/js/jquery.fullscreen-min.js delete mode 100755 noVNC/media/js/jquery.js delete mode 100755 noVNC/media/js/jquery.treeview.js delete mode 100755 noVNC/media/js/jquery.zclip.js delete mode 100755 noVNC/media/js/multiLanguage.js delete mode 100755 noVNC/media/js/scroll_fixed.js delete mode 100755 noVNC/media/js/templates/importvm.js delete mode 100755 noVNC/media/js/templates/vm.js delete mode 100755 noVNC/media/js/templates/wizard.js delete mode 100755 noVNC/novnc.html create mode 100644 noVNC/po/Makefile create mode 100644 noVNC/po/de.po create mode 100644 noVNC/po/el.po create mode 100644 noVNC/po/nl.po create mode 100644 noVNC/po/noVNC.pot create mode 100755 noVNC/po/po2js create mode 100644 noVNC/po/sv.po create mode 100755 noVNC/po/xgettext-html delete mode 100644 noVNC/tests/arrays.html delete mode 100644 noVNC/tests/arrays.js delete mode 100644 noVNC/tests/base64.html delete mode 100644 noVNC/tests/base64.js delete mode 100644 noVNC/tests/browser.js delete mode 100644 noVNC/tests/canvas.html delete mode 100644 noVNC/tests/cursor.html delete mode 100644 noVNC/tests/face.png delete mode 100644 noVNC/tests/face.png.js delete mode 100644 noVNC/tests/keyboard-tests.html create mode 100644 noVNC/tests/playback.js delete mode 100644 noVNC/tests/stats.js delete mode 100644 noVNC/tests/viewport.css delete mode 100644 noVNC/tests/viewport.html create mode 100755 noVNC/utils/b64-to-binary.pl create mode 100644 noVNC/utils/make-module-transform.js create mode 100755 noVNC/utils/use_require.js create mode 100644 noVNC/utils/websockify/.travis.yml delete mode 100644 noVNC/utils/websockify/include/base64.js delete mode 100644 noVNC/utils/websockify/include/web-socket-js/README.txt delete mode 100644 noVNC/utils/websockify/include/web-socket-js/WebSocketMain.swf delete mode 100644 noVNC/utils/websockify/include/web-socket-js/swfobject.js delete mode 100755 noVNC/utils/websockify/include/web-socket-js/web_socket.js create mode 100644 noVNC/utils/websockify/test-requirements.txt delete mode 100755 noVNC/utils/websockify/tests/b64_vs_utf8.py delete mode 100644 noVNC/utils/websockify/tests/base64.html delete mode 100644 noVNC/utils/websockify/tests/base64.js create mode 100755 noVNC/utils/websockify/tests/echo_client.py create mode 100644 noVNC/utils/websockify/tests/test_auth_plugins.py create mode 100644 noVNC/utils/websockify/tests/test_websockifyserver.py delete mode 100755 noVNC/utils/websockify/tests/utf8-list.py rename noVNC/utils/websockify/{tests => }/tox.ini (65%) create mode 100644 noVNC/utils/websockify/websockify/auth_plugins.py create mode 100644 noVNC/utils/websockify/websockify/websocketserver.py create mode 100644 noVNC/utils/websockify/websockify/websockifyserver.py delete mode 100644 tty.js/node_modules/tty.js/node_modules/express/node_modules/connect/node_modules/qs/.gitmodules delete mode 100644 tty.js/node_modules/tty.js/node_modules/express/node_modules/mkdirp/.gitignore.orig delete mode 100644 tty.js/node_modules/tty.js/node_modules/express/node_modules/mkdirp/.gitignore.rej diff --git a/Dockerfile b/Dockerfile index ef68d2f..b6329f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:14.04.3 +FROM ubuntu:14.04 MAINTAINER Doro Wu ENV DEBIAN_FRONTEND noninteractive diff --git a/noVNC/.gitlastcommit b/noVNC/.gitlastcommit new file mode 100644 index 0000000..5c223b7 --- /dev/null +++ b/noVNC/.gitlastcommit @@ -0,0 +1 @@ +56d97524807b125d047730331031ddd00f9c61f diff --git a/noVNC/.gitmodules b/noVNC/.gitmodules deleted file mode 100644 index 45574ae..0000000 --- a/noVNC/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "include/web-socket-js-project"] - path = include/web-socket-js-project - url = https://github.com/gimite/web-socket-js.git diff --git a/noVNC/.npmignore b/noVNC/.npmignore new file mode 100644 index 0000000..b572f7a --- /dev/null +++ b/noVNC/.npmignore @@ -0,0 +1,20 @@ +app +core +.gitmodules +node_modules +.* +*~ +*.swp +*.swo +tests +.travis.yml +utils +docs/notes +docs/links +docs/release.txt +docs/rfb_notes +docs/*.pdf +vnc.html +vnc_auto.html +karma.conf.js +docs/flash_policy.txt diff --git a/noVNC/.travis.yml b/noVNC/.travis.yml index 8192252..a7fe423 100644 --- a/noVNC/.travis.yml +++ b/noVNC/.travis.yml @@ -1,15 +1,17 @@ language: node_js +sudo: false +cache: + directories: + - node_modules node_js: -- '0.11.13' +- '6.1' 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 + - TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='Windows 10,Linux,OS X 10.11' + - TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='Windows 10,Linux,OS X 10.11' + - TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 10' + - TEST_BROWSER_NAME=safari TEST_BROWSER_OS='OS X 10.11' global: - secure: QE5GqGd2hrpQsIgd8dlv3oRUUHqZayomzzQjNXOB81VQi241uz/ru+3GtBZLB5WLZCq/Gj89vbLnR0LN4ixlmPaWv3/WJQGyDGuRD/vMnccVl+rBUP/Hh2zdYwiISIGcrywNAE+KLus/lyt/ahVgzbaRaDSzrM1HaZFT/rndGck= - secure: g75sdctEwj0hoLW0Y08Tdv8s5scNzplB6a9EtaJ2vJD9S/bK+AsPqbWesGv1UlrFPCWdbV7Vg61vkmoUjcmb5xhqFIjcM9TlYJoKWeOTsOmnQoSIkIq6gMF1k02+LmKInbPgIzrp3m3jluS1qaOs/EzFpDnJp9hWBiAfXa12Jxk= diff --git a/noVNC/LICENSE.txt b/noVNC/LICENSE.txt index e896efc..7b5de59 100644 --- a/noVNC/LICENSE.txt +++ b/noVNC/LICENSE.txt @@ -5,19 +5,19 @@ 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/jsunzip.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 + core/base64.js + core/des.js + core/display.js + core/input/devices.js + core/input/keysym.js + core/logo.js + core/playback.js + core/rfb.js + app/ui.js + core/util.js + core/websock.js + app/webutil.js + core/input/xtscancodes.js The HTML, CSS, font and images files that included with the noVNC source distibution (or repository) are not considered part of the @@ -29,12 +29,12 @@ The HTML, CSS, font and image files are licensed as follows: *.html : 2-Clause BSD license - include/*.css : 2-Clause BSD license + app/styles/*.css : 2-Clause BSD license - include/Orbitron* : SIL Open Font License 1.1 + app/styles/Orbitron* : SIL Open Font License 1.1 (Copyright 2009 Matt McInerney) - images/ : Creative Commons Attribution-ShareAlike + app/images/ : Creative Commons Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/3.0/ Some portions of noVNC are copyright to their individual authors. @@ -45,20 +45,16 @@ 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/base64.js : MPL 2.0 - - include/des.js : Various BSD style licenses + core/base64.js : MPL 2.0 - include/jsunzip.js : zlib/libpng license + core/des.js : Various BSD style licenses - include/web-socket-js/ : New BSD license (3-clause). Source code at - http://github.com/gimite/web-socket-js + utils/inflator.mod.js + include/inflator.js : MIT (for pako) - include/chrome-app/tcp-stream.js - : Apache 2.0 license - - utils/websockify - utils/websocket.py : LGPL 3 +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: @@ -70,6 +66,7 @@ The following license texts are included: docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD) docs/LICENSE.zlib docs/LICENSE.Apache-2.0 + docs/LICENSE.pako Or alternatively the license texts may be found here: diff --git a/noVNC/README.md b/noVNC/README.md index b5679cd..22755b8 100644 --- a/noVNC/README.md +++ b/noVNC/README.md @@ -1,45 +1,45 @@ ## noVNC: HTML5 VNC Client -[![Build Status](https://travis-ci.org/kanaka/noVNC.svg?branch=master)](https://travis-ci.org/kanaka/noVNC) +[![Build Status](https://travis-ci.org/novnc/noVNC.svg?branch=master)](https://travis-ci.org/novnc/noVNC) ### Description -noVNC is a HTML5 VNC client that runs well in any modern browser -including mobile browsers (iPhone/iPad and Android). +noVNC is a HTML5 VNC client that runs well in any modern browser including +mobile browsers (iOS and Android). -Many companies/projects have integrated noVNC including [Ganeti Web -Manager](http://code.osuosl.org/projects/ganeti-webmgr), +Many companies, projects and products 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) +[OpenNebula](http://opennebula.org/), +[LibVNCServer](http://libvncserver.sourceforge.net), and +[ThinLinc](https://cendio.com/thinlinc). See +[the Projects and Companies wiki page](https://github.com/novnc/noVNC/wiki/Projects-and-companies-using-noVNC) for a more complete list with additional info and links. ### News/help/contact Notable commits, announcements and news are posted to -@noVNC +@noVNC. -If you are a noVNC developer/integrator/user (or want to be) please -join the noVNC -discussion group +If you are a noVNC developer/integrator/user (or want to be) please join the + +noVNC discussion group. -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). +Bugs and feature requests can be submitted via +[github issues](https://github.com/novnc/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/novnc/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 @noVNC if you do. +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 @noVNC if you do. ### Features @@ -59,61 +59,72 @@ href="http://www.twitter.com/noVNC">@noVNC if you do. Running in Chrome before and after connecting: -  +  + -See more screenshots here. +See more screenshots +here. ### Browser Requirements -* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS - Safari, Opera 11+, Internet Explorer 9+, etc. +* Chrome 8, Firefox 4, Safari 6, Opera 12, IE 11, Edge 12, etc. -* HTML5 WebSockets: For browsers that do not have builtin - WebSockets support, the project includes - web-socket-js, - a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in - WebSocket support. +* HTML5 Canvas, WebSockets and Typed Arrays -* Fast Javascript Engine: this is not strictly a requirement, but - without a fast Javascript engine, noVNC might be painfully slow. +* Fast Javascript Engine: this is not strictly a requirement, but without a + fast Javascript engine, noVNC might be painfully slow. -* See the more detailed [browser compatibility wiki page](https://github.com/kanaka/noVNC/wiki/Browser-support). +* See the more detailed +[browser compatibility wiki page](https://github.com/novnc/noVNC/wiki/Browser-support). ### Server Requirements -Unless you are using a VNC server with support for WebSockets -connections (such as -[x11vnc/libvncserver](http://libvncserver.sourceforge.net/), +Unless you are using a VNC server with support for WebSockets 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 +[MobileVNC](http://www.smartlab.at/mobilevnc/)), you need to use a +WebSockets to TCP socket proxy. There is a python proxy included ('websockify'). ### Quick Start -* Use the launch script to start a mini-webserver and the WebSockets - proxy (websockify). The `--vnc` option is used to specify the location of - a running VNC server: +* Use the launch script to start a mini-webserver and the WebSockets proxy + (websockify). The `--vnc` option is used to specify the location of a running + VNC server: `./utils/launch.sh --vnc localhost:5901` -* Point your browser to the cut-and-paste URL that is output by the - launch script. Enter a password if the VNC server has one - configured. Hit the Connect button and enjoy! +* Point your browser to the cut-and-paste URL that is output by the launch + script. Enter a password if the VNC server has one configured. Hit the + Connect button and enjoy! ### Other Pages -* [Encrypted Connections](https://github.com/kanaka/websockify/wiki/Encrypted-Connections). How to setup websockify so that you can use encrypted connections from noVNC. +* [Modules/API](https://github.com/novnc/noVNC/wiki/Modules-API) - The library + modules and their Javascript API. -* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Starting a VNC server, advanced websockify usage, etc. +* [Integration](https://github.com/novnc/noVNC/wiki/Integration) - Get noVNC + to work in existing projects. -* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects. +* [Troubleshooting](https://github.com/novnc/noVNC/wiki/Troubleshooting) - How + to troubleshoot problems. -* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems. +* [Encrypted Connections](https://github.com/novnc/websockify/wiki/Encrypted-Connections) - + Setup websockify so that you can use encrypted connections from noVNC. + +* [Advanced Usage](https://github.com/novnc/noVNC/wiki/Advanced-usage) - + Generating an SSL certificate, starting a VNC server, advanced websockify + usage, etc. + +* [Testing](https://github.com/novnc/noVNC/wiki/Testing) - Run and write + tests. + +* [Translations](https://github.com/novnc/noVNC/wiki/Translations) - Add and + modify localization for JavaScript and HTML. ### Authors/Contributors @@ -123,16 +134,17 @@ use a WebSockets to TCP socket proxy. There is a python proxy included * [Samuel Mannehed](https://github.com/samhed) (Cendio) * [Peter Åstrand](https://github.com/astrand) (Cendio) * [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack) + * [Pierre Ossman](https://github.com/CendioOssman) (Cendio) * Notable contributions: - * UI and Icons : Chris Gordon + * UI and Icons : Pierre Ossman, Chris Gordon * Original Logo : Michael Sersen * tight encoding : Michael Tinglof (Mercuri.ca) * Included libraries: - * web-socket-js : Hiroshi Ichikawa (github.com/gimite/web-socket-js) * as3crypto : Henri Torgemane (code.google.com/p/as3crypto) * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net) - * jsunzip : Erik Moller (github.com/operasoftware/jsunzip), - * tinflate : Joergen Ibsen (ibsensoftware.com) * DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs) + * Pako : Vitaly Puzrin (https://github.com/nodeca/pako) + +* [Contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) diff --git a/noVNC/app/images/alt.svg b/noVNC/app/images/alt.svg new file mode 100644 index 0000000..e5bb461 --- /dev/null +++ b/noVNC/app/images/alt.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/noVNC/app/images/clipboard.svg b/noVNC/app/images/clipboard.svg new file mode 100644 index 0000000..79af275 --- /dev/null +++ b/noVNC/app/images/clipboard.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/noVNC/app/images/connect.svg b/noVNC/app/images/connect.svg new file mode 100644 index 0000000..56cde41 --- /dev/null +++ b/noVNC/app/images/connect.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/noVNC/app/images/ctrl.svg b/noVNC/app/images/ctrl.svg new file mode 100644 index 0000000..856e939 --- /dev/null +++ b/noVNC/app/images/ctrl.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/noVNC/app/images/ctrlaltdel.svg b/noVNC/app/images/ctrlaltdel.svg new file mode 100644 index 0000000..d7744ea --- /dev/null +++ b/noVNC/app/images/ctrlaltdel.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/noVNC/app/images/disconnect.svg b/noVNC/app/images/disconnect.svg new file mode 100644 index 0000000..6be7d18 --- /dev/null +++ b/noVNC/app/images/disconnect.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/noVNC/app/images/drag.svg b/noVNC/app/images/drag.svg new file mode 100644 index 0000000..139caf9 --- /dev/null +++ b/noVNC/app/images/drag.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/noVNC/app/images/error.svg b/noVNC/app/images/error.svg new file mode 100644 index 0000000..8356d3f --- /dev/null +++ b/noVNC/app/images/error.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/noVNC/app/images/esc.svg b/noVNC/app/images/esc.svg new file mode 100644 index 0000000..830152b --- /dev/null +++ b/noVNC/app/images/esc.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/noVNC/app/images/expander.svg b/noVNC/app/images/expander.svg new file mode 100644 index 0000000..e163535 --- /dev/null +++ b/noVNC/app/images/expander.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/noVNC/app/images/fullscreen.svg b/noVNC/app/images/fullscreen.svg new file mode 100644 index 0000000..29bd05d --- /dev/null +++ b/noVNC/app/images/fullscreen.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/noVNC/app/images/handle.svg b/noVNC/app/images/handle.svg new file mode 100644 index 0000000..4a7a126 --- /dev/null +++ b/noVNC/app/images/handle.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/noVNC/app/images/handle_bg.svg b/noVNC/app/images/handle_bg.svg new file mode 100644 index 0000000..7579c42 --- /dev/null +++ b/noVNC/app/images/handle_bg.svg @@ -0,0 +1,172 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/noVNC/app/images/icons/Makefile b/noVNC/app/images/icons/Makefile new file mode 100644 index 0000000..be564b4 --- /dev/null +++ b/noVNC/app/images/icons/Makefile @@ -0,0 +1,42 @@ +ICONS := \ + novnc-16x16.png \ + novnc-24x24.png \ + novnc-32x32.png \ + novnc-48x48.png \ + novnc-64x64.png + +ANDROID_LAUNCHER := \ + novnc-48x48.png \ + novnc-72x72.png \ + novnc-96x96.png \ + novnc-144x144.png \ + novnc-192x192.png + +IPHONE_LAUNCHER := \ + novnc-60x60.png \ + novnc-120x120.png + +IPAD_LAUNCHER := \ + novnc-76x76.png \ + novnc-152x152.png + +ALL_ICONS := $(ICONS) $(ANDROID_LAUNCHER) $(IPHONE_LAUNCHER) $(IPAD_LAUNCHER) + +all: $(ALL_ICONS) + +novnc-16x16.png: novnc-icon-sm.svg + convert -density 90 \ + -background transparent "$<" "$@" +novnc-24x24.png: novnc-icon-sm.svg + convert -density 135 \ + -background transparent "$<" "$@" +novnc-32x32.png: novnc-icon-sm.svg + convert -density 180 \ + -background transparent "$<" "$@" + +novnc-%.png: novnc-icon.svg + convert -density $$[`echo $* | cut -d x -f 1` * 90 / 48] \ + -background transparent "$<" "$@" + +clean: + rm -f *.png diff --git a/noVNC/app/images/icons/novnc-120x120.png b/noVNC/app/images/icons/novnc-120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..40823efbadf27f0286a84976a34d1cff8406f5a2 GIT binary patch literal 4028 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 literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-144x144.png b/noVNC/app/images/icons/novnc-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..eee71f11c74fba3be7a05d668aa1c1dc7d993d09 GIT binary patch literal 4582 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 P8v(kSw=`;ScCr5hJEV16 literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-152x152.png b/noVNC/app/images/icons/novnc-152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..0694b2de39b8e709b442f10905a6f3cf39f7c7bc GIT binary patch literal 5216 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 T|67&~=mgSG(N(Thw2S=@24lsF literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-16x16.png b/noVNC/app/images/icons/novnc-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..42108f409990be6cf93cec396ae65f78a2bb3cd1 GIT binary patch literal 675 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>32}9Ba4-i0PcZQH z^))gwGBh+~XkcI}VA9pqWhr8jmX=mgQBhS@H8C+UH8o|(V^CLDx3sj>)6-K^Q?s|X zzjB4u+gmm~T;tLu=Kud0%F6{cG&CMOV6m{UXlW6dG>LuQJfX5Ow)^)PLPKR-TwEL- zbsj!seDsLPz(Cs1PxQe9Mn^|Sp#9snv2WPGSX?Y}?;ewljg7Ifv6-2fv$M0St1HmG zGk0752U2V$L4Ls+PoI2VEpq2}(jA}(W0JSKi^T%=rPqKQ&H|6fVg?3f4G?CWG4sh= zpdfpRr>`sf11>&Z2E8R89dv*~D?MEtLnJPT_I2_#Iq&1=5WwVU9jt#)FFkQC3$C_&1g(BO>_Lj+BjwJ zl_tA{SsOz{p86hTD6o+77xA6gB`G+y;q%d=)-CrYEHlv3I`B5H-oa{~M8+Ckg?ajt zllD#8_wnT1#z$3o=Zu%ipMUkg^rO)+=I_%dZ#O)~UhuZB&&%zyU-CQs+mLGt zbhK)TYeY#(Vo9o1a#1RfVlXl=G}ARS&^0s(F*LR^Ft9Q)(>5>yGJ^Gv+o5R4%}>cp ztHiBAskpugs6i5BLvVgtNqJ&XDljI?^)mCai<1)zQuXqS(r3T3kpe1W@O1TaS?83{ F1OT_7-I@RZ literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-192x192.png b/noVNC/app/images/icons/novnc-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..ef9201f4370f886812acca9d780afb15c6b98452 GIT binary patch literal 5787 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 TuB&r{Oo09!W8Eqp`|$q(4_V)v literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-24x24.png b/noVNC/app/images/icons/novnc-24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..110613594b8e305e26cafc6ac77e6f40bbdc76c9 GIT binary patch literal 1000 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZk1_s8Y0G|+7paN@aYcn%5 zBO@bob8|~eOCag#>FMF&0VE;7(9lrTSye1ZOgvCr*Hl;8Ls>jnT){;_G*pzKfq}!B zgRzE@p_)NWO$}&>kdTn9tSqxLGm{e&uP?8>yu7ioG0=RVu|SIyoD`VdnH3ckx%{{| zwK*khCD^psw6wGw9Ua}=+zJa7|Nm$3_LdC~*GNtlzH^7!(o$>MG_mY#<)1$pdwO_) z?lCnrc>S7j!UWcZ3q@bOVvv`Y+Pan5$jD&oRMDwZ#k#tfy1SYE{kiAP<$L;+K~GQf z`E%y}eg+*Ko#V$D{{Cg?>f&;DcMlI&pEix##)hZ4nYpiz@5~uiD=VwZms$S(V>o}F z!N|yP(88Z}fa}C?t#EOcT3=9mctaPtmXUfb}`0<0GrG-UBMMYIr zRY5^PTU*=2#KhIr)y~e&!otGA!2uYCz~GJCTDKEO@stGl1v7X(|M$$}>4%kXWIi4^ zQ*{(5&Y0xw?!wT)DhpD}S>O>_%)nr>2ZR|n1iugl3bL1Y`ns||;Ns(D&|C7+K?kVE z(9^{+MB{wvMfTuB4g#zXS_NEc$`&^{GHXQ$Twk}*d`-uV>f>BVzyCju?XXhXck;=k z?`NN;sk`tkZ)(!en|k8o@@*cf>Rrcty$S<3o{B0>4PCKLF#GLxfmbFot4vNTy_o*J zj?;J7I@SfIudBM#ZU{_Kh*M#iP`^#+7k^Z}Lj5uEErPcW3ockJ9Gf`t-3ngYkQ$yN z9DDDy*U2&P^CixxRbH%PlqPgcUCq$cmigJuot0){6^3&C`kV(ooqBbvifPZpJ0c2t zhH+kp#PUD$9oSucvR0bUhttBws?Wp1GWF-m&7ZqZYjdyrsiw=gy*%fR{fWc!x(y#y z@@DExxUl%di<{3~Zs{1g>!o%5`5Smp{O0rts~7MuP06qF_`CJ!|E#NNE2n#u-hOg^ z@80@|(wWD-=bNl`-1J(i9~iI!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817lc#Plzi}fwi@@g@uK& zu`!UewY3Fut*oqoY$GEhPbA>&?d{>=0TD1XGz7{rG%zsKG4O`-8W=P;KktNZuEUAR$w&u0Vr%hc51^=rm2Ul_iAWqkFDL0nwO$w}+! zQ$|Ti2|GKx`SS((`xzo56@UC-0ESFNgoddpYe|Xd;>A2NGBT#7rfzO-z;FYGp`#-( zIvCCzQQZfmgi3<^f*Hae{(TTKW7_?{|DG@Tp%eLRscPG?r$AZ8ByV>Yh7ML)4Prw#(omh`i+KNIHe_CDPyn)mS4ECzw= zm9uz1a@{zxHusKaQG)pQZ>kDzzJZJ~4;~829k(nf>PoM4@b!N1=h3HEzub;+-@CVv z<=xHC%u^X`l7YzGl6tZ`{Z9+CThH@kM>>ad;hje$LbjYo5QTXnti?>U07Z8 ztbg9$t-CV4wO{{d4sFj~ek^a>JYcM-mbgZgq$HN4S|t~y0x1R~14A=iLjzqylMq8= zD+2>76EkfCBOoJK@3ErERK(!v>gTe~DWM4f!E0>0 literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-48x48.png b/noVNC/app/images/icons/novnc-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..f24cd6cc939a073d4ba178995eac2854d6acf607 GIT binary patch literal 1397 zcmZ`%X;2eJ6n;raKq8kF5+WghoB|;SNjMV&2?-ZR7zzS%nNUSxKoB*6qUqZH>W}Wsd*AnVzxVCTzAa0LlUP|& zECIkOS}ICJ$aohPIF#k1t1cigQwn2+0O!6Yu4Lj-?V^w-#sYle382{z@Ct2do&e;d z#%Vb~NDTlf=VEb$567_V#vkbQFulot;FJ2`VbA z>+8u^u9$1JYdkzWluGx>NxV$v_2vy2jSv<_lSurGMl6Seqtoe1<=Tk}{Moa_6DM#C z2BWog&GF+vrBW9cAvxJ;^=hI>B$7ycV`4}tDc&9)gvv_n%nV4SZX6D$uFmG_RZLfx z)qw-x=H^DBkXu^`A=BZtx`!@SpkCqhbE;^xJ5-_ z{QUeB3TJ}>TwPshG@4K-M1u3$BNDMmey7f z&}YnP2f)&!MZzTMtvdtCl>Uut3HnOiL5tGK=B)I=pYUn7o1VyoRt|oryp8P+Gruj4 z-z>KpiO)Y&8_llh=FL_A+VS|)+=gR^ZLjZ0t8IuBO>HT{8)T{WwGIvKfA$P^9{fnA zC~I6Q(rn?HYM$H4B!3)PNQe*Qd{;@}=r;B*fQhCHmd@9)>>FaM(+fR5bz!dO^i5qC zZqcR^&WKLjnY}KI(l(Jt=}r%mgcJ;_tmn z@W%!&?k&$<9o$&R8J1m(z&IGTE)T9rtBaVK5ZHE9`%6;CDt^E=F^ZEHx*G3I)@6wD z&cgltIOp>f)iCham;8?VrS;tNPDYg->^bAwXo0gH8c^(R`<=So)WfeD`J!V{s&_VI zOv6yg>*D)4U{lpJdi+M=!zTi{4p&aS^&WUV>(73xXDgqLE`yHf45ayqA8)73PjjsG zudFT=-L@Gz6s|W?FBRL&32b-b3zsZtuQhKw&GkO~;_`q*YfW{Fj?f~Om%h4I-&Q&| z{y9;9Lo%>0_ipBpjh;PG-I4+6P0PkH%uUePDfaUw7O*Qh%`Lsj8?v2V!jvTG0(H?*EsEEJI{ZZwzYy7uyF7|dUV=ri`%cpBArq7++JU^Mesq^HG zc}+l^*TrGlx(kM>ZG2nRj%@n}?EIH?s)CnlPu`FrK7Pm1(T}rC*vq|siG7aEz34M} zsKpRtlF+F$jr@xf`S5i_U@f|ip-oWx++&$|1^w(L=Y{G6E#Q3wSNO`Xb>9! literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-60x60.png b/noVNC/app/images/icons/novnc-60x60.png new file mode 100644 index 0000000000000000000000000000000000000000..06b0d609a0f7c45b1ad0481debf061affd6a2a17 GIT binary patch literal 1932 zcmZ{lc~FyC62LnbfrN01L3ea+x{3(`zY|v?d}7YGDM(M-{VLcnF?jC_nN z-7MA9)nOlC@-d_Y65F0lP9{?YRBIb+yb6!ofkSC+ZEYbPKnCCeSSPHdt0ja2#G@v` z*w`2q12JG^WMpM!1uj5$q2n#^24M#J`ueC5Gcz-41XWK@4;3$!N>QgWnateT9Os0y zkXz^_=s^LXnpr{?D8RtL02Rz;vkiOm$nLoZeoo4<9hj&dy@7 z^|NQ#@p0hsShlwO>1pEJ9O&q%@%jAWVXVD9XK;}G>#uO*hPJ1tXMVm>PLBGaL$tU! zpwsDBuHc12>v!)ky}eLe42g+=wm>A}KYD~;Tm+HGTq@-)E#V$M1PVoCWd$lLiG_ub zoJ>D_7=QZodLJLKu&^j8ftxq6`T3OYZsLazAe9>V`m#GaK}$fV*US7U#?_OwYQ(IcXPEA2?FR8DOJTO41ucwK{yum@->C-wH z8MKZL2oHzuZuRJBaC5V2Yr~b5!T$XkZEe6{FicELOifMEiIvOcuCA_IwyfQqA=U3a zNHDU3{X;#ZtTt3P{K32`M1a&6Svh< z2E_yuFJx#ay|!lpushc#=H>%q2Ly>(-hLj$DUuci_|dER0RT9mx6C!vtN+0xh3cLQ zmAIUe<$kckeV2G_8%?b@ygG((zLOf9aQ(T`#4h3a0qtCq_^TZq9XnbuicZgCHr~v9 zx0-^PFU+O6Ym;xHtgGIZ$39+JsS9c78mSK}_(fVq{qlCv&3uFKcbkyJfXy|WzlfU# zuy|#Bcp~OvWY}N1suErkbLHFxjKTS;W)09`qW5HCrPy9sGDcpix}QW7g*VUk%@&miDy0>cJJNi+ z8rt_HRW(Qg`%9^Hj9WU(zCGL1J9CaXdG=(cPvi;6Svmo!;#hNDWD2*6)2kTj`dVLi zTQN0VQ55b=FB*-F`1Y#gK;uLD`JMeU|1{RIe-iu6=e+p^LCTGh9gkl=)V-SO*sPi> zKT&;Y$kxA3OO;Dn98JEq*!{z2V$);gvW}^AM(?0woVZk_Ii(`~I3<-vEmf4LUR)yQ z9gs}_wM6xfcD#SYP;=vfL*A9Bn% zX^+%$pW6F+$#BVCY-NaCekCQZskN3c>MpO`7c!EjQTuI^=b6HN!GF8Io_FJ9)5XD* z_xDzdwlcYqzvY~L^ZIr}Ml|=urBf?MnpvAUmFH9?-_!mezxwnUn@K3HLl=sf*y``pMcb~gguFS|9?P+FMbjQ vY*_n3NLpf6Zfu4Ea&vQed%jA}jE_|+cxf5CYZkW{At&(m@Ryx;i~0Ir{{7o^ literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-64x64.png b/noVNC/app/images/icons/novnc-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0fb34181b419fc0188004002d01c69227c60cb GIT binary patch literal 1946 zcmZ`)cT|&E7Qdtk0YYdILQ4>cl!Sx?2%&{QfPe-BB8Uvq%c4UIO<0Y{Dx!>Mb_B)2 zGILaB071@K1hEVPx&js$5J!d@SDJ-UDT5&F^LXZu{bS#G@7?;l@80kH&iRf7OZ_y7 zR3ZRqO8k97kQ9DCs#tWNYdQ2930$(*b}xXYlj>`+cx2P!{X@0`?6(3?lmL80F2xK$ z4jW)D20(BIU~@)w+YUDX%-!H^VPcdZ5C~jcT%4Socsw3bTwPt=+}!wlzO%FQ7n957 zIyg8WCkmltM+qqE=;(-0lE0R}zyDwAXBiL>@Wn)^FQG3ts>b1PP+9smIw$~} zgEf#Eu$)-h3EG$<3@L`h>E`C<`uh6nG3wgd+NeEpA{i~zwugs@P$=~A@$vKX!|cc4^YN%L(jF27 z0fj`~I(+y5QmILAFF__FCnZ@DiE77>lc%OYB-#=iYth#SOr{N&%X#*U=;Z|#78VyS z5Qm1)Xx43QYH4Xig~IgOH6W2lTrRt%h0xiF5sSsa!Pcv*@bV?FSX-v2@wT?WU@!y% zR$m_`H&M}sw&XY(GiKPCnr_<`*FU$c8iND3kzt2u`xy}wRrm$a&qY5 z;h(8kQK1tRW%}R&Av@bRI@+qS5noiK{}w zh}^)Che$)g_s0BMtC%}7%t`+A>FX57=iY7C|5#la|0QQ_%h@S|Wq#uKlXr5XtErC= zL?tfJ&j(8JCjQ7!%kbZY2*sNCd{neZ&nA&`lU%Z0ylH|+(ggc~rHnQJY^ub^D=eb( z-m~TKfndE&f24RBvdEe9{dJiaW!hrg)iS*wKI-Y=^%iYxij`fr#Un<`V`a+d*5xtf znDSt3wCL=W^5!?IcMGLKb9eUF!Wp-~%z%oeEc#ySbTgOv z-S^8|gA;otEAQXFj<(rzwsB%={a*aVvB0spX zVOVx~`nUJ6VG`qkPr8`N=_@&1m&#R4F^exrf_ryvfJQDnX*uH5b7|kT0pU$@S>I+VZf;+_qh^?7zo%un959R%7) zSW&&iDpbcptBiL{-}$$9mj3h;c`wMmv|={q7$s(gZR{Z`pyyb8Lp^tdZ*@EWF#EBh z>4n3NpEY8$>$7k#v>995eYzx>B2`9E6YL7D`5`BfdzvdkYQyOkb^O&3W1#Oy z)WeY!&7W9wcWhlytgdp3sZ6m|UVjsk@^9%#^J=>AN2CgZLrHFX{VlDzLN9g2$>-1i@xF1g z=E=XtFMkvjZoF!lA6hQ90?!lo6AG#mc#+qq2QoAMS!C8%`IJyyXB=nUqEPK17(6`D z9@K%Gi7zE)tTZ+V_Ef3MIU9i$3|Z8tn~h_hbeT&~xU&4<@P8zZ4X4ysejLBPGUv#D z{QGRt0{M5hpRMbMi>jlR^zRMdlUyBEc08?WrQi3E&J9*nHZ1n6XU|7whSW9O%3pey z-TI*#b2WeQWAoLa`E`{x;mz}dMrBIf(1+B?9!;uKE!}bhaD%%W>}r2Oe=@L?izDO- zvGPPgd{!b7;K1fMF*$4|hZn}-3fOD`&)JskfRx2u56k`surEC!HL2kL14?jmCj>D3 t{6c7Yk~}XqD-rVY@>r=~r)9^-W+bxGvy#7m>qbRMAQ4M_8bmS0{{?}E<-Gs^ literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-72x72.png b/noVNC/app/images/icons/novnc-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..23163a22d06001a508d01b83be1942d4e1032bf9 GIT binary patch literal 2699 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 T1zQ>d^di7O*F>jQ+bQlpIb%xa literal 0 HcmV?d00001 diff --git a/noVNC/app/images/icons/novnc-96x96.png b/noVNC/app/images/icons/novnc-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..1a77c53f4cb2d2bf63c86f654ce044fde3dac1aa GIT binary patch literal 2351 zcmZ`)XH-+!7T)(JA@mZEuA##t0TL1d5=tn61O-ABEJ!hmVnJX)M@OQdQWae@SWrP> zRKx;<7!|P#C@QF0N~@Ig^_$j@bdC_-7$`3)3H8h8^HFM)3pL1aW}t2H z^Yeocz%y}3LuUXs92GEu(^)n>A)1OWkNqoc&~ay^+0?CnjsT*sjy zVnG4mINsNXd3e|;6riL8=qSCs9EXST{CwQe(XqVTV0ais0DXP^wl<8zq51p!7Z)3` zSbB0fo}LbS_rj%11SZpw!(qI6L;U@B?9wHj{(ewa1``t#9EbXPa%m}f%a(b*z8bNy zc1xF{EJ{vJPHt{a_wN(8ZdGTon9rUmH8+Euo$cet>Kz?GrBc1TXx-iT%9Yl!vC8S` z=6pV=sHg~qwlW!Z=Z?HVzA{<^cVF5<3>BQpQ*BBR?hOLIV#U5TMurmh;T+xu*3y{{*XtSD?mUc zRS=sF5Fq+KFs&nF9AqSC2!r|LepO8kaC-JN^(y*mFAM#BB9r?*9bUCbk8jvQ7MQ8( zoXE?={Hqghoge8XRi46+-A+h!JjALyVYT#eL!zi|ds9v?7}v$$Q`JA)$;}*UGFZFx zx*Mx;OJhBkTPDuBJ~1&{EB$AkB|*6lBER{qWysF!fuBgLK@wE8wpeCdpyzU^I$SS8Ezi6` z0kZ_f6uhgkJ8xW4i=A;ReqDK3U*3~)tw!Hp7|M%!-Zur2BOlL`o)T3m*8FAF87fk% z`c=Jl7GuPU)JO;SbJ#o8PtH@0AT{vLY=ox`J%Sjus+=RxB8XJ0a(^xEuyDB=x}*HFHx%ZDEo(m~I?k zw{7bR3k)sSBX&H*BsM2g{&coH;Arn!Y29J8<{kE=M#wZPU8HVyfag#V=2CHtY+AXx zt8O^9W#8CmSW$EI=G=-oV;lbxvRn>y<`VzLY^D1w>yIc-l_YjopJFy%dKJPm4)ONW z&2x~SCBUlvmaC3Fnrc6^px}K~yEwCJ^ZQ0HNIoVh(oKECALzK(VA@Yvvt>|x_2|iE z+jih;V>9|?Em4Ya=~(M^3l zv*H^ey24YiOUv`x!;4E4%DrBTyRCUO%CL9e;HSN1m2)!Qczqs-;5VKsKTd7WR1rB5 z&u-oGFk3-N8n-aIc5zx<)=Rsv_>&o#on#JimCH9Rd!3y#d}Fe7Mt=K@v=A+<@PJXx z$=i)1PnzW3W{uR7i_&4@LsI?RDv8xsy!PfqZd^-faY;Wm`s#fESAZ?wgvZ3w75mxu zJS?qDVd6nD*@!oi$dr(pU(OvZHll>U)*5M!dPiOpMrT}~QtYNaDoMqn6?eOQaKV5W z^bNgd!_`#`U_7$~WVPxy@yesSIxk$&H0Ny~FX^0rKoqALzdd1RE>uVfHPbdlDf1;6YbpJLDSji{o9Hj zc&DTfa}QI^a%Id@t=PF3hj=_%EzdxY;2h&np<6@xGrmC*8Rua3O2U`4&J$UwS`5Onso+0 zCSyfHhS)tRO^haRW-vJpOojuK9m!;&KdU?2mBw&J%9q%O1^?r)Zo`VzD>wgthg@RD y_lwi$dxnS&D>JeZ(!`LJmF2j4eM)*#LaNwtL)xmcVGm + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/noVNC/app/images/icons/novnc-icon.svg b/noVNC/app/images/icons/novnc-icon.svg new file mode 100644 index 0000000..1efff91 --- /dev/null +++ b/noVNC/app/images/icons/novnc-icon.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/noVNC/app/images/info.svg b/noVNC/app/images/info.svg new file mode 100644 index 0000000..557b772 --- /dev/null +++ b/noVNC/app/images/info.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/noVNC/app/images/keyboard.svg b/noVNC/app/images/keyboard.svg new file mode 100644 index 0000000..137b350 --- /dev/null +++ b/noVNC/app/images/keyboard.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/noVNC/app/images/mouse_left.svg b/noVNC/app/images/mouse_left.svg new file mode 100644 index 0000000..ce4cca4 --- /dev/null +++ b/noVNC/app/images/mouse_left.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/noVNC/app/images/mouse_middle.svg b/noVNC/app/images/mouse_middle.svg new file mode 100644 index 0000000..6603425 --- /dev/null +++ b/noVNC/app/images/mouse_middle.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/noVNC/app/images/mouse_none.svg b/noVNC/app/images/mouse_none.svg new file mode 100644 index 0000000..3e0f838 --- /dev/null +++ b/noVNC/app/images/mouse_none.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/noVNC/app/images/mouse_right.svg b/noVNC/app/images/mouse_right.svg new file mode 100644 index 0000000..f4bad76 --- /dev/null +++ b/noVNC/app/images/mouse_right.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/noVNC/app/images/power.svg b/noVNC/app/images/power.svg new file mode 100644 index 0000000..4925d3e --- /dev/null +++ b/noVNC/app/images/power.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/noVNC/app/images/settings.svg b/noVNC/app/images/settings.svg new file mode 100644 index 0000000..dbb2e80 --- /dev/null +++ b/noVNC/app/images/settings.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/noVNC/app/images/tab.svg b/noVNC/app/images/tab.svg new file mode 100644 index 0000000..1ccb322 --- /dev/null +++ b/noVNC/app/images/tab.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/noVNC/app/images/toggleextrakeys.svg b/noVNC/app/images/toggleextrakeys.svg new file mode 100644 index 0000000..b578c0d --- /dev/null +++ b/noVNC/app/images/toggleextrakeys.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/noVNC/app/images/warning.svg b/noVNC/app/images/warning.svg new file mode 100644 index 0000000..7114f9b --- /dev/null +++ b/noVNC/app/images/warning.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/noVNC/app/locale/de.js b/noVNC/app/locale/de.js new file mode 100644 index 0000000..6819c48 --- /dev/null +++ b/noVNC/app/locale/de.js @@ -0,0 +1,18 @@ +/* + * Translations for de + * + * This file was autotomatically generated from de.po + * DO NOT EDIT! + */ + +Language = { + "Connecting...": "Verbunden...", + "Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ", + "Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ", + "Disconnecting...": "Verbindung trennen...", + "Disconnected": "Verbindung zum Server getrennt", + "Must set host and port": "Richten Sie Host und Port ein", + "Password is required": "Passwort ist erforderlich", + "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt", + "Disconnect timeout": "Timeout beim trennen", +}; diff --git a/noVNC/app/locale/el.js b/noVNC/app/locale/el.js new file mode 100644 index 0000000..3ce6c38 --- /dev/null +++ b/noVNC/app/locale/el.js @@ -0,0 +1,74 @@ +/* + * Translations for el + * + * This file was autotomatically generated from el.po + * DO NOT EDIT! + */ + +Language = { + "Connecting...": "Συνδέεται...", + "Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ", + "Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ", + "Disconnecting...": "Aποσυνδέεται...", + "Disconnected": "Αποσυνδέθηκε", + "Must set host and port": "Πρέπει να οριστεί το όνομα και η πόρτα του διακομιστή", + "Password is required": "Απαιτείται ο κωδικός πρόσβασης", + "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Εφαρμογή λειτουργίας αποκοπής αφού δεν υποστηρίζονται οι λωρίδες κύλισης σε πλήρη οθόνη στον IE", + "Disconnect timeout": "Παρέλευση χρονικού ορίου αποσύνδεσης", + "noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα", + "Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου", + "Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου", + "viewport drag": "σύρσιμο θεατού πεδίου", + "Active Mouse Button": "Ενεργό Πλήκτρο Ποντικιού", + "No mousebutton": "Χωρίς Πλήκτρο Ποντικιού", + "Left mousebutton": "Αριστερό Πλήκτρο Ποντικιού", + "Middle mousebutton": "Μεσαίο Πλήκτρο Ποντικιού", + "Right mousebutton": "Δεξί Πλήκτρο Ποντικιού", + "Keyboard": "Πληκτρολόγιο", + "Show Keyboard": "Εμφάνιση Πληκτρολογίου", + "Extra keys": "Επιπλέον πλήκτρα", + "Show Extra Keys": "Εμφάνιση Επιπλέον Πλήκτρων", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Εναλλαγή Ctrl", + "Alt": "Alt", + "Toggle Alt": "Εναλλαγή Alt", + "Send Tab": "Αποστολή Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Αποστολή Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Αποστολή Ctrl-Alt-Del", + "Shutdown/Reboot": "Κλείσιμο/Επανεκκίνηση", + "Shutdown/Reboot...": "Κλείσιμο/Επανεκκίνηση...", + "Power": "Απενεργοποίηση", + "Shutdown": "Κλείσιμο", + "Reboot": "Επανεκκίνηση", + "Reset": "Επαναφορά", + "Clipboard": "Πρόχειρο", + "Clear": "Καθάρισμα", + "Fullscreen": "Πλήρης Οθόνη", + "Settings": "Ρυθμίσεις", + "Encrypt": "Κρυπτογράφηση", + "True Color": "Πραγματικά Χρώματα", + "Local Cursor": "Τοπικός Δρομέας", + "Clip to Window": "Αποκοπή στο όριο του Παράθυρου", + "Shared Mode": "Κοινόχρηστη Λειτουργία", + "View Only": "Μόνο Θέαση", + "Path:": "Διαδρομή:", + "Scaling Mode:": "Λειτουργία Κλιμάκωσης:", + "None": "Καμία", + "Local Scaling": "Τοπική Κλιμάκωση", + "Local Downscaling": "Τοπική Συρρίκνωση", + "Remote Resizing": "Απομακρυσμένη Αλλαγή μεγέθους", + "Repeater ID:": "Repeater ID:", + "Style:": "Στυλ:", + "default": "προεπιλεγμένο", + "Logging:": "Καταγραφή:", + "Apply": "Εφαρμογή", + "Host:": "Όνομα διακομιστή:", + "Port:": "Πόρτα διακομιστή:", + "Password:": "Κωδικός Πρόσβασης:", + "Token:": "Διακριτικό:", + "Send Password": "Αποστολή Κωδικού Πρόσβασης", + "Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas", +}; diff --git a/noVNC/app/locale/nl.js b/noVNC/app/locale/nl.js new file mode 100644 index 0000000..cacab30 --- /dev/null +++ b/noVNC/app/locale/nl.js @@ -0,0 +1,18 @@ +/* + * Translations for nl + * + * This file was autotomatically generated from nl.po + * DO NOT EDIT! + */ + +Language = { + "Connecting...": "Verbinden...", + "Connected (encrypted) to ": "Verbonden (versleuteld) met ", + "Connected (unencrypted) to ": "Verbonden (onversleuteld) met ", + "Disconnecting...": "Verbinding verbreken...", + "Disconnected": "Verbinding verbroken", + "Must set host and port": "Host en poort moeten worden ingesteld", + "Password is required": "Wachtwoord is vereist", + "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "''Clipping mode' ingeschakeld, omdat schuifbalken in volledige-scherm-modus in IE niet worden ondersteund", + "Disconnect timeout": "Timeout tijdens verbreken van verbinding", +}; diff --git a/noVNC/app/locale/sv.js b/noVNC/app/locale/sv.js new file mode 100644 index 0000000..b7f4c1f --- /dev/null +++ b/noVNC/app/locale/sv.js @@ -0,0 +1,77 @@ +/* + * Translations for sv + * + * This file was autotomatically generated from sv.po + * DO NOT EDIT! + */ + +Language = { + "Connecting...": "Ansluter...", + "Connected (encrypted) to ": "Ansluten (krypterat) till ", + "Connected (unencrypted) to ": "Ansluten (okrypterat) till ", + "Disconnecting...": "Kopplar ner...", + "Disconnected": "Frånkopplad", + "Must set host and port": "Du måste specifiera en host och port", + "Password is required": "Lösenord krävs", + "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Tvingar 'Clipping mode' eftersom skrollning inte stödjs av IE i fullskärm", + "Disconnect timeout": "Det tog för lång tid att koppla ner", + "noVNC encountered an error:": "noVNC stötte på ett problem:", + "Hide/Show the control bar": "Göm/Visa kontrollbaren", + "Move/Drag Viewport": "Flytta/Dra Vyn", + "viewport drag": "dra vy", + "Active Mouse Button": "Aktiv musknapp", + "No mousebutton": "Ingen musknapp", + "Left mousebutton": "Vänster musknapp", + "Middle mousebutton": "Mitten-musknapp", + "Right mousebutton": "Höger musknapp", + "Keyboard": "Tangentbord", + "Show Keyboard": "Visa Tangentbord", + "Extra keys": "Extraknappar", + "Show Extra Keys": "Visa Extraknappar", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Växla Ctrl", + "Alt": "Alt", + "Toggle Alt": "Växla Alt", + "Send Tab": "Skicka Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Skicka Escape", + "Ctrl+Alt+Del": "Ctrl+Alt+Del", + "Send Ctrl-Alt-Del": "Skicka Ctrl-Alt-Del", + "Shutdown/Reboot": "Stäng av/Boota om", + "Shutdown/Reboot...": "Stäng av/Boota om...", + "Power": "Ström", + "Shutdown": "Stäng av", + "Reboot": "Boota om", + "Reset": "Återställ", + "Clipboard": "Urklipp", + "Clear": "Rensa", + "Fullscreen": "Fullskärm", + "Settings": "Inställningar", + "Encrypt": "Kryptera", + "True Color": "Fullfärg", + "Local Cursor": "Lokal Muspekare", + "Clip to Window": "Begränsa till Fönster", + "Shared Mode": "Delat Läge", + "View Only": "Endast Visning", + "Path:": "Sökväg:", + "Scaling Mode:": "Skalningsläge:", + "None": "Ingen", + "Local Scaling": "Lokal Skalning", + "Local Downscaling": "Lokal Nedskalning", + "Remote Resizing": "Ändra Storlek", + "Repeater ID:": "Repeater-ID:", + "Style:": "Stil:", + "default": "standard", + "Logging:": "Loggning:", + "Apply": "Verkställ", + "Connect": "Anslut", + "Disconnect": "Koppla från", + "Connection": "Uppkoppling", + "Host:": "Värd:", + "Port:": "Port:", + "Password:": "Lösenord:", + "Token:": "Token:", + "Send Password": "Skicka Lösenord", + "Canvas not supported.": "Canvas stöds ej", +}; diff --git a/noVNC/app/sounds/CREDITS b/noVNC/app/sounds/CREDITS new file mode 100644 index 0000000..ec1fb55 --- /dev/null +++ b/noVNC/app/sounds/CREDITS @@ -0,0 +1,4 @@ +bell + Copyright: Dr. Richard Boulanger et al + URL: http://www.archive.org/details/Berklee44v12 + License: CC-BY Attribution 3.0 Unported diff --git a/noVNC/app/sounds/bell.mp3 b/noVNC/app/sounds/bell.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..fdbf149a1e9e58aa6a39adb6c681b910347d161a GIT binary patch literal 4531 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 DLJX+- literal 0 HcmV?d00001 diff --git a/noVNC/app/sounds/bell.oga b/noVNC/app/sounds/bell.oga new file mode 100644 index 0000000000000000000000000000000000000000..144d2b3670e8e294ee7cfe343355bf90123cb687 GIT binary patch literal 8495 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 literal 0 HcmV?d00001 diff --git a/noVNC/include/Orbitron700.ttf b/noVNC/app/styles/Orbitron700.ttf similarity index 100% rename from noVNC/include/Orbitron700.ttf rename to noVNC/app/styles/Orbitron700.ttf diff --git a/noVNC/include/Orbitron700.woff b/noVNC/app/styles/Orbitron700.woff similarity index 100% rename from noVNC/include/Orbitron700.woff rename to noVNC/app/styles/Orbitron700.woff diff --git a/noVNC/app/styles/auto.css b/noVNC/app/styles/auto.css new file mode 100644 index 0000000..50f9a82 --- /dev/null +++ b/noVNC/app/styles/auto.css @@ -0,0 +1,89 @@ +/* + * noVNC auto 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_container { + display: table; + width:100%; + height:100%; + background-color:#313131; + border-bottom-right-radius: 800px 600px; + /*border-top-left-radius: 800px 600px;*/ +} + +#noVNC_status { + font-size: 12px; + padding-top: 4px; + height:32px; + text-align: center; + font-weight: bold; + color: #fff; + z-index: 0; + position: absolute; + width: 100%; + margin-left: 0px; +} + +.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 */ +} + +#noVNC_buttons { + white-space: nowrap; +} + +/* 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; +} diff --git a/noVNC/app/styles/base.css b/noVNC/app/styles/base.css new file mode 100644 index 0000000..6bb23a4 --- /dev/null +++ b/noVNC/app/styles/base.css @@ -0,0 +1,876 @@ +/* + * noVNC base CSS + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2016 Samuel Mannehed for Cendio AB + * Copyright (C) 2016 Pierre Ossman 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). + */ + +/* + * Z index layers: + * + * 0: Main screen + * 10: Control bar + * 50: Transition blocker + * 60: Connection popups + * 100: Status bar + * ... + * 1000: Javascript crash + * ... + * 10000: Max (used for polyfills) + */ + +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%; + touch-action: none; +} + +html { + height:100%; +} + +.noVNC_only_touch.noVNC_hidden { + display: none; +} + +.noVNC_disabled { + color: rgb(128, 128, 128); +} + +/* ---------------------------------------- + * Spinner + * ---------------------------------------- + */ + +.noVNC_spinner { + position: relative; +} +.noVNC_spinner, .noVNC_spinner::before, .noVNC_spinner::after { + width: 10px; + height: 10px; + border-radius: 2px; + animation: noVNC_spinner 1.0s linear infinite; +} +.noVNC_spinner::before { + content: ""; + position: absolute; + left: 0px; + top: 0px; + animation-delay: -0.1s; +} +.noVNC_spinner::after { + content: ""; + position: absolute; + top: 0px; + left: 0px; + animation-delay: 0.1s; +} +@keyframes noVNC_spinner { + 0% { box-shadow: -60px 10px 0 rgba(255, 255, 255, 0); width: 20px; } + 25% { box-shadow: 20px 10px 0 rgba(255, 255, 255, 1); width: 10px; } + 50% { box-shadow: 60px 10px 0 rgba(255, 255, 255, 0); width: 10px; } +} + +/* ---------------------------------------- + * Input Elements + * ---------------------------------------- + */ + +input[type=input], input[type=password], input[type=number], +input:not([type]), textarea { + /* Disable default rendering */ + -webkit-appearance: none; + -moz-appearance: none; + background: none; + + margin: 2px; + padding: 2px; + border: 1px solid rgb(192, 192, 192); + border-radius: 5px; + color: black; + background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); +} + +input[type=button], input[type=submit], select { + /* Disable default rendering */ + -webkit-appearance: none; + -moz-appearance: none; + background: none; + + margin: 2px; + padding: 2px; + border: 1px solid rgb(192, 192, 192); + border-bottom-width: 2px; + border-radius: 5px; + color: black; + background: linear-gradient(to top, rgb(255, 255, 255), rgb(240, 240, 240)); + + /* This avoids it jumping around when :active */ + vertical-align: middle; +} + +input[type=button], input[type=submit] { + padding-left: 20px; + padding-right: 20px; +} + +option { + color: black; + background: white; +} + +input[type=input]:focus, input[type=password]:focus, +input:not([type]):focus, input[type=button]:focus, +input[type=submit]:focus, +textarea:focus, select:focus { + box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5); + border-color: rgb(74, 144, 217); + outline: none; +} + +input[type=button]::-moz-focus-inner, +input[type=submit]::-moz-focus-inner { + border: none; +} + +input[type=input]:disabled, input[type=password]:disabled, +input:not([type]):disabled, input[type=button]:disabled, +input[type=submit]:disabled, input[type=number]:disabled, +textarea:disabled, select:disabled { + color: rgb(128, 128, 128); + background: rgb(240, 240, 240); +} + +input[type=button]:active, input[type=submit]:active, +select:active { + border-bottom-width: 1px; + margin-top: 3px; +} + +:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled), +:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled), +:root:not(.noVNC_touch) select:hover:not(:disabled) { + background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250)); +} + +/* ---------------------------------------- + * WebKit centering hacks + * ---------------------------------------- + */ + +.noVNC_center { + /* + * This is a workaround because webkit misrenders transforms and + * uses non-integer coordinates, resulting in blurry content. + * Ideally we'd use "top: 50%; transform: translateY(-50%);" on + * the objects instead. + */ + display: flex; + align-items: center; + justify-content: center; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; +} +.noVNC_center > * { + pointer-events: auto; +} +.noVNC_vcenter { + display: flex; + flex-direction: column; + justify-content: center; + position: fixed; + top: 0; + left: 0; + height: 100%; + pointer-events: none; +} +.noVNC_vcenter > * { + pointer-events: auto; +} + +/* ---------------------------------------- + * Layering + * ---------------------------------------- + */ + +.noVNC_connect_layer { + z-index: 60; +} + +/* ---------------------------------------- + * Fallback error + * ---------------------------------------- + */ + +#noVNC_fallback_error { + position: fixed; + z-index: 1000; + left: 50%; + transform: translate(-50%, -50px); + transition: 0.5s ease-in-out; + + visibility: hidden; + opacity: 0; + + top: 60px; + padding: 15px; + width: auto; + + text-align: center; + font-weight: bold; + word-wrap: break-word; + color: #fff; + + border-radius: 10px; + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); + background: rgba(200,55,55,0.8); +} +#noVNC_fallback_error.noVNC_open { + transform: translate(-50%, 0); + visibility: visible; + opacity: 1; +} + +#noVNC_fallback_errormsg { + font-weight: normal; +} + +#noVNC_fallback_error .noVNC_location { + font-style: italic; + font-size: 0.8em; + color: rgba(255, 255, 255, 0.8); +} + +#noVNC_fallback_error .noVNC_stack { + padding: 10px; + margin: 10px; + font-size: 0.8em; + text-align: left; + white-space: pre; + border: 1px solid rgba(0, 0, 0, 0.5); + background: rgba(0, 0, 0, 0.2); +} + +/* ---------------------------------------- + * Control Bar + * ---------------------------------------- + */ + +#noVNC_control_bar_anchor { + /* The anchor is needed to get z-stacking to work */ + position: fixed; + z-index: 10; + + transition: 0.5s ease-in-out; + + /* Edge misrenders animations wihthout this */ + transform: translateX(0); +} +:root.noVNC_connected #noVNC_control_bar_anchor.noVNC_idle { + opacity: 0.8; +} +#noVNC_control_bar_anchor.noVNC_right { + left: auto; + right: 0; +} + +#noVNC_control_bar { + position: relative; + left: -100%; + + transition: 0.5s ease-in-out; + + background-color: rgb(110, 132, 163); + border-radius: 0 10px 10px 0; + +} +#noVNC_control_bar.noVNC_open { + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); + left: 0; +} +#noVNC_control_bar::before { + /* This extra element is to get a proper shadow */ + content: ""; + position: absolute; + z-index: -1; + height: 100%; + width: 30px; + left: -30px; + transition: box-shadow 0.5s ease-in-out; +} +#noVNC_control_bar.noVNC_open::before { + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); +} +.noVNC_right #noVNC_control_bar { + left: 100%; + border-radius: 10px 0 0 10px; +} +.noVNC_right #noVNC_control_bar.noVNC_open { + left: 0; +} +.noVNC_right #noVNC_control_bar::before { + visibility: hidden; +} + +#noVNC_control_bar_handle { + position: absolute; + left: -15px; + top: 0; + transform: translateY(35px); + width: calc(100% + 30px); + height: 50px; + z-index: -1; + cursor: pointer; + border-radius: 5px; + background-color: rgb(83, 99, 122); + background-image: url("../images/handle_bg.svg"); + background-repeat: no-repeat; + background-position: right; + box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.5); +} +#noVNC_control_bar_handle:after { + content: ""; + transition: transform 0.5s ease-in-out; + background: url("../images/handle.svg"); + position: absolute; + top: 22px; /* (50px-6px)/2 */ + right: 5px; + width: 5px; + height: 6px; +} +#noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { + transform: translateX(1px) rotate(180deg); +} +:root:not(.noVNC_connected) #noVNC_control_bar_handle { + display: none; +} +.noVNC_right #noVNC_control_bar_handle { + background-position: left; +} +.noVNC_right #noVNC_control_bar_handle:after { + left: 5px; + right: 0; + transform: translateX(1px) rotate(180deg); +} +.noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { + transform: none; +} +#noVNC_control_bar_handle div { + position: absolute; + right: -35px; + top: 0; + width: 50px; + height: 50px; +} +:root:not(.noVNC_touch) #noVNC_control_bar_handle div { + display: none; +} +.noVNC_right #noVNC_control_bar_handle div { + left: -35px; + right: auto; +} + +#noVNC_control_bar .noVNC_scroll { + max-height: 100vh; /* Chrome is buggy with 100% */ + overflow-x: hidden; + overflow-y: auto; + padding: 0 10px 0 5px; +} +.noVNC_right #noVNC_control_bar .noVNC_scroll { + padding: 0 5px 0 10px; +} + +/* General button style */ +.noVNC_button { + display: block; + padding: 4px 4px; + margin: 10px 0; + vertical-align: middle; + border:1px solid rgba(255, 255, 255, 0.2); + border-radius: 6px; +} +.noVNC_button.noVNC_selected { + border-color: rgba(0, 0, 0, 0.8); + background: rgba(0, 0, 0, 0.5); +} +.noVNC_button:disabled { + opacity: 0.4; +} +.noVNC_button:focus { + outline: none; +} +.noVNC_button:active { + padding-top: 5px; + padding-bottom: 3px; +} +:root:not(.noVNC_touch) .noVNC_button.noVNC_selected:hover { + border-color: rgba(0, 0, 0, 0.4); + background: rgba(0, 0, 0, 0.2); +} +:root:not(.noVNC_touch) .noVNC_button:hover { + background: rgba(255, 255, 255, 0.2); +} +.noVNC_button.noVNC_hidden { + display: none; +} + +/* Panels */ +.noVNC_panel { + transform: translateX(25px); + + transition: 0.5s ease-in-out; + + max-height: 100vh; /* Chrome is buggy with 100% */ + overflow-x: hidden; + overflow-y: auto; + + visibility: hidden; + opacity: 0; + + padding: 15px; + + background: #fff; + border-radius: 10px; + color: #000; + border: 2px solid #E0E0E0; + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); +} +.noVNC_panel.noVNC_open { + visibility: visible; + opacity: 1; + transform: translateX(75px); +} +.noVNC_right .noVNC_vcenter { + left: auto; + right: 0; +} +.noVNC_right .noVNC_panel { + transform: translateX(-25px); +} +.noVNC_right .noVNC_panel.noVNC_open { + transform: translateX(-75px); +} + +.noVNC_panel hr { + border: none; + border-top: 1px solid rgb(192, 192, 192); +} + +.noVNC_panel label { + display: block; + white-space: nowrap; +} + +.noVNC_panel .noVNC_heading { + background-color: rgb(110, 132, 163); + border-radius: 5px; + padding: 5px; + /* Compensate for padding in image */ + padding-right: 8px; + color: white; + font-size: 20px; + margin-bottom: 10px; + white-space: nowrap; +} +.noVNC_panel .noVNC_heading img { + vertical-align: bottom; +} + +.noVNC_submit { + float: right; +} + +/* Expanders */ +.noVNC_expander { + cursor: pointer; +} +.noVNC_expander::before { + content: url("../images/expander.svg"); + display: inline-block; + margin-right: 5px; + transition: 0.2s ease-in-out; +} +.noVNC_expander.noVNC_open::before { + transform: rotateZ(90deg); +} +.noVNC_expander ~ * { + margin: 5px; + margin-left: 10px; + padding: 5px; + background: rgba(0, 0, 0, 0.05); + border-radius: 5px; +} +.noVNC_expander:not(.noVNC_open) ~ * { + display: none; +} + +/* Control bar content */ + +#noVNC_control_bar .noVNC_logo { + font-size: 13px; +} + +:root:not(.noVNC_connected) #noVNC_view_drag_button { + display: none; +} + +/* noVNC Touch Device only buttons */ +:root:not(.noVNC_connected) #noVNC_mobile_buttons { + display: none; +} +:root:not(.noVNC_touch) #noVNC_mobile_buttons { + display: none; +} + +/* Extra manual keys */ +:root:not(.noVNC_connected) #noVNC_extra_keys { + display: none; +} + +#noVNC_modifiers { + background-color: rgb(92, 92, 92); + border: none; + padding: 0 10px; +} + +/* XVP Shutdown/Reboot */ +:root:not(.noVNC_connected) #noVNC_xvp_button { + display: none; +} +#noVNC_xvp { +} +#noVNC_xvp_buttons { + display: none; +} + +#noVNC_xvp input[type=button] { + width: 100%; +} + +/* Clipboard */ +:root:not(.noVNC_connected) #noVNC_clipboard_button { + display: none; +} +#noVNC_clipboard { + /* Full screen, minus padding and left and right margins */ + max-width: calc(100vw - 2*15px - 75px - 25px); +} +#noVNC_clipboard_text { + width: 500px; + max-width: 100%; +} + +/* Settings */ +#noVNC_settings { +} +#noVNC_settings ul { + list-style: none; + margin: 0px; + padding: 0px; +} +#noVNC_setting_port { + width: 80px; +} +#noVNC_setting_path { + width: 100px; +} + +/* Connection Controls */ +:root:not(.noVNC_connected) #noVNC_disconnect_button { + display: none; +} + +/* ---------------------------------------- + * Status Dialog + * ---------------------------------------- + */ + +#noVNC_status { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 100; + transform: translateY(-100%); + + cursor: pointer; + + transition: 0.5s ease-in-out; + + visibility: hidden; + opacity: 0; + + padding: 5px; + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + + line-height: 25px; + word-wrap: break-word; + color: #fff; + + border-bottom: 1px solid rgba(0, 0, 0, 0.9); +} +#noVNC_status.noVNC_open { + transform: translateY(0); + visibility: visible; + opacity: 1; +} + +#noVNC_status::before { + content: ""; + display: inline-block; + width: 25px; + height: 25px; + margin-right: 5px; +} + +#noVNC_status.noVNC_status_normal { + background: rgba(128,128,128,0.9); +} +#noVNC_status.noVNC_status_normal::before { + content: url("../images/info.svg") " "; +} +#noVNC_status.noVNC_status_error { + background: rgba(200,55,55,0.9); +} +#noVNC_status.noVNC_status_error::before { + content: url("../images/error.svg") " "; +} +#noVNC_status.noVNC_status_warn { + background: rgba(180,180,30,0.9); +} +#noVNC_status.noVNC_status_warn::before { + content: url("../images/warning.svg") " "; +} + +/* ---------------------------------------- + * Connect Dialog + * ---------------------------------------- + */ + +#noVNC_connect_dlg { + transition: 0.5s ease-in-out; + + transform: scale(0, 0); + visibility: hidden; + opacity: 0; +} +#noVNC_connect_dlg.noVNC_open { + transform: scale(1, 1); + visibility: visible; + opacity: 1; +} +#noVNC_connect_dlg .noVNC_logo { + transition: 0.5s ease-in-out; + padding: 10px; + margin-bottom: 10px; + + font-size: 80px; + text-align: center; + + border-radius: 5px; +} +@media (max-width: 440px) { + #noVNC_connect_dlg { + max-width: calc(100vw - 100px); + } + #noVNC_connect_dlg .noVNC_logo { + font-size: calc(25vw - 30px); + } +} +#noVNC_connect_button { + cursor: pointer; + + padding: 10px; + + color: white; + background-color: rgb(110, 132, 163); + border-radius: 12px; + + text-align: center; + font-size: 20px; + + box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); +} +#noVNC_connect_button div { + margin: 2px; + padding: 5px 30px; + border: 1px solid rgb(83, 99, 122); + border-bottom-width: 2px; + border-radius: 5px; + background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147)); + + /* This avoids it jumping around when :active */ + vertical-align: middle; +} +#noVNC_connect_button div:active { + border-bottom-width: 1px; + margin-top: 3px; +} +:root:not(.noVNC_touch) #noVNC_connect_button div:hover { + background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155)); +} + +#noVNC_connect_button img { + vertical-align: bottom; + height: 1.3em; +} + +/* ---------------------------------------- + * Password Dialog + * ---------------------------------------- + */ + +#noVNC_password_dlg { + position: relative; + + transform: translateY(-50px); +} +#noVNC_password_dlg.noVNC_open { + transform: translateY(0); +} +#noVNC_password_dlg ul { + list-style: none; + margin: 0px; + padding: 0px; +} + +/* ---------------------------------------- + * Main Area + * ---------------------------------------- + */ + +/* Transition screen */ +#noVNC_transition { + display: none; + + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + + color: white; + background: rgba(0, 0, 0, 0.5); + z-index: 50; + + /*display: flex;*/ + align-items: center; + justify-content: center; + flex-direction: column; +} +:root.noVNC_connecting #noVNC_transition, +:root.noVNC_disconnecting #noVNC_transition, +:root.noVNC_reconnecting #noVNC_transition { + display: flex; +} +:root:not(.noVNC_reconnecting) #noVNC_cancel_reconnect_button { + display: none; +} +#noVNC_transition_text { + font-size: 1.5em; +} + +/* Main container */ +#noVNC_container { + width: 100%; + height: 100%; + background-color: #313131; + border-bottom-right-radius: 800px 600px; + /*border-top-left-radius: 800px 600px;*/ +} + +#noVNC_keyboardinput { + width: 1px; + height: 1px; + background-color: #fff; + color: #fff; + border: 0; + position: absolute; + left: -40px; + z-index: -1; + ime-mode: disabled; +} + +/* HTML5 Canvas */ +#noVNC_screen { + display: flex; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(40, 40, 40); +} +:root:not(.noVNC_connected) #noVNC_screen { + display: none; +} + +/* 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 { + margin: auto; + /* IE miscalculates width without this :( */ + flex-shrink: 0; +} + +/*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 { + color:yellow; + font-family: 'Orbitron', 'OrbitronTTF', sans-serif; + line-height:90%; + text-shadow: 0.1em 0.1em 0 black; +} +.noVNC_logo span{ + color:green; +} + +#noVNC_bell { + display: none; +} + +/* ---------------------------------------- + * Media sizing + * ---------------------------------------- + */ + +@media screen and (max-width: 640px){ + #noVNC_logo { + font-size: 150px; + } +} + +@media screen and (min-width: 321px) and (max-width: 480px) { + #noVNC_logo { + font-size: 110px; + } +} + +@media screen and (max-width: 320px) { + #noVNC_logo { + font-size: 90px; + } +} diff --git a/noVNC/app/ui.js b/noVNC/app/ui.js new file mode 100644 index 0000000..33cd1d4 --- /dev/null +++ b/noVNC/app/ui.js @@ -0,0 +1,1761 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2016 Samuel Mannehed for Cendio AB + * Copyright (C) 2016 Pierre Ossman for Cendio AB + * Licensed under MPL 2.0 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/* jslint white: false, browser: true */ +/* global window, document.getElementById, Util, WebUtil, RFB, Display */ + +/* [module] + * import Util from "../core/util"; + * import KeyTable from "../core/input/keysym"; + * import keysyms from "./keysymdef"; + * import RFB from "../core/rfb"; + * import Display from "../core/display"; + * import WebUtil from "./webutil"; + */ + +var UI; + +(function () { + "use strict"; + + // Fallback for all uncought errors + window.addEventListener('error', function(event) { + try { + var msg, div, text; + + msg = document.getElementById('noVNC_fallback_errormsg'); + + // Only show the initial error + if (msg.hasChildNodes()) { + return false; + } + + div = document.createElement("div"); + div.appendChild(document.createTextNode(event.message)); + msg.appendChild(div); + + div = document.createElement("div"); + div.className = 'noVNC_location'; + text = event.filename + ":" + event.lineno + ":" + event.colno; + div.appendChild(document.createTextNode(text)); + msg.appendChild(div); + + if ((event.error !== undefined) && + (event.error.stack !== undefined)) { + div = document.createElement("div"); + div.className = 'noVNC_stack'; + div.appendChild(document.createTextNode(event.error.stack)); + msg.appendChild(div); + } + + document.getElementById('noVNC_fallback_error') + .classList.add("noVNC_open"); + } catch (exc) { + document.write("noVNC encountered an error."); + } + // Don't return true since this would prevent the error + // from being printed to the browser console. + return false; + }); + + // Set up translations + var LINGUAS = ["de", "el", "nl", "sv"]; + Util.Localisation.setup(LINGUAS); + if (Util.Localisation.language !== "en") { + WebUtil.load_scripts( + {'app': ["locale/" + Util.Localisation.language + ".js"]}); + } + + /* [begin skip-as-module] */ + // Load supporting scripts + WebUtil.load_scripts( + {'core': ["base64.js", "websock.js", "des.js", "input/keysymdef.js", + "input/xtscancodes.js", "input/util.js", "input/devices.js", + "display.js", "inflator.js", "rfb.js", "input/keysym.js"]}); + + window.onscriptsload = function () { UI.load(); }; + /* [end skip-as-module] */ + + var _ = Util.Localisation.get; + + UI = { + + connected: false, + desktopName: "", + + resizeTimeout: null, + statusTimeout: null, + hideKeyboardTimeout: null, + idleControlbarTimeout: null, + closeControlbarTimeout: null, + + controlbarGrabbed: false, + controlbarDrag: false, + controlbarMouseDownClientY: 0, + controlbarMouseDownOffsetY: 0, + + isSafari: false, + rememberedClipSetting: null, + lastKeyboardinput: null, + defaultKeyboardinputLen: 100, + + inhibit_reconnect: true, + reconnect_callback: null, + reconnect_password: null, + + // Setup rfb object, load settings from browser storage, then call + // UI.init to setup the UI/menus + load: function(callback) { + WebUtil.initSettings(UI.start, callback); + }, + + // Render default UI and initialize settings menu + start: function(callback) { + + // Setup global variables first + UI.isSafari = (navigator.userAgent.indexOf('Safari') !== -1 && + navigator.userAgent.indexOf('Chrome') === -1); + + UI.initSettings(); + + // Translate the DOM + Util.Localisation.translateDOM(); + + // Adapt the interface for touch screen devices + if (Util.isTouchDevice) { + document.documentElement.classList.add("noVNC_touch"); + // Remove the address bar + setTimeout(function() { window.scrollTo(0, 1); }, 100); + } + + // Restore control bar position + if (WebUtil.readSetting('controlbar_pos') === 'right') { + UI.toggleControlbarSide(); + } + + UI.initFullscreen(); + + // Setup event handlers + UI.addResizeHandlers(); + UI.addControlbarHandlers(); + UI.addTouchSpecificHandlers(); + UI.addExtraKeysHandlers(); + UI.addXvpHandlers(); + UI.addConnectionControlHandlers(); + UI.addClipboardHandlers(); + UI.addSettingsHandlers(); + document.getElementById("noVNC_status") + .addEventListener('click', UI.hideStatus); + + UI.openControlbar(); + + // Show the connect panel on first load unless autoconnecting + if (!autoconnect) { + UI.openConnectPanel(); + } + + UI.updateViewClip(); + + UI.updateVisualState(); + + document.getElementById('noVNC_setting_host').focus(); + + var autoconnect = WebUtil.getConfigVar('autoconnect', false); + if (autoconnect === 'true' || autoconnect == '1') { + autoconnect = true; + UI.connect(); + } else { + autoconnect = false; + } + + if (typeof callback === "function") { + callback(UI.rfb); + } + }, + + initFullscreen: function() { + // Only show the button if fullscreen is properly supported + // * Safari doesn't support alphanumerical input while in fullscreen + if (!UI.isSafari && + (document.documentElement.requestFullscreen || + document.documentElement.mozRequestFullScreen || + document.documentElement.webkitRequestFullscreen || + document.body.msRequestFullscreen)) { + document.getElementById('noVNC_fullscreen_button') + .classList.remove("noVNC_hidden"); + UI.addFullscreenHandlers(); + } + }, + + initSettings: function() { + var i; + + // Logging selection dropdown + var llevels = ['error', 'warn', 'info', 'debug']; + for (i = 0; i < llevels.length; i += 1) { + UI.addOption(document.getElementById('noVNC_setting_logging'),llevels[i], llevels[i]); + } + + // Settings with immediate effects + UI.initSetting('logging', 'warn'); + UI.updateLogging(); + + // if port == 80 (or 443) then it won't be present and should be + // set manually + var port = window.location.port; + if (!port) { + if (window.location.protocol.substring(0,5) == 'https') { + port = 443; + } + else if (window.location.protocol.substring(0,4) == 'http') { + port = 80; + } + } + + /* Populate the controls if defaults are provided in the URL */ + UI.initSetting('host', window.location.hostname); + UI.initSetting('port', port); + UI.initSetting('encrypt', (window.location.protocol === "https:")); + UI.initSetting('true_color', true); + UI.initSetting('cursor', !Util.isTouchDevice); + UI.initSetting('clip', false); + UI.initSetting('resize', 'off'); + UI.initSetting('shared', true); + UI.initSetting('view_only', false); + UI.initSetting('path', 'websockify'); + UI.initSetting('repeaterID', ''); + UI.initSetting('reconnect', false); + UI.initSetting('reconnect_delay', 5000); + + UI.setupSettingLabels(); + }, + + // Adds a link to the label elements on the corresponding input elements + setupSettingLabels: function() { + var labels = document.getElementsByTagName('LABEL'); + for (var i = 0; i < labels.length; i++) { + var htmlFor = labels[i].htmlFor; + if (htmlFor != '') { + var elem = document.getElementById(htmlFor); + if (elem) elem.label = labels[i]; + } else { + // If 'for' isn't set, use the first input element child + var children = labels[i].children; + for (var j = 0; j < children.length; j++) { + if (children[j].form !== undefined) { + children[j].label = labels[i]; + break; + } + } + } + } + }, + + initRFB: function() { + try { + UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'), + 'onNotification': UI.notification, + 'onUpdateState': UI.updateState, + 'onDisconnected': UI.disconnectFinished, + 'onPasswordRequired': UI.passwordRequired, + 'onXvpInit': UI.updateXvpButton, + 'onClipboard': UI.clipboardReceive, + 'onBell': UI.bell, + 'onFBUComplete': UI.initialResize, + 'onFBResize': UI.updateSessionSize, + 'onDesktopName': UI.updateDesktopName}); + return true; + } catch (exc) { + var msg = "Unable to create RFB client -- " + exc; + Util.Error(msg); + UI.showStatus(msg, 'error'); + return false; + } + }, + +/* ------^------- + * /INIT + * ============== + * EVENT HANDLERS + * ------v------*/ + + addResizeHandlers: function() { + window.addEventListener('resize', UI.applyResizeMode); + window.addEventListener('resize', UI.updateViewClip); + }, + + addControlbarHandlers: function() { + document.getElementById("noVNC_control_bar") + .addEventListener('mousemove', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('mouseup', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('mousedown', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('keypress', UI.activateControlbar); + + document.getElementById("noVNC_control_bar") + .addEventListener('mousedown', UI.keepControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('keypress', UI.keepControlbar); + + document.getElementById("noVNC_view_drag_button") + .addEventListener('click', UI.toggleViewDrag); + + document.getElementById("noVNC_control_bar_handle") + .addEventListener('mousedown', UI.controlbarHandleMouseDown); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('mouseup', UI.controlbarHandleMouseUp); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('mousemove', UI.dragControlbarHandle); + // resize events aren't available for elements + window.addEventListener('resize', UI.updateControlbarHandle); + + var exps = document.getElementsByClassName("noVNC_expander"); + for (var i = 0;i < exps.length;i++) { + exps[i].addEventListener('click', UI.toggleExpander); + } + }, + + addTouchSpecificHandlers: function() { + document.getElementById("noVNC_mouse_button0") + .addEventListener('click', function () { UI.setMouseButton(1); }); + document.getElementById("noVNC_mouse_button1") + .addEventListener('click', function () { UI.setMouseButton(2); }); + document.getElementById("noVNC_mouse_button2") + .addEventListener('click', function () { UI.setMouseButton(4); }); + document.getElementById("noVNC_mouse_button4") + .addEventListener('click', function () { UI.setMouseButton(0); }); + document.getElementById("noVNC_keyboard_button") + .addEventListener('click', UI.toggleVirtualKeyboard); + + document.getElementById("noVNC_keyboardinput") + .addEventListener('input', UI.keyInput); + document.getElementById("noVNC_keyboardinput") + .addEventListener('focus', UI.onfocusVirtualKeyboard); + document.getElementById("noVNC_keyboardinput") + .addEventListener('blur', UI.onblurVirtualKeyboard); + document.getElementById("noVNC_keyboardinput") + .addEventListener('submit', function () { return false; }); + + document.documentElement + .addEventListener('mousedown', UI.keepVirtualKeyboard, true); + + document.getElementById("noVNC_control_bar") + .addEventListener('touchstart', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('touchmove', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('touchend', UI.activateControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('input', UI.activateControlbar); + + document.getElementById("noVNC_control_bar") + .addEventListener('touchstart', UI.keepControlbar); + document.getElementById("noVNC_control_bar") + .addEventListener('input', UI.keepControlbar); + + document.getElementById("noVNC_control_bar_handle") + .addEventListener('touchstart', UI.controlbarHandleMouseDown); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('touchend', UI.controlbarHandleMouseUp); + document.getElementById("noVNC_control_bar_handle") + .addEventListener('touchmove', UI.dragControlbarHandle); + + window.addEventListener('load', UI.keyboardinputReset); + }, + + addExtraKeysHandlers: function() { + document.getElementById("noVNC_toggle_extra_keys_button") + .addEventListener('click', UI.toggleExtraKeys); + document.getElementById("noVNC_toggle_ctrl_button") + .addEventListener('click', UI.toggleCtrl); + document.getElementById("noVNC_toggle_alt_button") + .addEventListener('click', UI.toggleAlt); + document.getElementById("noVNC_send_tab_button") + .addEventListener('click', UI.sendTab); + document.getElementById("noVNC_send_esc_button") + .addEventListener('click', UI.sendEsc); + document.getElementById("noVNC_send_ctrl_alt_del_button") + .addEventListener('click', UI.sendCtrlAltDel); + }, + + addXvpHandlers: function() { + document.getElementById("noVNC_xvp_shutdown_button") + .addEventListener('click', function() { UI.rfb.xvpShutdown(); }); + document.getElementById("noVNC_xvp_reboot_button") + .addEventListener('click', function() { UI.rfb.xvpReboot(); }); + document.getElementById("noVNC_xvp_reset_button") + .addEventListener('click', function() { UI.rfb.xvpReset(); }); + document.getElementById("noVNC_xvp_button") + .addEventListener('click', UI.toggleXvpPanel); + }, + + addConnectionControlHandlers: function() { + document.getElementById("noVNC_disconnect_button") + .addEventListener('click', UI.disconnect); + document.getElementById("noVNC_connect_button") + .addEventListener('click', UI.connect); + document.getElementById("noVNC_cancel_reconnect_button") + .addEventListener('click', UI.cancelReconnect); + + document.getElementById("noVNC_password_button") + .addEventListener('click', UI.setPassword); + }, + + addClipboardHandlers: function() { + document.getElementById("noVNC_clipboard_button") + .addEventListener('click', UI.toggleClipboardPanel); + document.getElementById("noVNC_clipboard_text") + .addEventListener('focus', UI.displayBlur); + document.getElementById("noVNC_clipboard_text") + .addEventListener('blur', UI.displayFocus); + document.getElementById("noVNC_clipboard_text") + .addEventListener('change', UI.clipboardSend); + document.getElementById("noVNC_clipboard_clear_button") + .addEventListener('click', UI.clipboardClear); + }, + + // Add a call to save settings when the element changes, + // unless the optional parameter changeFunc is used instead. + addSettingChangeHandler: function(name, changeFunc) { + var settingElem = document.getElementById("noVNC_setting_" + name); + if (changeFunc === undefined) { + changeFunc = function () { UI.saveSetting(name); }; + } + settingElem.addEventListener('change', changeFunc); + }, + + addSettingsHandlers: function() { + document.getElementById("noVNC_settings_button") + .addEventListener('click', UI.toggleSettingsPanel); + + UI.addSettingChangeHandler('encrypt'); + UI.addSettingChangeHandler('true_color'); + UI.addSettingChangeHandler('cursor'); + UI.addSettingChangeHandler('cursor', UI.updateLocalCursor); + UI.addSettingChangeHandler('resize'); + UI.addSettingChangeHandler('resize', UI.enableDisableViewClip); + UI.addSettingChangeHandler('resize', UI.applyResizeMode); + UI.addSettingChangeHandler('clip'); + UI.addSettingChangeHandler('clip', UI.updateViewClip); + UI.addSettingChangeHandler('shared'); + UI.addSettingChangeHandler('view_only'); + UI.addSettingChangeHandler('view_only', UI.updateViewOnly); + UI.addSettingChangeHandler('host'); + UI.addSettingChangeHandler('port'); + UI.addSettingChangeHandler('path'); + UI.addSettingChangeHandler('repeaterID'); + UI.addSettingChangeHandler('logging'); + UI.addSettingChangeHandler('logging', UI.updateLogging); + UI.addSettingChangeHandler('reconnect'); + UI.addSettingChangeHandler('reconnect_delay'); + }, + + addFullscreenHandlers: function() { + document.getElementById("noVNC_fullscreen_button") + .addEventListener('click', UI.toggleFullscreen); + + window.addEventListener('fullscreenchange', UI.updateFullscreenButton); + window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton); + window.addEventListener('webkitfullscreenchange', UI.updateFullscreenButton); + window.addEventListener('msfullscreenchange', UI.updateFullscreenButton); + }, + +/* ------^------- + * /EVENT HANDLERS + * ============== + * VISUAL + * ------v------*/ + + updateState: function(rfb, state, oldstate) { + var msg; + + document.documentElement.classList.remove("noVNC_connecting"); + document.documentElement.classList.remove("noVNC_connected"); + document.documentElement.classList.remove("noVNC_disconnecting"); + document.documentElement.classList.remove("noVNC_reconnecting"); + + switch (state) { + case 'connecting': + document.getElementById("noVNC_transition_text").textContent = _("Connecting..."); + document.documentElement.classList.add("noVNC_connecting"); + break; + case 'connected': + UI.connected = true; + UI.inhibit_reconnect = false; + document.documentElement.classList.add("noVNC_connected"); + if (rfb && rfb.get_encrypt()) { + msg = _("Connected (encrypted) to ") + UI.desktopName; + } else { + msg = _("Connected (unencrypted) to ") + UI.desktopName; + } + UI.showStatus(msg); + break; + case 'disconnecting': + UI.connected = false; + document.getElementById("noVNC_transition_text").textContent = _("Disconnecting..."); + document.documentElement.classList.add("noVNC_disconnecting"); + break; + case 'disconnected': + UI.showStatus(_("Disconnected")); + break; + default: + msg = "Invalid UI state"; + Util.Error(msg); + UI.showStatus(msg, 'error'); + break; + } + + UI.updateVisualState(); + }, + + // Disable/enable controls depending on connection state + updateVisualState: function() { + //Util.Debug(">> updateVisualState"); + + UI.enableDisableViewClip(); + + if (Util.browserSupportsCursorURIs() && !Util.isTouchDevice) { + UI.enableSetting('cursor'); + } else { + UI.disableSetting('cursor'); + } + + if (UI.connected) { + UI.disableSetting('encrypt'); + UI.disableSetting('true_color'); + UI.disableSetting('shared'); + UI.disableSetting('host'); + UI.disableSetting('port'); + UI.disableSetting('path'); + UI.disableSetting('repeaterID'); + UI.updateViewClip(); + UI.setMouseButton(1); + + // Hide the controlbar after 2 seconds + UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000); + } else { + UI.enableSetting('encrypt'); + UI.enableSetting('true_color'); + UI.enableSetting('shared'); + UI.enableSetting('host'); + UI.enableSetting('port'); + UI.enableSetting('path'); + UI.enableSetting('repeaterID'); + UI.updateXvpButton(0); + UI.keepControlbar(); + } + + // Hide input related buttons in view only mode + if (UI.rfb && UI.rfb.get_view_only()) { + document.getElementById('noVNC_keyboard_button') + .classList.add('noVNC_hidden'); + document.getElementById('noVNC_toggle_extra_keys_button') + .classList.add('noVNC_hidden'); + } else { + document.getElementById('noVNC_keyboard_button') + .classList.remove('noVNC_hidden'); + document.getElementById('noVNC_toggle_extra_keys_button') + .classList.remove('noVNC_hidden'); + } + + // State change disables viewport dragging. + // It is enabled (toggled) by direct click on the button + UI.setViewDrag(false); + + // State change also closes the password dialog + document.getElementById('noVNC_password_dlg') + .classList.remove('noVNC_open'); + + //Util.Debug("<< updateVisualState"); + }, + + showStatus: function(text, status_type, time) { + var statusElem = document.getElementById('noVNC_status'); + + clearTimeout(UI.statusTimeout); + + if (typeof status_type === 'undefined') { + status_type = 'normal'; + } + + statusElem.classList.remove("noVNC_status_normal"); + statusElem.classList.remove("noVNC_status_warn"); + statusElem.classList.remove("noVNC_status_error"); + + switch (status_type) { + case 'warning': + case 'warn': + statusElem.classList.add("noVNC_status_warn"); + break; + case 'error': + statusElem.classList.add("noVNC_status_error"); + break; + case 'normal': + case 'info': + default: + statusElem.classList.add("noVNC_status_normal"); + break; + } + + statusElem.textContent = text; + statusElem.classList.add("noVNC_open"); + + // If no time was specified, show the status for 1.5 seconds + if (typeof time === 'undefined') { + time = 1500; + } + + // Error messages do not timeout + if (status_type !== 'error') { + UI.statusTimeout = window.setTimeout(UI.hideStatus, time); + } + }, + + hideStatus: function() { + clearTimeout(UI.statusTimeout); + document.getElementById('noVNC_status').classList.remove("noVNC_open"); + }, + + notification: function (rfb, msg, level, options) { + UI.showStatus(msg, level); + }, + + activateControlbar: function(event) { + clearTimeout(UI.idleControlbarTimeout); + // We manipulate the anchor instead of the actual control + // bar in order to avoid creating new a stacking group + document.getElementById('noVNC_control_bar_anchor') + .classList.remove("noVNC_idle"); + UI.idleControlbarTimeout = window.setTimeout(UI.idleControlbar, 2000); + }, + + idleControlbar: function() { + document.getElementById('noVNC_control_bar_anchor') + .classList.add("noVNC_idle"); + }, + + keepControlbar: function() { + clearTimeout(UI.closeControlbarTimeout); + }, + + openControlbar: function() { + document.getElementById('noVNC_control_bar') + .classList.add("noVNC_open"); + }, + + closeControlbar: function() { + UI.closeAllPanels(); + document.getElementById('noVNC_control_bar') + .classList.remove("noVNC_open"); + }, + + toggleControlbar: function() { + if (document.getElementById('noVNC_control_bar') + .classList.contains("noVNC_open")) { + UI.closeControlbar(); + } else { + UI.openControlbar(); + } + }, + + toggleControlbarSide: function () { + // Temporarily disable animation to avoid weird movement + var bar = document.getElementById('noVNC_control_bar'); + bar.style.transitionDuration = '0s'; + bar.addEventListener('transitionend', function () { this.style.transitionDuration = ""; }); + + var anchor = document.getElementById('noVNC_control_bar_anchor'); + if (anchor.classList.contains("noVNC_right")) { + WebUtil.writeSetting('controlbar_pos', 'left'); + anchor.classList.remove("noVNC_right"); + } else { + WebUtil.writeSetting('controlbar_pos', 'right'); + anchor.classList.add("noVNC_right"); + } + + // Consider this a movement of the handle + UI.controlbarDrag = true; + }, + + dragControlbarHandle: function (e) { + if (!UI.controlbarGrabbed) return; + + var ptr = Util.getPointerEvent(e); + + var anchor = document.getElementById('noVNC_control_bar_anchor'); + if (ptr.clientX < (window.innerWidth * 0.1)) { + if (anchor.classList.contains("noVNC_right")) { + UI.toggleControlbarSide(); + } + } else if (ptr.clientX > (window.innerWidth * 0.9)) { + if (!anchor.classList.contains("noVNC_right")) { + UI.toggleControlbarSide(); + } + } + + if (!UI.controlbarDrag) { + // The goal is to trigger on a certain physical width, the + // devicePixelRatio brings us a bit closer but is not optimal. + var dragThreshold = 10 * (window.devicePixelRatio || 1); + var dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY); + + if (dragDistance < dragThreshold) return; + + UI.controlbarDrag = true; + } + + var eventY = ptr.clientY - UI.controlbarMouseDownOffsetY; + + UI.moveControlbarHandle(eventY); + + e.preventDefault(); + e.stopPropagation(); + UI.keepControlbar(); + UI.activateControlbar(); + }, + + // Move the handle but don't allow any position outside the bounds + moveControlbarHandle: function (viewportRelativeY) { + var handle = document.getElementById("noVNC_control_bar_handle"); + var handleHeight = handle.getBoundingClientRect().height; + var controlbarBounds = document.getElementById("noVNC_control_bar") + .getBoundingClientRect(); + var margin = 10; + + // These heights need to be non-zero for the below logic to work + if (handleHeight === 0 || controlbarBounds.height === 0) { + return; + } + + var newY = viewportRelativeY; + + // Check if the coordinates are outside the control bar + if (newY < controlbarBounds.top + margin) { + // Force coordinates to be below the top of the control bar + newY = controlbarBounds.top + margin; + + } else if (newY > controlbarBounds.top + + controlbarBounds.height - handleHeight - margin) { + // Force coordinates to be above the bottom of the control bar + newY = controlbarBounds.top + + controlbarBounds.height - handleHeight - margin; + } + + // Corner case: control bar too small for stable position + if (controlbarBounds.height < (handleHeight + margin * 2)) { + newY = controlbarBounds.top + + (controlbarBounds.height - handleHeight) / 2; + } + + // The transform needs coordinates that are relative to the parent + var parentRelativeY = newY - controlbarBounds.top; + handle.style.transform = "translateY(" + parentRelativeY + "px)"; + }, + + updateControlbarHandle: function () { + // Since the control bar is fixed on the viewport and not the page, + // the move function expects coordinates relative the the viewport. + var handle = document.getElementById("noVNC_control_bar_handle"); + var handleBounds = handle.getBoundingClientRect(); + UI.moveControlbarHandle(handleBounds.top); + }, + + controlbarHandleMouseUp: function(e) { + if ((e.type == "mouseup") && (e.button != 0)) return; + + // mouseup and mousedown on the same place toggles the controlbar + if (UI.controlbarGrabbed && !UI.controlbarDrag) { + UI.toggleControlbar(); + e.preventDefault(); + e.stopPropagation(); + UI.keepControlbar(); + UI.activateControlbar(); + } + UI.controlbarGrabbed = false; + }, + + controlbarHandleMouseDown: function(e) { + if ((e.type == "mousedown") && (e.button != 0)) return; + + var ptr = Util.getPointerEvent(e); + + var handle = document.getElementById("noVNC_control_bar_handle"); + var bounds = handle.getBoundingClientRect(); + + Util.setCapture(handle); + UI.controlbarGrabbed = true; + UI.controlbarDrag = false; + + UI.controlbarMouseDownClientY = ptr.clientY; + UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top; + e.preventDefault(); + e.stopPropagation(); + UI.keepControlbar(); + UI.activateControlbar(); + }, + + toggleExpander: function(e) { + if (this.classList.contains("noVNC_open")) { + this.classList.remove("noVNC_open"); + } else { + this.classList.add("noVNC_open"); + } + }, + +/* ------^------- + * /VISUAL + * ============== + * SETTINGS + * ------v------*/ + + // Initial page load read/initialization of settings + initSetting: function(name, defVal) { + // Check Query string followed by cookie + var val = WebUtil.getConfigVar(name); + if (val === null) { + val = WebUtil.readSetting(name, defVal); + } + UI.updateSetting(name, val); + return val; + }, + + // Update cookie and form control setting. If value is not set, then + // updates from control to current cookie setting. + updateSetting: function(name, value) { + + // Save the cookie for this session + if (typeof value !== 'undefined') { + WebUtil.writeSetting(name, value); + } + + // Update the settings control + value = UI.getSetting(name); + + var ctrl = document.getElementById('noVNC_setting_' + name); + if (ctrl.type === 'checkbox') { + ctrl.checked = value; + + } else if (typeof ctrl.options !== 'undefined') { + for (var i = 0; i < ctrl.options.length; i += 1) { + if (ctrl.options[i].value === value) { + ctrl.selectedIndex = i; + break; + } + } + } else { + /*Weird IE9 error leads to 'null' appearring + in textboxes instead of ''.*/ + if (value === null) { + value = ""; + } + ctrl.value = value; + } + }, + + // Save control setting to cookie + saveSetting: function(name) { + var val, ctrl = document.getElementById('noVNC_setting_' + name); + if (ctrl.type === 'checkbox') { + val = ctrl.checked; + } else if (typeof ctrl.options !== 'undefined') { + val = ctrl.options[ctrl.selectedIndex].value; + } else { + val = ctrl.value; + } + WebUtil.writeSetting(name, val); + //Util.Debug("Setting saved '" + name + "=" + val + "'"); + return val; + }, + + // Read form control compatible setting from cookie + getSetting: function(name) { + var ctrl = document.getElementById('noVNC_setting_' + name); + var val = WebUtil.readSetting(name); + if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') { + if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) { + val = false; + } else { + val = true; + } + } + return val; + }, + + // These helpers compensate for the lack of parent-selectors and + // previous-sibling-selectors in CSS which are needed when we want to + // disable the labels that belong to disabled input elements. + disableSetting: function(name) { + var ctrl = document.getElementById('noVNC_setting_' + name); + ctrl.disabled = true; + ctrl.label.classList.add('noVNC_disabled'); + }, + + enableSetting: function(name) { + var ctrl = document.getElementById('noVNC_setting_' + name); + ctrl.disabled = false; + ctrl.label.classList.remove('noVNC_disabled'); + }, + +/* ------^------- + * /SETTINGS + * ============== + * PANELS + * ------v------*/ + + closeAllPanels: function() { + UI.closeSettingsPanel(); + UI.closeXvpPanel(); + UI.closeClipboardPanel(); + UI.closeExtraKeys(); + }, + +/* ------^------- + * /PANELS + * ============== + * SETTINGS (panel) + * ------v------*/ + + openSettingsPanel: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + // Refresh UI elements from saved cookies + UI.updateSetting('encrypt'); + UI.updateSetting('true_color'); + if (Util.browserSupportsCursorURIs()) { + UI.updateSetting('cursor'); + } else { + UI.updateSetting('cursor', !Util.isTouchDevice); + UI.disableSetting('cursor'); + } + UI.updateSetting('clip'); + UI.updateSetting('resize'); + UI.updateSetting('shared'); + UI.updateSetting('view_only'); + UI.updateSetting('path'); + UI.updateSetting('repeaterID'); + UI.updateSetting('logging'); + UI.updateSetting('reconnect'); + UI.updateSetting('reconnect_delay'); + + document.getElementById('noVNC_settings') + .classList.add("noVNC_open"); + document.getElementById('noVNC_settings_button') + .classList.add("noVNC_selected"); + }, + + closeSettingsPanel: function() { + document.getElementById('noVNC_settings') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_settings_button') + .classList.remove("noVNC_selected"); + }, + + toggleSettingsPanel: function() { + if (document.getElementById('noVNC_settings') + .classList.contains("noVNC_open")) { + UI.closeSettingsPanel(); + } else { + UI.openSettingsPanel(); + } + }, + +/* ------^------- + * /SETTINGS + * ============== + * XVP + * ------v------*/ + + openXvpPanel: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + document.getElementById('noVNC_xvp') + .classList.add("noVNC_open"); + document.getElementById('noVNC_xvp_button') + .classList.add("noVNC_selected"); + }, + + closeXvpPanel: function() { + document.getElementById('noVNC_xvp') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_xvp_button') + .classList.remove("noVNC_selected"); + }, + + toggleXvpPanel: function() { + if (document.getElementById('noVNC_xvp') + .classList.contains("noVNC_open")) { + UI.closeXvpPanel(); + } else { + UI.openXvpPanel(); + } + }, + + // Disable/enable XVP button + updateXvpButton: function(ver) { + if (ver >= 1 && !UI.rfb.get_view_only()) { + document.getElementById('noVNC_xvp_button') + .classList.remove("noVNC_hidden"); + } else { + document.getElementById('noVNC_xvp_button') + .classList.add("noVNC_hidden"); + // Close XVP panel if open + UI.closeXvpPanel(); + } + }, + +/* ------^------- + * /XVP + * ============== + * CLIPBOARD + * ------v------*/ + + openClipboardPanel: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + document.getElementById('noVNC_clipboard') + .classList.add("noVNC_open"); + document.getElementById('noVNC_clipboard_button') + .classList.add("noVNC_selected"); + }, + + closeClipboardPanel: function() { + document.getElementById('noVNC_clipboard') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_clipboard_button') + .classList.remove("noVNC_selected"); + }, + + toggleClipboardPanel: function() { + if (document.getElementById('noVNC_clipboard') + .classList.contains("noVNC_open")) { + UI.closeClipboardPanel(); + } else { + UI.openClipboardPanel(); + } + }, + + clipboardReceive: function(rfb, text) { + Util.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "..."); + document.getElementById('noVNC_clipboard_text').value = text; + Util.Debug("<< UI.clipboardReceive"); + }, + + clipboardClear: function() { + document.getElementById('noVNC_clipboard_text').value = ""; + UI.rfb.clipboardPasteFrom(""); + }, + + clipboardSend: function() { + var text = document.getElementById('noVNC_clipboard_text').value; + Util.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "..."); + UI.rfb.clipboardPasteFrom(text); + Util.Debug("<< UI.clipboardSend"); + }, + +/* ------^------- + * /CLIPBOARD + * ============== + * CONNECTION + * ------v------*/ + + openConnectPanel: function() { + document.getElementById('noVNC_connect_dlg') + .classList.add("noVNC_open"); + }, + + closeConnectPanel: function() { + document.getElementById('noVNC_connect_dlg') + .classList.remove("noVNC_open"); + }, + + connect: function(event, password) { + var host = UI.getSetting('host'); + var port = UI.getSetting('port'); + var path = UI.getSetting('path'); + + if (typeof password === 'undefined') { + password = WebUtil.getConfigVar('password'); + } + + if (password === null) { + password = undefined; + } + + if ((!host) || (!port)) { + var msg = _("Must set host and port"); + Util.Error(msg); + UI.showStatus(msg, 'error'); + return; + } + + if (!UI.initRFB()) return; + + UI.closeAllPanels(); + UI.closeConnectPanel(); + + UI.rfb.set_encrypt(UI.getSetting('encrypt')); + UI.rfb.set_true_color(UI.getSetting('true_color')); + UI.rfb.set_shared(UI.getSetting('shared')); + UI.rfb.set_repeaterID(UI.getSetting('repeaterID')); + + UI.updateLocalCursor(); + UI.updateViewOnly(); + + UI.rfb.connect(host, port, password, path); + }, + + disconnect: function() { + UI.closeAllPanels(); + UI.rfb.disconnect(); + + // Disable automatic reconnecting + UI.inhibit_reconnect = true; + + // Restore the callback used for initial resize + UI.rfb.set_onFBUComplete(UI.initialResize); + + // Don't display the connection settings until we're actually disconnected + }, + + reconnect: function() { + UI.reconnect_callback = null; + + // if reconnect has been disabled in the meantime, do nothing. + if (UI.inhibit_reconnect) { + return; + } + + UI.connect(null, UI.reconnect_password); + }, + + disconnectFinished: function (rfb, reason) { + if (typeof reason !== 'undefined') { + UI.showStatus(reason, 'error'); + } else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) { + document.getElementById("noVNC_transition_text").textContent = _("Reconnecting..."); + document.documentElement.classList.add("noVNC_reconnecting"); + + var delay = parseInt(UI.getSetting('reconnect_delay')); + UI.reconnect_callback = setTimeout(UI.reconnect, delay); + return; + } + + UI.openControlbar(); + UI.openConnectPanel(); + }, + + cancelReconnect: function() { + if (UI.reconnect_callback !== null) { + clearTimeout(UI.reconnect_callback); + UI.reconnect_callback = null; + } + + document.documentElement.classList.remove("noVNC_reconnecting"); + UI.openControlbar(); + UI.openConnectPanel(); + }, + +/* ------^------- + * /CONNECTION + * ============== + * PASSWORD + * ------v------*/ + + passwordRequired: function(rfb, msg) { + + document.getElementById('noVNC_password_dlg') + .classList.add('noVNC_open'); + + setTimeout(function () { + document.getElementById('noVNC_password_input').focus(); + }, 100); + + if (typeof msg === 'undefined') { + msg = _("Password is required"); + } + Util.Warn(msg); + UI.showStatus(msg, "warning"); + }, + + setPassword: function(e) { + var password = document.getElementById('noVNC_password_input').value; + UI.rfb.sendPassword(password); + UI.reconnect_password = password; + document.getElementById('noVNC_password_dlg') + .classList.remove('noVNC_open'); + // Prevent actually submitting the form + e.preventDefault(); + }, + +/* ------^------- + * /PASSWORD + * ============== + * FULLSCREEN + * ------v------*/ + + toggleFullscreen: function() { + if (document.fullscreenElement || // alternative standard method + document.mozFullScreenElement || // currently working methods + document.webkitFullscreenElement || + document.msFullscreenElement) { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } + } else { + if (document.documentElement.requestFullscreen) { + document.documentElement.requestFullscreen(); + } else if (document.documentElement.mozRequestFullScreen) { + document.documentElement.mozRequestFullScreen(); + } else if (document.documentElement.webkitRequestFullscreen) { + document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } else if (document.body.msRequestFullscreen) { + document.body.msRequestFullscreen(); + } + } + UI.enableDisableViewClip(); + UI.updateFullscreenButton(); + }, + + updateFullscreenButton: function() { + if (document.fullscreenElement || // alternative standard method + document.mozFullScreenElement || // currently working methods + document.webkitFullscreenElement || + document.msFullscreenElement ) { + document.getElementById('noVNC_fullscreen_button') + .classList.add("noVNC_selected"); + } else { + document.getElementById('noVNC_fullscreen_button') + .classList.remove("noVNC_selected"); + } + }, + +/* ------^------- + * /FULLSCREEN + * ============== + * RESIZE + * ------v------*/ + + // Apply remote resizing or local scaling + applyResizeMode: function() { + if (!UI.rfb) return; + + var screen = UI.screenSize(); + + if (screen && UI.connected && UI.rfb.get_display()) { + + var display = UI.rfb.get_display(); + var resizeMode = UI.getSetting('resize'); + display.set_scale(1); + + // Make sure the viewport is adjusted first + UI.updateViewClip(); + + if (resizeMode === 'remote') { + + // Request changing the resolution of the remote display to + // the size of the local browser viewport. + + // In order to not send multiple requests before the browser-resize + // is finished we wait 0.5 seconds before sending the request. + clearTimeout(UI.resizeTimeout); + UI.resizeTimeout = setTimeout(function(){ + // Request a remote size covering the viewport + if (UI.rfb.requestDesktopSize(screen.w, screen.h)) { + Util.Debug('Requested new desktop size: ' + + screen.w + 'x' + screen.h); + } + }, 500); + + } else if (resizeMode === 'scale' || resizeMode === 'downscale') { + var downscaleOnly = resizeMode === 'downscale'; + display.autoscale(screen.w, screen.h, downscaleOnly); + UI.fixScrollbars(); + } + } + }, + + // Gets the the size of the available viewport in the browser window + screenSize: function() { + var screen = document.getElementById('noVNC_screen'); + return {w: screen.offsetWidth, h: screen.offsetHeight}; + }, + + // Normally we only apply the current resize mode after a window resize + // event. This means that when a new connection is opened, there is no + // resize mode active. + // We have to wait until the first FBU because this is where the client + // will find the supported encodings of the server. Some calls later in + // the chain is dependant on knowing the server-capabilities. + initialResize: function(rfb, fbu) { + UI.applyResizeMode(); + // After doing this once, we remove the callback. + UI.rfb.set_onFBUComplete(function() { }); + }, + +/* ------^------- + * /RESIZE + * ============== + * CLIPPING + * ------v------*/ + + // Set and configure viewport clipping + setViewClip: function(clip) { + UI.updateSetting('clip', clip); + UI.updateViewClip(); + }, + + // Update parameters that depend on the clip setting + updateViewClip: function() { + if (!UI.rfb) return; + + var display = UI.rfb.get_display(); + var cur_clip = display.get_viewport(); + var new_clip = UI.getSetting('clip'); + + var resizeSetting = UI.getSetting('resize'); + if (resizeSetting === 'downscale' || resizeSetting === 'scale') { + // Disable clipping if we are scaling + new_clip = false; + } else if (Util.isTouchDevice) { + // Touch devices usually have shit scrollbars + new_clip = true; + } + + if (cur_clip !== new_clip) { + display.set_viewport(new_clip); + } + + var size = UI.screenSize(); + + if (new_clip && size) { + // When clipping is enabled, the screen is limited to + // the size of the browser window. + display.viewportChangeSize(size.w, size.h); + UI.fixScrollbars(); + } + + // Changing the viewport may change the state of + // the dragging button + UI.updateViewDrag(); + }, + + // Handle special cases where clipping is forced on/off or locked + enableDisableViewClip: function() { + var resizeSetting = UI.getSetting('resize'); + // Disable clipping if we are scaling, connected or on touch + if (resizeSetting === 'downscale' || resizeSetting === 'scale' || + Util.isTouchDevice) { + UI.disableSetting('clip'); + } else { + UI.enableSetting('clip'); + } + }, + +/* ------^------- + * /CLIPPING + * ============== + * VIEWDRAG + * ------v------*/ + + toggleViewDrag: function() { + if (!UI.rfb) return; + + var drag = UI.rfb.get_viewportDrag(); + UI.setViewDrag(!drag); + }, + + // Set the view drag mode which moves the viewport on mouse drags + setViewDrag: function(drag) { + if (!UI.rfb) return; + + UI.rfb.set_viewportDrag(drag); + + UI.updateViewDrag(); + }, + + updateViewDrag: function() { + var clipping = false; + + if (!UI.connected) return; + + // Check if viewport drag is possible. It is only possible + // if the remote display is clipping the client display. + if (UI.rfb.get_display().get_viewport() && + UI.rfb.get_display().clippingDisplay()) { + clipping = true; + } + + var viewDragButton = document.getElementById('noVNC_view_drag_button'); + + if (!clipping && + UI.rfb.get_viewportDrag()) { + // The size of the remote display is the same or smaller + // than the client display. Make sure viewport drag isn't + // active when it can't be used. + UI.rfb.set_viewportDrag(false); + } + + if (UI.rfb.get_viewportDrag()) { + viewDragButton.classList.add("noVNC_selected"); + } else { + viewDragButton.classList.remove("noVNC_selected"); + } + + // Different behaviour for touch vs non-touch + // The button is disabled instead of hidden on touch devices + if (Util.isTouchDevice) { + viewDragButton.classList.remove("noVNC_hidden"); + + if (clipping) { + viewDragButton.disabled = false; + } else { + viewDragButton.disabled = true; + } + } else { + viewDragButton.disabled = false; + + if (clipping) { + viewDragButton.classList.remove("noVNC_hidden"); + } else { + viewDragButton.classList.add("noVNC_hidden"); + } + } + }, + +/* ------^------- + * /VIEWDRAG + * ============== + * KEYBOARD + * ------v------*/ + + showVirtualKeyboard: function() { + if (!Util.isTouchDevice) return; + + var input = document.getElementById('noVNC_keyboardinput'); + + if (document.activeElement == input) return; + + input.focus(); + + try { + var l = input.value.length; + // Move the caret to the end + input.setSelectionRange(l, l); + } catch (err) {} // setSelectionRange is undefined in Google Chrome + }, + + hideVirtualKeyboard: function() { + if (!Util.isTouchDevice) return; + + var input = document.getElementById('noVNC_keyboardinput'); + + if (document.activeElement != input) return; + + input.blur(); + }, + + toggleVirtualKeyboard: function () { + if (document.getElementById('noVNC_keyboard_button') + .classList.contains("noVNC_selected")) { + UI.hideVirtualKeyboard(); + } else { + UI.showVirtualKeyboard(); + } + }, + + onfocusVirtualKeyboard: function(event) { + document.getElementById('noVNC_keyboard_button') + .classList.add("noVNC_selected"); + }, + + onblurVirtualKeyboard: function(event) { + document.getElementById('noVNC_keyboard_button') + .classList.remove("noVNC_selected"); + }, + + keepVirtualKeyboard: function(event) { + var input = document.getElementById('noVNC_keyboardinput'); + + // Only prevent focus change if the virtual keyboard is active + if (document.activeElement != input) { + return; + } + + // Only allow focus to move to other elements that need + // focus to function properly + if (event.target.form !== undefined) { + switch (event.target.type) { + case 'text': + case 'email': + case 'search': + case 'password': + case 'tel': + case 'url': + case 'textarea': + case 'select-one': + case 'select-multiple': + return; + } + } + + event.preventDefault(); + }, + + keyboardinputReset: function() { + var kbi = document.getElementById('noVNC_keyboardinput'); + kbi.value = new Array(UI.defaultKeyboardinputLen).join("_"); + UI.lastKeyboardinput = kbi.value; + }, + + // When normal keyboard events are left uncought, use the input events from + // the keyboardinput element instead and generate the corresponding key events. + // This code is required since some browsers on Android are inconsistent in + // sending keyCodes in the normal keyboard events when using on screen keyboards. + keyInput: function(event) { + + if (!UI.rfb) return; + + var newValue = event.target.value; + + if (!UI.lastKeyboardinput) { + UI.keyboardinputReset(); + } + var oldValue = UI.lastKeyboardinput; + + var newLen; + try { + // Try to check caret position since whitespace at the end + // will not be considered by value.length in some browsers + newLen = Math.max(event.target.selectionStart, newValue.length); + } catch (err) { + // selectionStart is undefined in Google Chrome + newLen = newValue.length; + } + var oldLen = oldValue.length; + + var backspaces; + var inputs = newLen - oldLen; + if (inputs < 0) { + backspaces = -inputs; + } else { + backspaces = 0; + } + + // Compare the old string with the new to account for + // text-corrections or other input that modify existing text + var i; + for (i = 0; i < Math.min(oldLen, newLen); i++) { + if (newValue.charAt(i) != oldValue.charAt(i)) { + inputs = newLen - i; + backspaces = oldLen - i; + break; + } + } + + // Send the key events + for (i = 0; i < backspaces; i++) { + UI.rfb.sendKey(KeyTable.XK_BackSpace); + } + for (i = newLen - inputs; i < newLen; i++) { + UI.rfb.sendKey(keysyms.fromUnicode(newValue.charCodeAt(i)).keysym); + } + + // Control the text content length in the keyboardinput element + if (newLen > 2 * UI.defaultKeyboardinputLen) { + UI.keyboardinputReset(); + } else if (newLen < 1) { + // There always have to be some text in the keyboardinput + // element with which backspace can interact. + UI.keyboardinputReset(); + // This sometimes causes the keyboard to disappear for a second + // but it is required for the android keyboard to recognize that + // text has been added to the field + event.target.blur(); + // This has to be ran outside of the input handler in order to work + setTimeout(event.target.focus.bind(event.target), 0); + } else { + UI.lastKeyboardinput = newValue; + } + }, + +/* ------^------- + * /KEYBOARD + * ============== + * EXTRA KEYS + * ------v------*/ + + openExtraKeys: function() { + UI.closeAllPanels(); + UI.openControlbar(); + + document.getElementById('noVNC_modifiers') + .classList.add("noVNC_open"); + document.getElementById('noVNC_toggle_extra_keys_button') + .classList.add("noVNC_selected"); + }, + + closeExtraKeys: function() { + document.getElementById('noVNC_modifiers') + .classList.remove("noVNC_open"); + document.getElementById('noVNC_toggle_extra_keys_button') + .classList.remove("noVNC_selected"); + }, + + toggleExtraKeys: function() { + if(document.getElementById('noVNC_modifiers') + .classList.contains("noVNC_open")) { + UI.closeExtraKeys(); + } else { + UI.openExtraKeys(); + } + }, + + sendEsc: function() { + UI.rfb.sendKey(KeyTable.XK_Escape); + }, + + sendTab: function() { + UI.rfb.sendKey(KeyTable.XK_Tab); + }, + + toggleCtrl: function() { + var btn = document.getElementById('noVNC_toggle_ctrl_button'); + if (btn.classList.contains("noVNC_selected")) { + UI.rfb.sendKey(KeyTable.XK_Control_L, false); + btn.classList.remove("noVNC_selected"); + } else { + UI.rfb.sendKey(KeyTable.XK_Control_L, true); + btn.classList.add("noVNC_selected"); + } + }, + + toggleAlt: function() { + var btn = document.getElementById('noVNC_toggle_alt_button'); + if (btn.classList.contains("noVNC_selected")) { + UI.rfb.sendKey(KeyTable.XK_Alt_L, false); + btn.classList.remove("noVNC_selected"); + } else { + UI.rfb.sendKey(KeyTable.XK_Alt_L, true); + btn.classList.add("noVNC_selected"); + } + }, + + sendCtrlAltDel: function() { + UI.rfb.sendCtrlAltDel(); + }, + +/* ------^------- + * /EXTRA KEYS + * ============== + * MISC + * ------v------*/ + + setMouseButton: function(num) { + var view_only = UI.rfb.get_view_only(); + if (UI.rfb && !view_only) { + UI.rfb.get_mouse().set_touchButton(num); + } + + var blist = [0, 1,2,4]; + for (var b = 0; b < blist.length; b++) { + var button = document.getElementById('noVNC_mouse_button' + + blist[b]); + if (blist[b] === num && !view_only) { + button.classList.remove("noVNC_hidden"); + } else { + button.classList.add("noVNC_hidden"); + } + } + }, + + displayBlur: function() { + if (UI.rfb && !UI.rfb.get_view_only()) { + UI.rfb.get_keyboard().set_focused(false); + UI.rfb.get_mouse().set_focused(false); + } + }, + + displayFocus: function() { + if (UI.rfb && !UI.rfb.get_view_only()) { + UI.rfb.get_keyboard().set_focused(true); + UI.rfb.get_mouse().set_focused(true); + } + }, + + updateLocalCursor: function() { + UI.rfb.set_local_cursor(UI.getSetting('cursor')); + }, + + updateViewOnly: function() { + UI.rfb.set_view_only(UI.getSetting('view_only')); + }, + + updateLogging: function() { + WebUtil.init_logging(UI.getSetting('logging')); + }, + + updateSessionSize: function(rfb, width, height) { + UI.updateViewClip(); + UI.fixScrollbars(); + }, + + fixScrollbars: function() { + // This is a hack because Chrome screws up the calculation + // for when scrollbars are needed. So to fix it we temporarily + // toggle them off and on. + var screen = document.getElementById('noVNC_screen'); + screen.style.overflow = 'hidden'; + // Force Chrome to recalculate the layout by asking for + // an element's dimensions + screen.getBoundingClientRect(); + screen.style.overflow = null; + }, + + updateDesktopName: function(rfb, name) { + UI.desktopName = name; + // Display the desktop name in the document title + document.title = name + " - noVNC"; + }, + + bell: function(rfb) { + if (WebUtil.getConfigVar('bell', 'on') === 'on') { + document.getElementById('noVNC_bell').play(); + } + }, + + //Helper to add options to dropdown. + addOption: function(selectbox, text, value) { + var optn = document.createElement("OPTION"); + optn.text = text; + optn.value = value; + selectbox.options.add(optn); + }, + +/* ------^------- + * /MISC + * ============== + */ + }; + + /* [module] UI.load(); */ +})(); + +/* [module] export default UI; */ diff --git a/noVNC/include/webutil.js b/noVNC/app/webutil.js similarity index 53% rename from noVNC/include/webutil.js rename to noVNC/app/webutil.js index e674bf9..e6e6afb 100644 --- a/noVNC/include/webutil.js +++ b/noVNC/app/webutil.js @@ -10,25 +10,12 @@ /*jslint bitwise: false, white: false, browser: true, devel: true */ /*global Util, window, document */ -// Globals defined here -var WebUtil = {}, $D; - -/* - * Simple DOM selector by ID +/* [module] + * import Util from "../core/util"; */ -if (!window.$D) { - window.$D = function (id) { - if (document.getElementById) { - return document.getElementById(id); - } else if (document.all) { - return document.all[id]; - } else if (document.layers) { - return document.layers[id]; - } - return undefined; - }; -} +// Globals defined here +var WebUtil = {}; /* * ------------------------------------------------------ @@ -90,6 +77,29 @@ WebUtil.getQueryVar = function (name, 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 @@ -199,41 +209,103 @@ WebUtil.eraseSetting = function (name) { } }; -/* - * Alternate stylesheet selection - */ -WebUtil.getStylesheets = function () { - "use strict"; - var links = document.getElementsByTagName("link"); - var sheets = []; +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; - for (var i = 0; i < links.length; i += 1) { - if (links[i].title && - links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) { - sheets.push(links[i]); - } + 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; } - return sheets; }; -// No sheet means try and use value from cookie, null sheet used to -// clear all alternates. -WebUtil.selectStylesheet = function (sheet) { +// 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). +WebUtil.get_include_uri = function (root_dir) { + return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI + root_dir + '/' : root_dir + '/'; +}; +WebUtil._loading_scripts = []; +WebUtil._pending_scripts = []; +WebUtil.load_scripts = function (files_by_dir) { "use strict"; - if (typeof sheet === 'undefined') { - sheet = 'default'; - } + var head = document.getElementsByTagName('head')[0], script, + ls = WebUtil._loading_scripts, ps = WebUtil._pending_scripts; - 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 { - //Util.Debug("Skipping stylesheet " + link.title); - link.disabled = true; + 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(); + } + } + } + }; + + var root_dirs = Object.keys(files_by_dir); + + for (var d = 0; d < root_dirs.length; d++) { + var root_dir = root_dirs[d]; + var files = files_by_dir[root_dir]; + + for (var f = 0; f < files.length; f++) { + script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = WebUtil.get_include_uri(root_dir) + 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); } } - return sheet; }; + +/* [module] export default WebUtil; */ diff --git a/noVNC/include/base64.js b/noVNC/core/base64.js similarity index 99% rename from noVNC/include/base64.js rename to noVNC/core/base64.js index 651fbad..2b4f948 100644 --- a/noVNC/include/base64.js +++ b/noVNC/core/base64.js @@ -85,7 +85,7 @@ var Base64 = { console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i); continue; } - + // Collect data into leftdata, update bitcount leftdata = (leftdata << 6) | c; leftbits += 6; @@ -111,3 +111,5 @@ var Base64 = { return result; } }; /* End of Base64 namespace */ + +/* [module] export default Base64; */ diff --git a/noVNC/include/des.js b/noVNC/core/des.js similarity index 99% rename from noVNC/include/des.js rename to noVNC/core/des.js index ecbc819..c9a4753 100644 --- a/noVNC/include/des.js +++ b/noVNC/core/des.js @@ -25,16 +25,16 @@ * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and - * without fee is hereby granted, provided that this copyright notice is kept - * intact. - * + * without fee is hereby granted, provided that this copyright notice is kept + * intact. + * * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. - * + * * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT @@ -77,7 +77,7 @@ /* jslint white: false */ -function DES(passwd) { +/* [module] export default */ function DES(passwd) { "use strict"; // Tables, permutations, S-boxes, etc. @@ -273,4 +273,4 @@ function DES(passwd) { setKeys(passwd); // Setup keys return {'encrypt': encrypt}; // Public interface -} // function DES +}; // function DES diff --git a/noVNC/core/display.js b/noVNC/core/display.js new file mode 100644 index 0000000..f7c437b --- /dev/null +++ b/noVNC/core/display.js @@ -0,0 +1,872 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2015 Samuel Mannehed for Cendio AB + * Licensed under MPL 2.0 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint browser: true, white: false */ +/*global Util, Base64, changeCursor */ + +/* [module] + * import Util from "./util"; + * import Base64 from "./base64"; + */ + +/* [module] export default */ function Display(defaults) { + this._drawCtx = null; + this._c_forceCanvas = false; + + this._renderQ = []; // queue drawing actions for in-oder rendering + this._flushing = false; + + // the full frame buffer (logical canvas) size + this._fb_width = 0; + this._fb_height = 0; + + this._prevDrawStyle = ""; + this._tile = null; + this._tile16x16 = null; + this._tile_x = 0; + this._tile_y = 0; + + Util.set_defaults(this, defaults, { + 'true_color': true, + 'colourMap': [], + 'scale': 1.0, + 'viewport': false, + 'render_mode': '', + "onFlush": function () {}, + }); + + Util.Debug(">> Display.constructor"); + + // The visible canvas + if (!this._target) { + throw new Error("Target must be set"); + } + + if (typeof this._target === 'string') { + throw new Error('target must be a DOM element'); + } + + if (!this._target.getContext) { + throw new Error("no getContext method"); + } + + this._targetCtx = this._target.getContext('2d'); + + // the visible canvas viewport (i.e. what actually gets seen) + this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height }; + + // The hidden canvas, where we do the actual rendering + this._backbuffer = document.createElement('canvas'); + this._drawCtx = this._backbuffer.getContext('2d'); + + this._damageBounds = { left:0, top:0, + right: this._backbuffer.width, + bottom: this._backbuffer.height }; + + Util.Debug("User Agent: " + navigator.userAgent); + if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); } + if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); } + if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); } + if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); } + + this.clear(); + + // Check canvas features + if ('createImageData' in this._drawCtx) { + this._render_mode = 'canvas rendering'; + } else { + throw new Error("Canvas does not support createImageData"); + } + + if (this._prefer_js === null) { + Util.Info("Prefering javascript operations"); + this._prefer_js = true; + } + + // Determine browser support for setting the cursor via data URI scheme + if (this._cursor_uri || this._cursor_uri === null || + this._cursor_uri === undefined) { + this._cursor_uri = Util.browserSupportsCursorURIs(); + } + + Util.Debug("<< Display.constructor"); +}; + +(function () { + "use strict"; + + var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false; + try { + new ImageData(new Uint8ClampedArray(4), 1, 1); + SUPPORTS_IMAGEDATA_CONSTRUCTOR = true; + } catch (ex) { + // ignore failure + } + + + Display.prototype = { + // Public methods + viewportChangePos: function (deltaX, deltaY) { + var vp = this._viewportLoc; + deltaX = Math.floor(deltaX); + deltaY = Math.floor(deltaY); + + if (!this._viewport) { + deltaX = -vp.w; // clamped later of out of bounds + deltaY = -vp.h; + } + + var vx2 = vp.x + vp.w - 1; + var vy2 = vp.y + vp.h - 1; + + // Position change + + if (deltaX < 0 && vp.x + deltaX < 0) { + deltaX = -vp.x; + } + if (vx2 + deltaX >= this._fb_width) { + deltaX -= vx2 + deltaX - this._fb_width + 1; + } + + if (vp.y + deltaY < 0) { + deltaY = -vp.y; + } + if (vy2 + deltaY >= this._fb_height) { + deltaY -= (vy2 + deltaY - this._fb_height + 1); + } + + if (deltaX === 0 && deltaY === 0) { + return; + } + Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); + + vp.x += deltaX; + vp.y += deltaY; + + this._damage(vp.x, vp.y, vp.w, vp.h); + + this.flip(); + }, + + viewportChangeSize: function(width, height) { + + if (!this._viewport || + typeof(width) === "undefined" || + typeof(height) === "undefined") { + + Util.Debug("Setting viewport to full display region"); + width = this._fb_width; + height = this._fb_height; + } + + if (width > this._fb_width) { + width = this._fb_width; + } + if (height > this._fb_height) { + height = this._fb_height; + } + + var vp = this._viewportLoc; + if (vp.w !== width || vp.h !== height) { + vp.w = width; + vp.h = height; + + var canvas = this._target; + canvas.width = width; + canvas.height = height; + + // The position might need to be updated if we've grown + this.viewportChangePos(0, 0); + + this._damage(vp.x, vp.y, vp.w, vp.h); + this.flip(); + + // Update the visible size of the target canvas + this._rescale(this._scale); + } + }, + + absX: function (x) { + return x / this._scale + this._viewportLoc.x; + }, + + absY: function (y) { + return y / this._scale + this._viewportLoc.y; + }, + + resize: function (width, height) { + this._prevDrawStyle = ""; + + this._fb_width = width; + this._fb_height = height; + + var canvas = this._backbuffer; + if (canvas.width !== width || canvas.height !== height) { + + // We have to save the canvas data since changing the size will clear it + var saveImg = null; + if (canvas.width > 0 && canvas.height > 0) { + saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height); + } + + if (canvas.width !== width) { + canvas.width = width; + } + if (canvas.height !== height) { + canvas.height = height; + } + + if (saveImg) { + this._drawCtx.putImageData(saveImg, 0, 0); + } + } + + // Readjust the viewport as it may be incorrectly sized + // and positioned + var vp = this._viewportLoc; + this.viewportChangeSize(vp.w, vp.h); + this.viewportChangePos(0, 0); + }, + + // Track what parts of the visible canvas that need updating + _damage: function(x, y, w, h) { + if (x < this._damageBounds.left) { + this._damageBounds.left = x; + } + if (y < this._damageBounds.top) { + this._damageBounds.top = y; + } + if ((x + w) > this._damageBounds.right) { + this._damageBounds.right = x + w; + } + if ((y + h) > this._damageBounds.bottom) { + this._damageBounds.bottom = y + h; + } + }, + + // Update the visible canvas with the contents of the + // rendering canvas + flip: function(from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + this._renderQ_push({ + 'type': 'flip' + }); + } else { + var x, y, vx, vy, w, h; + + x = this._damageBounds.left; + y = this._damageBounds.top; + w = this._damageBounds.right - x; + h = this._damageBounds.bottom - y; + + vx = x - this._viewportLoc.x; + vy = y - this._viewportLoc.y; + + if (vx < 0) { + w += vx; + x -= vx; + vx = 0; + } + if (vy < 0) { + h += vy; + y -= vy; + vy = 0; + } + + if ((vx + w) > this._viewportLoc.w) { + w = this._viewportLoc.w - vx; + } + if ((vy + h) > this._viewportLoc.h) { + h = this._viewportLoc.h - vy; + } + + if ((w > 0) && (h > 0)) { + // FIXME: We may need to disable image smoothing here + // as well (see copyImage()), but we haven't + // noticed any problem yet. + this._targetCtx.drawImage(this._backbuffer, + x, y, w, h, + vx, vy, w, h); + } + + this._damageBounds.left = this._damageBounds.top = 65535; + this._damageBounds.right = this._damageBounds.bottom = 0; + } + }, + + clear: function () { + if (this._logo) { + this.resize(this._logo.width, this._logo.height); + this.imageRect(0, 0, this._logo.type, this._logo.data); + } else { + this.resize(240, 20); + this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height); + } + this.flip(); + }, + + pending: function() { + return this._renderQ.length > 0; + }, + + flush: function() { + if (this._renderQ.length === 0) { + this._onFlush(); + } else { + this._flushing = true; + } + }, + + fillRect: function (x, y, width, height, color, from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + this._renderQ_push({ + 'type': 'fill', + 'x': x, + 'y': y, + 'width': width, + 'height': height, + 'color': color + }); + } else { + this._setFillColor(color); + this._drawCtx.fillRect(x, y, width, height); + this._damage(x, y, width, height); + } + }, + + copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + this._renderQ_push({ + 'type': 'copy', + 'old_x': old_x, + 'old_y': old_y, + 'x': new_x, + 'y': new_y, + 'width': w, + 'height': h, + }); + } else { + // Due to this bug among others [1] we need to disable the image-smoothing to + // avoid getting a blur effect when copying data. + // + // 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719 + // + // We need to set these every time since all properties are reset + // when the the size is changed + this._drawCtx.mozImageSmoothingEnabled = false; + this._drawCtx.webkitImageSmoothingEnabled = false; + this._drawCtx.msImageSmoothingEnabled = false; + this._drawCtx.imageSmoothingEnabled = false; + + this._drawCtx.drawImage(this._backbuffer, + old_x, old_y, w, h, + new_x, new_y, w, h); + this._damage(new_x, new_y, w, h); + } + }, + + imageRect: function(x, y, mime, arr) { + var img = new Image(); + img.src = "data: " + mime + ";base64," + Base64.encode(arr); + this._renderQ_push({ + 'type': 'img', + 'img': img, + 'x': x, + 'y': y + }); + }, + + // start updating a tile + startTile: function (x, y, width, height, color) { + this._tile_x = x; + this._tile_y = y; + if (width === 16 && height === 16) { + this._tile = this._tile16x16; + } else { + this._tile = this._drawCtx.createImageData(width, height); + } + + if (this._prefer_js) { + var bgr; + if (this._true_color) { + bgr = color; + } else { + bgr = this._colourMap[color[0]]; + } + var red = bgr[2]; + var green = bgr[1]; + var blue = bgr[0]; + + var data = this._tile.data; + for (var i = 0; i < width * height * 4; i += 4) { + data[i] = red; + data[i + 1] = green; + data[i + 2] = blue; + data[i + 3] = 255; + } + } else { + this.fillRect(x, y, width, height, color, true); + } + }, + + // update sub-rectangle of the current tile + subTile: function (x, y, w, h, color) { + if (this._prefer_js) { + var bgr; + if (this._true_color) { + bgr = color; + } else { + bgr = this._colourMap[color[0]]; + } + var red = bgr[2]; + var green = bgr[1]; + var blue = bgr[0]; + var xend = x + w; + var yend = y + h; + + var data = this._tile.data; + var width = this._tile.width; + for (var j = y; j < yend; j++) { + for (var i = x; i < xend; i++) { + var p = (i + (j * width)) * 4; + data[p] = red; + data[p + 1] = green; + data[p + 2] = blue; + data[p + 3] = 255; + } + } + } else { + this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color, true); + } + }, + + // draw the current tile to the screen + finishTile: function () { + if (this._prefer_js) { + this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y); + this._damage(this._tile_x, this._tile_y, + this._tile.width, this._tile.height); + } + // else: No-op -- already done by setSubTile + }, + + blitImage: function (x, y, width, height, arr, offset, from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + // NB(directxman12): it's technically more performant here to use preallocated arrays, + // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, + // this probably isn't getting called *nearly* as much + var new_arr = new Uint8Array(width * height * 4); + new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); + this._renderQ_push({ + 'type': 'blit', + 'data': new_arr, + 'x': x, + 'y': y, + 'width': width, + 'height': height, + }); + } else if (this._true_color) { + this._bgrxImageData(x, y, width, height, arr, offset); + } else { + this._cmapImageData(x, y, width, height, arr, offset); + } + }, + + blitRgbImage: function (x, y , width, height, arr, offset, from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + // NB(directxman12): it's technically more performant here to use preallocated arrays, + // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, + // this probably isn't getting called *nearly* as much + var new_arr = new Uint8Array(width * height * 3); + new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); + this._renderQ_push({ + 'type': 'blitRgb', + 'data': new_arr, + 'x': x, + 'y': y, + 'width': width, + 'height': height, + }); + } else if (this._true_color) { + this._rgbImageData(x, y, width, height, arr, offset); + } else { + // probably wrong? + this._cmapImageData(x, y, width, height, arr, offset); + } + }, + + blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) { + if (this._renderQ.length !== 0 && !from_queue) { + // NB(directxman12): it's technically more performant here to use preallocated arrays, + // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, + // this probably isn't getting called *nearly* as much + var new_arr = new Uint8Array(width * height * 4); + new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); + this._renderQ_push({ + 'type': 'blitRgbx', + 'data': new_arr, + 'x': x, + 'y': y, + 'width': width, + 'height': height, + }); + } else { + this._rgbxImageData(x, y, width, height, arr, offset); + } + }, + + drawImage: function (img, x, y) { + this._drawCtx.drawImage(img, x, y); + this._damage(x, y, img.width, img.height); + }, + + changeCursor: function (pixels, mask, hotx, hoty, w, h) { + if (this._cursor_uri === false) { + Util.Warn("changeCursor called but no cursor data URI support"); + return; + } + + if (this._true_color) { + Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h); + } else { + Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h, this._colourMap); + } + }, + + defaultCursor: function () { + this._target.style.cursor = "default"; + }, + + disableLocalCursor: function () { + this._target.style.cursor = "none"; + }, + + clippingDisplay: function () { + var vp = this._viewportLoc; + return this._fb_width > vp.w || this._fb_height > vp.h; + }, + + // Overridden getters/setters + set_scale: function (scale) { + this._rescale(scale); + }, + + set_viewport: function (viewport) { + this._viewport = viewport; + // May need to readjust the viewport dimensions + var vp = this._viewportLoc; + this.viewportChangeSize(vp.w, vp.h); + this.viewportChangePos(0, 0); + }, + + get_width: function () { + return this._fb_width; + }, + get_height: function () { + return this._fb_height; + }, + + autoscale: function (containerWidth, containerHeight, downscaleOnly) { + var vp = this._viewportLoc; + var targetAspectRatio = containerWidth / containerHeight; + var fbAspectRatio = vp.w / vp.h; + + var scaleRatio; + if (fbAspectRatio >= targetAspectRatio) { + scaleRatio = containerWidth / vp.w; + } else { + scaleRatio = containerHeight / vp.h; + } + + if (scaleRatio > 1.0 && downscaleOnly) { + scaleRatio = 1.0; + } + + this._rescale(scaleRatio); + }, + + // Private Methods + _rescale: function (factor) { + this._scale = factor; + var vp = this._viewportLoc; + + // NB(directxman12): If you set the width directly, or set the + // style width to a number, the canvas is cleared. + // However, if you set the style width to a string + // ('NNNpx'), the canvas is scaled without clearing. + var width = Math.round(factor * vp.w) + 'px'; + var height = Math.round(factor * vp.h) + 'px'; + + if ((this._target.style.width !== width) || + (this._target.style.height !== height)) { + this._target.style.width = width; + this._target.style.height = height; + } + }, + + _setFillColor: function (color) { + var bgr; + if (this._true_color) { + bgr = color; + } else { + bgr = this._colourMap[color]; + } + + var newStyle = 'rgb(' + bgr[2] + ',' + bgr[1] + ',' + bgr[0] + ')'; + if (newStyle !== this._prevDrawStyle) { + this._drawCtx.fillStyle = newStyle; + this._prevDrawStyle = newStyle; + } + }, + + _rgbImageData: function (x, y, width, height, arr, offset) { + var img = this._drawCtx.createImageData(width, height); + var data = img.data; + for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) { + data[i] = arr[j]; + data[i + 1] = arr[j + 1]; + data[i + 2] = arr[j + 2]; + data[i + 3] = 255; // Alpha + } + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); + }, + + _bgrxImageData: function (x, y, width, height, arr, offset) { + var img = this._drawCtx.createImageData(width, height); + var data = img.data; + for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) { + data[i] = arr[j + 2]; + data[i + 1] = arr[j + 1]; + data[i + 2] = arr[j]; + data[i + 3] = 255; // Alpha + } + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); + }, + + _rgbxImageData: function (x, y, width, height, arr, offset) { + // NB(directxman12): arr must be an Type Array view + var img; + if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) { + img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height); + } else { + img = this._drawCtx.createImageData(width, height); + img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4)); + } + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); + }, + + _cmapImageData: function (x, y, width, height, arr, offset) { + var img = this._drawCtx.createImageData(width, height); + var data = img.data; + var cmap = this._colourMap; + for (var i = 0, j = offset; i < width * height * 4; i += 4, j++) { + var bgr = cmap[arr[j]]; + data[i] = bgr[2]; + data[i + 1] = bgr[1]; + data[i + 2] = bgr[0]; + data[i + 3] = 255; // Alpha + } + this._drawCtx.putImageData(img, x, y); + this._damage(x, y, img.width, img.height); + }, + + _renderQ_push: function (action) { + this._renderQ.push(action); + if (this._renderQ.length === 1) { + // If this can be rendered immediately it will be, otherwise + // the scanner will wait for the relevant event + this._scan_renderQ(); + } + }, + + _resume_renderQ: function() { + // "this" is the object that is ready, not the + // display object + this.removeEventListener('load', this._noVNC_display._resume_renderQ); + this._noVNC_display._scan_renderQ(); + }, + + _scan_renderQ: function () { + var ready = true; + while (ready && this._renderQ.length > 0) { + var a = this._renderQ[0]; + switch (a.type) { + case 'flip': + this.flip(true); + break; + case 'copy': + this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true); + break; + case 'fill': + this.fillRect(a.x, a.y, a.width, a.height, a.color, true); + break; + case 'blit': + this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true); + break; + case 'blitRgb': + this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true); + break; + case 'blitRgbx': + this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true); + break; + case 'img': + if (a.img.complete) { + this.drawImage(a.img, a.x, a.y); + } else { + a.img._noVNC_display = this; + a.img.addEventListener('load', this._resume_renderQ); + // We need to wait for this image to 'load' + // to keep things in-order + ready = false; + } + break; + } + + if (ready) { + this._renderQ.shift(); + } + } + + if (this._renderQ.length === 0 && this._flushing) { + this._flushing = false; + this._onFlush(); + } + }, + }; + + Util.make_properties(Display, [ + ['target', 'wo', 'dom'], // Canvas element for rendering + ['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only) + ['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "type": mime-type, "data": data} + ['true_color', 'rw', 'bool'], // Use true-color pixel data + ['colourMap', 'rw', 'arr'], // Colour map array (when not true-color) + ['scale', 'rw', 'float'], // Display area scale factor 0.0 - 1.0 + ['viewport', 'rw', 'bool'], // Use viewport clipping + ['width', 'ro', 'int'], // Display area width + ['height', 'ro', 'int'], // Display area height + + ['render_mode', 'ro', 'str'], // Canvas rendering mode (read-only) + + ['prefer_js', 'rw', 'str'], // Prefer Javascript over canvas methods + ['cursor_uri', 'rw', 'raw'], // Can we render cursor using data URI + + ['onFlush', 'rw', 'func'], // onFlush(): A flush request has finished + ]); + + // Class Methods + Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) { + var w = w0; + var h = h0; + if (h < w) { + h = w; // increase h to make it square + } else { + w = h; // increase w to make it square + } + + var cur = []; + + // Push multi-byte little-endian values + cur.push16le = function (num) { + this.push(num & 0xFF, (num >> 8) & 0xFF); + }; + cur.push32le = function (num) { + this.push(num & 0xFF, + (num >> 8) & 0xFF, + (num >> 16) & 0xFF, + (num >> 24) & 0xFF); + }; + + var IHDRsz = 40; + var RGBsz = w * h * 4; + var XORsz = Math.ceil((w * h) / 8.0); + var ANDsz = Math.ceil((w * h) / 8.0); + + cur.push16le(0); // 0: Reserved + cur.push16le(2); // 2: .CUR type + cur.push16le(1); // 4: Number of images, 1 for non-animated ico + + // Cursor #1 header (ICONDIRENTRY) + cur.push(w); // 6: width + cur.push(h); // 7: height + cur.push(0); // 8: colors, 0 -> true-color + cur.push(0); // 9: reserved + cur.push16le(hotx); // 10: hotspot x coordinate + cur.push16le(hoty); // 12: hotspot y coordinate + cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz); + // 14: cursor data byte size + cur.push32le(22); // 18: offset of cursor data in the file + + // Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO) + cur.push32le(IHDRsz); // 22: InfoHeader size + cur.push32le(w); // 26: Cursor width + cur.push32le(h * 2); // 30: XOR+AND height + cur.push16le(1); // 34: number of planes + cur.push16le(32); // 36: bits per pixel + cur.push32le(0); // 38: Type of compression + + cur.push32le(XORsz + ANDsz); + // 42: Size of Image + cur.push32le(0); // 46: reserved + cur.push32le(0); // 50: reserved + cur.push32le(0); // 54: reserved + cur.push32le(0); // 58: reserved + + // 62: color data (RGBQUAD icColors[]) + var y, x; + for (y = h - 1; y >= 0; y--) { + for (x = 0; x < w; x++) { + if (x >= w0 || y >= h0) { + cur.push(0); // blue + cur.push(0); // green + cur.push(0); // red + cur.push(0); // alpha + } else { + var idx = y * Math.ceil(w0 / 8) + Math.floor(x / 8); + var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0; + if (cmap) { + idx = (w0 * y) + x; + var rgb = cmap[pixels[idx]]; + cur.push(rgb[2]); // blue + cur.push(rgb[1]); // green + cur.push(rgb[0]); // red + cur.push(alpha); // alpha + } else { + idx = ((w0 * y) + x) * 4; + cur.push(pixels[idx]); // blue + cur.push(pixels[idx + 1]); // green + cur.push(pixels[idx + 2]); // red + cur.push(alpha); // alpha + } + } + } + } + + // XOR/bitmask data (BYTE icXOR[]) + // (ignored, just needs to be the right size) + for (y = 0; y < h; y++) { + for (x = 0; x < Math.ceil(w / 8); x++) { + cur.push(0); + } + } + + // AND/bitmask data (BYTE icAND[]) + // (ignored, just needs to be the right size) + for (y = 0; y < h; y++) { + for (x = 0; x < Math.ceil(w / 8); x++) { + cur.push(0); + } + } + + var url = 'data:image/x-icon;base64,' + Base64.encode(cur); + target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default'; + }; +})(); diff --git a/noVNC/core/inflator.js b/noVNC/core/inflator.js new file mode 100644 index 0000000..973836d --- /dev/null +++ b/noVNC/core/inflator.js @@ -0,0 +1,2453 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Inflator = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o this.chunkSize) { + this.chunkSize = expected; + this.strm.output = new Uint8Array(this.chunkSize); + } + + this.strm.avail_out = this.chunkSize; + + zlib.inflate(this.strm, flush); + + return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out); + }, + + reset: function () { + zlib.inflateReset(this.strm); + } +}; + +module.exports = { Inflate: Inflate }; + +},{"pako/lib/zlib/inflate.js":6,"pako/lib/zlib/zstream.js":8}],2:[function(require,module,exports){ +'use strict'; + + +var TYPED_OK = (typeof Uint8Array !== 'undefined') && + (typeof Uint16Array !== 'undefined') && + (typeof Int32Array !== 'undefined'); + + +exports.assign = function (obj /*from1, from2, from3, ...*/) { + var sources = Array.prototype.slice.call(arguments, 1); + while (sources.length) { + var source = sources.shift(); + if (!source) { continue; } + + if (typeof source !== 'object') { + throw new TypeError(source + 'must be non-object'); + } + + for (var p in source) { + if (source.hasOwnProperty(p)) { + obj[p] = source[p]; + } + } + } + + return obj; +}; + + +// reduce buffer size, avoiding mem copy +exports.shrinkBuf = function (buf, size) { + if (buf.length === size) { return buf; } + if (buf.subarray) { return buf.subarray(0, size); } + buf.length = size; + return buf; +}; + + +var fnTyped = { + arraySet: function (dest, src, src_offs, len, dest_offs) { + if (src.subarray && dest.subarray) { + dest.set(src.subarray(src_offs, src_offs + len), dest_offs); + return; + } + // Fallback to ordinary array + for (var i = 0; i < len; i++) { + dest[dest_offs + i] = src[src_offs + i]; + } + }, + // Join array of chunks to single array. + flattenChunks: function (chunks) { + var i, l, len, pos, chunk, result; + + // calculate data length + len = 0; + for (i = 0, l = chunks.length; i < l; i++) { + len += chunks[i].length; + } + + // join chunks + result = new Uint8Array(len); + pos = 0; + for (i = 0, l = chunks.length; i < l; i++) { + chunk = chunks[i]; + result.set(chunk, pos); + pos += chunk.length; + } + + return result; + } +}; + +var fnUntyped = { + arraySet: function (dest, src, src_offs, len, dest_offs) { + for (var i = 0; i < len; i++) { + dest[dest_offs + i] = src[src_offs + i]; + } + }, + // Join array of chunks to single array. + flattenChunks: function (chunks) { + return [].concat.apply([], chunks); + } +}; + + +// Enable/Disable typed arrays use, for testing +// +exports.setTyped = function (on) { + if (on) { + exports.Buf8 = Uint8Array; + exports.Buf16 = Uint16Array; + exports.Buf32 = Int32Array; + exports.assign(exports, fnTyped); + } else { + exports.Buf8 = Array; + exports.Buf16 = Array; + exports.Buf32 = Array; + exports.assign(exports, fnUntyped); + } +}; + +exports.setTyped(TYPED_OK); + +},{}],3:[function(require,module,exports){ +'use strict'; + +// Note: adler32 takes 12% for level 0 and 2% for level 6. +// It doesn't worth to make additional optimizationa as in original. +// Small size is preferable. + +function adler32(adler, buf, len, pos) { + var s1 = (adler & 0xffff) |0, + s2 = ((adler >>> 16) & 0xffff) |0, + n = 0; + + while (len !== 0) { + // Set limit ~ twice less than 5552, to keep + // s2 in 31-bits, because we force signed ints. + // in other case %= will fail. + n = len > 2000 ? 2000 : len; + len -= n; + + do { + s1 = (s1 + buf[pos++]) |0; + s2 = (s2 + s1) |0; + } while (--n); + + s1 %= 65521; + s2 %= 65521; + } + + return (s1 | (s2 << 16)) |0; +} + + +module.exports = adler32; + +},{}],4:[function(require,module,exports){ +'use strict'; + +// Note: we can't get significant speed boost here. +// So write code to minimize size - no pregenerated tables +// and array tools dependencies. + + +// Use ordinary array, since untyped makes no boost here +function makeTable() { + var c, table = []; + + for (var n = 0; n < 256; n++) { + c = n; + for (var k = 0; k < 8; k++) { + c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; +} + +// Create table on load. Just 255 signed longs. Not a problem. +var crcTable = makeTable(); + + +function crc32(crc, buf, len, pos) { + var t = crcTable, + end = pos + len; + + crc ^= -1; + + for (var i = pos; i < end; i++) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + + +module.exports = crc32; + +},{}],5:[function(require,module,exports){ +'use strict'; + +// See state defs from inflate.js +var BAD = 30; /* got a data error -- remain here until reset */ +var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state.mode === LEN + strm.avail_in >= 6 + strm.avail_out >= 258 + start >= strm.avail_out + state.bits < 8 + + On return, state.mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm.avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm.avail_out >= 258 for each loop to avoid checking for + output space. + */ +module.exports = function inflate_fast(strm, start) { + var state; + var _in; /* local strm.input */ + var last; /* have enough input while in < last */ + var _out; /* local strm.output */ + var beg; /* inflate()'s initial strm.output */ + var end; /* while out < end, enough space available */ +//#ifdef INFLATE_STRICT + var dmax; /* maximum distance from zlib header */ +//#endif + var wsize; /* window size or zero if not using window */ + var whave; /* valid bytes in the window */ + var wnext; /* window write index */ + // Use `s_window` instead `window`, avoid conflict with instrumentation tools + var s_window; /* allocated sliding window, if wsize != 0 */ + var hold; /* local strm.hold */ + var bits; /* local strm.bits */ + var lcode; /* local strm.lencode */ + var dcode; /* local strm.distcode */ + var lmask; /* mask for first level of length codes */ + var dmask; /* mask for first level of distance codes */ + var here; /* retrieved table entry */ + var op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + var len; /* match length, unused bytes */ + var dist; /* match distance */ + var from; /* where to copy match from */ + var from_source; + + + var input, output; // JS specific, because we have no pointers + + /* copy state to local variables */ + state = strm.state; + //here = state.here; + _in = strm.next_in; + input = strm.input; + last = _in + (strm.avail_in - 5); + _out = strm.next_out; + output = strm.output; + beg = _out - (start - strm.avail_out); + end = _out + (strm.avail_out - 257); +//#ifdef INFLATE_STRICT + dmax = state.dmax; +//#endif + wsize = state.wsize; + whave = state.whave; + wnext = state.wnext; + s_window = state.window; + hold = state.hold; + bits = state.bits; + lcode = state.lencode; + dcode = state.distcode; + lmask = (1 << state.lenbits) - 1; + dmask = (1 << state.distbits) - 1; + + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + top: + do { + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + + here = lcode[hold & lmask]; + + dolen: + for (;;) { // Goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + if (op === 0) { /* literal */ + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + output[_out++] = here & 0xffff/*here.val*/; + } + else if (op & 16) { /* length base */ + len = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + len += hold & ((1 << op) - 1); + hold >>>= op; + bits -= op; + } + //Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = dcode[hold & dmask]; + + dodist: + for (;;) { // goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + + if (op & 16) { /* distance base */ + dist = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + } + dist += hold & ((1 << op) - 1); +//#ifdef INFLATE_STRICT + if (dist > dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } +//#endif + hold >>>= op; + bits -= op; + //Tracevv((stderr, "inflate: distance %u\n", dist)); + op = _out - beg; /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break top; + } + +// (!) This block is disabled in zlib defailts, +// don't enable it for binary compatibility +//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR +// if (len <= op - whave) { +// do { +// output[_out++] = 0; +// } while (--len); +// continue top; +// } +// len -= op - whave; +// do { +// output[_out++] = 0; +// } while (--op > whave); +// if (op === 0) { +// from = _out - dist; +// do { +// output[_out++] = output[from++]; +// } while (--len); +// continue top; +// } +//#endif + } + from = 0; // window index + from_source = s_window; + if (wnext === 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = 0; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + while (len > 2) { + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + len -= 3; + } + if (len) { + output[_out++] = from_source[from++]; + if (len > 1) { + output[_out++] = from_source[from++]; + } + } + } + else { + from = _out - dist; /* copy direct from output */ + do { /* minimum length is three */ + output[_out++] = output[from++]; + output[_out++] = output[from++]; + output[_out++] = output[from++]; + len -= 3; + } while (len > 2); + if (len) { + output[_out++] = output[from++]; + if (len > 1) { + output[_out++] = output[from++]; + } + } + } + } + else if ((op & 64) === 0) { /* 2nd level distance code */ + here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dodist; + } + else { + strm.msg = 'invalid distance code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } + else if ((op & 64) === 0) { /* 2nd level length code */ + here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue dolen; + } + else if (op & 32) { /* end-of-block */ + //Tracevv((stderr, "inflate: end of block\n")); + state.mode = TYPE; + break top; + } + else { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + break top; + } + + break; // need to emulate goto via "continue" + } + } while (_in < last && _out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + _in -= len; + bits -= len << 3; + hold &= (1 << bits) - 1; + + /* update state and return */ + strm.next_in = _in; + strm.next_out = _out; + strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); + strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); + state.hold = hold; + state.bits = bits; + return; +}; + +},{}],6:[function(require,module,exports){ +'use strict'; + + +var utils = require('../utils/common'); +var adler32 = require('./adler32'); +var crc32 = require('./crc32'); +var inflate_fast = require('./inffast'); +var inflate_table = require('./inftrees'); + +var CODES = 0; +var LENS = 1; +var DISTS = 2; + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + + +/* Allowed flush values; see deflate() and inflate() below for details */ +//var Z_NO_FLUSH = 0; +//var Z_PARTIAL_FLUSH = 1; +//var Z_SYNC_FLUSH = 2; +//var Z_FULL_FLUSH = 3; +var Z_FINISH = 4; +var Z_BLOCK = 5; +var Z_TREES = 6; + + +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ +var Z_OK = 0; +var Z_STREAM_END = 1; +var Z_NEED_DICT = 2; +//var Z_ERRNO = -1; +var Z_STREAM_ERROR = -2; +var Z_DATA_ERROR = -3; +var Z_MEM_ERROR = -4; +var Z_BUF_ERROR = -5; +//var Z_VERSION_ERROR = -6; + +/* The deflate compression method */ +var Z_DEFLATED = 8; + + +/* STATES ====================================================================*/ +/* ===========================================================================*/ + + +var HEAD = 1; /* i: waiting for magic header */ +var FLAGS = 2; /* i: waiting for method and flags (gzip) */ +var TIME = 3; /* i: waiting for modification time (gzip) */ +var OS = 4; /* i: waiting for extra flags and operating system (gzip) */ +var EXLEN = 5; /* i: waiting for extra length (gzip) */ +var EXTRA = 6; /* i: waiting for extra bytes (gzip) */ +var NAME = 7; /* i: waiting for end of file name (gzip) */ +var COMMENT = 8; /* i: waiting for end of comment (gzip) */ +var HCRC = 9; /* i: waiting for header crc (gzip) */ +var DICTID = 10; /* i: waiting for dictionary check value */ +var DICT = 11; /* waiting for inflateSetDictionary() call */ +var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ +var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ +var STORED = 14; /* i: waiting for stored size (length and complement) */ +var COPY_ = 15; /* i/o: same as COPY below, but only first time in */ +var COPY = 16; /* i/o: waiting for input or output to copy stored block */ +var TABLE = 17; /* i: waiting for dynamic block table lengths */ +var LENLENS = 18; /* i: waiting for code length code lengths */ +var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ +var LEN_ = 20; /* i: same as LEN below, but only first time in */ +var LEN = 21; /* i: waiting for length/lit/eob code */ +var LENEXT = 22; /* i: waiting for length extra bits */ +var DIST = 23; /* i: waiting for distance code */ +var DISTEXT = 24; /* i: waiting for distance extra bits */ +var MATCH = 25; /* o: waiting for output space to copy string */ +var LIT = 26; /* o: waiting for output space to write literal */ +var CHECK = 27; /* i: waiting for 32-bit check value */ +var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ +var DONE = 29; /* finished check, done -- remain here until reset */ +var BAD = 30; /* got a data error -- remain here until reset */ +var MEM = 31; /* got an inflate() memory error -- remain here until reset */ +var SYNC = 32; /* looking for synchronization bytes to restart inflate() */ + +/* ===========================================================================*/ + + + +var ENOUGH_LENS = 852; +var ENOUGH_DISTS = 592; +//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + +var MAX_WBITS = 15; +/* 32K LZ77 window */ +var DEF_WBITS = MAX_WBITS; + + +function zswap32(q) { + return (((q >>> 24) & 0xff) + + ((q >>> 8) & 0xff00) + + ((q & 0xff00) << 8) + + ((q & 0xff) << 24)); +} + + +function InflateState() { + this.mode = 0; /* current inflate mode */ + this.last = false; /* true if processing last block */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.havedict = false; /* true if dictionary provided */ + this.flags = 0; /* gzip header method and flags (0 if zlib) */ + this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ + this.check = 0; /* protected copy of check value */ + this.total = 0; /* protected copy of output count */ + // TODO: may be {} + this.head = null; /* where to save gzip header information */ + + /* sliding window */ + this.wbits = 0; /* log base 2 of requested window size */ + this.wsize = 0; /* window size or zero if not using window */ + this.whave = 0; /* valid bytes in the window */ + this.wnext = 0; /* window write index */ + this.window = null; /* allocated sliding window, if needed */ + + /* bit accumulator */ + this.hold = 0; /* input bit accumulator */ + this.bits = 0; /* number of bits in "in" */ + + /* for string and stored block copying */ + this.length = 0; /* literal or length of data to copy */ + this.offset = 0; /* distance back to copy string from */ + + /* for table and code decoding */ + this.extra = 0; /* extra bits needed */ + + /* fixed and dynamic code tables */ + this.lencode = null; /* starting table for length/literal codes */ + this.distcode = null; /* starting table for distance codes */ + this.lenbits = 0; /* index bits for lencode */ + this.distbits = 0; /* index bits for distcode */ + + /* dynamic table building */ + this.ncode = 0; /* number of code length code lengths */ + this.nlen = 0; /* number of length code lengths */ + this.ndist = 0; /* number of distance code lengths */ + this.have = 0; /* number of code lengths in lens[] */ + this.next = null; /* next available space in codes[] */ + + this.lens = new utils.Buf16(320); /* temporary storage for code lengths */ + this.work = new utils.Buf16(288); /* work area for code table building */ + + /* + because we don't have pointers in js, we use lencode and distcode directly + as buffers so we don't need codes + */ + //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */ + this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ + this.distdyn = null; /* dynamic table for distance codes (JS specific) */ + this.sane = 0; /* if false, allow invalid distance too far */ + this.back = 0; /* bits back of last unprocessed length/lit */ + this.was = 0; /* initial length of match */ +} + +function inflateResetKeep(strm) { + var state; + + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + strm.total_in = strm.total_out = state.total = 0; + strm.msg = ''; /*Z_NULL*/ + if (state.wrap) { /* to support ill-conceived Java test suite */ + strm.adler = state.wrap & 1; + } + state.mode = HEAD; + state.last = 0; + state.havedict = 0; + state.dmax = 32768; + state.head = null/*Z_NULL*/; + state.hold = 0; + state.bits = 0; + //state.lencode = state.distcode = state.next = state.codes; + state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS); + state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS); + + state.sane = 1; + state.back = -1; + //Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +function inflateReset(strm) { + var state; + + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + state.wsize = 0; + state.whave = 0; + state.wnext = 0; + return inflateResetKeep(strm); + +} + +function inflateReset2(strm, windowBits) { + var wrap; + var state; + + /* get the state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; + if (windowBits < 48) { + windowBits &= 15; + } + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) { + return Z_STREAM_ERROR; + } + if (state.window !== null && state.wbits !== windowBits) { + state.window = null; + } + + /* update state and reset the rest of it */ + state.wrap = wrap; + state.wbits = windowBits; + return inflateReset(strm); +} + +function inflateInit2(strm, windowBits) { + var ret; + var state; + + if (!strm) { return Z_STREAM_ERROR; } + //strm.msg = Z_NULL; /* in case we return an error */ + + state = new InflateState(); + + //if (state === Z_NULL) return Z_MEM_ERROR; + //Tracev((stderr, "inflate: allocated\n")); + strm.state = state; + state.window = null/*Z_NULL*/; + ret = inflateReset2(strm, windowBits); + if (ret !== Z_OK) { + strm.state = null/*Z_NULL*/; + } + return ret; +} + +function inflateInit(strm) { + return inflateInit2(strm, DEF_WBITS); +} + + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +var virgin = true; + +var lenfix, distfix; // We have no pointers in JS, so keep tables separate + +function fixedtables(state) { + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + var sym; + + lenfix = new utils.Buf32(512); + distfix = new utils.Buf32(32); + + /* literal/length table */ + sym = 0; + while (sym < 144) { state.lens[sym++] = 8; } + while (sym < 256) { state.lens[sym++] = 9; } + while (sym < 280) { state.lens[sym++] = 7; } + while (sym < 288) { state.lens[sym++] = 8; } + + inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); + + /* distance table */ + sym = 0; + while (sym < 32) { state.lens[sym++] = 5; } + + inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); + + /* do this just once */ + virgin = false; + } + + state.lencode = lenfix; + state.lenbits = 9; + state.distcode = distfix; + state.distbits = 5; +} + + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +function updatewindow(strm, src, end, copy) { + var dist; + var state = strm.state; + + /* if it hasn't been done already, allocate space for the window */ + if (state.window === null) { + state.wsize = 1 << state.wbits; + state.wnext = 0; + state.whave = 0; + + state.window = new utils.Buf8(state.wsize); + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state.wsize) { + utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0); + state.wnext = 0; + state.whave = state.wsize; + } + else { + dist = state.wsize - state.wnext; + if (dist > copy) { + dist = copy; + } + //zmemcpy(state->window + state->wnext, end - copy, dist); + utils.arraySet(state.window, src, end - copy, dist, state.wnext); + copy -= dist; + if (copy) { + //zmemcpy(state->window, end - copy, copy); + utils.arraySet(state.window, src, end - copy, copy, 0); + state.wnext = copy; + state.whave = state.wsize; + } + else { + state.wnext += dist; + if (state.wnext === state.wsize) { state.wnext = 0; } + if (state.whave < state.wsize) { state.whave += dist; } + } + } + return 0; +} + +function inflate(strm, flush) { + var state; + var input, output; // input/output buffers + var next; /* next input INDEX */ + var put; /* next output INDEX */ + var have, left; /* available input and output */ + var hold; /* bit buffer */ + var bits; /* bits in bit buffer */ + var _in, _out; /* save starting available input and output */ + var copy; /* number of stored or match bytes to copy */ + var from; /* where to copy match bytes from */ + var from_source; + var here = 0; /* current decoding table entry */ + var here_bits, here_op, here_val; // paked "here" denormalized (JS specific) + //var last; /* parent table entry */ + var last_bits, last_op, last_val; // paked "last" denormalized (JS specific) + var len; /* length to copy for repeats, bits to drop */ + var ret; /* return code */ + var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */ + var opts; + + var n; // temporary var for NEED_BITS + + var order = /* permutation of code lengths */ + [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + + + if (!strm || !strm.state || !strm.output || + (!strm.input && strm.avail_in !== 0)) { + return Z_STREAM_ERROR; + } + + state = strm.state; + if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */ + + + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + _in = have; + _out = left; + ret = Z_OK; + + inf_leave: // goto emulation + for (;;) { + switch (state.mode) { + case HEAD: + if (state.wrap === 0) { + state.mode = TYPEDO; + break; + } + //=== NEEDBITS(16); + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */ + state.check = 0/*crc32(0L, Z_NULL, 0)*/; + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = FLAGS; + break; + } + state.flags = 0; /* expect zlib header */ + if (state.head) { + state.head.done = false; + } + if (!(state.wrap & 1) || /* check if zlib header allowed */ + (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) { + strm.msg = 'incorrect header check'; + state.mode = BAD; + break; + } + if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + break; + } + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + len = (hold & 0x0f)/*BITS(4)*/ + 8; + if (state.wbits === 0) { + state.wbits = len; + } + else if (len > state.wbits) { + strm.msg = 'invalid window size'; + state.mode = BAD; + break; + } + state.dmax = 1 << len; + //Tracev((stderr, "inflate: zlib header ok\n")); + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = hold & 0x200 ? DICTID : TYPE; + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + break; + case FLAGS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.flags = hold; + if ((state.flags & 0xff) !== Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + break; + } + if (state.flags & 0xe000) { + strm.msg = 'unknown header flags set'; + state.mode = BAD; + break; + } + if (state.head) { + state.head.text = ((hold >> 8) & 1); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = TIME; + /* falls through */ + case TIME: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.time = hold; + } + if (state.flags & 0x0200) { + //=== CRC4(state.check, hold) + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + hbuf[2] = (hold >>> 16) & 0xff; + hbuf[3] = (hold >>> 24) & 0xff; + state.check = crc32(state.check, hbuf, 4, 0); + //=== + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = OS; + /* falls through */ + case OS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.xflags = (hold & 0xff); + state.head.os = (hold >> 8); + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = EXLEN; + /* falls through */ + case EXLEN: + if (state.flags & 0x0400) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length = hold; + if (state.head) { + state.head.extra_len = hold; + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + else if (state.head) { + state.head.extra = null/*Z_NULL*/; + } + state.mode = EXTRA; + /* falls through */ + case EXTRA: + if (state.flags & 0x0400) { + copy = state.length; + if (copy > have) { copy = have; } + if (copy) { + if (state.head) { + len = state.head.extra_len - state.length; + if (!state.head.extra) { + // Use untyped array for more conveniend processing later + state.head.extra = new Array(state.head.extra_len); + } + utils.arraySet( + state.head.extra, + input, + next, + // extra field is limited to 65536 bytes + // - no need for additional size check + copy, + /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ + len + ); + //zmemcpy(state.head.extra + len, next, + // len + copy > state.head.extra_max ? + // state.head.extra_max - len : copy); + } + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + state.length -= copy; + } + if (state.length) { break inf_leave; } + } + state.length = 0; + state.mode = NAME; + /* falls through */ + case NAME: + if (state.flags & 0x0800) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + // TODO: 2 or 1 bytes? + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.name_max*/)) { + state.head.name += String.fromCharCode(len); + } + } while (len && copy < have); + + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.name = null; + } + state.length = 0; + state.mode = COMMENT; + /* falls through */ + case COMMENT: + if (state.flags & 0x1000) { + if (have === 0) { break inf_leave; } + copy = 0; + do { + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head && len && + (state.length < 65536 /*state.head.comm_max*/)) { + state.head.comment += String.fromCharCode(len); + } + } while (len && copy < have); + if (state.flags & 0x0200) { + state.check = crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) { break inf_leave; } + } + else if (state.head) { + state.head.comment = null; + } + state.mode = HCRC; + /* falls through */ + case HCRC: + if (state.flags & 0x0200) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.check & 0xffff)) { + strm.msg = 'header crc mismatch'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + if (state.head) { + state.head.hcrc = ((state.flags >> 9) & 1); + state.head.done = true; + } + strm.adler = state.check = 0; + state.mode = TYPE; + break; + case DICTID: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + strm.adler = state.check = zswap32(hold); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = DICT; + /* falls through */ + case DICT: + if (state.havedict === 0) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + return Z_NEED_DICT; + } + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = TYPE; + /* falls through */ + case TYPE: + if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; } + /* falls through */ + case TYPEDO: + if (state.last) { + //--- BYTEBITS() ---// + hold >>>= bits & 7; + bits -= bits & 7; + //---// + state.mode = CHECK; + break; + } + //=== NEEDBITS(3); */ + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.last = (hold & 0x01)/*BITS(1)*/; + //--- DROPBITS(1) ---// + hold >>>= 1; + bits -= 1; + //---// + + switch ((hold & 0x03)/*BITS(2)*/) { + case 0: /* stored block */ + //Tracev((stderr, "inflate: stored block%s\n", + // state.last ? " (last)" : "")); + state.mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + //Tracev((stderr, "inflate: fixed codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = LEN_; /* decode codes */ + if (flush === Z_TREES) { + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break inf_leave; + } + break; + case 2: /* dynamic block */ + //Tracev((stderr, "inflate: dynamic codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = TABLE; + break; + case 3: + strm.msg = 'invalid block type'; + state.mode = BAD; + } + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break; + case STORED: + //--- BYTEBITS() ---// /* go to byte boundary */ + hold >>>= bits & 7; + bits -= bits & 7; + //---// + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { + strm.msg = 'invalid stored block lengths'; + state.mode = BAD; + break; + } + state.length = hold & 0xffff; + //Tracev((stderr, "inflate: stored length %u\n", + // state.length)); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = COPY_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case COPY_: + state.mode = COPY; + /* falls through */ + case COPY: + copy = state.length; + if (copy) { + if (copy > have) { copy = have; } + if (copy > left) { copy = left; } + if (copy === 0) { break inf_leave; } + //--- zmemcpy(put, next, copy); --- + utils.arraySet(output, input, next, copy, put); + //---// + have -= copy; + next += copy; + left -= copy; + put += copy; + state.length -= copy; + break; + } + //Tracev((stderr, "inflate: stored end\n")); + state.mode = TYPE; + break; + case TABLE: + //=== NEEDBITS(14); */ + while (bits < 14) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// +//#ifndef PKZIP_BUG_WORKAROUND + if (state.nlen > 286 || state.ndist > 30) { + strm.msg = 'too many length or distance symbols'; + state.mode = BAD; + break; + } +//#endif + //Tracev((stderr, "inflate: table sizes ok\n")); + state.have = 0; + state.mode = LENLENS; + /* falls through */ + case LENLENS: + while (state.have < state.ncode) { + //=== NEEDBITS(3); + while (bits < 3) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + while (state.have < 19) { + state.lens[order[state.have++]] = 0; + } + // We have separate tables & no pointers. 2 commented lines below not needed. + //state.next = state.codes; + //state.lencode = state.next; + // Switch to use dynamic table + state.lencode = state.lendyn; + state.lenbits = 7; + + opts = { bits: state.lenbits }; + ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); + state.lenbits = opts.bits; + + if (ret) { + strm.msg = 'invalid code lengths set'; + state.mode = BAD; + break; + } + //Tracev((stderr, "inflate: code lengths ok\n")); + state.have = 0; + state.mode = CODELENS; + /* falls through */ + case CODELENS: + while (state.have < state.nlen + state.ndist) { + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_val < 16) { + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.lens[state.have++] = here_val; + } + else { + if (here_val === 16) { + //=== NEEDBITS(here.bits + 2); + n = here_bits + 2; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + if (state.have === 0) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; + } + len = state.lens[state.have - 1]; + copy = 3 + (hold & 0x03);//BITS(2); + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + } + else if (here_val === 17) { + //=== NEEDBITS(here.bits + 3); + n = here_bits + 3; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 3 + (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + else { + //=== NEEDBITS(here.bits + 7); + n = here_bits + 7; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 11 + (hold & 0x7f);//BITS(7); + //--- DROPBITS(7) ---// + hold >>>= 7; + bits -= 7; + //---// + } + if (state.have + copy > state.nlen + state.ndist) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; + } + while (copy--) { + state.lens[state.have++] = len; + } + } + } + + /* handle error breaks in while */ + if (state.mode === BAD) { break; } + + /* check for end-of-block code (better have one) */ + if (state.lens[256] === 0) { + strm.msg = 'invalid code -- missing end-of-block'; + state.mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state.lenbits = 9; + + opts = { bits: state.lenbits }; + ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.lenbits = opts.bits; + // state.lencode = state.next; + + if (ret) { + strm.msg = 'invalid literal/lengths set'; + state.mode = BAD; + break; + } + + state.distbits = 6; + //state.distcode.copy(state.codes); + // Switch to use dynamic table + state.distcode = state.distdyn; + opts = { bits: state.distbits }; + ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.distbits = opts.bits; + // state.distcode = state.next; + + if (ret) { + strm.msg = 'invalid distances set'; + state.mode = BAD; + break; + } + //Tracev((stderr, 'inflate: codes ok\n')); + state.mode = LEN_; + if (flush === Z_TREES) { break inf_leave; } + /* falls through */ + case LEN_: + state.mode = LEN; + /* falls through */ + case LEN: + if (have >= 6 && left >= 258) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + inflate_fast(strm, _out); + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + if (state.mode === TYPE) { + state.back = -1; + } + break; + } + state.back = 0; + for (;;) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if (here_bits <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_op && (here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.lencode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + state.length = here_val; + if (here_op === 0) { + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + state.mode = LIT; + break; + } + if (here_op & 32) { + //Tracevv((stderr, "inflate: end of block\n")); + state.back = -1; + state.mode = TYPE; + break; + } + if (here_op & 64) { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + break; + } + state.extra = here_op & 15; + state.mode = LENEXT; + /* falls through */ + case LENEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //Tracevv((stderr, "inflate: length %u\n", state.length)); + state.was = state.length; + state.mode = DIST; + /* falls through */ + case DIST: + for (;;) { + here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if ((here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.distcode[last_val + + ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + if (here_op & 64) { + strm.msg = 'invalid distance code'; + state.mode = BAD; + break; + } + state.offset = here_val; + state.extra = (here_op) & 15; + state.mode = DISTEXT; + /* falls through */ + case DISTEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } +//#ifdef INFLATE_STRICT + if (state.offset > state.dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break; + } +//#endif + //Tracevv((stderr, "inflate: distance %u\n", state.offset)); + state.mode = MATCH; + /* falls through */ + case MATCH: + if (left === 0) { break inf_leave; } + copy = _out - left; + if (state.offset > copy) { /* copy from window */ + copy = state.offset - copy; + if (copy > state.whave) { + if (state.sane) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + break; + } +// (!) This block is disabled in zlib defailts, +// don't enable it for binary compatibility +//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR +// Trace((stderr, "inflate.c too far\n")); +// copy -= state.whave; +// if (copy > state.length) { copy = state.length; } +// if (copy > left) { copy = left; } +// left -= copy; +// state.length -= copy; +// do { +// output[put++] = 0; +// } while (--copy); +// if (state.length === 0) { state.mode = LEN; } +// break; +//#endif + } + if (copy > state.wnext) { + copy -= state.wnext; + from = state.wsize - copy; + } + else { + from = state.wnext - copy; + } + if (copy > state.length) { copy = state.length; } + from_source = state.window; + } + else { /* copy from output */ + from_source = output; + from = put - state.offset; + copy = state.length; + } + if (copy > left) { copy = left; } + left -= copy; + state.length -= copy; + do { + output[put++] = from_source[from++]; + } while (--copy); + if (state.length === 0) { state.mode = LEN; } + break; + case LIT: + if (left === 0) { break inf_leave; } + output[put++] = state.length; + left--; + state.mode = LEN; + break; + case CHECK: + if (state.wrap) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + // Use '|' insdead of '+' to make sure that result is signed + hold |= input[next++] << bits; + bits += 8; + } + //===// + _out -= left; + strm.total_out += _out; + state.total += _out; + if (_out) { + strm.adler = state.check = + /*UPDATE(state.check, put - _out, _out);*/ + (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out)); + + } + _out = left; + // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too + if ((state.flags ? hold : zswap32(hold)) !== state.check) { + strm.msg = 'incorrect data check'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: check matches trailer\n")); + } + state.mode = LENGTH; + /* falls through */ + case LENGTH: + if (state.wrap && state.flags) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) { break inf_leave; } + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.total & 0xffffffff)) { + strm.msg = 'incorrect length check'; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: length matches trailer\n")); + } + state.mode = DONE; + /* falls through */ + case DONE: + ret = Z_STREAM_END; + break inf_leave; + case BAD: + ret = Z_DATA_ERROR; + break inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + /* falls through */ + default: + return Z_STREAM_ERROR; + } + } + + // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + + if (state.wsize || (_out !== strm.avail_out && state.mode < BAD && + (state.mode < CHECK || flush !== Z_FINISH))) { + if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) { + state.mode = MEM; + return Z_MEM_ERROR; + } + } + _in -= strm.avail_in; + _out -= strm.avail_out; + strm.total_in += _in; + strm.total_out += _out; + state.total += _out; + if (state.wrap && _out) { + strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ + (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out)); + } + strm.data_type = state.bits + (state.last ? 64 : 0) + + (state.mode === TYPE ? 128 : 0) + + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); + if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) { + ret = Z_BUF_ERROR; + } + return ret; +} + +function inflateEnd(strm) { + + if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { + return Z_STREAM_ERROR; + } + + var state = strm.state; + if (state.window) { + state.window = null; + } + strm.state = null; + return Z_OK; +} + +function inflateGetHeader(strm, head) { + var state; + + /* check state */ + if (!strm || !strm.state) { return Z_STREAM_ERROR; } + state = strm.state; + if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; } + + /* save header structure */ + state.head = head; + head.done = false; + return Z_OK; +} + +function inflateSetDictionary(strm, dictionary) { + var dictLength = dictionary.length; + + var state; + var dictid; + var ret; + + /* check state */ + if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; } + state = strm.state; + + if (state.wrap !== 0 && state.mode !== DICT) { + return Z_STREAM_ERROR; + } + + /* check for correct dictionary identifier */ + if (state.mode === DICT) { + dictid = 1; /* adler32(0, null, 0)*/ + /* dictid = adler32(dictid, dictionary, dictLength); */ + dictid = adler32(dictid, dictionary, dictLength, 0); + if (dictid !== state.check) { + return Z_DATA_ERROR; + } + } + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary, dictLength, dictLength); + if (ret) { + state.mode = MEM; + return Z_MEM_ERROR; + } + state.havedict = 1; + // Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +exports.inflateReset = inflateReset; +exports.inflateReset2 = inflateReset2; +exports.inflateResetKeep = inflateResetKeep; +exports.inflateInit = inflateInit; +exports.inflateInit2 = inflateInit2; +exports.inflate = inflate; +exports.inflateEnd = inflateEnd; +exports.inflateGetHeader = inflateGetHeader; +exports.inflateSetDictionary = inflateSetDictionary; +exports.inflateInfo = 'pako inflate (from Nodeca project)'; + +/* Not implemented +exports.inflateCopy = inflateCopy; +exports.inflateGetDictionary = inflateGetDictionary; +exports.inflateMark = inflateMark; +exports.inflatePrime = inflatePrime; +exports.inflateSync = inflateSync; +exports.inflateSyncPoint = inflateSyncPoint; +exports.inflateUndermine = inflateUndermine; +*/ + +},{"../utils/common":2,"./adler32":3,"./crc32":4,"./inffast":5,"./inftrees":7}],7:[function(require,module,exports){ +'use strict'; + + +var utils = require('../utils/common'); + +var MAXBITS = 15; +var ENOUGH_LENS = 852; +var ENOUGH_DISTS = 592; +//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + +var CODES = 0; +var LENS = 1; +var DISTS = 2; + +var lbase = [ /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +]; + +var lext = [ /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 +]; + +var dbase = [ /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0 +]; + +var dext = [ /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64 +]; + +module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts) +{ + var bits = opts.bits; + //here = opts.here; /* table entry for duplication */ + + var len = 0; /* a code's length in bits */ + var sym = 0; /* index of code symbols */ + var min = 0, max = 0; /* minimum and maximum code lengths */ + var root = 0; /* number of index bits for root table */ + var curr = 0; /* number of index bits for current table */ + var drop = 0; /* code bits to drop for sub-table */ + var left = 0; /* number of prefix codes available */ + var used = 0; /* code entries in table used */ + var huff = 0; /* Huffman code */ + var incr; /* for incrementing code, index */ + var fill; /* index for replicating entries */ + var low; /* low bits for current root entry */ + var mask; /* mask for low root bits */ + var next; /* next available space in table */ + var base = null; /* base value table to use */ + var base_index = 0; +// var shoextra; /* extra bits table to use */ + var end; /* use base and extra for symbol > end */ + var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ + var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ + var extra = null; + var extra_index = 0; + + var here_bits, here_op, here_val; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) { + count[len] = 0; + } + for (sym = 0; sym < codes; sym++) { + count[lens[lens_index + sym]]++; + } + + /* bound code lengths, force root to be within code lengths */ + root = bits; + for (max = MAXBITS; max >= 1; max--) { + if (count[max] !== 0) { break; } + } + if (root > max) { + root = max; + } + if (max === 0) { /* no symbols to code at all */ + //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ + //table.bits[opts.table_index] = 1; //here.bits = (var char)1; + //table.val[opts.table_index++] = 0; //here.val = (var short)0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + + //table.op[opts.table_index] = 64; + //table.bits[opts.table_index] = 1; + //table.val[opts.table_index++] = 0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + opts.bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) { + if (count[min] !== 0) { break; } + } + if (root < min) { + root = min; + } + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return -1; + } /* over-subscribed */ + } + if (left > 0 && (type === CODES || max !== 1)) { + return -1; /* incomplete set */ + } + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) { + offs[len + 1] = offs[len] + count[len]; + } + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) { + if (lens[lens_index + sym] !== 0) { + work[offs[lens[lens_index + sym]]++] = sym; + } + } + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + // poor man optimization - use if-else instead of switch, + // to avoid deopts in old v8 + if (type === CODES) { + base = extra = work; /* dummy value--not used */ + end = 19; + + } else if (type === LENS) { + base = lbase; + base_index -= 257; + extra = lext; + extra_index -= 257; + end = 256; + + } else { /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize opts for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = table_index; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = -1; /* trigger new sub-table when len > root */ + used = 1 << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + var i = 0; + /* process all codes and make table entries */ + for (;;) { + i++; + /* create table entry */ + here_bits = len - drop; + if (work[sym] < end) { + here_op = 0; + here_val = work[sym]; + } + else if (work[sym] > end) { + here_op = extra[extra_index + work[sym]]; + here_val = base[base_index + work[sym]]; + } + else { + here_op = 32 + 64; /* end of block */ + here_val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1 << (len - drop); + fill = 1 << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; + } while (fill !== 0); + + /* backwards increment the len-bit code huff */ + incr = 1 << (len - 1); + while (huff & incr) { + incr >>= 1; + } + if (incr !== 0) { + huff &= incr - 1; + huff += incr; + } else { + huff = 0; + } + + /* go to next symbol, update count, len */ + sym++; + if (--count[len] === 0) { + if (len === max) { break; } + len = lens[lens_index + work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) !== low) { + /* if first time, transition to sub-tables */ + if (drop === 0) { + drop = root; + } + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = 1 << curr; + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) { break; } + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1 << curr; + if ((type === LENS && used > ENOUGH_LENS) || + (type === DISTS && used > ENOUGH_DISTS)) { + return 1; + } + + /* point entry in root table to sub-table */ + low = huff & mask; + /*table.op[low] = curr; + table.bits[low] = root; + table.val[low] = next - opts.table_index;*/ + table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff !== 0) { + //table.op[next + huff] = 64; /* invalid code marker */ + //table.bits[next + huff] = len - drop; + //table.val[next + huff] = 0; + table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; + } + + /* set return parameters */ + //opts.table_index += used; + opts.bits = root; + return 0; +}; + +},{"../utils/common":2}],8:[function(require,module,exports){ +'use strict'; + + +function ZStream() { + /* next input byte */ + this.input = null; // JS specific, because we have no pointers + this.next_in = 0; + /* number of bytes available at input */ + this.avail_in = 0; + /* total number of input bytes read so far */ + this.total_in = 0; + /* next output byte should be put there */ + this.output = null; // JS specific, because we have no pointers + this.next_out = 0; + /* remaining free space at output */ + this.avail_out = 0; + /* total number of bytes output so far */ + this.total_out = 0; + /* last error message, NULL if no error */ + this.msg = ''/*Z_NULL*/; + /* not visible by applications */ + this.state = null; + /* best guess about the data type: binary or text */ + this.data_type = 2/*Z_UNKNOWN*/; + /* adler32 value of the uncompressed data */ + this.adler = 0; +} + +module.exports = ZStream; + +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/noVNC/core/inflator.mod.js b/noVNC/core/inflator.mod.js new file mode 100644 index 0000000..26e6441 --- /dev/null +++ b/noVNC/core/inflator.mod.js @@ -0,0 +1,40 @@ +var zlib = require('pako/lib/zlib/inflate.js'); +var ZStream = require('pako/lib/zlib/zstream.js'); + +function Inflate() { + this.strm = new ZStream(); + this.chunkSize = 1024 * 10 * 10; + this.strm.output = new Uint8Array(this.chunkSize); + this.windowBits = 5; + + zlib.inflateInit(this.strm, this.windowBits); +}; + +Inflate.prototype = { + inflate: function (data, flush, expected) { + this.strm.input = data; + this.strm.avail_in = this.strm.input.length; + this.strm.next_in = 0; + this.strm.next_out = 0; + + // resize our output buffer if it's too small + // (we could just use multiple chunks, but that would cause an extra + // allocation each time to flatten the chunks) + if (expected > this.chunkSize) { + this.chunkSize = expected; + this.strm.output = new Uint8Array(this.chunkSize); + } + + this.strm.avail_out = this.chunkSize; + + zlib.inflate(this.strm, flush); + + return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out); + }, + + reset: function () { + zlib.inflateReset(this.strm); + } +}; + +module.exports = { Inflate: Inflate }; diff --git a/noVNC/include/input.js b/noVNC/core/input/devices.js similarity index 57% rename from noVNC/include/input.js rename to noVNC/core/input/devices.js index 5d9e209..2e41122 100644 --- a/noVNC/include/input.js +++ b/noVNC/core/input/devices.js @@ -8,7 +8,12 @@ /*jslint browser: true, white: false */ /*global window, Util */ -var Keyboard, Mouse; +/* [module] + * import Util from "../util"; + * import KeyboardUtil from "./util"; + */ + +/* [module] export */ var Keyboard; (function () { "use strict"; @@ -27,10 +32,10 @@ var Keyboard, Mouse; }); // create the keyboard handler - this._handler = new KeyEventDecoder(kbdUtil.ModifierSync(), - VerifyCharModifier( /* jshint newcap: false */ - TrackKeyState( - EscapeModifiers(this._handleRfbEvent.bind(this)) + this._handler = new KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), + KeyboardUtil.VerifyCharModifier( /* jshint newcap: false */ + KeyboardUtil.TrackKeyState( + KeyboardUtil.EscapeModifiers(this._handleRfbEvent.bind(this)) ) ) ); /* jshint newcap: true */ @@ -51,49 +56,45 @@ var Keyboard, Mouse; if (this._onKeyPress) { Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") + ", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")"); - this._onKeyPress(e.keysym.keysym, e.type == 'keydown'); + this._onKeyPress(e); } }, + setQEMUVNCKeyboardHandler: function () { + this._handler = new KeyboardUtil.QEMUKeyEventDecoder(KeyboardUtil.ModifierSync(), + KeyboardUtil.TrackQEMUKeyState( + this._handleRfbEvent.bind(this) + ) + ); + }, + _handleKeyDown: function (e) { - if (!this._focused) { return true; } + if (!this._focused) { return; } if (this._handler.keydown(e)) { // Suppress bubbling/default actions Util.stopEvent(e); - return false; } else { // Allow the event to bubble and become a keyPress event which // will have the character code translated - return true; } }, _handleKeyPress: function (e) { - if (!this._focused) { return true; } + if (!this._focused) { return; } if (this._handler.keypress(e)) { // Suppress bubbling/default actions Util.stopEvent(e); - return false; - } else { - // Allow the event to bubble and become a keyPress event which - // will have the character code translated - return true; } }, _handleKeyUp: function (e) { - if (!this._focused) { return true; } + if (!this._focused) { return; } if (this._handler.keyup(e)) { // Suppress bubbling/default actions Util.stopEvent(e); - return false; - } else { - // Allow the event to bubble and become a keyPress event which - // will have the character code translated - return true; } }, @@ -109,12 +110,12 @@ var Keyboard, Mouse; //Util.Debug(">> Keyboard.grab"); var c = this._target; - Util.addEvent(c, 'keydown', this._eventHandlers.keydown); - Util.addEvent(c, 'keyup', this._eventHandlers.keyup); - Util.addEvent(c, 'keypress', this._eventHandlers.keypress); + c.addEventListener('keydown', this._eventHandlers.keydown); + c.addEventListener('keyup', this._eventHandlers.keyup); + c.addEventListener('keypress', this._eventHandlers.keypress); // Release (key up) if window loses focus - Util.addEvent(window, 'blur', this._eventHandlers.blur); + window.addEventListener('blur', this._eventHandlers.blur); //Util.Debug("<< Keyboard.grab"); }, @@ -123,10 +124,10 @@ var Keyboard, Mouse; //Util.Debug(">> Keyboard.ungrab"); var c = this._target; - Util.removeEvent(c, 'keydown', this._eventHandlers.keydown); - Util.removeEvent(c, 'keyup', this._eventHandlers.keyup); - Util.removeEvent(c, 'keypress', this._eventHandlers.keypress); - Util.removeEvent(window, 'blur', this._eventHandlers.blur); + c.removeEventListener('keydown', this._eventHandlers.keydown); + c.removeEventListener('keyup', this._eventHandlers.keyup); + c.removeEventListener('keypress', this._eventHandlers.keypress); + window.removeEventListener('blur', this._eventHandlers.blur); // Release (key up) all keys that are in a down state this._allKeysUp(); @@ -145,11 +146,11 @@ var Keyboard, Mouse; ['onKeyPress', 'rw', 'func'] // Handler for key press/release ]); +})(); - // - // Mouse event handler - // +/* [module] export */ var Mouse; +(function () { Mouse = function (defaults) { this._mouseCaptured = false; @@ -160,7 +161,6 @@ var Keyboard, Mouse; Util.set_defaults(this, defaults, { 'target': document, 'focused': true, - 'scale': 1.0, 'touchButton': 1 }); @@ -177,9 +177,7 @@ var Keyboard, Mouse; // private methods _captureMouse: function () { // capturing the mouse ensures we get the mouseup event - if (this._target.setCapture) { - this._target.setCapture(); - } + Util.setCapture(this._target); // some browsers give us mouseup events regardless, // so if we never captured the mouse, we can disregard the event @@ -187,9 +185,7 @@ var Keyboard, Mouse; }, _releaseMouse: function () { - if (this._target.releaseCapture) { - this._target.releaseCapture(); - } + Util.releaseCapture(); this._mouseCaptured = false; }, @@ -198,21 +194,20 @@ var Keyboard, Mouse; }, _handleMouseButton: function (e, down) { - if (!this._focused) { return true; } + if (!this._focused) { return; } if (this._notify) { this._notify(e); } - var evt = (e ? e : window.event); - var pos = Util.getEventPosition(e, this._target, this._scale); + var pos = this._getMousePosition(e); var bmask; if (e.touches || e.changedTouches) { // Touch device // When two touches occur within 500 ms of each other and are - // closer than 20 pixels together a double click is triggered. + // close enough together a double click is triggered. if (down == 1) { if (this._doubleClickTimer === null) { this._lastTouchPos = pos; @@ -229,7 +224,8 @@ var Keyboard, Mouse; // The goal is to trigger on a certain physical width, the // devicePixelRatio brings us a bit closer but is not optimal. - if (d < 20 * window.devicePixelRatio) { + var threshold = 20 * (window.devicePixelRatio || 1); + if (d < threshold) { pos = this._lastTouchPos; } } @@ -237,14 +233,14 @@ var Keyboard, Mouse; } bmask = this._touchButton; // If bmask is set - } else if (evt.which) { + } else if (e.which) { /* everything except IE */ - bmask = 1 << evt.button; + bmask = 1 << e.button; } else { /* IE including 9 */ - bmask = (evt.button & 0x1) + // Left - (evt.button & 0x2) * 2 + // Right - (evt.button & 0x4) / 2; // Middle + bmask = (e.button & 0x1) + // Left + (e.button & 0x2) * 2 + // Right + (e.button & 0x4) / 2; // Middle } if (this._onMouseButton) { @@ -253,7 +249,6 @@ var Keyboard, Mouse; this._onMouseButton(pos.x, pos.y, down, bmask); } Util.stopEvent(e); - return false; }, _handleMouseDown: function (e) { @@ -269,62 +264,85 @@ var Keyboard, Mouse; }, _handleMouseWheel: function (e) { - if (!this._focused) { return true; } + if (!this._focused) { return; } if (this._notify) { this._notify(e); } - var evt = (e ? e : window.event); - var pos = Util.getEventPosition(e, this._target, this._scale); - var wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40; - var bmask; - if (wheelData > 0) { - bmask = 1 << 3; - } else { - bmask = 1 << 4; - } + var pos = this._getMousePosition(e); if (this._onMouseButton) { - this._onMouseButton(pos.x, pos.y, 1, bmask); - this._onMouseButton(pos.x, pos.y, 0, bmask); + if (e.deltaX < 0) { + this._onMouseButton(pos.x, pos.y, 1, 1 << 5); + this._onMouseButton(pos.x, pos.y, 0, 1 << 5); + } else if (e.deltaX > 0) { + this._onMouseButton(pos.x, pos.y, 1, 1 << 6); + this._onMouseButton(pos.x, pos.y, 0, 1 << 6); + } + + if (e.deltaY < 0) { + this._onMouseButton(pos.x, pos.y, 1, 1 << 3); + this._onMouseButton(pos.x, pos.y, 0, 1 << 3); + } else if (e.deltaY > 0) { + this._onMouseButton(pos.x, pos.y, 1, 1 << 4); + this._onMouseButton(pos.x, pos.y, 0, 1 << 4); + } } + Util.stopEvent(e); - return false; }, _handleMouseMove: function (e) { - if (! this._focused) { return true; } + if (! this._focused) { return; } if (this._notify) { this._notify(e); } - var evt = (e ? e : window.event); - var pos = Util.getEventPosition(e, this._target, this._scale); + var pos = this._getMousePosition(e); if (this._onMouseMove) { this._onMouseMove(pos.x, pos.y); } Util.stopEvent(e); - return false; }, _handleMouseDisable: function (e) { - if (!this._focused) { return true; } + if (!this._focused) { return; } - var evt = (e ? e : window.event); - var pos = Util.getEventPosition(e, this._target, this._scale); - - /* Stop propagation if inside canvas area */ - if ((pos.realx >= 0) && (pos.realy >= 0) && - (pos.realx < this._target.offsetWidth) && - (pos.realy < this._target.offsetHeight)) { + /* + * Stop propagation if inside canvas area + * Note: This is only needed for the 'click' event as it fails + * to fire properly for the target element so we have + * to listen on the document element instead. + */ + if (e.target == this._target) { //Util.Debug("mouse event disabled"); Util.stopEvent(e); - return false; } + }, - return true; + // Return coordinates relative to target + _getMousePosition: function(e) { + e = Util.getPointerEvent(e); + var bounds = this._target.getBoundingClientRect(); + var x, y; + // Clip to target bounds + if (e.clientX < bounds.left) { + x = 0; + } else if (e.clientX >= bounds.right) { + x = bounds.width - 1; + } else { + x = e.clientX - bounds.left; + } + if (e.clientY < bounds.top) { + y = 0; + } else if (e.clientY >= bounds.bottom) { + y = bounds.height - 1; + } else { + y = e.clientY - bounds.top; + } + return {x:x, y:y}; }, @@ -332,46 +350,44 @@ var Keyboard, Mouse; grab: function () { var c = this._target; - if ('ontouchstart' in document.documentElement) { - Util.addEvent(c, 'touchstart', this._eventHandlers.mousedown); - Util.addEvent(window, 'touchend', this._eventHandlers.mouseup); - Util.addEvent(c, 'touchend', this._eventHandlers.mouseup); - Util.addEvent(c, 'touchmove', this._eventHandlers.mousemove); - } else { - Util.addEvent(c, 'mousedown', this._eventHandlers.mousedown); - Util.addEvent(window, 'mouseup', this._eventHandlers.mouseup); - Util.addEvent(c, 'mouseup', this._eventHandlers.mouseup); - Util.addEvent(c, 'mousemove', this._eventHandlers.mousemove); - Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', - this._eventHandlers.mousewheel); + if (Util.isTouchDevice) { + c.addEventListener('touchstart', this._eventHandlers.mousedown); + window.addEventListener('touchend', this._eventHandlers.mouseup); + c.addEventListener('touchend', this._eventHandlers.mouseup); + c.addEventListener('touchmove', this._eventHandlers.mousemove); } + c.addEventListener('mousedown', this._eventHandlers.mousedown); + window.addEventListener('mouseup', this._eventHandlers.mouseup); + c.addEventListener('mouseup', this._eventHandlers.mouseup); + c.addEventListener('mousemove', this._eventHandlers.mousemove); + c.addEventListener('wheel', this._eventHandlers.mousewheel); - /* Work around right and middle click browser behaviors */ - Util.addEvent(document, 'click', this._eventHandlers.mousedisable); - Util.addEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable); + /* Prevent middle-click pasting (see above for why we bind to document) */ + document.addEventListener('click', this._eventHandlers.mousedisable); + + /* preventDefault() on mousedown doesn't stop this event for some + reason so we have to explicitly block it */ + c.addEventListener('contextmenu', this._eventHandlers.mousedisable); }, ungrab: function () { var c = this._target; - if ('ontouchstart' in document.documentElement) { - Util.removeEvent(c, 'touchstart', this._eventHandlers.mousedown); - Util.removeEvent(window, 'touchend', this._eventHandlers.mouseup); - Util.removeEvent(c, 'touchend', this._eventHandlers.mouseup); - Util.removeEvent(c, 'touchmove', this._eventHandlers.mousemove); - } else { - Util.removeEvent(c, 'mousedown', this._eventHandlers.mousedown); - Util.removeEvent(window, 'mouseup', this._eventHandlers.mouseup); - Util.removeEvent(c, 'mouseup', this._eventHandlers.mouseup); - Util.removeEvent(c, 'mousemove', this._eventHandlers.mousemove); - Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', - this._eventHandlers.mousewheel); + if (Util.isTouchDevice) { + c.removeEventListener('touchstart', this._eventHandlers.mousedown); + window.removeEventListener('touchend', this._eventHandlers.mouseup); + c.removeEventListener('touchend', this._eventHandlers.mouseup); + c.removeEventListener('touchmove', this._eventHandlers.mousemove); } + c.removeEventListener('mousedown', this._eventHandlers.mousedown); + window.removeEventListener('mouseup', this._eventHandlers.mouseup); + c.removeEventListener('mouseup', this._eventHandlers.mouseup); + c.removeEventListener('mousemove', this._eventHandlers.mousemove); + c.removeEventListener('wheel', this._eventHandlers.mousewheel); - /* Work around right and middle click browser behaviors */ - Util.removeEvent(document, 'click', this._eventHandlers.mousedisable); - Util.removeEvent(document.body, 'contextmenu', this._eventHandlers.mousedisable); + document.removeEventListener('click', this._eventHandlers.mousedisable); + c.removeEventListener('contextmenu', this._eventHandlers.mousedisable); } }; @@ -379,7 +395,6 @@ var Keyboard, Mouse; ['target', 'ro', 'dom'], // DOM element that captures mouse input ['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received ['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement - ['scale', 'rw', 'float'], // Viewport scale factor 0.0 - 1.0 ['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release ['onMouseMove', 'rw', 'func'], // Handler for mouse movement diff --git a/noVNC/core/input/keysym.js b/noVNC/core/input/keysym.js new file mode 100644 index 0000000..5983c38 --- /dev/null +++ b/noVNC/core/input/keysym.js @@ -0,0 +1,382 @@ +var KeyTable = { + 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 */ +}; + +/* [module] export default KeyTable; */ diff --git a/noVNC/include/keysymdef.js b/noVNC/core/input/keysymdef.js similarity index 98% rename from noVNC/include/keysymdef.js rename to noVNC/core/input/keysymdef.js index f94445c..c4d0ace 100644 --- a/noVNC/include/keysymdef.js +++ b/noVNC/core/input/keysymdef.js @@ -2,6 +2,7 @@ // (and optionally, key names) expected by the RFB protocol // How this file was generated: // node /Users/jalf/dev/mi/novnc/utils/parse.js /opt/X11/include/X11/keysymdef.h + var keysyms = (function(){ "use strict"; var keynames = null; @@ -9,7 +10,15 @@ var keysyms = (function(){ function lookup(k) { return k ? {keysym: k, keyname: keynames ? keynames[k] : k} : undefined; } return { - fromUnicode : function(u) { return lookup(codepoints[u]); }, + fromUnicode : function(u) { + var keysym = codepoints[u]; + if (keysym === undefined) { + keysym = 0x01000000 | u; + } + return lookup(keysym); + }, lookup : lookup }; })(); + +/* [module] export default keysyms */ diff --git a/noVNC/include/keyboard.js b/noVNC/core/input/util.js similarity index 66% rename from noVNC/include/keyboard.js rename to noVNC/core/input/util.js index 8667031..52b3128 100644 --- a/noVNC/include/keyboard.js +++ b/noVNC/core/input/util.js @@ -1,4 +1,11 @@ -var kbdUtil = (function() { +/* [module] + * import KeyTable from "./keysym"; + * import keysyms from "./keysymdef"; + */ + +var KeyboardUtil = {}; + +(function() { "use strict"; function substituteCodepoint(cp) { @@ -31,7 +38,7 @@ var kbdUtil = (function() { function hasShortcutModifier(charModifier, currentModifiers) { var mods = {}; for (var key in currentModifiers) { - if (parseInt(key) !== XK_Shift_L) { + if (parseInt(key) !== KeyTable.XK_Shift_L) { mods[key] = currentModifiers[key]; } } @@ -68,15 +75,15 @@ var kbdUtil = (function() { if (!charModifier) { if (isMac()) { // on Mac, Option (AKA Alt) is used as a char modifier - charModifier = [XK_Alt_L]; + charModifier = [KeyTable.XK_Alt_L]; } else if (isWindows()) { // on Windows, Ctrl+Alt is used as a char modifier - charModifier = [XK_Alt_L, XK_Control_L]; + charModifier = [KeyTable.XK_Alt_L, KeyTable.XK_Control_L]; } else if (isLinux()) { // on Linux, ISO Level 3 Shift (AltGr) is used as a char modifier - charModifier = [XK_ISO_Level3_Shift]; + charModifier = [KeyTable.XK_ISO_Level3_Shift]; } else { charModifier = []; @@ -84,11 +91,11 @@ var kbdUtil = (function() { } 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; + state[KeyTable.XK_Control_L] = false; + state[KeyTable.XK_Alt_L] = false; + state[KeyTable.XK_ISO_Level3_Shift] = false; + state[KeyTable.XK_Shift_L] = false; + state[KeyTable.XK_Meta_L] = false; function sync(evt, keysym) { var result = []; @@ -97,29 +104,29 @@ var kbdUtil = (function() { } 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)); + evt.ctrlKey !== state[KeyTable.XK_Control_L] && keysym !== KeyTable.XK_Control_L) { + state[KeyTable.XK_Control_L] = evt.ctrlKey; + result.push(syncKey(KeyTable.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)); + evt.altKey !== state[KeyTable.XK_Alt_L] && keysym !== KeyTable.XK_Alt_L) { + state[KeyTable.XK_Alt_L] = evt.altKey; + result.push(syncKey(KeyTable.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)); + evt.altGraphKey !== state[KeyTable.XK_ISO_Level3_Shift] && keysym !== KeyTable.XK_ISO_Level3_Shift) { + state[KeyTable.XK_ISO_Level3_Shift] = evt.altGraphKey; + result.push(syncKey(KeyTable.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)); + evt.shiftKey !== state[KeyTable.XK_Shift_L] && keysym !== KeyTable.XK_Shift_L) { + state[KeyTable.XK_Shift_L] = evt.shiftKey; + result.push(syncKey(KeyTable.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)); + evt.metaKey !== state[KeyTable.XK_Meta_L] && keysym !== KeyTable.XK_Meta_L) { + state[KeyTable.XK_Meta_L] = evt.metaKey; + result.push(syncKey(KeyTable.XK_Meta_L)); } return result; } @@ -177,10 +184,7 @@ var kbdUtil = (function() { codepoint = evt.keyCode; } if (codepoint) { - var res = keysyms.fromUnicode(substituteCodepoint(codepoint)); - if (res) { - return res; - } + return keysyms.fromUnicode(substituteCodepoint(codepoint)); } // we could check evt.key here. // Legal values are defined in http://www.w3.org/TR/DOM-Level-3-Events/#key-values-list, @@ -210,21 +214,21 @@ var kbdUtil = (function() { return shiftPressed ? keycode : keycode + 32; // A-Z } if (keycode >= 0x60 && keycode <= 0x69) { - return XK_KP_0 + (keycode - 0x60); // numpad 0-9 + return KeyTable.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; + case 0x20: return KeyTable.XK_space; + case 0x6a: return KeyTable.XK_KP_Multiply; + case 0x6b: return KeyTable.XK_KP_Add; + case 0x6c: return KeyTable.XK_KP_Separator; + case 0x6d: return KeyTable.XK_KP_Subtract; + case 0x6e: return KeyTable.XK_KP_Decimal; + case 0x6f: return KeyTable.XK_KP_Divide; + case 0xbb: return KeyTable.XK_plus; + case 0xbc: return KeyTable.XK_comma; + case 0xbd: return KeyTable.XK_minus; + case 0xbe: return KeyTable.XK_period; } return nonCharacterKey({keyCode: keycode}); @@ -238,53 +242,183 @@ var kbdUtil = (function() { var keycode = evt.keyCode; if (keycode >= 0x70 && keycode <= 0x87) { - return XK_F1 + keycode - 0x70; // F1-F24 + return KeyTable.XK_F1 + keycode - 0x70; // F1-F24 } switch (keycode) { - case 8 : return XK_BackSpace; - case 13 : return XK_Return; + case 8 : return KeyTable.XK_BackSpace; + case 13 : return KeyTable.XK_Return; - case 9 : return XK_Tab; + case 9 : return KeyTable.XK_Tab; - case 27 : return XK_Escape; - case 46 : return XK_Delete; + case 27 : return KeyTable.XK_Escape; + case 46 : return KeyTable.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 36 : return KeyTable.XK_Home; + case 35 : return KeyTable.XK_End; + case 33 : return KeyTable.XK_Page_Up; + case 34 : return KeyTable.XK_Page_Down; + case 45 : return KeyTable.XK_Insert; - case 37 : return XK_Left; - case 38 : return XK_Up; - case 39 : return XK_Right; - case 40 : return XK_Down; + case 37 : return KeyTable.XK_Left; + case 38 : return KeyTable.XK_Up; + case 39 : return KeyTable.XK_Right; + case 40 : return KeyTable.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 16 : return KeyTable.XK_Shift_L; + case 17 : return KeyTable.XK_Control_L; + case 18 : return KeyTable.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 + case 224 : return KeyTable.XK_Meta_L; + case 225 : return KeyTable.XK_ISO_Level3_Shift; // AltGr + case 91 : return KeyTable.XK_Super_L; // also: Windows-key + case 92 : return KeyTable.XK_Super_R; // also: Windows-key + case 93 : return KeyTable.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 - }; + + KeyboardUtil.hasShortcutModifier = hasShortcutModifier; + KeyboardUtil.hasCharModifier = hasCharModifier; + KeyboardUtil.ModifierSync = ModifierSync; + KeyboardUtil.getKey = getKey; + KeyboardUtil.getKeysym = getKeysym; + KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode; + KeyboardUtil.nonCharacterKey = nonCharacterKey; + KeyboardUtil.substituteCodepoint = substituteCodepoint; })(); +KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) { + "use strict"; + + function sendAll(evts) { + for (var i = 0; i < evts.length; ++i) { + next(evts[i]); + } + } + + var numPadCodes = ["Numpad0", "Numpad1", "Numpad2", + "Numpad3", "Numpad4", "Numpad5", "Numpad6", + "Numpad7", "Numpad8", "Numpad9", "NumpadDecimal"]; + + var numLockOnKeySyms = { + "Numpad0": 0xffb0, "Numpad1": 0xffb1, "Numpad2": 0xffb2, + "Numpad3": 0xffb3, "Numpad4": 0xffb4, "Numpad5": 0xffb5, + "Numpad6": 0xffb6, "Numpad7": 0xffb7, "Numpad8": 0xffb8, + "Numpad9": 0xffb9, "NumpadDecimal": 0xffac + }; + + var numLockOnKeyCodes = [96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 108, 110]; + + function isNumPadMultiKey(evt) { + return (numPadCodes.indexOf(evt.code) !== -1); + } + + function getNumPadKeySym(evt) { + if (numLockOnKeyCodes.indexOf(evt.keyCode) !== -1) { + return numLockOnKeySyms[evt.code]; + } + return 0; + } + + function process(evt, type) { + var result = {type: type}; + result.code = evt.code; + result.keysym = 0; + + if (isNumPadMultiKey(evt)) { + result.keysym = getNumPadKeySym(evt); + } + + var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier(); + var isShift = evt.keyCode === 0x10 || evt.key === 'Shift'; + + var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt)); + + next(result); + return suppress; + } + return { + keydown: function(evt) { + sendAll(modifierState.keydown(evt)); + return process(evt, 'keydown'); + }, + keypress: function(evt) { + return true; + }, + keyup: function(evt) { + sendAll(modifierState.keyup(evt)); + return process(evt, 'keyup'); + }, + syncModifiers: function(evt) { + sendAll(modifierState.syncAny(evt)); + }, + releaseAll: function() { next({type: 'releaseall'}); } + }; +}; + +KeyboardUtil.TrackQEMUKeyState = function(next) { + "use strict"; + var state = []; + + return function (evt) { + var last = state.length !== 0 ? state[state.length-1] : null; + + switch (evt.type) { + case 'keydown': + + if (!last || last.code !== evt.code) { + last = {code: evt.code}; + + if (state.length > 0 && state[state.length-1].code == 'ControlLeft') { + if (evt.code !== 'AltRight') { + next({code: 'ControlLeft', type: 'keydown', keysym: 0}); + } else { + state.pop(); + } + } + state.push(last); + } + if (evt.code !== 'ControlLeft') { + 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].code === evt.code) { + idx = i; + break; + } + } + // if we couldn't find a match (it happens), assume it was the last key pressed + if (idx === null) { + if (evt.code === 'ControlLeft') { + return; + } + idx = state.length - 1; + } + + state.splice(idx, 1); + next(evt); + break; + case 'releaseall': + /* jshint shadow: true */ + for (var i = 0; i < state.length; ++i) { + next({code: state[i].code, keysym: 0, type: 'keyup'}); + } + /* jshint shadow: false */ + state = []; + } + }; +}; + // 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) @@ -292,7 +426,7 @@ var kbdUtil = (function() { // - 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) { +KeyboardUtil.KeyEventDecoder = function(modifierState, next) { "use strict"; function sendAll(evts) { for (var i = 0; i < evts.length; ++i) { @@ -301,18 +435,18 @@ function KeyEventDecoder(modifierState, next) { } function process(evt, type) { var result = {type: type}; - var keyId = kbdUtil.getKey(evt); + var keyId = KeyboardUtil.getKey(evt); if (keyId) { result.keyId = keyId; } - var keysym = kbdUtil.getKeysym(evt); + var keysym = KeyboardUtil.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)) { + if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) { result.keysym = keysym; } @@ -321,11 +455,11 @@ function KeyEventDecoder(modifierState, next) { // 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)); + var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.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); + var stall = type === 'keydown' && modifierState.activeCharModifier() && !KeyboardUtil.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(); @@ -371,7 +505,7 @@ function KeyEventDecoder(modifierState, next) { }, 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. @@ -379,7 +513,7 @@ function KeyEventDecoder(modifierState, next) { // 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) { +KeyboardUtil.VerifyCharModifier = function(next) { "use strict"; var queue = []; var timer = null; @@ -429,14 +563,14 @@ function VerifyCharModifier(next) { 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) { +KeyboardUtil.TrackKeyState = function(next) { "use strict"; var state = []; @@ -516,11 +650,11 @@ function TrackKeyState(next) { 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) { +KeyboardUtil.EscapeModifiers = function(next) { "use strict"; return function(evt) { if (evt.type !== 'keydown' || evt.escape === undefined) { @@ -540,4 +674,6 @@ function EscapeModifiers(next) { } /* jshint shadow: false */ }; -} +}; + +/* [module] export default KeyboardUtil; */ diff --git a/noVNC/core/input/xtscancodes.js b/noVNC/core/input/xtscancodes.js new file mode 100644 index 0000000..542bcf7 --- /dev/null +++ b/noVNC/core/input/xtscancodes.js @@ -0,0 +1,151 @@ +var XtScancode = { + "Escape": 0x0001, + "Digit1": 0x0002, + "Digit2": 0x0003, + "Digit3": 0x0004, + "Digit4": 0x0005, + "Digit5": 0x0006, + "Digit6": 0x0007, + "Digit7": 0x0008, + "Digit8": 0x0009, + "Digit9": 0x000A, + "Digit0": 0x000B, + "Minus": 0x000C, + "Equal": 0x000D, + "Backspace": 0x000E, + "Tab": 0x000F, + "KeyQ": 0x0010, + "KeyW": 0x0011, + "KeyE": 0x0012, + "KeyR": 0x0013, + "KeyT": 0x0014, + "KeyY": 0x0015, + "KeyU": 0x0016, + "KeyI": 0x0017, + "KeyO": 0x0018, + "KeyP": 0x0019, + "BracketLeft": 0x001A, + "BracketRight": 0x001B, + "Enter": 0x001C, + "ControlLeft": 0x001D, + "KeyA": 0x001E, + "KeyS": 0x001F, + "KeyD": 0x0020, + "KeyF": 0x0021, + "KeyG": 0x0022, + "KeyH": 0x0023, + "KeyJ": 0x0024, + "KeyK": 0x0025, + "KeyL": 0x0026, + "Semicolon": 0x0027, + "Quote": 0x0028, + "Backquote": 0x0029, + "ShiftLeft": 0x002A, + "Backslash": 0x002B, + "KeyZ": 0x002C, + "KeyX": 0x002D, + "KeyC": 0x002E, + "KeyV": 0x002F, + "KeyB": 0x0030, + "KeyN": 0x0031, + "KeyM": 0x0032, + "Comma": 0x0033, + "Period": 0x0034, + "Slash": 0x0035, + "ShiftRight": 0x0036, + "NumpadMultiply": 0x0037, + "AltLeft": 0x0038, + "Space": 0x0039, + "CapsLock": 0x003A, + "F1": 0x003B, + "F2": 0x003C, + "F3": 0x003D, + "F4": 0x003E, + "F5": 0x003F, + "F6": 0x0040, + "F7": 0x0041, + "F8": 0x0042, + "F9": 0x0043, + "F10": 0x0044, + "Pause": 0xE045, + "ScrollLock": 0x0046, + "Numpad7": 0x0047, + "Numpad8": 0x0048, + "Numpad9": 0x0049, + "NumpadSubtract": 0x004A, + "Numpad4": 0x004B, + "Numpad5": 0x004C, + "Numpad6": 0x004D, + "NumpadAdd": 0x004E, + "Numpad1": 0x004F, + "Numpad2": 0x0050, + "Numpad3": 0x0051, + "Numpad0": 0x0052, + "NumpadDecimal": 0x0053, + "IntlBackslash": 0x0056, + "F11": 0x0057, + "F12": 0x0058, + "IntlYen": 0x007D, + "MediaTrackPrevious": 0xE010, + "MediaTrackNext": 0xE019, + "NumpadEnter": 0xE01C, + "ControlRight": 0xE01D, + "VolumeMute": 0xE020, + "MediaPlayPause": 0xE022, + "MediaStop": 0xE024, + "VolumeDown": 0xE02E, + "VolumeUp": 0xE030, + "BrowserHome": 0xE032, + "NumpadDivide": 0xE035, + "PrintScreen": 0xE037, + "AltRight": 0xE038, + "NumLock": 0x0045, + "Home": 0xE047, + "ArrowUp": 0xE048, + "PageUp": 0xE049, + "ArrowLeft": 0xE04B, + "ArrowRight": 0xE04D, + "End": 0xE04F, + "ArrowDown": 0xE050, + "PageDown": 0xE051, + "Insert": 0xE052, + "Delete": 0xE053, + "MetaLeft": 0xE05B, + "MetaRight": 0xE05C, + "OSLeft": 0xE05B, // OSLeft and OSRight are kept for compatability since + "OSRight": 0xE05C, // Firefox haven't updated to MetaLeft and MetaRight yet + "ContextMenu": 0xE05D, + "BrowserSearch": 0xE065, + "BrowserFavorites": 0xE066, + "BrowserRefresh": 0xE067, + "BrowserStop": 0xE068, + "BrowserForward": 0xE069, + "BrowserBack": 0xE06A, + "NumpadComma": 0x007E, + "NumpadEqual": 0x0059, + "F13": 0x0064, + "F14": 0x0065, + "F15": 0x0066, + "F16": 0x0067, + "F17": 0x0068, + "F18": 0x0069, + "F19": 0x006A, + "F20": 0x006B, + "F21": 0x006C, + "F22": 0x006D, + "F23": 0x006E, + "F24": 0x0076, + "KanaMode": 0x0070, + "Lang2": 0x0071, + "Lang1": 0x0072, + "IntlRo": 0x0073, + "Convert": 0x0079, + "NonConvert": 0x007B, + "LaunchApp2": 0xE021, + "Power": 0xE05E, + "LaunchApp1": 0xE06B, + "LaunchMail": 0xE06C, + "MediaSelect": 0xE06D, +}; + +/* [module] export default XtScancode */ diff --git a/noVNC/include/rfb.js b/noVNC/core/rfb.js similarity index 51% rename from noVNC/include/rfb.js rename to noVNC/core/rfb.js index e030b09..baf5f3c 100644 --- a/noVNC/include/rfb.js +++ b/noVNC/core/rfb.js @@ -1,7 +1,7 @@ /* * noVNC: HTML5 VNC client * Copyright (C) 2012 Joel Martin - * Copyright (C) 2013 Samuel Mannehed for Cendio AB + * Copyright (C) 2016 Samuel Mannehed for Cendio AB * Licensed under MPL 2.0 (see LICENSE.txt) * * See README.md for usage and integration instructions. @@ -10,252 +10,261 @@ * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca) */ +/* [module] + * import Util from "./util"; + * import Display from "./display"; + * import { Keyboard, Mouse } from "./input/devices" + * import Websock from "./websock" + * import Base64 from "./base64"; + * import DES from "./des"; + * import KeyTable from "./input/keysym"; + * import XtScancode from "./input/xtscancodes"; + * import Inflator from "./inflator.mod"; + */ /*jslint white: false, browser: true */ -/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */ +/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, KeyTable, Inflator, XtScancode */ -var RFB; - -(function () { +/* [module] export default */ function RFB(defaults) { "use strict"; - RFB = function (defaults) { - if (!defaults) { - defaults = {}; - } + if (!defaults) { + defaults = {}; + } - this._rfb_host = ''; - this._rfb_port = 5900; - this._rfb_password = ''; - this._rfb_path = ''; + this._rfb_host = ''; + this._rfb_port = 5900; + this._rfb_password = ''; + this._rfb_path = ''; - this._rfb_state = 'disconnected'; - this._rfb_version = 0; - this._rfb_max_version = 3.8; - this._rfb_auth_scheme = ''; + this._rfb_connection_state = ''; + this._rfb_init_state = ''; + this._rfb_version = 0; + this._rfb_max_version = 3.8; + this._rfb_auth_scheme = ''; + this._rfb_disconnect_reason = ""; - this._rfb_tightvnc = false; - this._rfb_xvp_ver = 0; + this._rfb_tightvnc = false; + this._rfb_xvp_ver = 0; - this._encHandlers = {}; - this._encNames = {}; - this._encStats = {}; + // In preference order + this._encodings = [ + ['COPYRECT', 0x01 ], + ['TIGHT', 0x07 ], + ['TIGHT_PNG', -260 ], + ['HEXTILE', 0x05 ], + ['RRE', 0x02 ], + ['RAW', 0x00 ], - this._sock = null; // Websock object - this._display = null; // Display object - this._keyboard = null; // Keyboard input handler object - this._mouse = null; // Mouse input handler object - this._sendTimer = null; // Send Queue check timer - this._disconnTimer = null; // disconnection timer - this._msgTimer = null; // queued handle_msg timer + // Psuedo-encoding settings - // Frame buffer update state - this._FBU = { - rects: 0, - subrects: 0, // RRE - lines: 0, // RAW - tiles: 0, // HEXTILE - bytes: 0, - x: 0, - y: 0, - width: 0, - height: 0, - encoding: 0, - subencoding: -1, - background: null, - zlib: [] // TIGHT zlib streams - }; + //['JPEG_quality_lo', -32 ], + ['JPEG_quality_med', -26 ], + //['JPEG_quality_hi', -23 ], + //['compress_lo', -255 ], + ['compress_hi', -247 ], - this._fb_Bpp = 4; - this._fb_depth = 3; - this._fb_width = 0; - this._fb_height = 0; - this._fb_name = ""; + ['DesktopSize', -223 ], + ['last_rect', -224 ], + ['Cursor', -239 ], + ['QEMUExtendedKeyEvent', -258 ], + ['ExtendedDesktopSize', -308 ], + ['xvp', -309 ], + ['Fence', -312 ], + ['ContinuousUpdates', -313 ] + ]; - this._rre_chunk_sz = 100; + this._encHandlers = {}; + this._encNames = {}; + this._encStats = {}; - this._timing = { - last_fbu: 0, - fbu_total: 0, - fbu_total_cnt: 0, - full_fbu_total: 0, - full_fbu_cnt: 0, + this._sock = null; // Websock object + this._display = null; // Display object + this._flushing = false; // Display flushing state + this._keyboard = null; // Keyboard input handler object + this._mouse = null; // Mouse input handler object + this._disconnTimer = null; // disconnection timer - fbu_rt_start: 0, - fbu_rt_total: 0, - fbu_rt_cnt: 0, - pixels: 0 - }; + this._supportsFence = false; - this._supportsSetDesktopSize = false; - this._screen_id = 0; - this._screen_flags = 0; + this._supportsContinuousUpdates = false; + this._enabledContinuousUpdates = false; - // Mouse state - this._mouse_buttonMask = 0; - this._mouse_arr = []; - this._viewportDragging = false; - this._viewportDragPos = {}; - - // set the default value on user-facing properties - Util.set_defaults(this, defaults, { - 'target': 'null', // VNC display rendering Canvas object - 'focusContainer': document, // DOM element that captures keyboard input - 'encrypt': false, // Use TLS/SSL/wss encryption - 'true_color': true, // Request true color pixel data - 'local_cursor': false, // Request locally rendered cursor - 'shared': true, // Request shared mode - 'view_only': false, // Disable client mouse/keyboard - 'xvp_password_sep': '@', // Separator for XVP password fields - 'disconnectTimeout': 3, // Time (s) to wait for disconnection - 'wsProtocols': ['binary', 'base64'], // Protocols to use in the WebSocket connection - 'repeaterID': '', // [UltraVNC] RepeaterID to connect to - 'viewportDrag': false, // Move the viewport on mouse drags - // Callback functions - 'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate, statusMsg): state update/change - 'onPasswordRequired': function () { }, // onPasswordRequired(rfb): VNC password is required - 'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received - 'onBell': function () { }, // onBell(rfb): RFB Bell message received - 'onFBUReceive': function () { }, // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed - 'onFBUComplete': function () { }, // onFBUComplete(rfb, fbu): RFB FBU received and processed - 'onFBResize': function () { }, // onFBResize(rfb, width, height): frame buffer resized - 'onDesktopName': function () { }, // onDesktopName(rfb, name): desktop name received - 'onXvpInit': function () { }, // onXvpInit(version): XVP extensions active for this connection - }); - - try{ - // Use my custom 'jpeg_quality'. - this._encodings = [ - // ['COPYRECT', 0x01 ], - ['TIGHT', 0x07 ], - ['TIGHT_PNG', -260 ], - ['HEXTILE', 0x05 ], - ['RRE', 0x02 ], - ['RAW', 0x00 ], - ['DesktopSize', -223 ], - ['Cursor', -239 ], - - // Psuedo-encoding settings - ['JPEG_quality_lo', -32 + jpeg_quality ], - // ['JPEG_quality_med', -28 ], - // ['JPEG_quality_hi', -23 ], - // ['compress_lo', -255 ], - ['compress_hi', -247 ], - ['last_rect', -224 ], - ['xvp', -309 ], - ['ExtendedDesktopSize', -308 ] - ]; - } - catch(e){ - // If my custom 'jpeg_quality' is not defined, just use default setting. - this._encodings = [ - ['COPYRECT', 0x01 ], - ['TIGHT', 0x07 ], - ['TIGHT_PNG', -260 ], - ['HEXTILE', 0x05 ], - ['RRE', 0x02 ], - ['RAW', 0x00 ], - ['DesktopSize', -223 ], - ['Cursor', -239 ], - - // Psuedo-encoding settings - //['JPEG_quality_lo', -32 ], - ['JPEG_quality_med', -26 ], - //['JPEG_quality_hi', -23 ], - //['compress_lo', -255 ], - ['compress_hi', -247 ], - ['last_rect', -224 ], - ['xvp', -309 ], - ['ExtendedDesktopSize', -308 ] - ]; - } - - // main setup - Util.Debug(">> RFB.constructor"); - - // populate encHandlers with bound versions - Object.keys(RFB.encodingHandlers).forEach(function (encName) { - this._encHandlers[encName] = RFB.encodingHandlers[encName].bind(this); - }.bind(this)); - - // Create lookup tables based on encoding number - for (var i = 0; i < this._encodings.length; i++) { - this._encHandlers[this._encodings[i][1]] = this._encHandlers[this._encodings[i][0]]; - this._encNames[this._encodings[i][1]] = this._encodings[i][0]; - this._encStats[this._encodings[i][1]] = [0, 0]; - } - - // NB: nothing that needs explicit teardown should be done - // before this point, since this can throw an exception - try { - this._display = new Display({target: this._target}); - } catch (exc) { - Util.Error("Display exception: " + exc); - throw exc; - } - - this._keyboard = new Keyboard({target: this._focusContainer, - onKeyPress: this._handleKeyPress.bind(this)}); - - this._mouse = new Mouse({target: this._target, - onMouseButton: this._handleMouseButton.bind(this), - onMouseMove: this._handleMouseMove.bind(this), - notify: this._keyboard.sync.bind(this._keyboard)}); - - this._sock = new Websock(); - this._sock.on('message', this._handle_message.bind(this)); - this._sock.on('open', function () { - if (this._rfb_state === 'connect') { - this._updateState('ProtocolVersion', "Starting VNC handshake"); - } else { - this._fail("Got unexpected WebSocket connection"); - } - }.bind(this)); - this._sock.on('close', function (e) { - Util.Warn("WebSocket on-close event"); - var msg = ""; - if (e.code) { - msg = " (code: " + e.code; - if (e.reason) { - msg += ", reason: " + e.reason; - } - msg += ")"; - } - if (this._rfb_state === 'disconnect') { - this._updateState('disconnected', 'VNC disconnected' + msg); - } else if (this._rfb_state === 'ProtocolVersion') { - this._fail('Failed to connect to server' + msg); - } else if (this._rfb_state in {'failed': 1, 'disconnected': 1}) { - Util.Error("Received onclose while disconnected" + msg); - } else { - this._fail("Server disconnected" + msg); - } - this._sock.off('close'); - }.bind(this)); - this._sock.on('error', function (e) { - Util.Warn("WebSocket on-error event"); - }); - - this._init_vars(); - - var rmode = this._display.get_render_mode(); - if (Websock_native) { - Util.Info("Using native WebSockets"); - this._updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode); - } else { - Util.Warn("Using web-socket-js bridge. Flash version: " + Util.Flash.version); - if (!Util.Flash || Util.Flash.version < 9) { - this._cleanupSocket('fatal'); - throw new Exception("WebSockets or Adobe Flash is required"); - } else if (document.location.href.substr(0, 7) === 'file://') { - this._cleanupSocket('fatal'); - throw new Exception("'file://' URL is incompatible with Adobe Flash"); - } else { - this._updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode); - } - } - - Util.Debug("<< RFB.constructor"); + // Frame buffer update state + this._FBU = { + rects: 0, + subrects: 0, // RRE + lines: 0, // RAW + tiles: 0, // HEXTILE + bytes: 0, + x: 0, + y: 0, + width: 0, + height: 0, + encoding: 0, + subencoding: -1, + background: null, + zlib: [] // TIGHT zlib streams }; + this._fb_Bpp = 4; + this._fb_depth = 3; + this._fb_width = 0; + this._fb_height = 0; + this._fb_name = ""; + + this._destBuff = null; + this._paletteBuff = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel) + + this._rre_chunk_sz = 100; + + this._timing = { + last_fbu: 0, + fbu_total: 0, + fbu_total_cnt: 0, + full_fbu_total: 0, + full_fbu_cnt: 0, + + fbu_rt_start: 0, + fbu_rt_total: 0, + fbu_rt_cnt: 0, + pixels: 0 + }; + + this._supportsSetDesktopSize = false; + this._screen_id = 0; + this._screen_flags = 0; + + // Mouse state + this._mouse_buttonMask = 0; + this._mouse_arr = []; + this._viewportDragging = false; + this._viewportDragPos = {}; + this._viewportHasMoved = false; + + // QEMU Extended Key Event support - default to false + this._qemuExtKeyEventSupported = false; + + // set the default value on user-facing properties + Util.set_defaults(this, defaults, { + 'target': 'null', // VNC display rendering Canvas object + 'focusContainer': document, // DOM element that captures keyboard input + 'encrypt': false, // Use TLS/SSL/wss encryption + 'true_color': true, // Request true color pixel data + 'local_cursor': false, // Request locally rendered cursor + 'shared': true, // Request shared mode + 'view_only': false, // Disable client mouse/keyboard + 'xvp_password_sep': '@', // Separator for XVP password fields + 'disconnectTimeout': 3, // Time (s) to wait for disconnection + 'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection + 'repeaterID': '', // [UltraVNC] RepeaterID to connect to + 'viewportDrag': false, // Move the viewport on mouse drags + + // Callback functions + 'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate): connection state change + 'onNotification': function () { }, // onNotification(rfb, msg, level, options): notification for UI + 'onDisconnected': function () { }, // onDisconnected(rfb, reason): disconnection finished + 'onPasswordRequired': function () { }, // onPasswordRequired(rfb, msg): VNC password is required + 'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received + 'onBell': function () { }, // onBell(rfb): RFB Bell message received + 'onFBUReceive': function () { }, // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed + 'onFBUComplete': function () { }, // onFBUComplete(rfb, fbu): RFB FBU received and processed + 'onFBResize': function () { }, // onFBResize(rfb, width, height): frame buffer resized + 'onDesktopName': function () { }, // onDesktopName(rfb, name): desktop name received + 'onXvpInit': function () { } // onXvpInit(version): XVP extensions active for this connection + }); + + // main setup + Util.Debug(">> RFB.constructor"); + + // populate encHandlers with bound versions + Object.keys(RFB.encodingHandlers).forEach(function (encName) { + this._encHandlers[encName] = RFB.encodingHandlers[encName].bind(this); + }.bind(this)); + + // Create lookup tables based on encoding number + for (var i = 0; i < this._encodings.length; i++) { + this._encHandlers[this._encodings[i][1]] = this._encHandlers[this._encodings[i][0]]; + this._encNames[this._encodings[i][1]] = this._encodings[i][0]; + this._encStats[this._encodings[i][1]] = [0, 0]; + } + + // NB: nothing that needs explicit teardown should be done + // before this point, since this can throw an exception + try { + this._display = new Display({target: this._target, + onFlush: this._onFlush.bind(this)}); + } catch (exc) { + Util.Error("Display exception: " + exc); + throw exc; + } + + this._keyboard = new Keyboard({target: this._focusContainer, + onKeyPress: this._handleKeyPress.bind(this)}); + + this._mouse = new Mouse({target: this._target, + onMouseButton: this._handleMouseButton.bind(this), + onMouseMove: this._handleMouseMove.bind(this), + notify: this._keyboard.sync.bind(this._keyboard)}); + + this._sock = new Websock(); + this._sock.on('message', this._handle_message.bind(this)); + this._sock.on('open', function () { + if ((this._rfb_connection_state === 'connecting') && + (this._rfb_init_state === '')) { + this._rfb_init_state = 'ProtocolVersion'; + Util.Debug("Starting VNC handshake"); + } else { + this._fail("Unexpected server connection"); + } + }.bind(this)); + this._sock.on('close', function (e) { + Util.Warn("WebSocket on-close event"); + var msg = ""; + if (e.code) { + msg = " (code: " + e.code; + if (e.reason) { + msg += ", reason: " + e.reason; + } + msg += ")"; + } + switch (this._rfb_connection_state) { + case 'disconnecting': + this._updateConnectionState('disconnected'); + break; + case 'connecting': + this._fail('Failed to connect to server', msg); + break; + case 'connected': + // Handle disconnects that were initiated server-side + this._updateConnectionState('disconnecting'); + this._updateConnectionState('disconnected'); + break; + case 'disconnected': + this._fail("Unexpected server disconnect", + "Already disconnected: " + msg); + break; + default: + this._fail("Unexpected server disconnect", + "Not in any state yet: " + msg); + break; + } + this._sock.off('close'); + }.bind(this)); + this._sock.on('error', function (e) { + Util.Warn("WebSocket on-error event"); + }); + + this._init_vars(); + this._cleanup(); + + var rmode = this._display.get_render_mode(); + Util.Info("Using native WebSockets, render mode: " + rmode); + + Util.Debug("<< RFB.constructor"); +}; + +(function() { + var _ = Util.Localisation.get; + RFB.prototype = { // Public methods connect: function (host, port, password, path) { @@ -265,14 +274,17 @@ var RFB; this._rfb_path = (path !== undefined) ? path : ""; if (!this._rfb_host || !this._rfb_port) { - return this._fail("Must set host and port"); + return this._fail( + _("Must set host and port")); } - this._updateState('connect'); + this._rfb_init_state = ''; + this._updateConnectionState('connecting'); + return true; }, disconnect: function () { - this._updateState('disconnect', 'Disconnecting'); + this._updateConnectionState('disconnecting'); this._sock.off('error'); this._sock.off('message'); this._sock.off('open'); @@ -280,22 +292,20 @@ var RFB; sendPassword: function (passwd) { this._rfb_password = passwd; - this._rfb_state = 'Authentication'; - setTimeout(this._init_msg.bind(this), 1); + setTimeout(this._init_msg.bind(this), 0); }, sendCtrlAltDel: function () { - if (this._rfb_state !== 'normal' || this._view_only) { return false; } + if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; } Util.Info("Sending Ctrl-Alt-Del"); - var arr = []; - arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 1)); - arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 1)); - arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 1)); - arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 0)); - arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 0)); - arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 0)); - this._sock.send(arr); + RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 1); + RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 1); + RFB.messages.keyEvent(this._sock, KeyTable.XK_Delete, 1); + RFB.messages.keyEvent(this._sock, KeyTable.XK_Delete, 0); + RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 0); + RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 0); + return true; }, xvpOp: function (ver, op) { @@ -319,47 +329,41 @@ var RFB; // Send a key press. If 'down' is not specified then send a down key // followed by an up key. - sendKey: function (code, down) { - if (this._rfb_state !== "normal" || this._view_only) { return false; } - var arr = []; + sendKey: function (keysym, down) { + if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; } if (typeof down !== 'undefined') { - Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code); - arr = arr.concat(RFB.messages.keyEvent(code, down ? 1 : 0)); + Util.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym); + RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0); } else { - Util.Info("Sending key code (down + up): " + code); - arr = arr.concat(RFB.messages.keyEvent(code, 1)); - arr = arr.concat(RFB.messages.keyEvent(code, 0)); + Util.Info("Sending keysym (down + up): " + keysym); + RFB.messages.keyEvent(this._sock, keysym, 1); + RFB.messages.keyEvent(this._sock, keysym, 0); } - this._sock.send(arr); + return true; }, clipboardPasteFrom: function (text) { - if (this._rfb_state !== 'normal') { return; } - this._sock.send(RFB.messages.clientCutText(text)); + if (this._rfb_connection_state !== 'connected' || this._view_only) { + return; + } + RFB.messages.clientCutText(this._sock, text); }, - setDesktopSize: function (width, height) { - if (this._rfb_state !== "normal") { return; } + // Requests a change of remote desktop size. This message is an extension + // and may only be sent if we have received an ExtendedDesktopSize message + requestDesktopSize: function (width, height) { + if (this._rfb_connection_state !== 'connected' || + this._view_only) { + return false; + } if (this._supportsSetDesktopSize) { - - var arr = [251]; // msg-type - arr.push8(0); // padding - arr.push16(width); // width - arr.push16(height); // height - - arr.push8(1); // number-of-screens - arr.push8(0); // padding - - // screen array - arr.push32(this._screen_id); // id - arr.push16(0); // x-position - arr.push16(0); // y-position - arr.push16(width); // width - arr.push16(height); // height - arr.push32(this._screen_flags); // flags - - this._sock.send(arr); + RFB.messages.setDesktopSize(this._sock, width, height, + this._screen_id, this._screen_flags); + this._sock.flush(); + return true; + } else { + return false; } }, @@ -368,6 +372,7 @@ var RFB; _connect: function () { Util.Debug(">> RFB.connect"); + this._init_vars(); var uri; if (typeof UsingSocketIO !== 'undefined') { @@ -379,15 +384,30 @@ var RFB; uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path; Util.Info("connecting to " + uri); - this._sock.open(uri, this._wsProtocols); + try { + // WebSocket.onopen transitions to the RFB init states + this._sock.open(uri, this._wsProtocols); + } catch (e) { + if (e.name === 'SyntaxError') { + this._fail("Invalid host or port value given", e); + } else { + this._fail("Error while connecting", e); + } + } Util.Debug("<< RFB.connect"); }, + _disconnect: function () { + Util.Debug(">> RFB.disconnect"); + this._cleanup(); + this._sock.close(); + this._print_stats(); + Util.Debug("<< RFB.disconnect"); + }, + _init_vars: function () { // reset state - this._sock.init(); - this._FBU.rects = 0; this._FBU.subrects = 0; // RRE and HEXTILE this._FBU.lines = 0; // RAW @@ -404,8 +424,7 @@ var RFB; } for (i = 0; i < 4; i++) { - this._FBU.zlibs[i] = new TINF(); - this._FBU.zlibs[i].init(); + this._FBU.zlibs[i] = new Inflator.Inflate(); } }, @@ -426,173 +445,201 @@ var RFB; } }, - _cleanupSocket: function (state) { - if (this._sendTimer) { - clearInterval(this._sendTimer); - this._sendTimer = null; + _cleanup: function () { + if (!this._view_only) { this._keyboard.ungrab(); } + if (!this._view_only) { this._mouse.ungrab(); } + this._display.defaultCursor(); + if (Util.get_logging() !== 'debug') { + // Show noVNC logo on load and when disconnected, unless in + // debug mode + this._display.clear(); } - - if (this._msgTimer) { - clearInterval(this._msgTimer); - this._msgTimer = null; - } - - if (this._display && this._display.get_context()) { - this._keyboard.ungrab(); - this._mouse.ungrab(); - if (state !== 'connect' && state !== 'loaded') { - this._display.defaultCursor(); - } - if (Util.get_logging() !== 'debug' || state === 'loaded') { - // Show noVNC logo on load and when disconnected, unless in - // debug mode - this._display.clear(); - } - } - - this._sock.close(); }, /* - * Page states: - * loaded - page load, equivalent to disconnected - * disconnected - idle state - * connect - starting to connect (to ProtocolVersion) - * normal - connected - * disconnect - starting to disconnect - * failed - abnormal disconnect - * fatal - failed to load page, or fatal error - * - * RFB protocol initialization states: - * ProtocolVersion - * Security - * Authentication - * password - waiting for password, not part of RFB - * SecurityResult - * ClientInitialization - not triggered by server message - * ServerInitialization (to normal) + * Connection states: + * connecting + * connected + * disconnecting + * disconnected - permanent state */ - _updateState: function (state, statusMsg) { - var oldstate = this._rfb_state; + _updateConnectionState: function (state) { + var oldstate = this._rfb_connection_state; if (state === oldstate) { - // Already here, ignore Util.Debug("Already in state '" + state + "', ignoring"); + return; } - /* - * These are disconnected states. A previous connect may - * asynchronously cause a connection so make sure we are closed. - */ - if (state in {'disconnected': 1, 'loaded': 1, 'connect': 1, - 'disconnect': 1, 'failed': 1, 'fatal': 1}) { - this._cleanupSocket(state); - } - - if (oldstate === 'fatal') { - Util.Error('Fatal error, cannot continue'); - } - - var cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : ""; - var fullmsg = "New state '" + state + "', was '" + oldstate + "'." + cmsg; - if (state === 'failed' || state === 'fatal') { - Util.Error(cmsg); - } else { - Util.Warn(cmsg); - } - - if (oldstate === 'failed' && state === 'disconnected') { - // do disconnect action, but stay in failed state - this._rfb_state = 'failed'; - } else { - this._rfb_state = state; - } - - if (this._disconnTimer && this._rfb_state !== 'disconnect') { - Util.Debug("Clearing disconnect timer"); - clearTimeout(this._disconnTimer); - this._disconnTimer = null; - this._sock.off('close'); // make sure we don't get a double event + // The 'disconnected' state is permanent for each RFB object + if (oldstate === 'disconnected') { + Util.Error("Tried changing state of a disconnected RFB object"); + return; } + // Ensure proper transitions before doing anything switch (state) { - case 'normal': - if (oldstate === 'disconnected' || oldstate === 'failed') { - Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'"); + case 'connected': + if (oldstate !== 'connecting') { + Util.Error("Bad transition to connected state, " + + "previous connection state: " + oldstate); + return; } break; - case 'connect': - this._init_vars(); - this._connect(); - // WebSocket.onopen transitions to 'ProtocolVersion' - break; - - case 'disconnect': - this._disconnTimer = setTimeout(function () { - this._fail("Disconnect timeout"); - }.bind(this), this._disconnectTimeout * 1000); - - this._print_stats(); - - // WebSocket.onclose transitions to 'disconnected' - break; - - case 'failed': - if (oldstate === 'disconnected') { - Util.Error("Invalid transition from 'disconnected' to 'failed'"); - } else if (oldstate === 'normal') { - Util.Error("Error while connected."); - } else if (oldstate === 'init') { - Util.Error("Error while initializing."); + case 'disconnected': + if (oldstate !== 'disconnecting') { + Util.Error("Bad transition to disconnected state, " + + "previous connection state: " + oldstate); + return; } + break; - // Make sure we transition to disconnected - setTimeout(function () { - this._updateState('disconnected'); - }.bind(this), 50); + case 'connecting': + if (oldstate !== '') { + Util.Error("Bad transition to connecting state, " + + "previous connection state: " + oldstate); + return; + } + break; + case 'disconnecting': + if (oldstate !== 'connected' && oldstate !== 'connecting') { + Util.Error("Bad transition to disconnecting state, " + + "previous connection state: " + oldstate); + return; + } break; default: - // No state change action to take + Util.Error("Unknown connection state: " + state); + return; } - if (oldstate === 'failed' && state === 'disconnected') { - this._onUpdateState(this, state, oldstate); - } else { - this._onUpdateState(this, state, oldstate, statusMsg); + // State change actions + + this._rfb_connection_state = state; + this._onUpdateState(this, state, oldstate); + + var smsg = "New state '" + state + "', was '" + oldstate + "'."; + Util.Debug(smsg); + + if (this._disconnTimer && state !== 'disconnecting') { + Util.Debug("Clearing disconnect timer"); + clearTimeout(this._disconnTimer); + this._disconnTimer = null; + + // make sure we don't get a double event + this._sock.off('close'); + } + + switch (state) { + case 'disconnected': + // Call onDisconnected callback after onUpdateState since + // we don't know if the UI only displays the latest message + if (this._rfb_disconnect_reason !== "") { + this._onDisconnected(this, this._rfb_disconnect_reason); + } else { + // No reason means clean disconnect + this._onDisconnected(this); + } + break; + + case 'connecting': + this._connect(); + break; + + case 'disconnecting': + this._disconnect(); + + this._disconnTimer = setTimeout(function () { + this._rfb_disconnect_reason = _("Disconnect timeout"); + this._updateConnectionState('disconnected'); + }.bind(this), this._disconnectTimeout * 1000); + break; } }, - _fail: function (msg) { - this._updateState('failed', msg); + /* Print errors and disconnect + * + * The optional parameter 'details' is used for information that + * should be logged but not sent to the user interface. + */ + _fail: function (msg, details) { + var fullmsg = msg; + if (typeof details !== 'undefined') { + fullmsg = msg + " (" + details + ")"; + } + switch (this._rfb_connection_state) { + case 'disconnecting': + Util.Error("Failed when disconnecting: " + fullmsg); + break; + case 'connected': + Util.Error("Failed while connected: " + fullmsg); + break; + case 'connecting': + Util.Error("Failed when connecting: " + fullmsg); + break; + default: + Util.Error("RFB failure: " + fullmsg); + break; + } + this._rfb_disconnect_reason = msg; //This is sent to the UI + + // Transition to disconnected without waiting for socket to close + this._updateConnectionState('disconnecting'); + this._updateConnectionState('disconnected'); + return false; }, + /* + * Send a notification to the UI. Valid levels are: + * 'normal'|'warn'|'error' + * + * NOTE: Options could be added in the future. + * NOTE: If this function is called multiple times, remember that the + * interface could be only showing the latest notification. + */ + _notification: function(msg, level, options) { + switch (level) { + case 'normal': + case 'warn': + case 'error': + Util.Debug("Notification[" + level + "]:" + msg); + break; + default: + Util.Error("Invalid notification level: " + level); + return; + } + + if (options) { + this._onNotification(this, msg, level, options); + } else { + this._onNotification(this, msg, level); + } + }, + _handle_message: function () { if (this._sock.rQlen() === 0) { Util.Warn("handle_message called on an empty receive queue"); return; } - switch (this._rfb_state) { + switch (this._rfb_connection_state) { case 'disconnected': - case 'failed': Util.Error("Got data while disconnected"); break; - case 'normal': - if (this._normal_msg() && this._sock.rQlen() > 0) { - // true means we can continue processing - // Give other events a chance to run - if (this._msgTimer === null) { - Util.Debug("More data to process, creating timer"); - this._msgTimer = setTimeout(function () { - this._msgTimer = null; - this._handle_message(); - }.bind(this), 10); - } else { - Util.Debug("More data to process, existing timer"); + case 'connected': + while (true) { + if (this._flushing) { + break; + } + if (!this._normal_msg()) { + break; + } + if (this._sock.rQlen() === 0) { + break; } } break; @@ -602,16 +649,22 @@ var RFB; } }, - _checkEvents: function () { - if (this._rfb_state === 'normal' && !this._viewportDragging && this._mouse_arr.length > 0) { - this._sock.send(this._mouse_arr); - this._mouse_arr = []; - } - }, - - _handleKeyPress: function (keysym, down) { + _handleKeyPress: function (keyevent) { if (this._view_only) { return; } // View only, skip keyboard, events - this._sock.send(RFB.messages.keyEvent(keysym, down)); + + var down = (keyevent.type == 'keydown'); + if (this._qemuExtKeyEventSupported) { + var scancode = XtScancode[keyevent.code]; + if (scancode) { + var keysym = keyevent.keysym; + RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode); + } else { + Util.Error('Unable to find a xt scancode for code = ' + keyevent.code); + } + } else { + keysym = keyevent.keysym.keysym; + RFB.messages.keyEvent(this._sock, keysym, down); + } }, _handleMouseButton: function (x, y, down, bmask) { @@ -630,24 +683,38 @@ var RFB; return; } else { this._viewportDragging = false; + + // If the viewport didn't actually move, then treat as a mouse click event + // Send the button down event here, as the button up event is sent at the end of this function + if (!this._viewportHasMoved && !this._view_only) { + RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), bmask); + } + this._viewportHasMoved = false; } } if (this._view_only) { return; } // View only, skip mouse events - this._mouse_arr = this._mouse_arr.concat( - RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask)); - this._sock.send(this._mouse_arr); - this._mouse_arr = []; + if (this._rfb_connection_state !== 'connected') { return; } + RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask); }, _handleMouseMove: function (x, y) { if (this._viewportDragging) { var deltaX = this._viewportDragPos.x - x; var deltaY = this._viewportDragPos.y - y; - this._viewportDragPos = {'x': x, 'y': y}; - this._display.viewportChangePos(deltaX, deltaY); + // The goal is to trigger on a certain physical width, the + // devicePixelRatio brings us a bit closer but is not optimal. + var dragThreshold = 10 * (window.devicePixelRatio || 1); + + if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold || + Math.abs(deltaY) > dragThreshold)) { + this._viewportHasMoved = true; + + this._viewportDragPos = {'x': x, 'y': y}; + this._display.viewportChangePos(deltaX, deltaY); + } // Skip sending mouse events return; @@ -655,17 +722,16 @@ var RFB; if (this._view_only) { return; } // View only, skip mouse events - this._mouse_arr = this._mouse_arr.concat( - RFB.messages.pointerEvent(this._display.absX(x), this._display.absY(y), this._mouse_buttonMask)); - - this._checkEvents(); + if (this._rfb_connection_state !== 'connected') { return; } + RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask); }, // Message Handlers _negotiate_protocol_version: function () { if (this._sock.rQlen() < 12) { - return this._fail("Incomplete protocol version"); + return this._fail("Error while negotiating with server", + "Incomplete protocol version"); } var sversion = this._sock.rQshiftStr(12).substr(4, 7); @@ -686,10 +752,12 @@ var RFB; case "003.008": case "004.000": // Intel AMT KVM case "004.001": // RealVNC 4.6 + case "005.000": // RealVNC 5.3 this._rfb_version = 3.8; break; default: - return this._fail("Invalid server version " + sversion); + return this._fail("Unsupported server", + "Invalid server version: " + sversion); } if (is_repeater) { @@ -705,14 +773,12 @@ var RFB; this._rfb_version = this._rfb_max_version; } - // Send updates either at a rate of 1 update per 50ms, or - // whatever slower rate the network can handle - this._sendTimer = setInterval(this._sock.flush.bind(this._sock), 50); - var cversion = "00" + parseInt(this._rfb_version, 10) + ".00" + ((this._rfb_version * 10) % 10); this._sock.send_string("RFB " + cversion + "\n"); - this._updateState('Security', 'Sent ProtocolVersion: ' + cversion); + Util.Debug('Sent ProtocolVersion: ' + cversion); + + this._rfb_init_state = 'Security'; }, _negotiate_security: function () { @@ -724,20 +790,37 @@ var RFB; if (num_types === 0) { var strlen = this._sock.rQshift32(); var reason = this._sock.rQshiftStr(strlen); - return this._fail("Security failure: " + reason); + return this._fail("Error while negotiating with server", + "Security failure: " + reason); } - this._rfb_auth_scheme = 0; var types = this._sock.rQshiftBytes(num_types); Util.Debug("Server security types: " + types); - for (var i = 0; i < types.length; i++) { - if (types[i] > this._rfb_auth_scheme && (types[i] <= 16 || types[i] == 22)) { - this._rfb_auth_scheme = types[i]; + + // Polyfill since IE and PhantomJS doesn't have + // TypedArray.includes() + function includes(item, array) { + for (var i = 0; i < array.length; i++) { + if (array[i] === item) { + return true; + } } + return false; } - if (this._rfb_auth_scheme === 0) { - return this._fail("Unsupported security types: " + types); + // Look for each auth in preferred order + this._rfb_auth_scheme = 0; + if (includes(1, types)) { + this._rfb_auth_scheme = 1; // None + } else if (includes(22, types)) { + this._rfb_auth_scheme = 22; // XVP + } else if (includes(16, types)) { + this._rfb_auth_scheme = 16; // Tight + } else if (includes(2, types)) { + this._rfb_auth_scheme = 2; // VNC Auth + } else { + return this._fail("Unsupported server", + "Unsupported security types: " + types); } this._sock.send([this._rfb_auth_scheme]); @@ -747,7 +830,9 @@ var RFB; this._rfb_auth_scheme = this._sock.rQshift32(); } - this._updateState('Authentication', 'Authenticating using scheme: ' + this._rfb_auth_scheme); + this._rfb_init_state = 'Authentication'; + Util.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme); + return this._init_msg(); // jump to authentication }, @@ -756,9 +841,9 @@ var RFB; var xvp_sep = this._xvp_password_sep; var xvp_auth = this._rfb_password.split(xvp_sep); if (xvp_auth.length < 3) { - this._updateState('password', 'XVP credentials required (user' + xvp_sep + - 'target' + xvp_sep + 'password) -- got only ' + this._rfb_password); - this._onPasswordRequired(this); + var msg = 'XVP credentials required (user' + xvp_sep + + 'target' + xvp_sep + 'password) -- got only ' + this._rfb_password; + this._onPasswordRequired(this, msg); return false; } @@ -774,18 +859,17 @@ var RFB; _negotiate_std_vnc_auth: function () { if (this._rfb_password.length === 0) { - // Notify via both callbacks since it's kind of - // an RFB state change and a UI interface issue - this._updateState('password', "Password Required"); this._onPasswordRequired(this); + return false; } if (this._sock.rQwait("auth challenge", 16)) { return false; } - var challenge = this._sock.rQshiftBytes(16); + // TODO(directxman12): make genDES not require an Array + var challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16)); var response = RFB.genDES(this._rfb_password, challenge); this._sock.send(response); - this._updateState("SecurityResult"); + this._rfb_init_state = "SecurityResult"; return true; }, @@ -806,12 +890,16 @@ var RFB; if (serverSupportedTunnelTypes[0]) { if (serverSupportedTunnelTypes[0].vendor != clientSupportedTunnelTypes[0].vendor || serverSupportedTunnelTypes[0].signature != clientSupportedTunnelTypes[0].signature) { - return this._fail("Client's tunnel type had the incorrect vendor or signature"); + return this._fail("Unsupported server", + "Client's tunnel type had the incorrect " + + "vendor or signature"); } this._sock.send([0, 0, 0, 0]); // use NOTUNNEL return false; // wait until we receive the sub auth count to continue } else { - return this._fail("Server wanted tunnels, but doesn't support the notunnel type"); + return this._fail("Unsupported server", + "Server wanted tunnels, but doesn't support " + + "the notunnel type"); } }, @@ -832,6 +920,11 @@ var RFB; // second pass, do the sub-auth negotiation if (this._sock.rQwait("sub auth count", 4)) { return false; } var subAuthCount = this._sock.rQshift32(); + if (subAuthCount === 0) { // empty sub-auth list received means 'no auth' subtype selected + this._rfb_init_state = 'SecurityResult'; + return true; + } + if (this._sock.rQwait("sub auth capabilities", 16 * subAuthCount, 4)) { return false; } var clientSupportedTypes = { @@ -853,18 +946,21 @@ var RFB; switch (authType) { case 'STDVNOAUTH__': // no auth - this._updateState('SecurityResult'); + this._rfb_init_state = 'SecurityResult'; return true; case 'STDVVNCAUTH_': // VNC auth this._rfb_auth_scheme = 2; return this._init_msg(); default: - return this._fail("Unsupported tiny auth scheme: " + authType); + return this._fail("Unsupported server", + "Unsupported tiny auth scheme: " + + authType); } } } - this._fail("No supported sub-auth types!"); + return this._fail("Unsupported server", + "No supported sub-auth types!"); }, _negotiate_authentication: function () { @@ -873,14 +969,14 @@ var RFB; if (this._sock.rQwait("auth reason", 4)) { return false; } var strlen = this._sock.rQshift32(); var reason = this._sock.rQshiftStr(strlen); - return this._fail("Auth failure: " + reason); + return this._fail("Authentication failure", reason); case 1: // no auth if (this._rfb_version >= 3.8) { - this._updateState('SecurityResult'); + this._rfb_init_state = 'SecurityResult'; return true; } - this._updateState('ClientInitialisation', "No auth required"); + this._rfb_init_state = 'ClientInitialisation'; return this._init_msg(); case 22: // XVP auth @@ -893,7 +989,9 @@ var RFB; return this._negotiate_tight_auth(); default: - return this._fail("Unsupported auth scheme: " + this._rfb_auth_scheme); + return this._fail("Unsupported server", + "Unsupported auth scheme: " + + this._rfb_auth_scheme); } }, @@ -901,20 +999,24 @@ var RFB; if (this._sock.rQwait('VNC auth response ', 4)) { return false; } switch (this._sock.rQshift32()) { case 0: // OK - this._updateState('ClientInitialisation', 'Authentication OK'); + this._rfb_init_state = 'ClientInitialisation'; + Util.Debug('Authentication OK'); return this._init_msg(); case 1: // failed if (this._rfb_version >= 3.8) { var length = this._sock.rQshift32(); if (this._sock.rQwait("SecurityResult reason", length, 8)) { return false; } var reason = this._sock.rQshiftStr(length); - return this._fail(reason); + return this._fail("Authentication failure", reason); } else { return this._fail("Authentication failure"); } return false; case 2: - return this._fail("Too many auth attempts"); + return this._fail("Too many authentication attempts"); + default: + return this._fail("Unsupported server", + "Unknown SecurityResult"); } }, @@ -924,6 +1026,7 @@ var RFB; /* Screen size */ this._fb_width = this._sock.rQshift16(); this._fb_height = this._sock.rQshift16(); + this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4); /* PIXEL_FORMAT */ var bpp = this._sock.rQshift8(); @@ -958,18 +1061,17 @@ var RFB; var totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16; if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + name_length)) { return false; } - var i; - for (i = 0; i < numServerMessages; i++) { - var srvMsg = this._sock.rQshiftStr(16); - } + // we don't actually do anything with the capability information that TIGHT sends, + // so we just skip the all of this. - for (i = 0; i < numClientMessages; i++) { - var clientMsg = this._sock.rQshiftStr(16); - } + // TIGHT server message capabilities + this._sock.rQskipBytes(16 * numServerMessages); - for (i = 0; i < numEncodings; i++) { - var encoding = this._sock.rQshiftStr(16); - } + // TIGHT client message capabilities + this._sock.rQskipBytes(16 * numClientMessages); + + // TIGHT encoding capabilities + this._sock.rQskipBytes(16 * numEncodings); } // NB(directxman12): these are down here so that we don't run them multiple times @@ -1008,8 +1110,9 @@ var RFB; this._display.set_true_color(this._true_color); this._display.resize(this._fb_width, this._fb_height); this._onFBResize(this, this._fb_width, this._fb_height); - this._keyboard.grab(); - this._mouse.grab(); + + if (!this._view_only) { this._keyboard.grab(); } + if (!this._view_only) { this._mouse.grab(); } if (this._true_color) { this._fb_Bpp = 4; @@ -1019,28 +1122,27 @@ var RFB; this._fb_depth = 1; } - var response = RFB.messages.pixelFormat(this._fb_Bpp, this._fb_depth, this._true_color); - response = response.concat( - RFB.messages.clientEncodings(this._encodings, this._local_cursor, this._true_color)); - response = response.concat( - RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(), - this._fb_width, this._fb_height)); + RFB.messages.pixelFormat(this._sock, this._fb_Bpp, this._fb_depth, this._true_color); + RFB.messages.clientEncodings(this._sock, this._encodings, this._local_cursor, this._true_color); + RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fb_width, this._fb_height); this._timing.fbu_rt_start = (new Date()).getTime(); this._timing.pixels = 0; - this._sock.send(response); - this._checkEvents(); - - if (this._encrypt) { - this._updateState('normal', 'Connected (encrypted) to: ' + this._fb_name); - } else { - this._updateState('normal', 'Connected (unencrypted) to: ' + this._fb_name); - } + this._updateConnectionState('connected'); + return true; }, + /* RFB protocol initialization states: + * ProtocolVersion + * Security + * Authentication + * SecurityResult + * ClientInitialization - not triggered by server message + * ServerInitialization + */ _init_msg: function () { - switch (this._rfb_state) { + switch (this._rfb_init_state) { case 'ProtocolVersion': return this._negotiate_protocol_version(); @@ -1055,11 +1157,15 @@ var RFB; case 'ClientInitialisation': this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation - this._updateState('ServerInitialisation', "Authentication OK"); + this._rfb_init_state = 'ServerInitialisation'; return true; case 'ServerInitialisation': return this._negotiate_server_init(); + + default: + return this._fail("Internal error", "Unknown init state: " + + this._rfb_init_state); } }, @@ -1085,6 +1191,8 @@ var RFB; _handle_server_cut_text: function () { Util.Debug("ServerCutText"); + if (this._view_only) { return true; } + if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; } this._sock.rQskipBytes(3); // Padding var length = this._sock.rQshift32(); @@ -1096,6 +1204,49 @@ var RFB; return true; }, + _handle_server_fence_msg: function() { + if (this._sock.rQwait("ServerFence header", 8, 1)) { return false; } + this._sock.rQskipBytes(3); // Padding + var flags = this._sock.rQshift32(); + var length = this._sock.rQshift8(); + + if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; } + + if (length > 64) { + Util.Warn("Bad payload length (" + length + ") in fence response"); + length = 64; + } + + var payload = this._sock.rQshiftStr(length); + + this._supportsFence = true; + + /* + * Fence flags + * + * (1<<0) - BlockBefore + * (1<<1) - BlockAfter + * (1<<2) - SyncNext + * (1<<31) - Request + */ + + if (!(flags & (1<<31))) { + return this._fail("Internal error", + "Unexpected fence response"); + } + + // Filter out unsupported flags + // FIXME: support syncNext + flags &= (1<<0) | (1<<1); + + // BlockBefore and BlockAfter are automatically handled by + // the fact that we process each incoming message + // synchronuosly. + RFB.messages.clientFence(this._sock, flags, payload); + + return true; + }, + _handle_xvp_msg: function () { if (this._sock.rQwait("XVP version and message", 3, 1)) { return false; } this._sock.rQskip8(); // Padding @@ -1104,7 +1255,8 @@ var RFB; switch (xvp_msg) { case 0: // XVP_FAIL - this._updateState(this._rfb_state, "Operation Failed"); + Util.Error("Operation Failed"); + this._notification("XVP Operation Failed", 'error'); break; case 1: // XVP_INIT this._rfb_xvp_ver = xvp_ver; @@ -1112,7 +1264,8 @@ var RFB; this._onXvpInit(this._rfb_xvp_ver); break; default: - this._fail("Disconnected: illegal server XVP message " + xvp_msg); + this._fail("Unexpected server message", + "Illegal server XVP message " + xvp_msg); break; } @@ -1131,9 +1284,9 @@ var RFB; switch (msg_type) { case 0: // FramebufferUpdate var ret = this._framebufferUpdate(); - if (ret) { - this._sock.send(RFB.messages.fbUpdateRequests(this._display.getCleanDirtyReset(), - this._fb_width, this._fb_height)); + if (ret && !this._enabledContinuousUpdates) { + RFB.messages.fbUpdateRequest(this._sock, true, 0, 0, + this._fb_width, this._fb_height); } return ret; @@ -1148,16 +1301,41 @@ var RFB; case 3: // ServerCutText return this._handle_server_cut_text(); + case 150: // EndOfContinuousUpdates + var first = !(this._supportsContinuousUpdates); + this._supportsContinuousUpdates = true; + this._enabledContinuousUpdates = false; + if (first) { + this._enabledContinuousUpdates = true; + this._updateContinuousUpdates(); + Util.Info("Enabling continuous updates."); + } else { + // FIXME: We need to send a framebufferupdaterequest here + // if we add support for turning off continuous updates + } + return true; + + case 248: // ServerFence + return this._handle_server_fence_msg(); + case 250: // XVP return this._handle_xvp_msg(); default: - this._fail("Disconnected: illegal server message type " + msg_type); + this._fail("Unexpected server message", "Type:" + msg_type); Util.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30)); return true; } }, + _onFlush: function() { + this._flushing = false; + // Resume processing + if (this._sock.rQlen() > 0) { + this._handle_message(); + } + }, + _framebufferUpdate: function () { var ret = true; var now; @@ -1172,10 +1350,18 @@ var RFB; now = (new Date()).getTime(); Util.Info("First FBU latency: " + (now - this._timing.fbu_rt_start)); } + + // Make sure the previous frame is fully rendered first + // to avoid building up an excessive queue + if (this._display.pending()) { + this._flushing = true; + this._display.flush(); + return false; + } } while (this._FBU.rects > 0) { - if (this._rfb_state !== "normal") { return false; } + if (this._rfb_connection_state !== 'connected') { return false; } if (this._sock.rQwait("FBU", this._FBU.bytes)) { return false; } if (this._FBU.bytes === 0) { @@ -1197,7 +1383,8 @@ var RFB; 'encodingName': this._encNames[this._FBU.encoding]}); if (!this._encNames[this._FBU.encoding]) { - this._fail("Disconnected: unsupported encoding " + + this._fail("Unexpected server message", + "Unsupported encoding " + this._FBU.encoding); return false; } @@ -1244,6 +1431,8 @@ var RFB; if (!ret) { return ret; } // need more data } + this._display.flip(); + this._onFBUComplete(this, {'x': this._FBU.x, 'y': this._FBU.y, 'width': this._FBU.width, 'height': this._FBU.height, @@ -1252,6 +1441,13 @@ var RFB; return true; // We finished this FBU }, + + _updateContinuousUpdates: function() { + if (!this._enabledContinuousUpdates) { return; } + + RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0, + this._fb_width, this._fb_height); + } }; Util.make_properties(RFB, [ @@ -1269,15 +1465,17 @@ var RFB; ['viewportDrag', 'rw', 'bool'], // Move the viewport on mouse drags // Callback functions - ['onUpdateState', 'rw', 'func'], // onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change - ['onPasswordRequired', 'rw', 'func'], // onPasswordRequired(rfb): VNC password is required + ['onUpdateState', 'rw', 'func'], // onUpdateState(rfb, state, oldstate): connection state change + ['onNotification', 'rw', 'func'], // onNotification(rfb, msg, level, options): notification for the UI + ['onDisconnected', 'rw', 'func'], // onDisconnected(rfb, reason): disconnection finished + ['onPasswordRequired', 'rw', 'func'], // onPasswordRequired(rfb, msg): VNC password is required ['onClipboard', 'rw', 'func'], // onClipboard(rfb, text): RFB clipboard contents received ['onBell', 'rw', 'func'], // onBell(rfb): RFB Bell message received ['onFBUReceive', 'rw', 'func'], // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed ['onFBUComplete', 'rw', 'func'], // onFBUComplete(rfb, fbu): RFB FBU received and processed ['onFBResize', 'rw', 'func'], // onFBResize(rfb, width, height): frame buffer resized ['onDesktopName', 'rw', 'func'], // onDesktopName(rfb, name): desktop name received - ['onXvpInit', 'rw', 'func'], // onXvpInit(version): XVP extensions active for this connection + ['onXvpInit', 'rw', 'func'] // onXvpInit(version): XVP extensions active for this connection ]); RFB.prototype.set_local_cursor = function (cursor) { @@ -1292,6 +1490,27 @@ var RFB; this._display.disableLocalCursor(); } } + + // Need to send an updated list of encodings if we are connected + if (this._rfb_connection_state === "connected") { + RFB.messages.clientEncodings(this._sock, this._encodings, cursor, + this._true_color); + } + }; + + RFB.prototype.set_view_only = function (view_only) { + this._view_only = view_only; + + if (this._rfb_connection_state === "connecting" || + this._rfb_connection_state === "connected") { + if (view_only) { + this._keyboard.ungrab(); + this._mouse.ungrab(); + } else { + this._keyboard.grab(); + this._mouse.grab(); + } + } }; RFB.prototype.get_display = function () { return this._display; }; @@ -1300,64 +1519,234 @@ var RFB; // Class Methods RFB.messages = { - keyEvent: function (keysym, down) { - var arr = [4]; - arr.push8(down); - arr.push16(0); - arr.push32(keysym); - return arr; + keyEvent: function (sock, keysym, down) { + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 4; // msg-type + buff[offset + 1] = down; + + buff[offset + 2] = 0; + buff[offset + 3] = 0; + + buff[offset + 4] = (keysym >> 24); + buff[offset + 5] = (keysym >> 16); + buff[offset + 6] = (keysym >> 8); + buff[offset + 7] = keysym; + + sock._sQlen += 8; + sock.flush(); }, - pointerEvent: function (x, y, mask) { - var arr = [5]; // msg-type - arr.push8(mask); - arr.push16(x); - arr.push16(y); - return arr; + QEMUExtendedKeyEvent: function (sock, keysym, down, keycode) { + function getRFBkeycode(xt_scancode) { + var upperByte = (keycode >> 8); + var lowerByte = (keycode & 0x00ff); + if (upperByte === 0xe0 && lowerByte < 0x7f) { + lowerByte = lowerByte | 0x80; + return lowerByte; + } + return xt_scancode; + } + + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 255; // msg-type + buff[offset + 1] = 0; // sub msg-type + + buff[offset + 2] = (down >> 8); + buff[offset + 3] = down; + + buff[offset + 4] = (keysym >> 24); + buff[offset + 5] = (keysym >> 16); + buff[offset + 6] = (keysym >> 8); + buff[offset + 7] = keysym; + + var RFBkeycode = getRFBkeycode(keycode); + + buff[offset + 8] = (RFBkeycode >> 24); + buff[offset + 9] = (RFBkeycode >> 16); + buff[offset + 10] = (RFBkeycode >> 8); + buff[offset + 11] = RFBkeycode; + + sock._sQlen += 12; + sock.flush(); + }, + + pointerEvent: function (sock, x, y, mask) { + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 5; // msg-type + + buff[offset + 1] = mask; + + buff[offset + 2] = x >> 8; + buff[offset + 3] = x; + + buff[offset + 4] = y >> 8; + buff[offset + 5] = y; + + sock._sQlen += 6; + sock.flush(); }, // TODO(directxman12): make this unicode compatible? - clientCutText: function (text) { - var arr = [6]; // msg-type - arr.push8(0); // padding - arr.push8(0); // padding - arr.push8(0); // padding - arr.push32(text.length); + clientCutText: function (sock, text) { + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 6; // msg-type + + buff[offset + 1] = 0; // padding + buff[offset + 2] = 0; // padding + buff[offset + 3] = 0; // padding + var n = text.length; + + buff[offset + 4] = n >> 24; + buff[offset + 5] = n >> 16; + buff[offset + 6] = n >> 8; + buff[offset + 7] = n; + for (var i = 0; i < n; i++) { - arr.push(text.charCodeAt(i)); + buff[offset + 8 + i] = text.charCodeAt(i); } - return arr; + sock._sQlen += 8 + n; + sock.flush(); }, - pixelFormat: function (bpp, depth, true_color) { - var arr = [0]; // msg-type - arr.push8(0); // padding - arr.push8(0); // padding - arr.push8(0); // padding + setDesktopSize: function (sock, width, height, id, flags) { + var buff = sock._sQ; + var offset = sock._sQlen; - arr.push8(bpp * 8); // bits-per-pixel - arr.push8(depth * 8); // depth - arr.push8(0); // little-endian - arr.push8(true_color ? 1 : 0); // true-color + buff[offset] = 251; // msg-type + buff[offset + 1] = 0; // padding + buff[offset + 2] = width >> 8; // width + buff[offset + 3] = width; + buff[offset + 4] = height >> 8; // height + buff[offset + 5] = height; - arr.push16(255); // red-max - arr.push16(255); // green-max - arr.push16(255); // blue-max - arr.push8(16); // red-shift - arr.push8(8); // green-shift - arr.push8(0); // blue-shift + buff[offset + 6] = 1; // number-of-screens + buff[offset + 7] = 0; // padding - arr.push8(0); // padding - arr.push8(0); // padding - arr.push8(0); // padding - return arr; + // screen array + buff[offset + 8] = id >> 24; // id + buff[offset + 9] = id >> 16; + buff[offset + 10] = id >> 8; + buff[offset + 11] = id; + buff[offset + 12] = 0; // x-position + buff[offset + 13] = 0; + buff[offset + 14] = 0; // y-position + buff[offset + 15] = 0; + buff[offset + 16] = width >> 8; // width + buff[offset + 17] = width; + buff[offset + 18] = height >> 8; // height + buff[offset + 19] = height; + buff[offset + 20] = flags >> 24; // flags + buff[offset + 21] = flags >> 16; + buff[offset + 22] = flags >> 8; + buff[offset + 23] = flags; + + sock._sQlen += 24; + sock.flush(); }, - clientEncodings: function (encodings, local_cursor, true_color) { - var i, encList = []; + clientFence: function (sock, flags, payload) { + var buff = sock._sQ; + var offset = sock._sQlen; + buff[offset] = 248; // msg-type + + buff[offset + 1] = 0; // padding + buff[offset + 2] = 0; // padding + buff[offset + 3] = 0; // padding + + buff[offset + 4] = flags >> 24; // flags + buff[offset + 5] = flags >> 16; + buff[offset + 6] = flags >> 8; + buff[offset + 7] = flags; + + var n = payload.length; + + buff[offset + 8] = n; // length + + for (var i = 0; i < n; i++) { + buff[offset + 9 + i] = payload.charCodeAt(i); + } + + sock._sQlen += 9 + n; + sock.flush(); + }, + + enableContinuousUpdates: function (sock, enable, x, y, width, height) { + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 150; // msg-type + buff[offset + 1] = enable; // enable-flag + + buff[offset + 2] = x >> 8; // x + buff[offset + 3] = x; + buff[offset + 4] = y >> 8; // y + buff[offset + 5] = y; + buff[offset + 6] = width >> 8; // width + buff[offset + 7] = width; + buff[offset + 8] = height >> 8; // height + buff[offset + 9] = height; + + sock._sQlen += 10; + sock.flush(); + }, + + pixelFormat: function (sock, bpp, depth, true_color) { + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 0; // msg-type + + buff[offset + 1] = 0; // padding + buff[offset + 2] = 0; // padding + buff[offset + 3] = 0; // padding + + buff[offset + 4] = bpp * 8; // bits-per-pixel + buff[offset + 5] = depth * 8; // depth + buff[offset + 6] = 0; // little-endian + buff[offset + 7] = true_color ? 1 : 0; // true-color + + buff[offset + 8] = 0; // red-max + buff[offset + 9] = 255; // red-max + + buff[offset + 10] = 0; // green-max + buff[offset + 11] = 255; // green-max + + buff[offset + 12] = 0; // blue-max + buff[offset + 13] = 255; // blue-max + + buff[offset + 14] = 16; // red-shift + buff[offset + 15] = 8; // green-shift + buff[offset + 16] = 0; // blue-shift + + buff[offset + 17] = 0; // padding + buff[offset + 18] = 0; // padding + buff[offset + 19] = 0; // padding + + sock._sQlen += 20; + sock.flush(); + }, + + clientEncodings: function (sock, encodings, local_cursor, true_color) { + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 2; // msg-type + buff[offset + 1] = 0; // padding + + // offset + 2 and offset + 3 are encoding count + + var i, j = offset + 4, cnt = 0; for (i = 0; i < encodings.length; i++) { if (encodings[i][0] === "Cursor" && !local_cursor) { Util.Debug("Skipping Cursor pseudo-encoding"); @@ -1365,56 +1754,48 @@ var RFB; // TODO: remove this when we have tight+non-true-color Util.Warn("Skipping tight as it is only supported with true color"); } else { - encList.push(encodings[i][1]); + var enc = encodings[i][1]; + buff[j] = enc >> 24; + buff[j + 1] = enc >> 16; + buff[j + 2] = enc >> 8; + buff[j + 3] = enc; + + j += 4; + cnt++; } } - var arr = [2]; // msg-type - arr.push8(0); // padding + buff[offset + 2] = cnt >> 8; + buff[offset + 3] = cnt; - arr.push16(encList.length); // encoding count - for (i = 0; i < encList.length; i++) { - arr.push32(encList[i]); - } - - return arr; + sock._sQlen += j - offset; + sock.flush(); }, - fbUpdateRequests: function (cleanDirty, fb_width, fb_height) { - var arr = []; + fbUpdateRequest: function (sock, incremental, x, y, w, h) { + var buff = sock._sQ; + var offset = sock._sQlen; - var cb = cleanDirty.cleanBox; - var w, h; - if (cb.w > 0 && cb.h > 0) { - w = typeof cb.w === "undefined" ? fb_width : cb.w; - h = typeof cb.h === "undefined" ? fb_height : cb.h; - // Request incremental for clean box - arr = arr.concat(RFB.messages.fbUpdateRequest(1, cb.x, cb.y, w, h)); - } - - for (var i = 0; i < cleanDirty.dirtyBoxes.length; i++) { - var db = cleanDirty.dirtyBoxes[i]; - // Force all (non-incremental) for dirty box - w = typeof db.w === "undefined" ? fb_width : db.w; - h = typeof db.h === "undefined" ? fb_height : db.h; - arr = arr.concat(RFB.messages.fbUpdateRequest(0, db.x, db.y, w, h)); - } - - return arr; - }, - - fbUpdateRequest: function (incremental, x, y, w, h) { if (typeof(x) === "undefined") { x = 0; } if (typeof(y) === "undefined") { y = 0; } - var arr = [3]; // msg-type - arr.push8(incremental); - arr.push16(x); - arr.push16(y); - arr.push16(w); - arr.push16(h); + buff[offset] = 3; // msg-type + buff[offset + 1] = incremental ? 1 : 0; - return arr; + buff[offset + 2] = (x >> 8) & 0xFF; + buff[offset + 3] = x & 0xFF; + + buff[offset + 4] = (y >> 8) & 0xFF; + buff[offset + 5] = y & 0xFF; + + buff[offset + 6] = (w >> 8) & 0xFF; + buff[offset + 7] = w & 0xFF; + + buff[offset + 8] = (h >> 8) & 0xFF; + buff[offset + 9] = h & 0xFF; + + sock._sQlen += 10; + sock.flush(); } }; @@ -1426,10 +1807,6 @@ var RFB; return (new DES(passwd)).encrypt(challenge); }; - RFB.extract_data_uri = function (arr) { - return ";base64," + Base64.encode(arr); - }; - RFB.encodingHandlers = { RAW: function () { if (this._FBU.lines === 0) { @@ -1460,15 +1837,10 @@ var RFB; COPYRECT: function () { this._FBU.bytes = 4; if (this._sock.rQwait("COPYRECT", 4)) { return false; } - this._display.renderQ_push({ - 'type': 'copy', - 'old_x': this._sock.rQshift16(), - 'old_y': this._sock.rQshift16(), - 'x': this._FBU.x, - 'y': this._FBU.y, - 'width': this._FBU.width, - 'height': this._FBU.height - }); + this._display.copyImage(this._sock.rQshift16(), this._sock.rQshift16(), + this._FBU.x, this._FBU.y, this._FBU.width, + this._FBU.height); + this._FBU.rects--; this._FBU.bytes = 0; return true; @@ -1521,7 +1893,8 @@ var RFB; if (this._sock.rQwait("HEXTILE subencoding", this._FBU.bytes)) { return false; } var subencoding = rQ[rQi]; // Peek if (subencoding > 30) { // Raw - this._fail("Disconnected: illegal hextile subencoding " + subencoding); + this._fail("Unexpected server message", + "Illegal hextile subencoding: " + subencoding); return false; } @@ -1573,11 +1946,21 @@ var RFB; rQi += this._FBU.bytes - 1; } else { if (this._FBU.subencoding & 0x02) { // Background - this._FBU.background = rQ.slice(rQi, rQi + this._fb_Bpp); + if (this._fb_Bpp == 1) { + this._FBU.background = rQ[rQi]; + } else { + // fb_Bpp is 4 + this._FBU.background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; + } rQi += this._fb_Bpp; } if (this._FBU.subencoding & 0x04) { // Foreground - this._FBU.foreground = rQ.slice(rQi, rQi + this._fb_Bpp); + if (this._fb_Bpp == 1) { + this._FBU.foreground = rQ[rQi]; + } else { + // this._fb_Bpp is 4 + this._FBU.foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; + } rQi += this._fb_Bpp; } @@ -1589,7 +1972,12 @@ var RFB; for (var s = 0; s < subrects; s++) { var color; if (this._FBU.subencoding & 0x10) { // SubrectsColoured - color = rQ.slice(rQi, rQi + this._fb_Bpp); + if (this._fb_Bpp === 1) { + color = rQ[rQi]; + } else { + // _fb_Bpp is 4 + color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; + } rQi += this._fb_Bpp; } else { color = this._FBU.foreground; @@ -1638,7 +2026,9 @@ var RFB; display_tight: function (isTightPNG) { if (this._fb_depth === 1) { - this._fail("Tight protocol handler only implements true color mode"); + this._fail("Internal error", + "Tight protocol handler only implements " + + "true color mode"); } this._FBU.bytes = 1; // compression-control byte @@ -1655,7 +2045,7 @@ var RFB; var resetStreams = 0; var streamId = -1; - var decompress = function (data) { + var decompress = function (data, expected) { for (var i = 0; i < 4; i++) { if ((resetStreams >> i) & 1) { this._FBU.zlibs[i].reset(); @@ -1663,61 +2053,96 @@ var RFB; } } - var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0); - if (uncompressed.status !== 0) { + //var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0); + var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected); + /*if (uncompressed.status !== 0) { Util.Error("Invalid data in zlib stream"); - } + }*/ - return uncompressed.data; + //return uncompressed.data; + return uncompressed; }.bind(this); - var indexedToRGB = function (data, numColors, palette, width, height) { + var indexedToRGBX2Color = function (data, palette, width, height) { // Convert indexed (palette based) image data to RGB // TODO: reduce number of calculations inside loop - var dest = []; - var x, y, dp, sp; - if (numColors === 2) { - var w = Math.floor((width + 7) / 8); - var w1 = Math.floor(width / 8); + var dest = this._destBuff; + var w = Math.floor((width + 7) / 8); + var w1 = Math.floor(width / 8); - for (y = 0; y < height; y++) { - var b; - for (x = 0; x < w1; x++) { - for (b = 7; b >= 0; b--) { - dp = (y * width + x * 8 + 7 - b) * 3; - sp = (data[y * w + x] >> b & 1) * 3; - dest[dp] = palette[sp]; - dest[dp + 1] = palette[sp + 1]; - dest[dp + 2] = palette[sp + 2]; - } - } - - for (b = 7; b >= 8 - width % 8; b--) { - dp = (y * width + x * 8 + 7 - b) * 3; - sp = (data[y * w + x] >> b & 1) * 3; + /*for (var y = 0; y < height; y++) { + var b, x, dp, sp; + var yoffset = y * width; + var ybitoffset = y * w; + var xoffset, targetbyte; + for (x = 0; x < w1; x++) { + xoffset = yoffset + x * 8; + targetbyte = data[ybitoffset + x]; + for (b = 7; b >= 0; b--) { + dp = (xoffset + 7 - b) * 3; + sp = (targetbyte >> b & 1) * 3; dest[dp] = palette[sp]; dest[dp + 1] = palette[sp + 1]; dest[dp + 2] = palette[sp + 2]; } } - } else { - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - dp = (y * width + x) * 3; - sp = data[y * width + x] * 3; + + xoffset = yoffset + x * 8; + targetbyte = data[ybitoffset + x]; + for (b = 7; b >= 8 - width % 8; b--) { + dp = (xoffset + 7 - b) * 3; + sp = (targetbyte >> b & 1) * 3; + dest[dp] = palette[sp]; + dest[dp + 1] = palette[sp + 1]; + dest[dp + 2] = palette[sp + 2]; + } + }*/ + + for (var y = 0; y < height; y++) { + var b, x, dp, sp; + for (x = 0; x < w1; x++) { + for (b = 7; b >= 0; b--) { + dp = (y * width + x * 8 + 7 - b) * 4; + sp = (data[y * w + x] >> b & 1) * 3; dest[dp] = palette[sp]; dest[dp + 1] = palette[sp + 1]; dest[dp + 2] = palette[sp + 2]; + dest[dp + 3] = 255; } } + + for (b = 7; b >= 8 - width % 8; b--) { + dp = (y * width + x * 8 + 7 - b) * 4; + sp = (data[y * w + x] >> b & 1) * 3; + dest[dp] = palette[sp]; + dest[dp + 1] = palette[sp + 1]; + dest[dp + 2] = palette[sp + 2]; + dest[dp + 3] = 255; + } + } + + return dest; + }.bind(this); + + var indexedToRGBX = function (data, palette, width, height) { + // Convert indexed (palette based) image data to RGB + var dest = this._destBuff; + var total = width * height * 4; + for (var i = 0, j = 0; i < total; i += 4, j++) { + var sp = data[j] * 3; + dest[i] = palette[sp]; + dest[i + 1] = palette[sp + 1]; + dest[i + 2] = palette[sp + 2]; + dest[i + 3] = 255; } return dest; }.bind(this); - var rQ = this._sock.get_rQ(); var rQi = this._sock.get_rQi(); - var cmode, clength, data; + var rQ = this._sock.rQwhole(); + var cmode, data; + var cl_header, cl_data; var handlePalette = function () { var numColors = rQ[rQi + 2] + 1; @@ -1730,37 +2155,51 @@ var RFB; var raw = false; if (rowSize * this._FBU.height < 12) { raw = true; - clength = [0, rowSize * this._FBU.height]; + cl_header = 0; + cl_data = rowSize * this._FBU.height; + //clength = [0, rowSize * this._FBU.height]; } else { - clength = RFB.encodingHandlers.getTightCLength(this._sock.rQslice(3 + paletteSize, - 3 + paletteSize + 3)); + // begin inline getTightCLength (returning two-item arrays is bad for performance with GC) + var cl_offset = rQi + 3 + paletteSize; + cl_header = 1; + cl_data = 0; + cl_data += rQ[cl_offset] & 0x7f; + if (rQ[cl_offset] & 0x80) { + cl_header++; + cl_data += (rQ[cl_offset + 1] & 0x7f) << 7; + if (rQ[cl_offset + 1] & 0x80) { + cl_header++; + cl_data += rQ[cl_offset + 2] << 14; + } + } + // end inline getTightCLength } - this._FBU.bytes += clength[0] + clength[1]; + this._FBU.bytes += cl_header + cl_data; if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; } // Shift ctl, filter id, num colors, palette entries, and clength off this._sock.rQskipBytes(3); - var palette = this._sock.rQshiftBytes(paletteSize); - this._sock.rQskipBytes(clength[0]); + //var palette = this._sock.rQshiftBytes(paletteSize); + this._sock.rQshiftTo(this._paletteBuff, paletteSize); + this._sock.rQskipBytes(cl_header); if (raw) { - data = this._sock.rQshiftBytes(clength[1]); + data = this._sock.rQshiftBytes(cl_data); } else { - data = decompress(this._sock.rQshiftBytes(clength[1])); + data = decompress(this._sock.rQshiftBytes(cl_data), rowSize * this._FBU.height); } // Convert indexed (palette based) image data to RGB - var rgb = indexedToRGB(data, numColors, palette, this._FBU.width, this._FBU.height); + var rgbx; + if (numColors == 2) { + rgbx = indexedToRGBX2Color(data, this._paletteBuff, this._FBU.width, this._FBU.height); + this._display.blitRgbxImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, rgbx, 0, false); + } else { + rgbx = indexedToRGBX(data, this._paletteBuff, this._FBU.width, this._FBU.height); + this._display.blitRgbxImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, rgbx, 0, false); + } - this._display.renderQ_push({ - 'type': 'blitRgb', - 'data': rgb, - 'x': this._FBU.x, - 'y': this._FBU.y, - 'width': this._FBU.width, - 'height': this._FBU.height - }); return true; }.bind(this); @@ -1770,30 +2209,37 @@ var RFB; var uncompressedSize = this._FBU.width * this._FBU.height * this._fb_depth; if (uncompressedSize < 12) { raw = true; - clength = [0, uncompressedSize]; + cl_header = 0; + cl_data = uncompressedSize; } else { - clength = RFB.encodingHandlers.getTightCLength(this._sock.rQslice(1, 4)); + // begin inline getTightCLength (returning two-item arrays is for peformance with GC) + var cl_offset = rQi + 1; + cl_header = 1; + cl_data = 0; + cl_data += rQ[cl_offset] & 0x7f; + if (rQ[cl_offset] & 0x80) { + cl_header++; + cl_data += (rQ[cl_offset + 1] & 0x7f) << 7; + if (rQ[cl_offset + 1] & 0x80) { + cl_header++; + cl_data += rQ[cl_offset + 2] << 14; + } + } + // end inline getTightCLength } - this._FBU.bytes = 1 + clength[0] + clength[1]; + this._FBU.bytes = 1 + cl_header + cl_data; if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; } // Shift ctl, clength off - this._sock.rQshiftBytes(1 + clength[0]); + this._sock.rQshiftBytes(1 + cl_header); if (raw) { - data = this._sock.rQshiftBytes(clength[1]); + data = this._sock.rQshiftBytes(cl_data); } else { - data = decompress(this._sock.rQshiftBytes(clength[1])); + data = decompress(this._sock.rQshiftBytes(cl_data), uncompressedSize); } - this._display.renderQ_push({ - 'type': 'blitRgb', - 'data': data, - 'x': this._FBU.x, - 'y': this._FBU.y, - 'width': this._FBU.width, - 'height': this._FBU.height - }); + this._display.blitRgbImage(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, data, 0, false); return true; }.bind(this); @@ -1812,10 +2258,13 @@ var RFB; else if (ctl === 0x0A) cmode = "png"; else if (ctl & 0x04) cmode = "filter"; else if (ctl < 0x04) cmode = "copy"; - else return this._fail("Illegal tight compression received, ctl: " + ctl); + else return this._fail("Unexpected server message", + "Illegal tight compression received, " + + "ctl: " + ctl); if (isTightPNG && (cmode === "filter" || cmode === "copy")) { - return this._fail("filter/copy received in tightPNG mode"); + return this._fail("Unexpected server message", + "filter/copy received in tightPNG mode"); } switch (cmode) { @@ -1841,35 +2290,33 @@ var RFB; // Determine FBU.bytes switch (cmode) { case "fill": - this._sock.rQskip8(); // shift off ctl - var color = this._sock.rQshiftBytes(this._fb_depth); - this._display.renderQ_push({ - 'type': 'fill', - 'x': this._FBU.x, - 'y': this._FBU.y, - 'width': this._FBU.width, - 'height': this._FBU.height, - 'color': [color[2], color[1], color[0]] - }); + // skip ctl byte + this._display.fillRect(this._FBU.x, this._FBU.y, this._FBU.width, this._FBU.height, [rQ[rQi + 3], rQ[rQi + 2], rQ[rQi + 1]], false); + this._sock.rQskipBytes(4); break; case "png": case "jpeg": - clength = RFB.encodingHandlers.getTightCLength(this._sock.rQslice(1, 4)); - this._FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data + // begin inline getTightCLength (returning two-item arrays is for peformance with GC) + var cl_offset = rQi + 1; + cl_header = 1; + cl_data = 0; + cl_data += rQ[cl_offset] & 0x7f; + if (rQ[cl_offset] & 0x80) { + cl_header++; + cl_data += (rQ[cl_offset + 1] & 0x7f) << 7; + if (rQ[cl_offset + 1] & 0x80) { + cl_header++; + cl_data += rQ[cl_offset + 2] << 14; + } + } + // end inline getTightCLength + this._FBU.bytes = 1 + cl_header + cl_data; // ctl + clength size + jpeg-data if (this._sock.rQwait("TIGHT " + cmode, this._FBU.bytes)) { return false; } // We have everything, render it - this._sock.rQskipBytes(1 + clength[0]); // shift off clt + compact length - var img = new Image(); - img.src = "data: image/" + cmode + - RFB.extract_data_uri(this._sock.rQshiftBytes(clength[1])); - this._display.renderQ_push({ - 'type': 'img', - 'img': img, - 'x': this._FBU.x, - 'y': this._FBU.y - }); - img = null; + this._sock.rQskipBytes(1 + cl_header); // shift off clt + compact length + data = this._sock.rQshiftBytes(cl_data); + this._display.imageRect(this._FBU.x, this._FBU.y, "image/" + cmode, data); break; case "filter": var filterId = rQ[rQi + 1]; @@ -1878,8 +2325,9 @@ var RFB; } else { // Filter 0, Copy could be valid here, but servers don't send it as an explicit filter // Filter 2, Gradient is valid but not use if jpeg is enabled - // TODO(directxman12): why aren't we just calling '_fail' here - throw new Error("Unsupported tight subencoding received, filter: " + filterId); + this._fail("Unexpected server message", + "Unsupported tight subencoding received, " + + "filter: " + filterId); } break; case "copy": @@ -1905,9 +2353,11 @@ var RFB; handle_FB_resize: function () { this._fb_width = this._FBU.width; this._fb_height = this._FBU.height; + this._destBuff = new Uint8Array(this._fb_width * this._fb_height * 4); this._display.resize(this._fb_width, this._fb_height); this._onFBResize(this, this._fb_width, this._fb_height); this._timing.fbu_rt_start = (new Date()).getTime(); + this._updateContinuousUpdates(); this._FBU.bytes = 0; this._FBU.rects -= 1; @@ -1927,9 +2377,9 @@ var RFB; this._sock.rQskipBytes(1); // number-of-screens this._sock.rQskipBytes(3); // padding - for (var i=0; i 0) || + (navigator.msMaxTouchPoints > 0); +window.addEventListener('touchstart', function onFirstTouch() { + Util.isTouchDevice = true; + window.removeEventListener('touchstart', onFirstTouch, false); +}, 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)}; + +(function () { + "use strict"; + // 'presto': (function () { return (!window.opera) ? false : true; }()), + var detectPresto = function () { + return !!window.opera; + }; + + // '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) { + try { + v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); + } 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}; +}()); + + +Util.Localisation = { + // Currently configured language + language: 'en', + + // Configure suitable language based on user preferences + setup: function (supportedLanguages) { + var userLanguages; + + Util.Localisation.language = 'en'; // Default: US English + + /* + * Navigator.languages only available in Chrome (32+) and FireFox (32+) + * Fall back to navigator.language for other browsers + */ + if (typeof window.navigator.languages == 'object') { + userLanguages = window.navigator.languages; + } else { + userLanguages = [navigator.language || navigator.userLanguage]; + } + + for (var i = 0;i < userLanguages.length;i++) { + var userLang = userLanguages[i]; + userLang = userLang.toLowerCase(); + userLang = userLang.replace("_", "-"); + userLang = userLang.split("-"); + + // Built-in default? + if ((userLang[0] === 'en') && + ((userLang[1] === undefined) || (userLang[1] === 'us'))) { + return; + } + + // First pass: perfect match + for (var j = 0;j < supportedLanguages.length;j++) { + var supLang = supportedLanguages[j]; + supLang = supLang.toLowerCase(); + supLang = supLang.replace("_", "-"); + supLang = supLang.split("-"); + + if (userLang[0] !== supLang[0]) + continue; + if (userLang[1] !== supLang[1]) + continue; + + Util.Localisation.language = supportedLanguages[j]; + return; + } + + // Second pass: fallback + for (var j = 0;j < supportedLanguages.length;j++) { + supLang = supportedLanguages[j]; + supLang = supLang.toLowerCase(); + supLang = supLang.replace("_", "-"); + supLang = supLang.split("-"); + + if (userLang[0] !== supLang[0]) + continue; + if (supLang[1] !== undefined) + continue; + + Util.Localisation.language = supportedLanguages[j]; + return; + } + } + }, + + // Retrieve localised text + get: function (id) { + if (typeof Language !== 'undefined' && Language[id]) { + return Language[id]; + } else { + return id; + } + }, + + // Traverses the DOM and translates relevant fields + // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate + translateDOM: function () { + function process(elem, enabled) { + function isAnyOf(searchElement, items) { + return items.indexOf(searchElement) !== -1; + } + + function translateAttribute(elem, attr) { + var str = elem.getAttribute(attr); + str = Util.Localisation.get(str); + elem.setAttribute(attr, str); + } + + function translateTextNode(node) { + var str = node.data.trim(); + str = Util.Localisation.get(str); + node.data = str; + } + + if (elem.hasAttribute("translate")) { + if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) { + enabled = true; + } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) { + enabled = false; + } + } + + if (enabled) { + if (elem.hasAttribute("abbr") && + elem.tagName === "TH") { + translateAttribute(elem, "abbr"); + } + if (elem.hasAttribute("alt") && + isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) { + translateAttribute(elem, "alt"); + } + if (elem.hasAttribute("download") && + isAnyOf(elem.tagName, ["A", "AREA"])) { + translateAttribute(elem, "download"); + } + if (elem.hasAttribute("label") && + isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP", + "OPTION", "TRACK"])) { + translateAttribute(elem, "label"); + } + // FIXME: Should update "lang" + if (elem.hasAttribute("placeholder") && + isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) { + translateAttribute(elem, "placeholder"); + } + if (elem.hasAttribute("title")) { + translateAttribute(elem, "title"); + } + if (elem.hasAttribute("value") && + elem.tagName === "INPUT" && + isAnyOf(elem.getAttribute("type"), ["reset", "button"])) { + translateAttribute(elem, "value"); + } + } + + for (var i = 0;i < elem.childNodes.length;i++) { + node = elem.childNodes[i]; + if (node.nodeType === node.ELEMENT_NODE) { + process(node, enabled); + } else if (node.nodeType === node.TEXT_NODE && enabled) { + translateTextNode(node); + } + } + } + + process(document.body, true); + }, +}; + +// Emulate Element.setCapture() when not supported + +Util._captureRecursion = false; +Util._captureProxy = function (e) { + // Recursion protection as we'll see our own event + if (Util._captureRecursion) return; + + // Clone the event as we cannot dispatch an already dispatched event + var newEv = new e.constructor(e.type, e); + + Util._captureRecursion = true; + Util._captureElem.dispatchEvent(newEv); + Util._captureRecursion = false; + + // Avoid double events + e.stopPropagation(); + + // Respect the wishes of the redirected event handlers + if (newEv.defaultPrevented) { + e.preventDefault(); + } + + // Implicitly release the capture on button release + if ((e.type === "mouseup") || (e.type === "touchend")) { + Util.releaseCapture(); + } +}; + +// Follow cursor style of target element +Util._captureElemChanged = function() { + var captureElem = document.getElementById("noVNC_mouse_capture_elem"); + captureElem.style.cursor = window.getComputedStyle(Util._captureElem).cursor; +}; +Util._captureObserver = new MutationObserver(Util._captureElemChanged); + +Util._captureIndex = 0; + +Util.setCapture = function (elem) { + if (elem.setCapture) { + + elem.setCapture(); + + // IE releases capture on 'click' events which might not trigger + elem.addEventListener('mouseup', Util.releaseCapture); + elem.addEventListener('touchend', Util.releaseCapture); + + } else { + // Release any existing capture in case this method is + // called multiple times without coordination + Util.releaseCapture(); + + // Safari on iOS 9 has a broken constructor for TouchEvent. + // We are fine in this case however, since Safari seems to + // have some sort of implicit setCapture magic anyway. + if (window.TouchEvent !== undefined) { + try { + new TouchEvent("touchstart"); + } catch (TypeError) { + return; + } + } + + var captureElem = document.getElementById("noVNC_mouse_capture_elem"); + + if (captureElem === null) { + captureElem = document.createElement("div"); + captureElem.id = "noVNC_mouse_capture_elem"; + captureElem.style.position = "fixed"; + captureElem.style.top = "0px"; + captureElem.style.left = "0px"; + captureElem.style.width = "100%"; + captureElem.style.height = "100%"; + captureElem.style.zIndex = 10000; + captureElem.style.display = "none"; + document.body.appendChild(captureElem); + + // This is to make sure callers don't get confused by having + // our blocking element as the target + captureElem.addEventListener('contextmenu', Util._captureProxy); + + captureElem.addEventListener('mousemove', Util._captureProxy); + captureElem.addEventListener('mouseup', Util._captureProxy); + + captureElem.addEventListener('touchmove', Util._captureProxy); + captureElem.addEventListener('touchend', Util._captureProxy); + } + + Util._captureElem = elem; + Util._captureIndex++; + + // Track cursor and get initial cursor + Util._captureObserver.observe(elem, {attributes:true}); + Util._captureElemChanged(); + + captureElem.style.display = null; + + // We listen to events on window in order to keep tracking if it + // happens to leave the viewport + window.addEventListener('mousemove', Util._captureProxy); + window.addEventListener('mouseup', Util._captureProxy); + + window.addEventListener('touchmove', Util._captureProxy); + window.addEventListener('touchend', Util._captureProxy); + } +}; + +Util.releaseCapture = function () { + if (document.releaseCapture) { + + document.releaseCapture(); + + } else { + if (!Util._captureElem) { + return; + } + + // There might be events already queued, so we need to wait for + // them to flush. E.g. contextmenu in Microsoft Edge + window.setTimeout(function(expected) { + // Only clear it if it's the expected grab (i.e. no one + // else has initiated a new grab) + if (Util._captureIndex === expected) { + Util._captureElem = null; + } + }, 0, Util._captureIndex); + + Util._captureObserver.disconnect(); + + var captureElem = document.getElementById("noVNC_mouse_capture_elem"); + captureElem.style.display = "none"; + + window.removeEventListener('mousemove', Util._captureProxy); + window.removeEventListener('mouseup', Util._captureProxy); + + window.removeEventListener('touchmove', Util._captureProxy); + window.removeEventListener('touchend', Util._captureProxy); + } +}; + +/* [module] export default Util; */ diff --git a/noVNC/include/websock.js b/noVNC/core/websock.js similarity index 53% rename from noVNC/include/websock.js rename to noVNC/core/websock.js index cc82e5a..51d9b62 100644 --- a/noVNC/include/websock.js +++ b/noVNC/core/websock.js @@ -3,10 +3,8 @@ * 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) - * via websockify. This is accomplished by base64 encoding the data - * stream between Websock and websockify. + * Websock is similar to the standard WebSocket object but with extra + * buffer handling. * * Websock has built-in receive queue buffering; the message event * does not contain actual data but is simply a notification that @@ -14,50 +12,29 @@ * read binary data off of the receive queue. */ +/* [module] + * import Util from "./util"; + */ + /*jslint browser: true, bitwise: true */ -/*global Util, Base64 */ +/*global Util*/ - -// Load Flash WebSocket emulator if needed - -// 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 () { - window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() + - "web-socket-js/WebSocketMain.swf"; - if (Util.Engine.trident) { - Util.Debug("Forcing uncached load of WebSocketMain.swf"); - window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random(); - } - Util.load_scripts(["web-socket-js/swfobject.js", - "web-socket-js/web_socket.js"]); - })(); -} - - -function Websock() { +/* [module] export default */ function Websock() { "use strict"; this._websocket = null; // WebSocket object - this._rQ = []; // Receive queue - this._rQi = 0; // Receive queue index - this._rQmax = 10000; // Max receive queue size before compacting - this._sQ = []; // Send queue - this._mode = 'base64'; // Current WebSocket mode: 'binary', 'base64' - this.maxBufferedAmount = 200; + 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 + + this._sQbufferSize = 1024 * 10; // 10 KiB + // called in init: this._sQ = new Uint8Array(this._sQbufferSize); + this._sQlen = 0; + this._sQ = null; // Send queue this._eventHandlers = { 'message': function () {}, @@ -65,10 +42,32 @@ function Websock() { 'close': function () {}, 'error': function () {} }; -} +}; (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; + + var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB + + 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)); + }; + } + })(); + Websock.prototype = { // Getters and Setters get_sQ: function () { @@ -89,7 +88,7 @@ function Websock() { // Receive Queue rQlen: function () { - return this._rQ.length - this._rQi; + return this._rQlen - this._rQi; }, rQpeek8: function () { @@ -108,15 +107,7 @@ function Websock() { this._rQi += num; }, - rQunshift8: function (num) { - if (this._rQi === 0) { - this._rQ.unshift(num); - } else { - this._rQi--; - this._rQ[this._rQi] = num; - } - }, - + // TODO(directxman12): test performance with these vs a DataView rQshift16: function () { return (this._rQ[this._rQi++] << 8) + this._rQ[this._rQi++]; @@ -131,22 +122,33 @@ function Websock() { rQshiftStr: function (len) { if (typeof(len) === 'undefined') { len = this.rQlen(); } - var arr = this._rQ.slice(this._rQi, this._rQi + len); + var arr = new Uint8Array(this._rQ.buffer, this._rQi, len); this._rQi += len; - return String.fromCharCode.apply(null, arr); + return typedArrayToString(arr); }, rQshiftBytes: function (len) { if (typeof(len) === 'undefined') { len = this.rQlen(); } this._rQi += len; - return this._rQ.slice(this._rQi - len, this._rQi); + 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 this._rQ.slice(this._rQi + start, this._rQi + end); + return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start); } else { - return this._rQ.slice(this._rQi + start); + return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start); } }, @@ -154,7 +156,7 @@ function Websock() { // 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._rQ.length - this._rQi; // Skip rQlen() function call + var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call if (rQlen < num) { if (goback) { if (this._rQi < goback) { @@ -174,23 +176,16 @@ function Websock() { Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount); } - if (this._websocket.bufferedAmount < this.maxBufferedAmount) { - if (this._sQ.length > 0) { - this._websocket.send(this._encode_message()); - this._sQ = []; - } - - return true; - } else { - Util.Info("Delaying send, bufferedAmount: " + - this._websocket.bufferedAmount); - return false; + if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) { + this._websocket.send(this._encode_message()); + this._sQlen = 0; } }, send: function (arr) { - this._sQ = this._sQ.concat(arr); - return this.flush(); + this._sQ.set(arr, this._sQlen); + this._sQlen += arr.length; + this.flush(); }, send_string: function (str) { @@ -208,90 +203,31 @@ function Websock() { this._eventHandlers[evt] = handler; }, - init: function (protocols, ws_schema) { - this._rQ = []; + _allocate_buffers: function () { + this._rQ = new Uint8Array(this._rQbufferSize); + this._sQ = new Uint8Array(this._sQbufferSize); + }, + + init: function () { + this._allocate_buffers(); this._rQi = 0; - this._sQ = []; 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") { - if (wsbt) { - protocols = ['binary', 'base64']; - } else { - protocols = 'base64'; - } - } - - if (!wsbt) { - if (protocols === 'binary') { - throw new Error('WebSocket binary sub-protocol requested but not supported'); - } - - if (typeof(protocols) === 'object') { - var new_protocols = []; - - for (var i = 0; i < protocols.length; i++) { - if (protocols[i] === 'binary') { - Util.Error('Skipping unsupported WebSocket binary sub-protocol'); - } else { - new_protocols.push(protocols[i]); - } - } - - if (new_protocols.length > 0) { - protocols = new_protocols; - } else { - throw new Error("Only WebSocket binary sub-protocol was requested and is not supported."); - } - } - } - - return protocols; }, open: function (uri, protocols) { var ws_schema = uri.match(/^([a-z]+):\/\//)[1]; - protocols = this.init(protocols, ws_schema); + this.init(); this._websocket = new WebSocket(uri, protocols); - - if (protocols.indexOf('binary') >= 0) { - this._websocket.binaryType = 'arraybuffer'; - } + 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 = 'base64'; - Util.Error('Server select no sub-protocol!: ' + this._websocket.protocol); } + this._eventHandlers.open(); Util.Debug("<< WebSock.onopen"); }).bind(this); @@ -321,26 +257,56 @@ function Websock() { // private methods _encode_message: function () { - if (this._mode === 'binary') { - // Put in a binary arraybuffer - return (new Uint8Array(this._sQ)).buffer; - } else { - // base64 encode - return Base64.encode(this._sQ); + // 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) { - if (this._mode === 'binary') { - // push arraybuffer values onto the end - var u8 = new Uint8Array(data); - for (var i = 0; i < u8.length; i++) { - this._rQ.push(u8[i]); - } - } else { - // base64 decode and concat to end - this._rQ = this._rQ.concat(Base64.decode(data, 0)); + // 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) { @@ -349,9 +315,11 @@ function Websock() { if (this.rQlen() > 0) { this._eventHandlers.message(); // Compact the receive queue - if (this._rQ.length > this._rQmax) { - this._rQ = this._rQ.slice(this._rQi); + 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"); diff --git a/noVNC/custom.css b/noVNC/custom.css deleted file mode 100644 index 582b821..0000000 --- a/noVNC/custom.css +++ /dev/null @@ -1,116 +0,0 @@ -.custom_toolbar { - position: absolute; - width: 52px; - height: 99.5%; - top: 0px; - left: -65px; - z-index: 100; - padding: 2px; - background-color: #EEEEEE; - box-shadow: 1px 0 5px #000000; -} - -.custom_toolbar_divider1 { - width: 40px; - height: 1px; - margin: 5px; - background-color: #000; - opacity: 0.1; -} - -.custom_toolbar_divider2 { - width: 40px; - height: 1px; - margin: 5px; - background-color: #000; - opacity: 0.0; -} - -.custom_toolbar_clicker { - position: absolute; - width: 43px; - height: 36px; - margin-top: -2px; - margin-left: 54px; - line-height: 60px; - text-align: center; - opacity: 1.0; - cursor: pointer; - background-repeat: no-repeat; - background-position: center; - background-image: url(images/pop_more.png); -} - -.custom_toolbar_btn { - width: 40px; - height: 20px; - - font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; - font-size: 13px; - line-height: 20px; - text-align: center; - margin: 15px 5px 15px 5px; - - border: 1px solid #bbbbbb; - border-radius: 4px; - color: #33444E; - background-color: #EEEEEE; - box-shadow: 0px 0px 3px #bbbbbb; - cursor: pointer; -} - -.custom_toolbar_btn_hover { - -webkit-box-shadow: inset 0px 0px 5px 2px rgba(0,0,0,0.3); - -moz-box-shadow: inset 0px 0px 5px 2px rgba(0,0,0,0.3); - box-shadow: inset 0px 0px 5px 2px rgba(0,0,0,0.3); -} - -.custom_toolbar_btn_selected { - background-color: #bfbfbf; -} - -.custom_mask { - top: 0px; - left: 0px; - display: none;; - position: absolute; - background-color: #000; - opacity: 0.5; - z-index: 200; -} - -.custom_pause_icon { - background-repeat: no-repeat; - background-position: center; - background-image: url("images/pause.png"); -} - -.custom_resume_icon { - background-repeat: no-repeat; - background-position: center; - background-image: url("images/resume.png"); -} - -.custom_scale_icon { - background-repeat: no-repeat; - background-position: center; - background-image: url("images/scale.png"); -} - -.custom_not_scale_icon { - background-repeat: no-repeat; - background-position: center; - background-image: url("images/not_scale.png"); -} - -.custom_quality_icon { - margin: 3px 0px 0px 8px; - width: 24px; - height: 14px; - border-radius: 12px; - color: #fff; - background-color: #4d4d4d; - font-size: 12px; - line-height: 14px; - font-weight: bolder; -} \ No newline at end of file diff --git a/noVNC/docs/LICENSE.pako b/noVNC/docs/LICENSE.pako new file mode 100644 index 0000000..e6c9e5a --- /dev/null +++ b/noVNC/docs/LICENSE.pako @@ -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. diff --git a/noVNC/docs/VERSION b/noVNC/docs/VERSION index bd73f47..ee6cdce 100644 --- a/noVNC/docs/VERSION +++ b/noVNC/docs/VERSION @@ -1 +1 @@ -0.4 +0.6.1 diff --git a/noVNC/docs/notes b/noVNC/docs/notes index 9bcc6af..dfef0bd 100644 --- a/noVNC/docs/notes +++ b/noVNC/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 websockify 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 core/inflator.mod.js -o core/inflator.js -s Inflator diff --git a/noVNC/docs/release.txt b/noVNC/docs/release.txt index 1660b9b..3e03635 100644 --- a/noVNC/docs/release.txt +++ b/noVNC/docs/release.txt @@ -1,9 +1,34 @@ -- Update and commit docs/VERSION -- Create version tag and tarball from tag - WVER=0.3 - git tag v${WVER} - git push origin master - git push origin v${WVER} - git archive --format=tar --prefix=novnc-${WVER}/ v${WVER} > novnc-${WVER}.tar - gzip novnc-${WVER}.tar -- Upload tarball to repo +- 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) diff --git a/noVNC/favicon.ico b/noVNC/favicon.ico deleted file mode 120000 index 45399c8..0000000 --- a/noVNC/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -images/favicon.ico \ No newline at end of file diff --git a/noVNC/images/.@__thumb/defaultscreen_320x460.png b/noVNC/images/.@__thumb/defaultscreen_320x460.png deleted file mode 100755 index b22e38e4339d4ac9fb438c3040bff9f5969bc7b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10699 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 diff --git a/noVNC/images/.@__thumb/defaultscreen_700x700.png b/noVNC/images/.@__thumb/defaultscreen_700x700.png deleted file mode 100755 index cec5e4f3f2b15d769c15c38d94b20e915a87cf9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13610 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 h9L0Q;mMPc~`*+k5QnC$S)(8^i+NJk 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 ZhWH|QIz&%hDDAc?k(QdSY6a99`9Gcv4ATGr diff --git a/noVNC/images/.@__thumb/s100screen_700x700.png b/noVNC/images/.@__thumb/s100screen_700x700.png deleted file mode 100755 index e525593f6ec0352ab6ae61fecb40834c98765a40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3277 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 LNkvXXu0mjfF&sIR diff --git a/noVNC/images/.@__thumb/s800screen_320x460.png b/noVNC/images/.@__thumb/s800screen_320x460.png deleted file mode 100755 index b22e38e4339d4ac9fb438c3040bff9f5969bc7b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10699 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 diff --git a/noVNC/images/.@__thumb/s800screen_700x700.png b/noVNC/images/.@__thumb/s800screen_700x700.png deleted file mode 100755 index d73af5b40ae8d7ca5c70bb4743f51b40b031e932..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17519 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 diff --git a/noVNC/images/connect.png b/noVNC/images/connect.png deleted file mode 100644 index 79e71adb85cbdd2da5b59d9e2c38609dd526d1be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 404 zcmV;F0c-w=P)P000>X1^@s6#OZ}&00047NklS9D+ncfe;N96$KSXNI`*e04{(81y?{Ei6R9Of(lMRK|`ICPyi+&eiUAZ zm9zdypa3aP8p(P)``(OKvkOZEAgLANt&n8Es+ql)h-}pOd;)L4*vyoZaUk(X(h2YZ zD6n1y173kMNkcPx$w|M`b>JGf1FjY{IRJKnLo*x0Y$q|00GDQVGp9jbXaej5Yrr!= z6v_iSix@a`oW`_)kjbxOY4Z0?^0`QVpqcM2o%j@J13gJ2vf(nCK`weuV_Im@L}d5R zDRAuZbw>Wp5CivAbYhOP!dl(+VukP6=Za^N0FPdB9#J3{kN0zN8cC>vtYm2u*aKRw zgA~{TE`S}r|8)dB0q4NbGfRO7GaHt4T@FcoWtg~-r2gDO6$V{N1I=O}sav-{^k{{! y4cr3b0^fFPm1r~rl2*vhD1?cbjhg8Ee|!N&_KPMesy;aY0000?EKa`a+$6maEEblBXv zqublI^Z5ssChmi=1rK>`n#>Cpc5LscXyxJQ&70LIB0IC{X&RH3tZ?$3uhC)uAAQ%~ z=dm#0#6w#pC2J)m-^*`i-jt|dOLJh#dSJNyOLkI^a{|+uvu}bz`JFkM*S+>@X!rHq z_o7j{y!=7c)kEp~_RqX&X~247rqA8G!CuF+|LYjD=OyU#_5bp}sYKbLh*2~7a?){X-J diff --git a/noVNC/images/ctrlaltdel.png b/noVNC/images/ctrlaltdel.png deleted file mode 100644 index 31922e53242fe85fb1db641a15339e2c33670e5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmV-D0mA-?P)P000>X1^@s6#OZ}&00035NklEIGHbhH+fhROmqGP}QpED4br)*+f` z_?l_{ec#LO+c)rEu-peF-2wwZk-8P|U}oc72a=w3m0px=;itmLe1 z#^)NUsv0@xQqw6Qf3i;eeYVpf={%ANX10{n1-g;U+RRoliQfnwftyI?tt`uHNl#|h z+ZT|ukaT5ci zMJI${Kqr++6=)XZJ#Ys&cHgZ7UcC3Sx~_*sQC#x%+c;nbytZig#9=-FEZMBb(?pXA P00000NkvXXu0mjfvjB`6 diff --git a/noVNC/images/disconnect.png b/noVNC/images/disconnect.png deleted file mode 100644 index 8832f5ea7e2bdde7016b9429917fd453e42aadb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1378 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 diff --git a/noVNC/images/esc.png b/noVNC/images/esc.png deleted file mode 100644 index ece5f7cbef684a41f4a7bb9d632e4cb2310d6b5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)y9AG<$XUxK0|o|0K2I0N5Rc<;r)=~(;vmpwt~763 z(rf03YMC%Gv!>p;`x=s-3jg3Sv+J08Hk7^Wk+j5=)(kI8VZ)Z0e~+b2TwvK-mAbF2 z{@d)ElGk?5n>TO1$DHDGXL8aMJSYA45_?{oD9s()th9+WhB^2Fuf*zCdB(y{77Tew z&kS1%{uE|Re6Mm~@64I2-aW6}r=)cIK&W#;RvKH+>Qzztr;B)Z{7}kVtXRpGB`8_- zo?X|~X2#5`rF$<%rB3&A;!r=3y(X+NI`nR)$B{ESr#C&d_Xb*BZ7(n}k>jDxsd>+C zaTaJQEjIC;?vf&+TE00gNaUzi!N1irSGT2d zB?5_@acqyryU7ALyz3h9c@D1yz$pMUXc)psjw1kIZjC@DSJ!pvuZF$S5EEVD`sO-K zwz~vXl@a^8vuXCdXQVt?^|DBBs};`<&ZW>?Y2J;;?7QO=uH&8|a7?raW54z=y-+UK zVvcb1_^ff#m~1{1q4bC`vi{NXgX{e4l9BSnCkyj_O)k$bT-NXTtaW?GeaRR8=>9={ zI559(gopow&Y>}1=uA{k`xoEyx2KvRChM%TiVOhwVpN6^_0yNId8osL^rTNZQIULc_^3E@(-y%Spc5Q~vy(d8L!)(x1n*7)_*losOPO6XtxtI&bNw>fdLNZ8Dwf z$IIAo{quy`M$-j;DK3=%w?ZjAaObK&H{HAptRzKWIeQ7*KiU)h_)g-r{Sm_V_d7i@ ze&ymaw?pTfHml?IIZTe*dmMBUR5LfIWL5K>i#m4b+qJBy1&05xz4K!eX8XL_`Om8f zVbx5BqIT4GSlSgy?m1KRiH|?#SMyHA8TzN@`>bi|&VIbRcH2a6i~WBVl#% zWRl#M`RQ-2?~glB>Kmrt@$!yDk*4p)b3T2KBbe4c{Fv#s_FrD!=E(K=v)<*pOqy(B slDTM#cEa+F0UAq7I2|-YE9TfUc1w106`8X%0>hZW)78&qol`;+0Ig`bxBvhE diff --git a/noVNC/images/keyboard.png b/noVNC/images/keyboard.png deleted file mode 100644 index f797952513b39cc75d0f0dd4d6716608e10217ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1283 zcmV+e1^oJnP)X1^@s6sPETi000EaNklRNjzKsl+3H2fX*Se?>)crik)8OqXL!g(Zk)QUl+u@eZ{N?8!v_!lURzreuTz#~)o$Ou{ncK!u58uobz4={SGnacq(;4w(VW_$&Tn0? zLI}}g46&zAPcHz$ULt<47-LVhIeLJBh>%XF$y!}QAP_(_7UAhsnk>u0Krn#eSd^ug z(`1!b(Ld0S;X^TADCEg3n(+F)h>wnk)A;$yRWL9yH#>)^$thg9d>IhH?CdP2Ca3Y! z+$BH=%+FuP)Z`TAlCx;HT3FcjvlqAg;`IegPfj5@GlR{|O)M-fVtR5KKVFzYty=4~ ztKG0fWOsT+R-mdX6o&#p2t`()soJ)OkYyQ)Cd1*7006QqL)A1WiV6n6AuCW-4T`LS zfkAdSplT`{vJ3_WiE(J^mfypX-^W&bH_NSCXW|FATe*W}`{>?%qSaUdQqGj>F}4;qINgs8wq? z^3GB8_xJJpckYp8Sr|P$hDapBQ%_PN7+mkKYV`y^?C#TK0kuNAd)jltXu2&tkJ-U z58kKbOcIrH1t&i_iO8AL;_9U4fg~KzHkK$PNeru;=_E_bY=I=X`e&0s?}Fa+y5r zwvA|P7!KKiTsDVhqk(8N3e~A{qhL_AR>R<62)ak-s}+kXoMV4j#+H5vY>}A`x{LY_0?>}|w)ThJ4!xj6} t2m;U`qQbdz=l(i-_H6Z4&i6*0`Va9a7RbS?<+lI;002ovPDHLkV1f&1X|4bO diff --git a/noVNC/images/mouse_left.png b/noVNC/images/mouse_left.png deleted file mode 100644 index 1de7a486c76ff1b95efd7e5ee36dd6943cc6dd3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 511 zcmVX1^@s6HR9gx0005TNkly zKhD}f5XIlxu*TB5qC!GGMv9cV1WC&YKqwXxk_(^!S^NKxY&N7`ufy{^BuRp5wTe=yggB0|-EL8<)l{q1 zx;aUA_riL;R!Ne;vMfj`A*BRk49#Zq*`4x_>nA1i%`P>6%qjo?002ovPDHLkV1jL+ B<81%{ diff --git a/noVNC/images/mouse_middle.png b/noVNC/images/mouse_middle.png deleted file mode 100644 index 81fbd9bd375b2daa88b6bac6a7972c58b708a67b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 517 zcmV+g0{Z=lP)X1^@s6HR9gx0005ZNkly zzltJ35XQfr(H;kS;J`?bd5jA#-1w4>%zXkyP6VL?HBbltU?dpHCifEe1osR^UUJ;X z1cRfFO!r0W*)ulkvb)@VSrz^Ls=KHz;G>D86P$Co+wG9g=OLv;9LE3v%d!9faU4Sk z0ZJ)M)5PI$kc1H8c^=V%XS3OlAP62f=cn9oIK=l~k4o{cpPta^bWZVPGJ%v5jYi|d zGtLd0&F0HF6aW}wnP(OX1tqT6>xiOAoaYoUrBrnQux&d7zLc{pOJOO25CUo4noE*I zi4|}h#~H_VyUjqND9Ysa`@OyurfFsjK@i*orj#nNG8Nr!Hwl7(FvbWWnMwR-A_P)O z06>x?5`bikq19@gZk9VY9FIqgMk75@QwIR)^?LeNJm2CTJ%6CI*SK`}9;JKxYG9q- z4g6|_(xBgO0(KndX)qYP2xQx~epfV`&2PT%-wkcI+i+d?rxpwVuIv8sJnv@cVzB_F zbX%!ZK3_dN9*>iLzb~(8I-N?+d2)%N{l4@0T&z|rQZAR_I1a)vM6p;zE|-Jv`&h5n zD3waGR;yi{q^o;jxm?OH3}F}sgb)xyfH8(@wR-PP`Oozit@F&YG*W3V00000NkvXX Hu0mjf=bh+1 diff --git a/noVNC/images/mouse_none.png b/noVNC/images/mouse_none.png deleted file mode 100644 index 93dbf5780777973578edaf7ae35f65ed01e5718e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 497 zcmVX1^@s6HR9gx0005FNkly z!HVKQ5QhIwv}2%i@+3$eV-E8o-hBc+tO#O*=)n*Y#FOAj_Br+m_8B~RS~ikHFlwZ` z_b|+0CpM$Y>@fS^{-W!vqNpz5e-kMOIOpnkJfdE&Ln(!#C;$MC;{X7PqJWeVlv3EX zjq~}e2q7d4!wTz7%jNRNWHM3L3W7kDH6D*OAIGtZqDWn{tZTF1@1Hs6S~130rGZAH zq4|Elk37$%5lku7GXS`*TLIsSIgX>TG$W-%S@*_-5SrJ(MNw2T4u?YplIM9Pb~>HR zqp)qel8~n9U0_P7=Cu`>PNyPGQ^FV{gqXL75CWxMmOu!h04T;7hQr}oXSM5u%jJS7 zip)eq9ROrDo0&)P+QlP!{Xpeh>(=8(RG#gJflc}}@P`#DgMR%Ju;+Q-gCO`Tkn6hU zTQL|6z9mWWFmyB;!T0^2Mlb;QzW+N6!@HsD^%|7YL#NYudiQZ0$6`L8R}Oqxi^W25 z&c!W;@%ye;E4kfnNxR*K=XuDo49#W}wOS2Hl3=&nq19@sUaxm^lis}xo6SaLSq96p nAf<$q5{xl)yWK}`$`{uk{?qW@S6u}n00000NkvXXu0mjfItA$@ diff --git a/noVNC/images/mouse_right.png b/noVNC/images/mouse_right.png deleted file mode 100644 index 355b25dc9a03e6d8dc4b956dc51185b5ff8f63e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 513 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 Dmku7D diff --git a/noVNC/images/pop_more_hover.png b/noVNC/images/pop_more_hover.png deleted file mode 100755 index 626299e706ae83b5124c11d8a277c971ed563dff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4673 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 Dmku7D diff --git a/noVNC/images/power.png b/noVNC/images/power.png deleted file mode 100644 index f68fd0813c02a625f3fb5097353b7825f287939f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)r;MP5p&nmUBLf4Yu&0Y-h{y4#VS!wS40wF6uK&wz zeJ1b0dp`q-xCQJ1W&L*#{jK6yZ}@>J>(2+?q>Rf`4Fzff;%=K%nq(i6F}{53M#?;U zLBZzq!>RXNprLZ*hUsB!Gewso}{}^ zyS5zqsqt+q$3eS=bG3syL}aJxswlO({ZMz^<8@I+aHiGz+=bUm8NM^#j@;<7{?FO# zrb}1(?6I-+nz%_ndc~vpE9T2SE8eKOh+S~6fqC~wzI*9n-{OAGRx&S`su`qNAN+(V z&&XBkX?|VBYQH4q-F|DjwzF)~vb(lB%jUA~j<{R9onkH8Tw^2Zl+T|DXQ^pge?vdW h@Q(gJk3Tz4NXMlvaGP>8%>o!=44$rjF6*2UngBV_o1g#y diff --git a/noVNC/images/resume.png b/noVNC/images/resume.png deleted file mode 100644 index 17e6a27851f0ccd22f82f5b293babb4d41bf64bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2963 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 R<0tpelaO1a@z`qz{{ek@u2=v7 diff --git a/noVNC/images/screen_320x460.png b/noVNC/images/screen_320x460.png deleted file mode 100644 index 172ec555c304fdcd2cd34c3f920af2cce1009406..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12778 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 diff --git a/noVNC/images/screen_57x57.png b/noVNC/images/screen_57x57.png deleted file mode 100644 index e2085f29fc2ca0147251b8327d06081670943f0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1807 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 diff --git a/noVNC/images/settings.png b/noVNC/images/settings.png deleted file mode 100644 index a43f5e100b3dc9b5aa30812521b036e085b44a39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2495 zcmZWq2{ap67mm@f1(k}e3~4G-RY|Rth{RgU#IA}GOAR0Knplb(*-?ZwKD}lgz1@N<{mC zgt7iq{!H2pO9THaZ)1wiuP9-HDzwN87W5^b67wj}f|6FgVcZV{$DmL``DtHF2@-Q8d zh$%I5K+clPszf)Il|p}A@vJD$n9C{WNhDL*ayESt3FAtrNO}?%Fwlnclx;Su=g?hr zKNRRh8TTkZ0;x3fJ~1eF7Rl@^SJfjh1?8tcPLH$#DO@0bJe%9UxMr7LaZkHdTg!Fz z!=hd0p?QtY^^Kp;t~EBlA4$6<_(2C-q&j%TaJPALJtSc^JbiioiDL4=+J$tq^T^0d z&x7O7JDB!3ehcH66a~_W$uwh8Z#J=`!hU^z>uFf@9NwVdygn1Vh1Wlc3n9Y%=mI8(BcN1nS zEePZnwlVha!*sbf9kX`AfW4X-pJ2ZnaS?4STPA!y87N{KE7P9U`tFa{{2(ZQ#% zrMZqhT???c__y<<`qqSX<1X2)n;Ir=oEBy4y9ub3Kka~U0cOlRciJ8KUNE=mmB-X} zgHSml%67OZC-EzY}Wq@|xUU!dVO-_)Phm|uA*BXtD6VUSWHDOXk5 zQW@ner1m7A7+_V+um$kLs5V)vKi*mbT?0nMJyMTPE%iBn(^gG{mMu!i619A6F^s6t zPhI4%bsY5IQanRZt0(Dm@?W3NBhH)?Ajm!I3q{D}X@$)QS40p_%}b3aVRGxD)AUD( zVd1yP(_7%tX$G?7L{c#~*8WCV*!-$w%g7#4cprIH!m5EeBsC=|^yfPO=u z_x*z~5(@bSVO%tTx>(ymOlTowh&DnSaTnlwsRxA~|A*v%Jf`GOVhDxKpwNOK2fW@s zv@nJN6naqT`}#*GiTJ$^JuD>fTNM%!Ne(1a$w7=zqy|C*`JH?}7egYVL&JOm$VA3B z-QVfHHDK)Rhx}I?9Ukd+e|66pwf2;@w`F)mHky)IW5dd(g$I{fu$wG|d ze9__p6WkgfAMeb09B}J?dhyshtD+}vXUM0e9zf;!?ae{B&)zxhEoBoisbXBO1aw(x zl*GMvTX_S=d^jd{A8$~k#v&fHc)T}DPR%wg=#JtcHbZEq@Y^?@@9p%W_V&W>U55SA z#A#Rvpi(2J=jXq??(U9%IX+GZHZp4a`f@ara5j$gw!dF0pk})#DJiKmI5@b4Kp+Tb zHLG3D^a=}Ghle!m6jxNx<31D>7Mg8;{_I{@os%Q~;p4}WS=N*H0|OrXi@AW<*QQRYsojc=jg3?QgV&x94`)Xbi3K4M5j#eB{HqJOxw)QjW#!*4*0??F^CwA5 zON(A=-u=D2yxa|k!%Oz#1|QV&Q{*^0IE>%4P(BuDGkIG$vn{WW0DGDD z<(w>>;B_?i2aDQK-)hxZ?e@O&qA!N7L-o4)I!+on&f4Jd#cw+6GOfspzWpOk>Z9$l z4Vjsl>&IS4eC5r~dUSVpZym9x&(8QO$;oXAq8jR2TlYi_m@x`CnPjPYm4_uIRyNkw zkqS~$VX;m4%$ml=#_HPI4M6oA2(#IBx3Q?t-+^u>4Fa{rYkGKiRNon7Zf|enF@31) ziXp0(*W|-cO^Q22aAR(c0_=*Dvf`KxX@N7Qg9lj>L+#T%psklIsw{7K)eV@tgTFql zxu=^-@3?*YwymG9Z_?w!wqW7H^7(}Y#?&}CSqeWTk!e+!n3A%={jh}-I`?*e+7rea z_@vQ>1{5wu<)|8u$8*jYr+HOnFMRqG6{ivzb7&%ZW%Si6yuIYrJ;9^(;gOM%A;G~K z>YAEW5=`n}D@&(yMPV@5-rSV?^e+)&ChVLX{WlR40s;bOO|1CqGcz(6bp-{)=OEB! zTiae>{#7Qwgq7X?J9Kh%T$Q7vqHSAFz{OQ`JcOcKRDYZCFw}&Ux3om%I>yw`#|z|n zUlKHYQJnLb55uSVnr?6NtOE5cX-q_q{|&zGO$v7#sNWPVA1%QlSze#%Y5)KL diff --git a/noVNC/images/showextrakeys.png b/noVNC/images/showextrakeys.png deleted file mode 100644 index ad8e0a70d3eb7b5f4462f808ab987053fa4b0e56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 735 zcmV<50wDc~P)P000>X1^@s6#OZ}&00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-3J*792=g7&T0Sv0&Gb{ zK~zY`y;U)56hRdJ-ppi!CtQjM#~p&_LBP(=X7C5tSO^h>^eIGa36M2V8-Xh|B|c z0YCsW^8Y^p1OTrA>=4nxq$KMA7Do8L0eDPA`$bOYTou6Q5WwB?-B01PwB;o+fp z@6A9JPGh|HW^;3MYIb&(lu~2>JOprWOfs+X(zO7*G{&IWY)S}WI)qTwS_1&I)`+60 zs=k~97!Nf5{J2{SKSYZsk! zq|T{@Q;fA%^7^l4S+>${x24zX0RVcv9@_1;WLdVNwO;Mx$$S4qYyEJ{)3QZGpGKVQ zol@%Q+}s@P?d=IOf3Vg*D|!~HBfiQIZhFw~y}v0UHRs&Y_V#w_oLlObbksbl7bJR= zW!a-Biry2^oe}TWL6&6?c6WEL1K79Lh9b~<$*uY=B349xlweX3xmDIsHAW{M0OxBN zM#hm`x?LGbnNFs>_XJ?74D&DZq%~Aq4WMI;iJAF}QtEy|Z#01TB4*CS_6MPNtqWrX RMSuVR002ovPDHLkV1j-yMWX-! diff --git a/noVNC/images/tab.png b/noVNC/images/tab.png deleted file mode 100644 index 84134872a881a627f745a432a3c951254cda26cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~%1|*NXY)uAIY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP)y9AGr(VqkNfCexMc)B=-cpQH_#nA7tgMe!?hZ5_{ zMsAl!Od$nK=NL4coJ!_2I4fwan^WPez236Q zxV~)Pmbreb!~RE$eG$IAmU)pz*R|Ls(Q=zi{&XKq&Aq}nY2Dc~42<~)YG*C`o4+Gi zfzhrp-BY~xz4ptrjS(^y&#NZ29sIEF7IV+@&p+#?rS3j(MVx!X=$#3b5uMxna)02 zw(86+ea1O6Bj-JL&z$wG@ph=1;^e&(PHLnW`jei;9RARTq fhprAg`;}G9rs2;o<>%Ld!NuU|>gTe~DWM4f3=5y^ diff --git a/noVNC/images/topbackground.png b/noVNC/images/topbackground.png deleted file mode 100755 index c2b6d465db8a79b48d26c8d4050d4f4fb4298528..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2835 zcmV+u3+(iXP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(`>RI+y?e7jKeZ#YO-C z06 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); diff --git a/noVNC/include/display.js b/noVNC/include/display.js deleted file mode 100644 index 9a8e455..0000000 --- a/noVNC/include/display.js +++ /dev/null @@ -1,793 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2012 Joel Martin - * Copyright (C) 2015 Samuel Mannehed for Cendio AB - * Licensed under MPL 2.0 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/*jslint browser: true, white: false */ -/*global Util, Base64, changeCursor */ - -var Display; - -(function () { - "use strict"; - - Display = function (defaults) { - this._drawCtx = null; - this._c_forceCanvas = false; - - this._renderQ = []; // queue drawing actions for in-oder rendering - - // the full frame buffer (logical canvas) size - this._fb_width = 0; - this._fb_height = 0; - - // the size limit of the viewport (start disabled) - this._maxWidth = 0; - this._maxHeight = 0; - - // the visible "physical canvas" viewport - this._viewportLoc = { 'x': 0, 'y': 0, 'w': 0, 'h': 0 }; - this._cleanRect = { 'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1 }; - - this._prevDrawStyle = ""; - this._tile = null; - this._tile16x16 = null; - this._tile_x = 0; - this._tile_y = 0; - - Util.set_defaults(this, defaults, { - 'true_color': true, - 'colourMap': [], - 'scale': 1.0, - 'viewport': false, - 'render_mode': '' - }); - - Util.Debug(">> Display.constructor"); - - if (!this._target) { - throw new Error("Target must be set"); - } - - if (typeof this._target === 'string') { - throw new Error('target must be a DOM element'); - } - - if (!this._target.getContext) { - throw new Error("no getContext method"); - } - - if (!this._drawCtx) { - this._drawCtx = this._target.getContext('2d'); - } - - Util.Debug("User Agent: " + navigator.userAgent); - if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); } - if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); } - if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); } - if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); } - - this.clear(); - - // Check canvas features - if ('createImageData' in this._drawCtx) { - this._render_mode = 'canvas rendering'; - } else { - throw new Error("Canvas does not support createImageData"); - } - - if (this._prefer_js === null) { - Util.Info("Prefering javascript operations"); - this._prefer_js = true; - } - - // Determine browser support for setting the cursor via data URI scheme - if (this._cursor_uri || this._cursor_uri === null || - this._cursor_uri === undefined) { - this._cursor_uri = Util.browserSupportsCursorURIs(); - } - - Util.Debug("<< Display.constructor"); - }; - - Display.prototype = { - // Public methods - viewportChangePos: function (deltaX, deltaY) { - var vp = this._viewportLoc; - - if (!this._viewport) { - deltaX = -vp.w; // clamped later of out of bounds - deltaY = -vp.h; - } - - var vx2 = vp.x + vp.w - 1; - var vy2 = vp.y + vp.h - 1; - - // Position change - - if (deltaX < 0 && vp.x + deltaX < 0) { - deltaX = -vp.x; - } - if (vx2 + deltaX >= this._fb_width) { - deltaX -= vx2 + deltaX - this._fb_width + 1; - } - - if (vp.y + deltaY < 0) { - deltaY = -vp.y; - } - if (vy2 + deltaY >= this._fb_height) { - deltaY -= (vy2 + deltaY - this._fb_height + 1); - } - - if (deltaX === 0 && deltaY === 0) { - return; - } - Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); - - vp.x += deltaX; - vx2 += deltaX; - vp.y += deltaY; - vy2 += deltaY; - - // Update the clean rectangle - var cr = this._cleanRect; - if (vp.x > cr.x1) { - cr.x1 = vp.x; - } - if (vx2 < cr.x2) { - cr.x2 = vx2; - } - if (vp.y > cr.y1) { - cr.y1 = vp.y; - } - if (vy2 < cr.y2) { - cr.y2 = vy2; - } - - var x1, w; - if (deltaX < 0) { - // Shift viewport left, redraw left section - x1 = 0; - w = -deltaX; - } else { - // Shift viewport right, redraw right section - x1 = vp.w - deltaX; - w = deltaX; - } - - var y1, h; - if (deltaY < 0) { - // Shift viewport up, redraw top section - y1 = 0; - h = -deltaY; - } else { - // Shift viewport down, redraw bottom section - y1 = vp.h - deltaY; - h = deltaY; - } - - // Copy the valid part of the viewport to the shifted location - var saveStyle = this._drawCtx.fillStyle; - var canvas = this._target; - this._drawCtx.fillStyle = "rgb(255,255,255)"; - if (deltaX !== 0) { - this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, -deltaX, 0, vp.w, vp.h); - this._drawCtx.fillRect(x1, 0, w, vp.h); - } - if (deltaY !== 0) { - this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, 0, -deltaY, vp.w, vp.h); - this._drawCtx.fillRect(0, y1, vp.w, h); - } - this._drawCtx.fillStyle = saveStyle; - }, - - viewportChangeSize: function(width, height) { - - if (typeof(width) === "undefined" || typeof(height) === "undefined") { - - Util.Debug("Setting viewport to full display region"); - width = this._fb_width; - height = this._fb_height; - } - - var vp = this._viewportLoc; - if (vp.w !== width || vp.h !== height) { - - if (this._viewport) { - if (this._maxWidth !== 0 && width > this._maxWidth) { - width = this._maxWidth; - } - if (this._maxHeight !== 0 && height > this._maxHeight) { - height = this._maxHeight; - } - } - - var cr = this._cleanRect; - - if (width < vp.w && cr.x2 > vp.x + width - 1) { - cr.x2 = vp.x + width - 1; - } - if (height < vp.h && cr.y2 > vp.y + height - 1) { - cr.y2 = vp.y + height - 1; - } - - vp.w = width; - vp.h = height; - - var canvas = this._target; - if (canvas.width !== width || canvas.height !== height) { - - // We have to save the canvas data since changing the size will clear it - var saveImg = null; - if (vp.w > 0 && vp.h > 0 && canvas.width > 0 && canvas.height > 0) { - var img_width = canvas.width < vp.w ? canvas.width : vp.w; - var img_height = canvas.height < vp.h ? canvas.height : vp.h; - saveImg = this._drawCtx.getImageData(0, 0, img_width, img_height); - } - - if (canvas.width !== width) { canvas.width = width; } - if (canvas.height !== height) { canvas.height = height; } - - if (this._viewport) { - canvas.style.height = height + 'px'; - canvas.style.width = width + 'px'; - } - - if (saveImg) { - this._drawCtx.putImageData(saveImg, 0, 0); - } - } - } - }, - - // Return a map of clean and dirty areas of the viewport and reset the - // tracking of clean and dirty areas - // - // Returns: { 'cleanBox': { 'x': x, 'y': y, 'w': w, 'h': h}, - // 'dirtyBoxes': [{ 'x': x, 'y': y, 'w': w, 'h': h }, ...] } - getCleanDirtyReset: function () { - var vp = this._viewportLoc; - var cr = this._cleanRect; - - var cleanBox = { 'x': cr.x1, 'y': cr.y1, - 'w': cr.x2 - cr.x1 + 1, 'h': cr.y2 - cr.y1 + 1 }; - - var dirtyBoxes = []; - if (cr.x1 >= cr.x2 || cr.y1 >= cr.y2) { - // Whole viewport is dirty - dirtyBoxes.push({ 'x': vp.x, 'y': vp.y, 'w': vp.w, 'h': vp.h }); - } else { - // Redraw dirty regions - var vx2 = vp.x + vp.w - 1; - var vy2 = vp.y + vp.h - 1; - - if (vp.x < cr.x1) { - // left side dirty region - dirtyBoxes.push({'x': vp.x, 'y': vp.y, - 'w': cr.x1 - vp.x + 1, 'h': vp.h}); - } - if (vx2 > cr.x2) { - // right side dirty region - dirtyBoxes.push({'x': cr.x2 + 1, 'y': vp.y, - 'w': vx2 - cr.x2, 'h': vp.h}); - } - if(vp.y < cr.y1) { - // top/middle dirty region - dirtyBoxes.push({'x': cr.x1, 'y': vp.y, - 'w': cr.x2 - cr.x1 + 1, 'h': cr.y1 - vp.y}); - } - if (vy2 > cr.y2) { - // bottom/middle dirty region - dirtyBoxes.push({'x': cr.x1, 'y': cr.y2 + 1, - 'w': cr.x2 - cr.x1 + 1, 'h': vy2 - cr.y2}); - } - } - - this._cleanRect = {'x1': vp.x, 'y1': vp.y, - 'x2': vp.x + vp.w - 1, 'y2': vp.y + vp.h - 1}; - - return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; - }, - - absX: function (x) { - return x + this._viewportLoc.x; - }, - - absY: function (y) { - return y + this._viewportLoc.y; - }, - - resize: function (width, height) { - this._prevDrawStyle = ""; - - this._fb_width = width; - this._fb_height = height; - - this._rescale(this._scale); - - this.viewportChangeSize(); - }, - - clear: function () { - if (this._logo) { - this.resize(this._logo.width, this._logo.height); - this.blitStringImage(this._logo.data, 0, 0); - } else { - if (Util.Engine.trident === 6) { - // NB(directxman12): there's a bug in IE10 where we can fail to actually - // clear the canvas here because of the resize. - // Clearing the current viewport first fixes the issue - this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); - } - this.resize(240, 20); - this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h); - } - - this._renderQ = []; - }, - - fillRect: function (x, y, width, height, color) { - this._setFillColor(color); - this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height); - }, - - copyImage: function (old_x, old_y, new_x, new_y, w, h) { - var x1 = old_x - this._viewportLoc.x; - var y1 = old_y - this._viewportLoc.y; - var x2 = new_x - this._viewportLoc.x; - var y2 = new_y - this._viewportLoc.y; - - this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h); - }, - - // start updating a tile - startTile: function (x, y, width, height, color) { - this._tile_x = x; - this._tile_y = y; - if (width === 16 && height === 16) { - this._tile = this._tile16x16; - } else { - this._tile = this._drawCtx.createImageData(width, height); - } - - if (this._prefer_js) { - var bgr; - if (this._true_color) { - bgr = color; - } else { - bgr = this._colourMap[color[0]]; - } - var red = bgr[2]; - var green = bgr[1]; - var blue = bgr[0]; - - var data = this._tile.data; - for (var i = 0; i < width * height * 4; i += 4) { - data[i] = red; - data[i + 1] = green; - data[i + 2] = blue; - data[i + 3] = 255; - } - } else { - this.fillRect(x, y, width, height, color); - } - }, - - // update sub-rectangle of the current tile - subTile: function (x, y, w, h, color) { - if (this._prefer_js) { - var bgr; - if (this._true_color) { - bgr = color; - } else { - bgr = this._colourMap[color[0]]; - } - var red = bgr[2]; - var green = bgr[1]; - var blue = bgr[0]; - var xend = x + w; - var yend = y + h; - - var data = this._tile.data; - var width = this._tile.width; - for (var j = y; j < yend; j++) { - for (var i = x; i < xend; i++) { - var p = (i + (j * width)) * 4; - data[p] = red; - data[p + 1] = green; - data[p + 2] = blue; - data[p + 3] = 255; - } - } - } else { - this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color); - } - }, - - // draw the current tile to the screen - finishTile: function () { - if (this._prefer_js) { - this._drawCtx.putImageData(this._tile, this._tile_x - this._viewportLoc.x, - this._tile_y - this._viewportLoc.y); - } - // else: No-op -- already done by setSubTile - }, - - blitImage: function (x, y, width, height, arr, offset) { - if (this._true_color) { - this._bgrxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); - } else { - this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); - } - }, - - blitRgbImage: function (x, y , width, height, arr, offset) { - if (this._true_color) { - this._rgbImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); - } else { - // probably wrong? - this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset); - } - }, - - blitStringImage: function (str, x, y) { - var img = new Image(); - img.onload = function () { - this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y); - }.bind(this); - img.src = str; - return img; // for debugging purposes - }, - - // wrap ctx.drawImage but relative to viewport - drawImage: function (img, x, y) { - this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y); - }, - - renderQ_push: function (action) { - this._renderQ.push(action); - if (this._renderQ.length === 1) { - // If this can be rendered immediately it will be, otherwise - // the scanner will start polling the queue (every - // requestAnimationFrame interval) - this._scan_renderQ(); - } - }, - - changeCursor: function (pixels, mask, hotx, hoty, w, h) { - if (this._cursor_uri === false) { - Util.Warn("changeCursor called but no cursor data URI support"); - return; - } - - if (this._true_color) { - Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h); - } else { - Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h, this._colourMap); - } - }, - - defaultCursor: function () { - this._target.style.cursor = "default"; - }, - - disableLocalCursor: function () { - this._target.style.cursor = "none"; - }, - - clippingDisplay: function () { - var vp = this._viewportLoc; - - var fbClip = this._fb_width > vp.w || this._fb_height > vp.h; - var limitedVp = this._maxWidth !== 0 && this._maxHeight !== 0; - var clipping = false; - - if (limitedVp) { - clipping = vp.w > this._maxWidth || vp.h > this._maxHeight; - } - - return fbClip || (limitedVp && clipping); - }, - - // Overridden getters/setters - get_context: function () { - return this._drawCtx; - }, - - set_scale: function (scale) { - this._rescale(scale); - }, - - set_width: function (w) { - this._fb_width = w; - }, - get_width: function () { - return this._fb_width; - }, - - set_height: function (h) { - this._fb_height = h; - }, - get_height: function () { - return this._fb_height; - }, - - autoscale: function (containerWidth, containerHeight, downscaleOnly) { - var targetAspectRatio = containerWidth / containerHeight; - var fbAspectRatio = this._fb_width / this._fb_height; - - var scaleRatio; - if (fbAspectRatio >= targetAspectRatio) { - scaleRatio = containerWidth / this._fb_width; - } else { - scaleRatio = containerHeight / this._fb_height; - } - - var targetW, targetH; - if (scaleRatio > 1.0 && downscaleOnly) { - targetW = this._fb_width; - targetH = this._fb_height; - scaleRatio = 1.0; - } else if (fbAspectRatio >= targetAspectRatio) { - targetW = containerWidth; - targetH = Math.round(containerWidth / fbAspectRatio); - } else { - targetW = Math.round(containerHeight * fbAspectRatio); - targetH = containerHeight; - } - - // NB(directxman12): If you set the width directly, or set the - // style width to a number, the canvas is cleared. - // However, if you set the style width to a string - // ('NNNpx'), the canvas is scaled without clearing. - this._target.style.width = targetW + 'px'; - this._target.style.height = targetH + 'px'; - - this._scale = scaleRatio; - - return scaleRatio; // so that the mouse, etc scale can be set - }, - - // Private Methods - _rescale: function (factor) { - this._scale = factor; - - var w; - var h; - - if (this._viewport && - this._maxWidth !== 0 && this._maxHeight !== 0) { - w = Math.min(this._fb_width, this._maxWidth); - h = Math.min(this._fb_height, this._maxHeight); - } else { - w = this._fb_width; - h = this._fb_height; - } - - this._target.style.width = Math.round(factor * w) + 'px'; - this._target.style.height = Math.round(factor * h) + 'px'; - }, - - _setFillColor: function (color) { - var bgr; - if (this._true_color) { - bgr = color; - } else { - bgr = this._colourMap[color[0]]; - } - - var newStyle = 'rgb(' + bgr[2] + ',' + bgr[1] + ',' + bgr[0] + ')'; - if (newStyle !== this._prevDrawStyle) { - this._drawCtx.fillStyle = newStyle; - this._prevDrawStyle = newStyle; - } - }, - - _rgbImageData: function (x, y, vx, vy, width, height, arr, offset) { - var img = this._drawCtx.createImageData(width, height); - var data = img.data; - for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) { - data[i] = arr[j]; - data[i + 1] = arr[j + 1]; - data[i + 2] = arr[j + 2]; - data[i + 3] = 255; // Alpha - } - this._drawCtx.putImageData(img, x - vx, y - vy); - }, - - _bgrxImageData: function (x, y, vx, vy, width, height, arr, offset) { - var img = this._drawCtx.createImageData(width, height); - var data = img.data; - for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) { - data[i] = arr[j + 2]; - data[i + 1] = arr[j + 1]; - data[i + 2] = arr[j]; - data[i + 3] = 255; // Alpha - } - this._drawCtx.putImageData(img, x - vx, y - vy); - }, - - _cmapImageData: function (x, y, vx, vy, width, height, arr, offset) { - var img = this._drawCtx.createImageData(width, height); - var data = img.data; - var cmap = this._colourMap; - for (var i = 0, j = offset; i < width * height * 4; i += 4, j++) { - var bgr = cmap[arr[j]]; - data[i] = bgr[2]; - data[i + 1] = bgr[1]; - data[i + 2] = bgr[0]; - data[i + 3] = 255; // Alpha - } - this._drawCtx.putImageData(img, x - vx, y - vy); - }, - - _scan_renderQ: function () { - var ready = true; - while (ready && this._renderQ.length > 0) { - var a = this._renderQ[0]; - switch (a.type) { - case 'copy': - this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height); - break; - case 'fill': - this.fillRect(a.x, a.y, a.width, a.height, a.color); - break; - case 'blit': - this.blitImage(a.x, a.y, a.width, a.height, a.data, 0); - break; - case 'blitRgb': - this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0); - break; - case 'img': - if (a.img.complete) { - this.drawImage(a.img, a.x, a.y); - } else { - // We need to wait for this image to 'load' - // to keep things in-order - ready = false; - } - break; - } - - if (ready) { - this._renderQ.shift(); - } - } - - if (this._renderQ.length > 0) { - requestAnimFrame(this._scan_renderQ.bind(this)); - } - }, - }; - - Util.make_properties(Display, [ - ['target', 'wo', 'dom'], // Canvas element for rendering - ['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only) - ['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "data": data} - ['true_color', 'rw', 'bool'], // Use true-color pixel data - ['colourMap', 'rw', 'arr'], // Colour map array (when not true-color) - ['scale', 'rw', 'float'], // Display area scale factor 0.0 - 1.0 - ['viewport', 'rw', 'bool'], // Use viewport clipping - ['width', 'rw', 'int'], // Display area width - ['height', 'rw', 'int'], // Display area height - ['maxWidth', 'rw', 'int'], // Viewport max width (0 if disabled) - ['maxHeight', 'rw', 'int'], // Viewport max height (0 if disabled) - - ['render_mode', 'ro', 'str'], // Canvas rendering mode (read-only) - - ['prefer_js', 'rw', 'str'], // Prefer Javascript over canvas methods - ['cursor_uri', 'rw', 'raw'] // Can we render cursor using data URI - ]); - - // Class Methods - Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) { - var w = w0; - var h = h0; - if (h < w) { - h = w; // increase h to make it square - } else { - w = h; // increase w to make it square - } - - var cur = []; - - // Push multi-byte little-endian values - cur.push16le = function (num) { - this.push(num & 0xFF, (num >> 8) & 0xFF); - }; - cur.push32le = function (num) { - this.push(num & 0xFF, - (num >> 8) & 0xFF, - (num >> 16) & 0xFF, - (num >> 24) & 0xFF); - }; - - var IHDRsz = 40; - var RGBsz = w * h * 4; - var XORsz = Math.ceil((w * h) / 8.0); - var ANDsz = Math.ceil((w * h) / 8.0); - - cur.push16le(0); // 0: Reserved - cur.push16le(2); // 2: .CUR type - cur.push16le(1); // 4: Number of images, 1 for non-animated ico - - // Cursor #1 header (ICONDIRENTRY) - cur.push(w); // 6: width - cur.push(h); // 7: height - cur.push(0); // 8: colors, 0 -> true-color - cur.push(0); // 9: reserved - cur.push16le(hotx); // 10: hotspot x coordinate - cur.push16le(hoty); // 12: hotspot y coordinate - cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz); - // 14: cursor data byte size - cur.push32le(22); // 18: offset of cursor data in the file - - // Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO) - cur.push32le(IHDRsz); // 22: InfoHeader size - cur.push32le(w); // 26: Cursor width - cur.push32le(h * 2); // 30: XOR+AND height - cur.push16le(1); // 34: number of planes - cur.push16le(32); // 36: bits per pixel - cur.push32le(0); // 38: Type of compression - - cur.push32le(XORsz + ANDsz); - // 42: Size of Image - cur.push32le(0); // 46: reserved - cur.push32le(0); // 50: reserved - cur.push32le(0); // 54: reserved - cur.push32le(0); // 58: reserved - - // 62: color data (RGBQUAD icColors[]) - var y, x; - for (y = h - 1; y >= 0; y--) { - for (x = 0; x < w; x++) { - if (x >= w0 || y >= h0) { - cur.push(0); // blue - cur.push(0); // green - cur.push(0); // red - cur.push(0); // alpha - } else { - var idx = y * Math.ceil(w0 / 8) + Math.floor(x / 8); - var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0; - if (cmap) { - idx = (w0 * y) + x; - var rgb = cmap[pixels[idx]]; - cur.push(rgb[2]); // blue - cur.push(rgb[1]); // green - cur.push(rgb[0]); // red - cur.push(alpha); // alpha - } else { - idx = ((w0 * y) + x) * 4; - cur.push(pixels[idx + 2]); // blue - cur.push(pixels[idx + 1]); // green - cur.push(pixels[idx]); // red - cur.push(alpha); // alpha - } - } - } - } - - // XOR/bitmask data (BYTE icXOR[]) - // (ignored, just needs to be the right size) - for (y = 0; y < h; y++) { - for (x = 0; x < Math.ceil(w / 8); x++) { - cur.push(0); - } - } - - // AND/bitmask data (BYTE icAND[]) - // (ignored, just needs to be the right size) - for (y = 0; y < h; y++) { - for (x = 0; x < Math.ceil(w / 8); x++) { - cur.push(0); - } - } - - var url = 'data:image/x-icon;base64,' + Base64.encode(cur); - target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default'; - }; -})(); diff --git a/noVNC/include/font-awesome/css/font-awesome.css b/noVNC/include/font-awesome/css/font-awesome.css deleted file mode 100755 index ab5a4c1..0000000 --- a/noVNC/include/font-awesome/css/font-awesome.css +++ /dev/null @@ -1,66 +0,0 @@ -/*! - * Font Awesome 3.2.1 - * the iconic font designed for Bootstrap - * ------------------------------------------------------------------------------ - * The full suite of pictographic icons, examples, and documentation can be - * found at http://fontawesome.io. Stay up to date on Twitter at - * http://twitter.com/fontawesome. - * - * License - * ------------------------------------------------------------------------------ - * - The Font Awesome font is licensed under SIL OFL 1.1 - - * http://scripts.sil.org/OFL - * - Font Awesome CSS, LESS, and SASS files are licensed under MIT License - - * http://opensource.org/licenses/mit-license.html - * - Font Awesome documentation licensed under CC BY 3.0 - - * http://creativecommons.org/licenses/by/3.0/ - * - Attribution is no longer required in Font Awesome 3.0, but much appreciated: - * "Font Awesome by Dave Gandy - http://fontawesome.io" - * - * Author - Dave Gandy - * ------------------------------------------------------------------------------ - * Email: dave@fontawesome.io - * Twitter: http://twitter.com/davegandy - * Work: Lead Product Designer @ Kyruus - http://kyruus.com - */ -/* FONT PATH - * -------------------------- */ -@font-face { - font-family: 'FontAwesome'; - src: url('../font/fontawesome-webfont.eot?v=3.2.1'); - src: url('../font/fontawesome-webfont.eot?#iefix&v=3.2.1') format('embedded-opentype'), url('../font/fontawesome-webfont.woff?v=3.2.1') format('woff'), url('../font/fontawesome-webfont.ttf?v=3.2.1') format('truetype'), url('../font/fontawesome-webfont.svg#fontawesomeregular?v=3.2.1') format('svg'); - font-weight: normal; - font-style: normal; -} -/* FONT AWESOME CORE - * -------------------------- */ -[class^="icon-"], -[class*=" icon-"] { - font-family: FontAwesome; - font-weight: normal; - font-style: normal; - text-decoration: inherit; - -webkit-font-smoothing: antialiased; - *margin-right: .3em; -} -[class^="icon-"]:before, -[class*=" icon-"]:before { - text-decoration: inherit; - display: inline-block; - speak: none; -} - -/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen - readers do not read off random characters that represent icons */ -.icon-long-arrow-down:before { - content: "\f175"; -} -.icon-long-arrow-up:before { - content: "\f176"; -} -.icon-long-arrow-left:before { - content: "\f177"; -} -.icon-long-arrow-right:before { - content: "\f178"; -} diff --git a/noVNC/include/font-awesome/font/FontAwesome.otf b/noVNC/include/font-awesome/font/FontAwesome.otf deleted file mode 100755 index 8b0f54e47e1d356dcf1496942a50e228e0f1ee14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62856 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