diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/images/alt.png | bin | 0 -> 339 bytes | |||
-rw-r--r-- | app/images/clipboard.png | bin | 0 -> 501 bytes | |||
-rw-r--r-- | app/images/connect.png | bin | 0 -> 404 bytes | |||
-rw-r--r-- | app/images/ctrl.png | bin | 0 -> 354 bytes | |||
-rw-r--r-- | app/images/ctrlaltdel.png | bin | 0 -> 317 bytes | |||
-rw-r--r-- | app/images/disconnect.png | bin | 0 -> 1378 bytes | |||
-rw-r--r-- | app/images/drag.png | bin | 0 -> 963 bytes | |||
-rw-r--r-- | app/images/esc.png | bin | 0 -> 385 bytes | |||
-rw-r--r-- | app/images/favicon.ico | bin | 0 -> 1150 bytes | |||
-rw-r--r-- | app/images/favicon.png | bin | 0 -> 453 bytes | |||
-rw-r--r-- | app/images/fullscreen.png | bin | 0 -> 851 bytes | |||
-rw-r--r-- | app/images/keyboard.png | bin | 0 -> 1283 bytes | |||
-rw-r--r-- | app/images/mouse_left.png | bin | 0 -> 511 bytes | |||
-rw-r--r-- | app/images/mouse_middle.png | bin | 0 -> 517 bytes | |||
-rw-r--r-- | app/images/mouse_none.png | bin | 0 -> 497 bytes | |||
-rw-r--r-- | app/images/mouse_right.png | bin | 0 -> 513 bytes | |||
-rw-r--r-- | app/images/power.png | bin | 0 -> 390 bytes | |||
-rw-r--r-- | app/images/screen_320x460.png | bin | 0 -> 12778 bytes | |||
-rw-r--r-- | app/images/screen_57x57.png | bin | 0 -> 1807 bytes | |||
-rw-r--r-- | app/images/screen_700x700.png | bin | 0 -> 17930 bytes | |||
-rw-r--r-- | app/images/settings.png | bin | 0 -> 2495 bytes | |||
-rw-r--r-- | app/images/tab.png | bin | 0 -> 387 bytes | |||
-rw-r--r-- | app/images/toggleextrakeys.png | bin | 0 -> 735 bytes | |||
-rw-r--r-- | app/styles/Orbitron700.ttf | bin | 0 -> 38580 bytes | |||
-rw-r--r-- | app/styles/Orbitron700.woff | bin | 0 -> 17472 bytes | |||
-rw-r--r-- | app/styles/base.css | 527 | ||||
-rw-r--r-- | app/styles/black.css | 71 | ||||
-rw-r--r-- | app/styles/blue.css | 64 | ||||
-rw-r--r-- | app/ui.js | 1309 | ||||
-rw-r--r-- | app/webutil.js | 281 |
30 files changed, 2252 insertions, 0 deletions
diff --git a/app/images/alt.png b/app/images/alt.png Binary files differnew file mode 100644 index 0000000..d42af7b --- /dev/null +++ b/app/images/alt.png diff --git a/app/images/clipboard.png b/app/images/clipboard.png Binary files differnew file mode 100644 index 0000000..24df33c --- /dev/null +++ b/app/images/clipboard.png diff --git a/app/images/connect.png b/app/images/connect.png Binary files differnew file mode 100644 index 0000000..79e71ad --- /dev/null +++ b/app/images/connect.png diff --git a/app/images/ctrl.png b/app/images/ctrl.png Binary files differnew file mode 100644 index 0000000..a63b601 --- /dev/null +++ b/app/images/ctrl.png diff --git a/app/images/ctrlaltdel.png b/app/images/ctrlaltdel.png Binary files differnew file mode 100644 index 0000000..31922e5 --- /dev/null +++ b/app/images/ctrlaltdel.png diff --git a/app/images/disconnect.png b/app/images/disconnect.png Binary files differnew file mode 100644 index 0000000..8832f5e --- /dev/null +++ b/app/images/disconnect.png diff --git a/app/images/drag.png b/app/images/drag.png Binary files differnew file mode 100644 index 0000000..433f896 --- /dev/null +++ b/app/images/drag.png diff --git a/app/images/esc.png b/app/images/esc.png Binary files differnew file mode 100644 index 0000000..ece5f7c --- /dev/null +++ b/app/images/esc.png diff --git a/app/images/favicon.ico b/app/images/favicon.ico Binary files differnew file mode 100644 index 0000000..c999634 --- /dev/null +++ b/app/images/favicon.ico diff --git a/app/images/favicon.png b/app/images/favicon.png Binary files differnew file mode 100644 index 0000000..e2bdb19 --- /dev/null +++ b/app/images/favicon.png diff --git a/app/images/fullscreen.png b/app/images/fullscreen.png Binary files differnew file mode 100644 index 0000000..f4fa0ce --- /dev/null +++ b/app/images/fullscreen.png diff --git a/app/images/keyboard.png b/app/images/keyboard.png Binary files differnew file mode 100644 index 0000000..f797952 --- /dev/null +++ b/app/images/keyboard.png diff --git a/app/images/mouse_left.png b/app/images/mouse_left.png Binary files differnew file mode 100644 index 0000000..1de7a48 --- /dev/null +++ b/app/images/mouse_left.png diff --git a/app/images/mouse_middle.png b/app/images/mouse_middle.png Binary files differnew file mode 100644 index 0000000..81fbd9b --- /dev/null +++ b/app/images/mouse_middle.png diff --git a/app/images/mouse_none.png b/app/images/mouse_none.png Binary files differnew file mode 100644 index 0000000..93dbf57 --- /dev/null +++ b/app/images/mouse_none.png diff --git a/app/images/mouse_right.png b/app/images/mouse_right.png Binary files differnew file mode 100644 index 0000000..355b25d --- /dev/null +++ b/app/images/mouse_right.png diff --git a/app/images/power.png b/app/images/power.png Binary files differnew file mode 100644 index 0000000..f68fd08 --- /dev/null +++ b/app/images/power.png diff --git a/app/images/screen_320x460.png b/app/images/screen_320x460.png Binary files differnew file mode 100644 index 0000000..172ec55 --- /dev/null +++ b/app/images/screen_320x460.png diff --git a/app/images/screen_57x57.png b/app/images/screen_57x57.png Binary files differnew file mode 100644 index 0000000..e2085f2 --- /dev/null +++ b/app/images/screen_57x57.png diff --git a/app/images/screen_700x700.png b/app/images/screen_700x700.png Binary files differnew file mode 100644 index 0000000..ae67768 --- /dev/null +++ b/app/images/screen_700x700.png diff --git a/app/images/settings.png b/app/images/settings.png Binary files differnew file mode 100644 index 0000000..a43f5e1 --- /dev/null +++ b/app/images/settings.png diff --git a/app/images/tab.png b/app/images/tab.png Binary files differnew file mode 100644 index 0000000..8413487 --- /dev/null +++ b/app/images/tab.png diff --git a/app/images/toggleextrakeys.png b/app/images/toggleextrakeys.png Binary files differnew file mode 100644 index 0000000..ad8e0a7 --- /dev/null +++ b/app/images/toggleextrakeys.png diff --git a/app/styles/Orbitron700.ttf b/app/styles/Orbitron700.ttf Binary files differnew file mode 100644 index 0000000..e28729d --- /dev/null +++ b/app/styles/Orbitron700.ttf diff --git a/app/styles/Orbitron700.woff b/app/styles/Orbitron700.woff Binary files differnew file mode 100644 index 0000000..61db630 --- /dev/null +++ b/app/styles/Orbitron700.woff diff --git a/app/styles/base.css b/app/styles/base.css new file mode 100644 index 0000000..382234e --- /dev/null +++ b/app/styles/base.css @@ -0,0 +1,527 @@ +/* + * noVNC base CSS + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2016 Samuel Mannehed for Cendio AB + * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + +body { + margin:0; + padding:0; + font-family: Helvetica; + /*Background image with light grey curve.*/ + background-color:#494949; + background-repeat:no-repeat; + background-position:right bottom; + height:100%; +} + +html { + height:100%; +} + +#noVNC_controls ul { + list-style: none; + margin: 0px; + padding: 0px; +} +#noVNC_controls li { + padding-bottom:8px; +} + +#noVNC_setting_host { + width:150px; +} +#noVNC_setting_port { + width: 80px; +} +#noVNC_setting_password { + width: 150px; +} +#noVNC_setting_encrypt { +} +#noVNC_setting_path { + width: 100px; +} +#noVNC_connect_button { + width: 110px; + float:right; +} + +#noVNC_buttons { + white-space: nowrap; +} + +#noVNC_view_drag_button { + display: none; +} +#noVNC_sendCtrlAltDel_button { + display: none; +} +#noVNC_fullscreen_button { + display: none; +} +#noVNC_xvp_buttons { + display: none; +} +#noVNC_mobile_buttons { + display: none; +} + +#noVNC_extra_keys { + display: inline; + list-style-type: none; + padding: 0px; + margin: 0px; + position: relative; +} + +.noVNC_buttons_left { + float: left; + z-index: 1; + position: relative; +} + +.noVNC_buttons_right { + float:right; + right: 0px; + z-index: 2; + position: absolute; +} + +#noVNC_status { + font-size: 12px; + padding-top: 4px; + height:32px; + text-align: center; + font-weight: bold; + color: #fff; +} + +#noVNC_settings_menu { + margin: 3px; + text-align: left; +} +#noVNC_settings_menu ul { + list-style: none; + margin: 0px; + padding: 0px; +} + +#noVNC_settings_apply { + float:right; +} + +#noVNC_container { + display: table; + width:100%; + height:100%; + background-color:#313131; + border-bottom-right-radius: 800px 600px; + /*border-top-left-radius: 800px 600px;*/ +} + +#noVNC_screen { + display: none; + position: absolute; + margin: 0px; + padding: 0px; + bottom: 0px; + top: 36px; /* the height of the control bar */ + left: 0px; + right: 0px; + width: auto; + height: auto; +} + +/* Do not set width/height for VNC_canvas or incorrect + * scaling will occur. Canvas size depends on remote VNC + * settings and noVNC settings. */ +#noVNC_canvas { + position: absolute; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; +} + +#VNC_clipboard_clear_button { + float:right; +} +#VNC_clipboard_text { + font-size: 11px; +} + +#noVNC_clipboard_clear_button { + float:right; +} + +/*Bubble contents divs*/ +#noVNC_settings { + display:none; + margin-top:73px; + right:20px; + position:fixed; +} + +#noVNC_controls { + display:none; + margin-top:73px; + right:12px; + position:fixed; +} +#noVNC_controls.top:after { + right:15px; +} + +#noVNC_description { + display:none; + position:fixed; + + margin-top:73px; + right:20px; + left:20px; + padding:15px; + color:#000; + background:#eee; /* default background for browsers without gradient support */ + + border:2px solid #E0E0E0; + -webkit-border-radius:10px; + -moz-border-radius:10px; + border-radius:10px; +} + +#noVNC_popup_status { + display:none; + position: fixed; + z-index: 1; + + margin:15px; + margin-top:60px; + padding:15px; + width:auto; + + text-align:center; + font-weight:bold; + word-wrap:break-word; + color:#fff; + background:rgba(0,0,0,0.65); + + -webkit-border-radius:10px; + -moz-border-radius:10px; + border-radius:10px; +} + +#noVNC_xvp { + display:none; + margin-top:73px; + right:30px; + position:fixed; +} +#noVNC_xvp.top:after { + right:125px; +} + +#noVNC_clipboard { + display:none; + margin-top:73px; + right:30px; + position:fixed; +} +#noVNC_clipboard.top:after { + right:85px; +} + +#noVNC_keyboardinput { + width:1px; + height:1px; + background-color:#fff; + color:#fff; + border:0; + position: relative; + left: -40px; + z-index: -1; + ime-mode: disabled; +} + +/* + * Advanced Styling + */ + +.noVNC_status_normal { + background: #b2bdcd; /* Old browsers */ + background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} +.noVNC_status_error { + background: #f04040; /* Old browsers */ + background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} +.noVNC_status_warn { + background: #f0f040; /* Old browsers */ + background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ +} + +/* Control bar */ +#noVNC_control_bar { + position:fixed; + + display:block; + height:36px; + left:0; + top:0; + width:100%; + z-index:200; +} + +.noVNC_status_button { + padding: 4px 4px; + vertical-align: middle; + border:1px solid #869dbc; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + background: #b2bdcd; /* Old browsers */ + background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ + /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ +} + +.noVNC_status_button_selected { + padding: 4px 4px; + vertical-align: middle; + border:1px solid #4366a9; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + background: #779ced; /* Old browsers */ + background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */ + /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ +} + +.noVNC_status_button:disabled { + opacity: 0.4; +} + + +/*Settings Bubble*/ +.triangle-right { + position:relative; + padding:15px; + margin:1em 0 3em; + color:#fff; + background:#fff; /* default background for browsers without gradient support */ + /* css3 */ + /*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698)); + background:-moz-linear-gradient(#2e88c4, #075698); + background:-o-linear-gradient(#2e88c4, #075698); + background:linear-gradient(#2e88c4, #075698);*/ + -webkit-border-radius:10px; + -moz-border-radius:10px; + border-radius:10px; + color:#000; + border:2px solid #E0E0E0; +} + +.triangle-right.top:after { + border-color: transparent #E0E0E0; + border-width: 20px 20px 0 0; + bottom: auto; + left: auto; + right: 50px; + top: -20px; +} + +.triangle-right:after { + content:""; + position:absolute; + bottom:-20px; /* value = - border-top-width - border-bottom-width */ + left:50px; /* controls horizontal position */ + border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */ + border-style:solid; + border-color:#E0E0E0 transparent; + /* reduce the damage in FF3.0 */ + display:block; + width:0; +} + +.triangle-right.top:after { + top:-40px; /* value = - border-top-width - border-bottom-width */ + right:50px; /* controls horizontal position */ + bottom:auto; + left:auto; + border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */ + border-color:transparent #E0E0E0; +} + +/*Default noVNC logo.*/ +/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ +@font-face { + font-family: 'Orbitron'; + font-style: normal; + font-weight: 700; + src: local('?'), url('Orbitron700.woff') format('woff'), + url('Orbitron700.ttf') format('truetype'); +} + +#noVNC_logo { + margin-top: 170px; + margin-left: 10px; + color:yellow; + text-align:left; + font-family: 'Orbitron', 'OrbitronTTF', sans-serif; + line-height:90%; + text-shadow: + 5px 5px 0 #000, + -1px -1px 0 #000, + 1px -1px 0 #000, + -1px 1px 0 #000, + 1px 1px 0 #000; +} + + +#noVNC_logo span{ + color:green; +} + +/* ---------------------------------------- + * Media sizing + * ---------------------------------------- + */ + + +.noVNC_status_button { + font-size: 12px; +} + +#noVNC_clipboard_text { + width: 500px; +} + +#noVNC_logo { + font-size: 180px; +} + +.noVNC_buttons_left { + padding-left: 10px; +} + +.noVNC_buttons_right { + padding-right: 10px; +} + +#noVNC_status { + z-index: 0; + position: absolute; + width: 100%; + margin-left: 0px; +} + +#noVNC_toggleExtraKeys_button { display: none; } +#noVNC_toggleCtrl_button { display: inline; } +#noVNC_toggleAlt_button { display: inline; } +#noVNC_sendTab_button { display: inline; } +#noVNC_sendEsc_button { display: inline; } + +/* left-align the status text on lower resolutions */ +@media screen and (max-width: 800px){ + #noVNC_status { + z-index: 1; + position: relative; + width: auto; + float: left; + margin-left: 4px; + } +} + +@media screen and (max-width: 640px){ + #noVNC_clipboard_text { + width: 410px; + } + #noVNC_logo { + font-size: 150px; + } + .noVNC_status_button { + font-size: 10px; + } + .noVNC_buttons_left { + padding-left: 0px; + } + .noVNC_buttons_right { + padding-right: 0px; + } + /* collapse the extra keys on lower resolutions */ + #noVNC_toggleExtraKeys_button { + display: inline; + } + #noVNC_toggleCtrl_button { + display: none; + position: absolute; + top: 30px; + left: 0px; + } + #noVNC_toggleAlt_button { + display: none; + position: absolute; + top: 65px; + left: 0px; + } + #noVNC_sendTab_button { + display: none; + position: absolute; + top: 100px; + left: 0px; + } + #noVNC_sendEsc_button { + display: none; + position: absolute; + top: 135px; + left: 0px; + } +} + +@media screen and (min-width: 321px) and (max-width: 480px) { + #noVNC_clipboard_text { + width: 250px; + } + #noVNC_logo { + font-size: 110px; + } +} + +@media screen and (max-width: 320px) { + .noVNC_status_button { + font-size: 9px; + } + #noVNC_clipboard_text { + width: 220px; + } + #noVNC_logo { + font-size: 90px; + } +} diff --git a/app/styles/black.css b/app/styles/black.css new file mode 100644 index 0000000..5c4558d --- /dev/null +++ b/app/styles/black.css @@ -0,0 +1,71 @@ +/* + * noVNC black CSS + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB + * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + +#noVNC_keyboardinput { + background-color:#000; +} + +.noVNC_status_normal { + background: #4c4c4c; /* Old browsers */ + background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} +.noVNC_status_error { + background: #f04040; /* Old browsers */ + background: -moz-linear-gradient(top, #f04040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + background: linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} +.noVNC_status_warn { + background: #f0f040; /* Old browsers */ + background: -moz-linear-gradient(top, #f0f040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + background: linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} + +.triangle-right { + border:2px solid #fff; + background:#000; + color:#fff; +} + +.noVNC_status_button { + font-size: 12px; + vertical-align: middle; + border:1px solid #4c4c4c; + + background: #4c4c4c; /* Old browsers */ + background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} + +.noVNC_status_button_selected { + background: #9dd53a; /* Old browsers */ + background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */ +} diff --git a/app/styles/blue.css b/app/styles/blue.css new file mode 100644 index 0000000..4ab53bd --- /dev/null +++ b/app/styles/blue.css @@ -0,0 +1,64 @@ +/* + * noVNC blue CSS + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 Samuel Mannehed for Cendio AB + * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + +.noVNC_status_normal { + background-color:#04073d; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.54, rgb(10,15,79)), + color-stop(0.5, rgb(4,7,61)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(10,15,79) 54%, + rgb(4,7,61) 50% + ); +} +.noVNC_status_error { + background-color:#f04040; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.54, rgb(240,64,64)), + color-stop(0.5, rgb(4,7,61)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(4,7,61) 54%, + rgb(249,64,64) 50% + ); +} +.noVNC_status_warn { + background-color:#f0f040; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.54, rgb(240,240,64)), + color-stop(0.5, rgb(4,7,61)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(4,7,61) 54%, + rgb(240,240,64) 50% + ); +} + +.triangle-right { + border:2px solid #fff; + background:#04073d; + color:#fff; +} + +#noVNC_keyboardinput { + background-color:#04073d; +} + diff --git a/app/ui.js b/app/ui.js new file mode 100644 index 0000000..c8251fb --- /dev/null +++ b/app/ui.js @@ -0,0 +1,1309 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2016 Samuel Mannehed 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/keysym"; + * import RFB from "../core/rfb"; + * import Display from "../core/display"; + * import WebUtil from "./webutil"; + */ + +var UI; + +(function () { + "use strict"; + + /* [begin skip-as-module] */ + // Load supporting scripts + Util.load_scripts( + {'core': ["base64.js", "websock.js", "des.js", "keysymdef.js", + "xtscancodes.js", "keyboard.js", "input.js", "display.js", + "inflator.js", "rfb.js", "keysym.js"], + '.': ["webutil.js"]}); + + window.onscriptsload = function () { UI.load(); }; + /* [end skip-as-module] */ + + UI = { + + rfb_state: 'loaded', + + resizeTimeout: null, + popupStatusTimeout: null, + hideKeyboardTimeout: null, + + settingsOpen: false, + connSettingsOpen: false, + clipboardOpen: false, + keyboardVisible: false, + extraKeysVisible: false, + + isTouchDevice: false, + isSafari: false, + rememberedClipSetting: null, + lastKeyboardinput: null, + defaultKeyboardinputLen: 100, + + ctrlOn: false, + altOn: false, + + // 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) { + UI.isTouchDevice = 'ontouchstart' in document.documentElement; + + // Stylesheet selection dropdown + var sheet = WebUtil.selectStylesheet(); + var sheets = WebUtil.getStylesheets(); + var i; + for (i = 0; i < sheets.length; i += 1) { + UI.addOption(document.getElementById('noVNC_setting_stylesheet'),sheets[i].title, sheets[i].title); + } + + // 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'); + WebUtil.init_logging(UI.getSetting('logging')); + + UI.initSetting('stylesheet', 'default'); + WebUtil.selectStylesheet(null); + // call twice to get around webkit bug + WebUtil.selectStylesheet(UI.getSetting('stylesheet')); + + // 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('password', ''); + UI.initSetting('encrypt', (window.location.protocol === "https:")); + UI.initSetting('true_color', true); + UI.initSetting('cursor', !UI.isTouchDevice); + UI.initSetting('resize', 'off'); + UI.initSetting('shared', true); + UI.initSetting('view_only', false); + UI.initSetting('path', 'websockify'); + UI.initSetting('repeaterID', ''); + UI.initSetting('token', ''); + + var autoconnect = WebUtil.getConfigVar('autoconnect', false); + if (autoconnect === 'true' || autoconnect == '1') { + autoconnect = true; + UI.connect(); + } else { + autoconnect = false; + } + + UI.updateVisualState(); + + document.getElementById('noVNC_setting_host').focus(); + + // Show mouse selector buttons on touch screen devices + if (UI.isTouchDevice) { + // Show mobile buttons + document.getElementById('noVNC_mobile_buttons').style.display = "inline"; + UI.setMouseButton(); + // Remove the address bar + setTimeout(function() { window.scrollTo(0, 1); }, 100); + UI.forceSetting('clip', true); + } else { + UI.initSetting('clip', false); + } + + UI.setViewClip(); + UI.setBarPosition(); + + Util.addEvent(window, 'resize', function () { + UI.applyResizeMode(); + UI.setViewClip(); + UI.updateViewDrag(); + UI.setBarPosition(); + } ); + + UI.isSafari = (navigator.userAgent.indexOf('Safari') != -1 && + navigator.userAgent.indexOf('Chrome') == -1); + + // 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').style.display = "inline"; + Util.addEvent(window, 'fullscreenchange', UI.updateFullscreenButton); + Util.addEvent(window, 'mozfullscreenchange', UI.updateFullscreenButton); + Util.addEvent(window, 'webkitfullscreenchange', UI.updateFullscreenButton); + Util.addEvent(window, 'msfullscreenchange', UI.updateFullscreenButton); + } + + Util.addEvent(window, 'load', UI.keyboardinputReset); + + // While connected we want to display a confirmation dialogue + // if the user tries to leave the page + Util.addEvent(window, 'beforeunload', function (e) { + if (UI.rfb && UI.rfb_state === 'normal') { + var msg = "You are currently connected."; + e.returnValue = msg; + return msg; + } else { + return void 0; // To prevent the dialogue when disconnected + } + }); + + // Show description by default when hosted at for kanaka.github.com + if (location.host === "kanaka.github.io") { + // Open the description dialog + document.getElementById('noVNC_description').style.display = "block"; + } else { + // Show the connect panel on first load unless autoconnecting + if (autoconnect === UI.connSettingsOpen) { + UI.toggleConnectPanel(); + } + } + + // Add mouse event click/focus/blur event handlers to the UI + UI.addMouseHandlers(); + + if (typeof callback === "function") { + callback(UI.rfb); + } + }, + + initRFB: function() { + try { + UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'), + 'onUpdateState': UI.updateState, + 'onXvpInit': UI.updateXvpButton, + 'onClipboard': UI.clipboardReceive, + 'onFBUComplete': UI.initialResize, + 'onFBResize': UI.updateViewDrag, + 'onDesktopName': UI.updateDocumentTitle}); + return true; + } catch (exc) { + UI.updateState(null, 'fatal', null, 'Unable to create RFB client -- ' + exc); + return false; + } + }, + + addMouseHandlers: function() { + // Setup interface handlers that can't be inline + document.getElementById("noVNC_view_drag_button").onclick = UI.toggleViewDrag; + document.getElementById("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); }; + document.getElementById("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); }; + document.getElementById("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); }; + document.getElementById("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); }; + document.getElementById("noVNC_keyboard_button").onclick = UI.showKeyboard; + + document.getElementById("noVNC_keyboardinput").oninput = UI.keyInput; + document.getElementById("noVNC_keyboardinput").onblur = UI.hideKeyboard; + document.getElementById("noVNC_keyboardinput").onsubmit = function () { return false; }; + + document.getElementById("noVNC_toggleExtraKeys_button").onclick = UI.toggleExtraKeys; + document.getElementById("noVNC_toggleCtrl_button").onclick = UI.toggleCtrl; + document.getElementById("noVNC_toggleAlt_button").onclick = UI.toggleAlt; + document.getElementById("noVNC_sendTab_button").onclick = UI.sendTab; + document.getElementById("noVNC_sendEsc_button").onclick = UI.sendEsc; + + document.getElementById("noVNC_sendCtrlAltDel_button").onclick = UI.sendCtrlAltDel; + document.getElementById("noVNC_xvpShutdown_button").onclick = function() { UI.rfb.xvpShutdown(); }, + document.getElementById("noVNC_xvpReboot_button").onclick = function() { UI.rfb.xvpReboot(); }, + document.getElementById("noVNC_xvpReset_button").onclick = function() { UI.rfb.xvpReset(); }, + document.getElementById("noVNC_status").onclick = UI.popupStatus; + document.getElementById("noVNC_popup_status").onclick = UI.closePopup; + document.getElementById("noVNC_toggleXvp_button").onclick = UI.toggleXvpPanel; + document.getElementById("noVNC_clipboard_button").onclick = UI.toggleClipboardPanel; + document.getElementById("noVNC_fullscreen_button").onclick = UI.toggleFullscreen; + document.getElementById("noVNC_settings_button").onclick = UI.toggleSettingsPanel; + document.getElementById("noVNC_connectPanel_button").onclick = UI.toggleConnectPanel; + document.getElementById("noVNC_disconnect_button").onclick = UI.disconnect; + document.getElementById("noVNC_description_button").onclick = UI.toggleConnectPanel; + + document.getElementById("noVNC_clipboard_text").onfocus = UI.displayBlur; + document.getElementById("noVNC_clipboard_text").onblur = UI.displayFocus; + document.getElementById("noVNC_clipboard_text").onchange = UI.clipboardSend; + document.getElementById("noVNC_clipboard_clear_button").onclick = UI.clipboardClear; + + document.getElementById("noVNC_settings_menu").onmouseover = UI.displayBlur; + document.getElementById("noVNC_settings_menu").onmouseover = UI.displayFocus; + document.getElementById("noVNC_settings_apply").onclick = UI.settingsApply; + + document.getElementById("noVNC_connect_button").onclick = UI.connect; + + document.getElementById("noVNC_setting_resize").onchange = UI.enableDisableViewClip; + }, + +/* ------^------- + * /INIT + * ============== + * VISUAL + * ------v------*/ + + updateState: function(rfb, state, oldstate, msg) { + UI.rfb_state = state; + var klass; + switch (state) { + case 'failed': + case 'fatal': + klass = "noVNC_status_error"; + break; + case 'normal': + klass = "noVNC_status_normal"; + break; + case 'disconnected': + document.getElementById('noVNC_logo').style.display = "block"; + document.getElementById('noVNC_screen').style.display = "none"; + /* falls through */ + case 'loaded': + klass = "noVNC_status_normal"; + break; + case 'password': + UI.toggleConnectPanel(); + + document.getElementById('noVNC_connect_button').value = "Send Password"; + document.getElementById('noVNC_connect_button').onclick = UI.setPassword; + document.getElementById('noVNC_setting_password').focus(); + + klass = "noVNC_status_warn"; + break; + default: + klass = "noVNC_status_warn"; + break; + } + + if (typeof(msg) !== 'undefined') { + document.getElementById('noVNC_control_bar').setAttribute("class", klass); + document.getElementById('noVNC_status').innerHTML = msg; + } + + UI.updateVisualState(); + }, + + // Disable/enable controls depending on connection state + updateVisualState: function() { + var connected = UI.rfb && UI.rfb_state === 'normal'; + + //Util.Debug(">> updateVisualState"); + document.getElementById('noVNC_setting_encrypt').disabled = connected; + document.getElementById('noVNC_setting_true_color').disabled = connected; + if (Util.browserSupportsCursorURIs()) { + document.getElementById('noVNC_setting_cursor').disabled = connected; + } else { + UI.updateSetting('cursor', !UI.isTouchDevice); + document.getElementById('noVNC_setting_cursor').disabled = true; + } + + UI.enableDisableViewClip(); + document.getElementById('noVNC_setting_resize').disabled = connected; + document.getElementById('noVNC_setting_shared').disabled = connected; + document.getElementById('noVNC_setting_view_only').disabled = connected; + document.getElementById('noVNC_setting_path').disabled = connected; + document.getElementById('noVNC_setting_repeaterID').disabled = connected; + + if (connected) { + UI.setViewClip(); + UI.setMouseButton(1); + document.getElementById('noVNC_clipboard_button').style.display = "inline"; + document.getElementById('noVNC_keyboard_button').style.display = "inline"; + document.getElementById('noVNC_extra_keys').style.display = ""; + document.getElementById('noVNC_sendCtrlAltDel_button').style.display = "inline"; + } else { + UI.setMouseButton(); + document.getElementById('noVNC_clipboard_button').style.display = "none"; + document.getElementById('noVNC_keyboard_button').style.display = "none"; + document.getElementById('noVNC_extra_keys').style.display = "none"; + document.getElementById('noVNC_sendCtrlAltDel_button').style.display = "none"; + UI.updateXvpButton(0); + } + + // State change disables viewport dragging. + // It is enabled (toggled) by direct click on the button + UI.updateViewDrag(false); + + switch (UI.rfb_state) { + case 'fatal': + case 'failed': + case 'disconnected': + document.getElementById('noVNC_connectPanel_button').style.display = ""; + document.getElementById('noVNC_disconnect_button').style.display = "none"; + UI.connSettingsOpen = false; + UI.toggleConnectPanel(); + break; + case 'loaded': + document.getElementById('noVNC_connectPanel_button').style.display = ""; + document.getElementById('noVNC_disconnect_button').style.display = "none"; + break; + default: + document.getElementById('noVNC_connectPanel_button').style.display = "none"; + document.getElementById('noVNC_disconnect_button').style.display = ""; + break; + } + + //Util.Debug("<< updateVisualState"); + }, + + popupStatus: function(text) { + var psp = document.getElementById('noVNC_popup_status'); + + clearTimeout(UI.popupStatusTimeout); + + if (typeof text === 'string') { + psp.innerHTML = text; + } else { + psp.innerHTML = document.getElementById('noVNC_status').innerHTML; + } + psp.style.display = "block"; + psp.style.left = window.innerWidth/2 - + parseInt(window.getComputedStyle(psp).width)/2 -30 + "px"; + + // Show the popup for a maximum of 1.5 seconds + UI.popupStatusTimeout = setTimeout(UI.closePopup, 1500); + }, + + closePopup: function() { + clearTimeout(UI.popupStatusTimeout); + document.getElementById('noVNC_popup_status').style.display = "none"; + }, + +/* ------^------- + * /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; + }, + + // Force a setting to be a certain value + forceSetting: function(name, val) { + UI.updateSetting(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; + }, + + // Save/apply settings when 'Apply' button is pressed + settingsApply: function() { + //Util.Debug(">> settingsApply"); + UI.saveSetting('encrypt'); + UI.saveSetting('true_color'); + if (Util.browserSupportsCursorURIs()) { + UI.saveSetting('cursor'); + } + + UI.saveSetting('resize'); + + if (UI.getSetting('resize') === 'downscale' || UI.getSetting('resize') === 'scale') { + UI.forceSetting('clip', false); + } + + UI.saveSetting('clip'); + UI.saveSetting('shared'); + UI.saveSetting('view_only'); + UI.saveSetting('path'); + UI.saveSetting('repeaterID'); + UI.saveSetting('stylesheet'); + UI.saveSetting('logging'); + + // Settings with immediate (non-connected related) effect + WebUtil.selectStylesheet(UI.getSetting('stylesheet')); + WebUtil.init_logging(UI.getSetting('logging')); + UI.setViewClip(); + UI.updateViewDrag(); + //Util.Debug("<< settingsApply"); + }, + + // Open menu + openSettingsMenu: function() { + // Close the description panel + document.getElementById('noVNC_description').style.display = "none"; + // Close clipboard panel if open + if (UI.clipboardOpen === true) { + UI.toggleClipboardPanel(); + } + // Close connection settings if open + if (UI.connSettingsOpen === true) { + UI.toggleConnectPanel(); + } + // Close XVP panel if open + if (UI.xvpOpen === true) { + UI.toggleXvpPanel(); + } + document.getElementById('noVNC_settings').style.display = "block"; + document.getElementById('noVNC_settings_button').className = "noVNC_status_button_selected"; + UI.settingsOpen = true; + }, + + // Close menu (without applying settings) + closeSettingsMenu: function() { + document.getElementById('noVNC_settings').style.display = "none"; + document.getElementById('noVNC_settings_button').className = "noVNC_status_button"; + UI.settingsOpen = false; + }, + + // Toggle the settings menu: + // On open, settings are refreshed from saved cookies. + // On close, settings are applied + toggleSettingsPanel: function() { + // Close the description panel + document.getElementById('noVNC_description').style.display = "none"; + if (UI.settingsOpen) { + UI.settingsApply(); + UI.closeSettingsMenu(); + } else { + UI.updateSetting('encrypt'); + UI.updateSetting('true_color'); + if (Util.browserSupportsCursorURIs()) { + UI.updateSetting('cursor'); + } else { + UI.updateSetting('cursor', !UI.isTouchDevice); + document.getElementById('noVNC_setting_cursor').disabled = true; + } + UI.updateSetting('clip'); + UI.updateSetting('resize'); + UI.updateSetting('shared'); + UI.updateSetting('view_only'); + UI.updateSetting('path'); + UI.updateSetting('repeaterID'); + UI.updateSetting('stylesheet'); + UI.updateSetting('logging'); + + UI.openSettingsMenu(); + } + }, + +/* ------^------- + * /SETTINGS + * ============== + * XVP + * ------v------*/ + + // Show the XVP panel + toggleXvpPanel: function() { + // Close the description panel + document.getElementById('noVNC_description').style.display = "none"; + // Close settings if open + if (UI.settingsOpen === true) { + UI.settingsApply(); + UI.closeSettingsMenu(); + } + // Close connection settings if open + if (UI.connSettingsOpen === true) { + UI.toggleConnectPanel(); + } + // Close clipboard panel if open + if (UI.clipboardOpen === true) { + UI.toggleClipboardPanel(); + } + // Toggle XVP panel + if (UI.xvpOpen === true) { + document.getElementById('noVNC_xvp').style.display = "none"; + document.getElementById('noVNC_toggleXvp_button').className = "noVNC_status_button"; + UI.xvpOpen = false; + } else { + document.getElementById('noVNC_xvp').style.display = "block"; + document.getElementById('noVNC_toggleXvp_button').className = "noVNC_status_button_selected"; + UI.xvpOpen = true; + } + }, + + // Disable/enable XVP button + updateXvpButton: function(ver) { + if (ver >= 1) { + document.getElementById('noVNC_toggleXvp_button').style.display = 'inline'; + } else { + document.getElementById('noVNC_toggleXvp_button').style.display = 'none'; + // Close XVP panel if open + if (UI.xvpOpen === true) { + UI.toggleXvpPanel(); + } + } + }, + +/* ------^------- + * /XVP + * ============== + * CLIPBOARD + * ------v------*/ + + // Show the clipboard panel + toggleClipboardPanel: function() { + // Close the description panel + document.getElementById('noVNC_description').style.display = "none"; + // Close settings if open + if (UI.settingsOpen === true) { + UI.settingsApply(); + UI.closeSettingsMenu(); + } + // Close connection settings if open + if (UI.connSettingsOpen === true) { + UI.toggleConnectPanel(); + } + // Close XVP panel if open + if (UI.xvpOpen === true) { + UI.toggleXvpPanel(); + } + // Toggle Clipboard Panel + if (UI.clipboardOpen === true) { + document.getElementById('noVNC_clipboard').style.display = "none"; + document.getElementById('noVNC_clipboard_button').className = "noVNC_status_button"; + UI.clipboardOpen = false; + } else { + document.getElementById('noVNC_clipboard').style.display = "block"; + document.getElementById('noVNC_clipboard_button').className = "noVNC_status_button_selected"; + UI.clipboardOpen = true; + } + }, + + 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------*/ + + // Show the connection settings panel/menu + toggleConnectPanel: function() { + // Close the description panel + document.getElementById('noVNC_description').style.display = "none"; + // Close connection settings if open + if (UI.settingsOpen === true) { + UI.settingsApply(); + UI.closeSettingsMenu(); + document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button"; + } + // Close clipboard panel if open + if (UI.clipboardOpen === true) { + UI.toggleClipboardPanel(); + } + // Close XVP panel if open + if (UI.xvpOpen === true) { + UI.toggleXvpPanel(); + } + + // Toggle Connection Panel + if (UI.connSettingsOpen === true) { + document.getElementById('noVNC_controls').style.display = "none"; + document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button"; + UI.connSettingsOpen = false; + UI.saveSetting('host'); + UI.saveSetting('port'); + UI.saveSetting('token'); + //UI.saveSetting('password'); + } else { + document.getElementById('noVNC_controls').style.display = "block"; + document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button_selected"; + UI.connSettingsOpen = true; + document.getElementById('noVNC_setting_host').focus(); + } + }, + + connect: function() { + UI.closeSettingsMenu(); + UI.toggleConnectPanel(); + + var host = document.getElementById('noVNC_setting_host').value; + var port = document.getElementById('noVNC_setting_port').value; + var password = document.getElementById('noVNC_setting_password').value; + var token = document.getElementById('noVNC_setting_token').value; + var path = document.getElementById('noVNC_setting_path').value; + + //if token is in path then ignore the new token variable + if (token) { + path = WebUtil.injectParamIfMissing(path, "token", token); + } + + if ((!host) || (!port)) { + throw new Error("Must set host and port"); + } + + if (!UI.initRFB()) return; + + UI.rfb.set_encrypt(UI.getSetting('encrypt')); + UI.rfb.set_true_color(UI.getSetting('true_color')); + UI.rfb.set_local_cursor(UI.getSetting('cursor')); + UI.rfb.set_shared(UI.getSetting('shared')); + UI.rfb.set_view_only(UI.getSetting('view_only')); + UI.rfb.set_repeaterID(UI.getSetting('repeaterID')); + + UI.rfb.connect(host, port, password, path); + + //Close dialog. + setTimeout(UI.setBarPosition, 100); + document.getElementById('noVNC_logo').style.display = "none"; + document.getElementById('noVNC_screen').style.display = "inline"; + }, + + disconnect: function() { + UI.closeSettingsMenu(); + UI.rfb.disconnect(); + + // Restore the callback used for initial resize + UI.rfb.set_onFBUComplete(UI.initialResize); + + document.getElementById('noVNC_logo').style.display = "block"; + document.getElementById('noVNC_screen').style.display = "none"; + + // Don't display the connection settings until we're actually disconnected + }, + + setPassword: function() { + UI.rfb.sendPassword(document.getElementById('noVNC_setting_password').value); + //Reset connect button. + document.getElementById('noVNC_connect_button').value = "Connect"; + document.getElementById('noVNC_connect_button').onclick = UI.connect; + //Hide connection panel. + UI.toggleConnectPanel(); + return false; + }, + +/* ------^------- + * /CONNECTION + * ============== + * 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').className = "noVNC_status_button_selected"; + } else { + document.getElementById('noVNC_fullscreen_button').className = "noVNC_status_button"; + } + }, + +/* ------^------- + * /FULLSCREEN + * ============== + * RESIZE + * ------v------*/ + + // Apply remote resizing or local scaling + applyResizeMode: function() { + if (!UI.rfb) return; + + var screen = UI.screenSize(); + + if (screen && UI.rfb_state === 'normal' && UI.rfb.get_display()) { + + var display = UI.rfb.get_display(); + var resizeMode = UI.getSetting('resize'); + + 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(){ + + // Limit the viewport to the size of the browser window + display.set_maxWidth(screen.w); + display.set_maxHeight(screen.h); + + Util.Debug('Attempting requestDesktopSize(' + + screen.w + ', ' + screen.h + ')'); + + // Request a remote size covering the viewport + UI.rfb.requestDesktopSize(screen.w, screen.h); + }, 500); + + } else if (resizeMode === 'scale' || resizeMode === 'downscale') { + var downscaleOnly = resizeMode === 'downscale'; + var scaleRatio = display.autoscale(screen.w, screen.h, downscaleOnly); + UI.rfb.get_mouse().set_scale(scaleRatio); + Util.Debug('Scaling by ' + UI.rfb.get_mouse().get_scale()); + } + } + }, + + // The screen is always the same size as the available viewport + // in the browser window minus the height of the control bar + screenSize: function() { + var screen = document.getElementById('noVNC_screen'); + + // Hide the scrollbars until the size is calculated + screen.style.overflow = "hidden"; + + var pos = Util.getPosition(screen); + var w = pos.width; + var h = pos.height; + + screen.style.overflow = "visible"; + + if (isNaN(w) || isNaN(h)) { + return false; + } else { + return {w: w, h: h}; + } + }, + + // 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) { + var display; + if (UI.rfb) { + display = UI.rfb.get_display(); + } else { + UI.forceSetting('clip', clip); + return; + } + + var cur_clip = display.get_viewport(); + + if (typeof(clip) !== 'boolean') { + // Use current setting + clip = UI.getSetting('clip'); + } + + if (clip && !cur_clip) { + // Turn clipping on + UI.updateSetting('clip', true); + } else if (!clip && cur_clip) { + // Turn clipping off + UI.updateSetting('clip', false); + display.set_viewport(false); + // Disable max dimensions + display.set_maxWidth(0); + display.set_maxHeight(0); + display.viewportChangeSize(); + } + if (UI.getSetting('clip')) { + // If clipping, update clipping settings + display.set_viewport(true); + + var size = UI.screenSize(); + if (size) { + display.set_maxWidth(size.w); + display.set_maxHeight(size.h); + + // Hide potential scrollbars that can skew the position + document.getElementById('noVNC_screen').style.overflow = "hidden"; + + // The x position marks the left margin of the canvas, + // remove the margin from both sides to keep it centered + var new_w = size.w - (2 * Util.getPosition(document.getElementById('noVNC_canvas')).x); + + document.getElementById('noVNC_screen').style.overflow = "visible"; + + display.viewportChangeSize(new_w, size.h); + } + } + }, + + // Handle special cases where clipping is forced on/off or locked + enableDisableViewClip: function() { + var resizeSetting = document.getElementById('noVNC_setting_resize'); + var connected = UI.rfb && UI.rfb_state === 'normal'; + + if (UI.isSafari) { + // Safari auto-hides the scrollbars which makes them + // impossible to use in most cases + UI.setViewClip(true); + document.getElementById('noVNC_setting_clip').disabled = true; + } else if (resizeSetting.value === 'downscale' || resizeSetting.value === 'scale') { + // Disable clipping if we are scaling + UI.setViewClip(false); + document.getElementById('noVNC_setting_clip').disabled = true; + } else if (document.msFullscreenElement) { + // The browser is IE and we are in fullscreen mode. + // - We need to force clipping while in fullscreen since + // scrollbars doesn't work. + UI.popupStatus("Forcing clipping mode since scrollbars aren't supported by IE in fullscreen"); + UI.rememberedClipSetting = UI.getSetting('clip'); + UI.setViewClip(true); + document.getElementById('noVNC_setting_clip').disabled = true; + } else if (document.body.msRequestFullscreen && UI.rememberedClip !== null) { + // Restore view clip to what it was before fullscreen on IE + UI.setViewClip(UI.rememberedClipSetting); + document.getElementById('noVNC_setting_clip').disabled = connected || UI.isTouchDevice; + } else { + document.getElementById('noVNC_setting_clip').disabled = connected || UI.isTouchDevice; + if (UI.isTouchDevice) { + UI.setViewClip(true); + } + } + }, + +/* ------^------- + * /CLIPPING + * ============== + * VIEWDRAG + * ------v------*/ + + // Update the viewport drag state + updateViewDrag: function(drag) { + if (!UI.rfb) return; + + var viewDragButton = document.getElementById('noVNC_view_drag_button'); + + // Check if viewport drag is possible. It is only possible + // if the remote display is clipping the client display. + if (UI.rfb_state === 'normal' && + UI.rfb.get_display().get_viewport() && + UI.rfb.get_display().clippingDisplay()) { + + viewDragButton.style.display = "inline"; + viewDragButton.disabled = false; + + } else { + // 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. + if (UI.rfb.get_viewportDrag) { + viewDragButton.className = "noVNC_status_button"; + UI.rfb.set_viewportDrag(false); + } + + // The button is disabled instead of hidden on touch devices + if (UI.rfb_state === 'normal' && UI.isTouchDevice) { + viewDragButton.style.display = "inline"; + viewDragButton.disabled = true; + } else { + viewDragButton.style.display = "none"; + } + return; + } + + if (typeof(drag) !== "undefined" && + typeof(drag) !== "object") { + if (drag) { + viewDragButton.className = "noVNC_status_button_selected"; + UI.rfb.set_viewportDrag(true); + } else { + viewDragButton.className = "noVNC_status_button"; + UI.rfb.set_viewportDrag(false); + } + } + }, + + toggleViewDrag: function() { + if (!UI.rfb) return; + + var viewDragButton = document.getElementById('noVNC_view_drag_button'); + if (UI.rfb.get_viewportDrag()) { + viewDragButton.className = "noVNC_status_button"; + UI.rfb.set_viewportDrag(false); + } else { + viewDragButton.className = "noVNC_status_button_selected"; + UI.rfb.set_viewportDrag(true); + } + }, + +/* ------^------- + * /VIEWDRAG + * ============== + * KEYBOARD + * ------v------*/ + + // On touch devices, show the OS keyboard + showKeyboard: function() { + var kbi = document.getElementById('noVNC_keyboardinput'); + var skb = document.getElementById('noVNC_keyboard_button'); + var l = kbi.value.length; + if(UI.keyboardVisible === false) { + kbi.focus(); + try { kbi.setSelectionRange(l, l); } // Move the caret to the end + catch (err) {} // setSelectionRange is undefined in Google Chrome + UI.keyboardVisible = true; + skb.className = "noVNC_status_button_selected"; + } else if(UI.keyboardVisible === true) { + kbi.blur(); + skb.className = "noVNC_status_button"; + UI.keyboardVisible = false; + } + }, + + hideKeyboard: function() { + document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button"; + //Weird bug in iOS if you change keyboardVisible + //here it does not actually occur so next time + //you click keyboard icon it doesnt work. + UI.hideKeyboardTimeout = setTimeout(function() { + UI.keyboardVisible = false; + },100); + }, + + keepKeyboard: function() { + clearTimeout(UI.hideKeyboardTimeout); + if(UI.keyboardVisible === true) { + document.getElementById('noVNC_keyboardinput').focus(); + document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button_selected"; + } else if(UI.keyboardVisible === false) { + document.getElementById('noVNC_keyboardinput').blur(); + document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button"; + } + }, + + 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(newValue.charCodeAt(i)); + } + + // 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(UI.keepKeyboard, 0); + } else { + UI.lastKeyboardinput = newValue; + } + }, + + toggleExtraKeys: function() { + UI.keepKeyboard(); + if(UI.extraKeysVisible === false) { + document.getElementById('noVNC_toggleCtrl_button').style.display = "inline"; + document.getElementById('noVNC_toggleAlt_button').style.display = "inline"; + document.getElementById('noVNC_sendTab_button').style.display = "inline"; + document.getElementById('noVNC_sendEsc_button').style.display = "inline"; + document.getElementById('noVNC_toggleExtraKeys_button').className = "noVNC_status_button_selected"; + UI.extraKeysVisible = true; + } else if(UI.extraKeysVisible === true) { + document.getElementById('noVNC_toggleCtrl_button').style.display = ""; + document.getElementById('noVNC_toggleAlt_button').style.display = ""; + document.getElementById('noVNC_sendTab_button').style.display = ""; + document.getElementById('noVNC_sendEsc_button').style.display = ""; + document.getElementById('noVNC_toggleExtraKeys_button').className = "noVNC_status_button"; + UI.extraKeysVisible = false; + } + }, + + sendEsc: function() { + UI.keepKeyboard(); + UI.rfb.sendKey(KeyTable.XK_Escape); + }, + + sendTab: function() { + UI.keepKeyboard(); + UI.rfb.sendKey(KeyTable.XK_Tab); + }, + + toggleCtrl: function() { + UI.keepKeyboard(); + if(UI.ctrlOn === false) { + UI.rfb.sendKey(KeyTable.XK_Control_L, true); + document.getElementById('noVNC_toggleCtrl_button').className = "noVNC_status_button_selected"; + UI.ctrlOn = true; + } else if(UI.ctrlOn === true) { + UI.rfb.sendKey(KeyTable.XK_Control_L, false); + document.getElementById('noVNC_toggleCtrl_button').className = "noVNC_status_button"; + UI.ctrlOn = false; + } + }, + + toggleAlt: function() { + UI.keepKeyboard(); + if(UI.altOn === false) { + UI.rfb.sendKey(KeyTable.XK_Alt_L, true); + document.getElementById('noVNC_toggleAlt_button').className = "noVNC_status_button_selected"; + UI.altOn = true; + } else if(UI.altOn === true) { + UI.rfb.sendKey(KeyTable.XK_Alt_L, false); + document.getElementById('noVNC_toggleAlt_button').className = "noVNC_status_button"; + UI.altOn = false; + } + }, + + sendCtrlAltDel: function() { + UI.rfb.sendCtrlAltDel(); + }, + +/* ------^------- + * /KEYBOARD + * ============== + * MISC + * ------v------*/ + + setMouseButton: function(num) { + if (typeof num === 'undefined') { + // Disable mouse buttons + num = -1; + } + if (UI.rfb) { + 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) { + button.style.display = ""; + } else { + button.style.display = "none"; + } + } + }, + + displayBlur: function() { + if (!UI.rfb) return; + + UI.rfb.get_keyboard().set_focused(false); + UI.rfb.get_mouse().set_focused(false); + }, + + displayFocus: function() { + if (!UI.rfb) return; + + UI.rfb.get_keyboard().set_focused(true); + UI.rfb.get_mouse().set_focused(true); + }, + + // Display the desktop name in the document title + updateDocumentTitle: function(rfb, name) { + document.title = name + " - noVNC"; + }, + + //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); + }, + + setBarPosition: function() { + document.getElementById('noVNC_control_bar').style.top = (window.pageYOffset) + 'px'; + document.getElementById('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px'; + + var vncwidth = document.getElementById('noVNC_container').style.offsetWidth; + document.getElementById('noVNC_control_bar').style.width = vncwidth + 'px'; + } + +/* ------^------- + * /MISC + * ============== + */ + }; + + /* [module] UI.load(); */ +})(); + +/* [module] export default UI; */ diff --git a/app/webutil.js b/app/webutil.js new file mode 100644 index 0000000..f5f3077 --- /dev/null +++ b/app/webutil.js @@ -0,0 +1,281 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2012 Joel Martin + * Copyright (C) 2013 NTT corp. + * Licensed under MPL 2.0 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint bitwise: false, white: false, browser: true, devel: true */ +/*global Util, window, document */ + +/* [module] + * import Util from "../core/util"; + */ + +// Globals defined here +var WebUtil = {}; + +/* + * ------------------------------------------------------ + * Namespaced in WebUtil + * ------------------------------------------------------ + */ + +// init log level reading the logging HTTP param +WebUtil.init_logging = function (level) { + "use strict"; + if (typeof level !== "undefined") { + Util._log_level = level; + } else { + var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/); + Util._log_level = (param || ['', Util._log_level])[1]; + } + Util.init_logging(); +}; + + +WebUtil.dirObj = function (obj, depth, parent) { + "use strict"; + if (! depth) { depth = 2; } + if (! parent) { parent = ""; } + + // Print the properties of the passed-in object + var msg = ""; + for (var i in obj) { + if ((depth > 1) && (typeof obj[i] === "object")) { + // Recurse attributes that are objects + msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i); + } else { + //val = new String(obj[i]).replace("\n", " "); + var val = ""; + if (typeof(obj[i]) === "undefined") { + val = "undefined"; + } else { + val = obj[i].toString().replace("\n", " "); + } + if (val.length > 30) { + val = val.substr(0, 30) + "..."; + } + msg += parent + "." + i + ": " + val + "\n"; + } + } + return msg; +}; + +// Read a query string variable +WebUtil.getQueryVar = function (name, defVal) { + "use strict"; + var re = new RegExp('.*[?&]' + name + '=([^&#]*)'), + match = document.location.href.match(re); + if (typeof defVal === 'undefined') { defVal = null; } + if (match) { + return decodeURIComponent(match[1]); + } else { + return defVal; + } +}; + +// Read a hash fragment variable +WebUtil.getHashVar = function (name, defVal) { + "use strict"; + var re = new RegExp('.*[&#]' + name + '=([^&]*)'), + match = document.location.hash.match(re); + if (typeof defVal === 'undefined') { defVal = null; } + if (match) { + return decodeURIComponent(match[1]); + } else { + return defVal; + } +}; + +// Read a variable from the fragment or the query string +// Fragment takes precedence +WebUtil.getConfigVar = function (name, defVal) { + "use strict"; + var val = WebUtil.getHashVar(name); + if (val === null) { + val = WebUtil.getQueryVar(name, defVal); + } + return val; +}; + +/* + * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html + */ + +// No days means only for this browser session +WebUtil.createCookie = function (name, value, days) { + "use strict"; + var date, expires; + if (days) { + date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toGMTString(); + } else { + expires = ""; + } + + var secure; + if (document.location.protocol === "https:") { + secure = "; secure"; + } else { + secure = ""; + } + document.cookie = name + "=" + value + expires + "; path=/" + secure; +}; + +WebUtil.readCookie = function (name, defaultValue) { + "use strict"; + var nameEQ = name + "=", + ca = document.cookie.split(';'); + + for (var i = 0; i < ca.length; i += 1) { + var c = ca[i]; + while (c.charAt(0) === ' ') { c = c.substring(1, c.length); } + if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); } + } + return (typeof defaultValue !== 'undefined') ? defaultValue : null; +}; + +WebUtil.eraseCookie = function (name) { + "use strict"; + WebUtil.createCookie(name, "", -1); +}; + +/* + * Setting handling. + */ + +WebUtil.initSettings = function (callback /*, ...callbackArgs */) { + "use strict"; + var callbackArgs = Array.prototype.slice.call(arguments, 1); + if (window.chrome && window.chrome.storage) { + window.chrome.storage.sync.get(function (cfg) { + WebUtil.settings = cfg; + console.log(WebUtil.settings); + if (callback) { + callback.apply(this, callbackArgs); + } + }); + } else { + // No-op + if (callback) { + callback.apply(this, callbackArgs); + } + } +}; + +// No days means only for this browser session +WebUtil.writeSetting = function (name, value) { + "use strict"; + if (window.chrome && window.chrome.storage) { + //console.log("writeSetting:", name, value); + if (WebUtil.settings[name] !== value) { + WebUtil.settings[name] = value; + window.chrome.storage.sync.set(WebUtil.settings); + } + } else { + localStorage.setItem(name, value); + } +}; + +WebUtil.readSetting = function (name, defaultValue) { + "use strict"; + var value; + if (window.chrome && window.chrome.storage) { + value = WebUtil.settings[name]; + } else { + value = localStorage.getItem(name); + } + if (typeof value === "undefined") { + value = null; + } + if (value === null && typeof defaultValue !== undefined) { + return defaultValue; + } else { + return value; + } +}; + +WebUtil.eraseSetting = function (name) { + "use strict"; + if (window.chrome && window.chrome.storage) { + window.chrome.storage.sync.remove(name); + delete WebUtil.settings[name]; + } else { + localStorage.removeItem(name); + } +}; + +/* + * Alternate stylesheet selection + */ +WebUtil.getStylesheets = function () { + "use strict"; + var links = document.getElementsByTagName("link"); + var sheets = []; + + for (var i = 0; i < links.length; i += 1) { + if (links[i].title && + links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) { + sheets.push(links[i]); + } + } + return sheets; +}; + +// No sheet means try and use value from cookie, null sheet used to +// clear all alternates. +WebUtil.selectStylesheet = function (sheet) { + "use strict"; + if (typeof sheet === 'undefined') { + sheet = 'default'; + } + + 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; + } + } + return sheet; +}; + +WebUtil.injectParamIfMissing = function (path, param, value) { + // force pretend that we're dealing with a relative path + // (assume that we wanted an extra if we pass one in) + path = "/" + path; + + var elem = document.createElement('a'); + elem.href = path; + + var param_eq = encodeURIComponent(param) + "="; + var query; + if (elem.search) { + query = elem.search.slice(1).split('&'); + } else { + query = []; + } + + if (!query.some(function (v) { return v.startsWith(param_eq); })) { + query.push(param_eq + encodeURIComponent(value)); + elem.search = "?" + query.join("&"); + } + + // some browsers (e.g. IE11) may occasionally omit the leading slash + // in the elem.pathname string. Handle that case gracefully. + if (elem.pathname.charAt(0) == "/") { + return elem.pathname.slice(1) + elem.search + elem.hash; + } else { + return elem.pathname + elem.search + elem.hash; + } +}; + +/* [module] export default WebUtil; */ |