]> git.proxmox.com Git - mirror_novnc.git/blob - app/webutil.js
e552d421386756e7f02c63641a781b877e3e56d7
[mirror_novnc.git] / app / webutil.js
1 /*
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2019 The noVNC Authors
4 * Licensed under MPL 2.0 (see LICENSE.txt)
5 *
6 * See README.md for usage and integration instructions.
7 */
8
9 import { init_logging as main_init_logging } from '../core/util/logging.js';
10
11 // init log level reading the logging HTTP param
12 export function init_logging(level) {
13 "use strict";
14 if (typeof level !== "undefined") {
15 main_init_logging(level);
16 } else {
17 const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
18 main_init_logging(param || undefined);
19 }
20 }
21
22 // Read a query string variable
23 export function getQueryVar(name, defVal) {
24 "use strict";
25 const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
26 match = document.location.href.match(re);
27 if (typeof defVal === 'undefined') { defVal = null; }
28
29 if (match) {
30 return decodeURIComponent(match[1]);
31 }
32
33 return defVal;
34 }
35
36 // Read a hash fragment variable
37 export function getHashVar(name, defVal) {
38 "use strict";
39 const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
40 match = document.location.hash.match(re);
41 if (typeof defVal === 'undefined') { defVal = null; }
42
43 if (match) {
44 return decodeURIComponent(match[1]);
45 }
46
47 return defVal;
48 }
49
50 // Read a variable from the fragment or the query string
51 // Fragment takes precedence
52 export function getConfigVar(name, defVal) {
53 "use strict";
54 const val = getHashVar(name);
55
56 if (val === null) {
57 return getQueryVar(name, defVal);
58 }
59
60 return val;
61 }
62
63 /*
64 * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
65 */
66
67 // No days means only for this browser session
68 export function createCookie(name, value, days) {
69 "use strict";
70 let date, expires;
71 if (days) {
72 date = new Date();
73 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
74 expires = "; expires=" + date.toGMTString();
75 } else {
76 expires = "";
77 }
78
79 let secure;
80 if (document.location.protocol === "https:") {
81 secure = "; secure";
82 } else {
83 secure = "";
84 }
85 document.cookie = name + "=" + value + expires + "; path=/" + secure;
86 }
87
88 export function readCookie(name, defaultValue) {
89 "use strict";
90 const nameEQ = name + "=";
91 const ca = document.cookie.split(';');
92
93 for (let i = 0; i < ca.length; i += 1) {
94 let c = ca[i];
95 while (c.charAt(0) === ' ') {
96 c = c.substring(1, c.length);
97 }
98 if (c.indexOf(nameEQ) === 0) {
99 return c.substring(nameEQ.length, c.length);
100 }
101 }
102
103 return (typeof defaultValue !== 'undefined') ? defaultValue : null;
104 }
105
106 export function eraseCookie(name) {
107 "use strict";
108 createCookie(name, "", -1);
109 }
110
111 /*
112 * Setting handling.
113 */
114
115 let settings = {};
116
117 export function initSettings() {
118 if (!window.chrome || !window.chrome.storage) {
119 settings = {};
120 return Promise.resolve();
121 }
122
123 return new Promise(resolve => window.chrome.storage.sync.get(resolve))
124 .then((cfg) => { settings = cfg; });
125 }
126
127 // Update the settings cache, but do not write to permanent storage
128 export function setSetting(name, value) {
129 settings[name] = value;
130 }
131
132 // No days means only for this browser session
133 export function writeSetting(name, value) {
134 "use strict";
135 if (settings[name] === value) return;
136 settings[name] = value;
137 if (window.chrome && window.chrome.storage) {
138 window.chrome.storage.sync.set(settings);
139 } else {
140 localStorage.setItem(name, value);
141 }
142 }
143
144 export function readSetting(name, defaultValue) {
145 "use strict";
146 let value;
147 if ((name in settings) || (window.chrome && window.chrome.storage)) {
148 value = settings[name];
149 } else {
150 value = localStorage.getItem(name);
151 settings[name] = value;
152 }
153 if (typeof value === "undefined") {
154 value = null;
155 }
156
157 if (value === null && typeof defaultValue !== "undefined") {
158 return defaultValue;
159 }
160
161 return value;
162 }
163
164 export function eraseSetting(name) {
165 "use strict";
166 // Deleting here means that next time the setting is read when using local
167 // storage, it will be pulled from local storage again.
168 // If the setting in local storage is changed (e.g. in another tab)
169 // between this delete and the next read, it could lead to an unexpected
170 // value change.
171 delete settings[name];
172 if (window.chrome && window.chrome.storage) {
173 window.chrome.storage.sync.remove(name);
174 } else {
175 localStorage.removeItem(name);
176 }
177 }
178
179 export function injectParamIfMissing(path, param, value) {
180 // force pretend that we're dealing with a relative path
181 // (assume that we wanted an extra if we pass one in)
182 path = "/" + path;
183
184 const elem = document.createElement('a');
185 elem.href = path;
186
187 const param_eq = encodeURIComponent(param) + "=";
188 let query;
189 if (elem.search) {
190 query = elem.search.slice(1).split('&');
191 } else {
192 query = [];
193 }
194
195 if (!query.some(v => v.startsWith(param_eq))) {
196 query.push(param_eq + encodeURIComponent(value));
197 elem.search = "?" + query.join("&");
198 }
199
200 // some browsers (e.g. IE11) may occasionally omit the leading slash
201 // in the elem.pathname string. Handle that case gracefully.
202 if (elem.pathname.charAt(0) == "/") {
203 return elem.pathname.slice(1) + elem.search + elem.hash;
204 }
205
206 return elem.pathname + elem.search + elem.hash;
207 }
208
209 // sadly, we can't use the Fetch API until we decide to drop
210 // IE11 support or polyfill promises and fetch in IE11.
211 // resolve will receive an object on success, while reject
212 // will receive either an event or an error on failure.
213 export function fetchJSON(path) {
214 return new Promise((resolve, reject) => {
215 // NB: IE11 doesn't support JSON as a responseType
216 const req = new XMLHttpRequest();
217 req.open('GET', path);
218
219 req.onload = () => {
220 if (req.status === 200) {
221 let resObj;
222 try {
223 resObj = JSON.parse(req.responseText);
224 } catch (err) {
225 reject(err);
226 }
227 resolve(resObj);
228 } else {
229 reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
230 }
231 };
232
233 req.onerror = evt => reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
234
235 req.ontimeout = evt => reject(new Error("XHR timed out while trying to load '" + path + "'"));
236
237 req.send();
238 });
239 }