]> git.proxmox.com Git - mirror_novnc.git/blob - app/webutil.js
Move app-specific location of language files
[mirror_novnc.git] / app / webutil.js
1 /*
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Copyright (C) 2013 NTT corp.
5 * Licensed under MPL 2.0 (see LICENSE.txt)
6 *
7 * See README.md for usage and integration instructions.
8 */
9
10 /*jslint bitwise: false, white: false, browser: true, devel: true */
11 /*global Util, window, document */
12
13 /* [module]
14 * import Util from "../core/util";
15 */
16
17 // Globals defined here
18 var WebUtil = {};
19
20 /*
21 * ------------------------------------------------------
22 * Namespaced in WebUtil
23 * ------------------------------------------------------
24 */
25
26 // init log level reading the logging HTTP param
27 WebUtil.init_logging = function (level) {
28 "use strict";
29 if (typeof level !== "undefined") {
30 Util._log_level = level;
31 } else {
32 var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
33 Util._log_level = (param || ['', Util._log_level])[1];
34 }
35 Util.init_logging();
36 };
37
38
39 WebUtil.dirObj = function (obj, depth, parent) {
40 "use strict";
41 if (! depth) { depth = 2; }
42 if (! parent) { parent = ""; }
43
44 // Print the properties of the passed-in object
45 var msg = "";
46 for (var i in obj) {
47 if ((depth > 1) && (typeof obj[i] === "object")) {
48 // Recurse attributes that are objects
49 msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
50 } else {
51 //val = new String(obj[i]).replace("\n", " ");
52 var val = "";
53 if (typeof(obj[i]) === "undefined") {
54 val = "undefined";
55 } else {
56 val = obj[i].toString().replace("\n", " ");
57 }
58 if (val.length > 30) {
59 val = val.substr(0, 30) + "...";
60 }
61 msg += parent + "." + i + ": " + val + "\n";
62 }
63 }
64 return msg;
65 };
66
67 // Read a query string variable
68 WebUtil.getQueryVar = function (name, defVal) {
69 "use strict";
70 var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
71 match = document.location.href.match(re);
72 if (typeof defVal === 'undefined') { defVal = null; }
73 if (match) {
74 return decodeURIComponent(match[1]);
75 } else {
76 return defVal;
77 }
78 };
79
80 // Read a hash fragment variable
81 WebUtil.getHashVar = function (name, defVal) {
82 "use strict";
83 var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
84 match = document.location.hash.match(re);
85 if (typeof defVal === 'undefined') { defVal = null; }
86 if (match) {
87 return decodeURIComponent(match[1]);
88 } else {
89 return defVal;
90 }
91 };
92
93 // Read a variable from the fragment or the query string
94 // Fragment takes precedence
95 WebUtil.getConfigVar = function (name, defVal) {
96 "use strict";
97 var val = WebUtil.getHashVar(name);
98 if (val === null) {
99 val = WebUtil.getQueryVar(name, defVal);
100 }
101 return val;
102 };
103
104 /*
105 * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
106 */
107
108 // No days means only for this browser session
109 WebUtil.createCookie = function (name, value, days) {
110 "use strict";
111 var date, expires;
112 if (days) {
113 date = new Date();
114 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
115 expires = "; expires=" + date.toGMTString();
116 } else {
117 expires = "";
118 }
119
120 var secure;
121 if (document.location.protocol === "https:") {
122 secure = "; secure";
123 } else {
124 secure = "";
125 }
126 document.cookie = name + "=" + value + expires + "; path=/" + secure;
127 };
128
129 WebUtil.readCookie = function (name, defaultValue) {
130 "use strict";
131 var nameEQ = name + "=",
132 ca = document.cookie.split(';');
133
134 for (var i = 0; i < ca.length; i += 1) {
135 var c = ca[i];
136 while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
137 if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
138 }
139 return (typeof defaultValue !== 'undefined') ? defaultValue : null;
140 };
141
142 WebUtil.eraseCookie = function (name) {
143 "use strict";
144 WebUtil.createCookie(name, "", -1);
145 };
146
147 /*
148 * Setting handling.
149 */
150
151 WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
152 "use strict";
153 var callbackArgs = Array.prototype.slice.call(arguments, 1);
154 if (window.chrome && window.chrome.storage) {
155 window.chrome.storage.sync.get(function (cfg) {
156 WebUtil.settings = cfg;
157 console.log(WebUtil.settings);
158 if (callback) {
159 callback.apply(this, callbackArgs);
160 }
161 });
162 } else {
163 // No-op
164 if (callback) {
165 callback.apply(this, callbackArgs);
166 }
167 }
168 };
169
170 // No days means only for this browser session
171 WebUtil.writeSetting = function (name, value) {
172 "use strict";
173 if (window.chrome && window.chrome.storage) {
174 //console.log("writeSetting:", name, value);
175 if (WebUtil.settings[name] !== value) {
176 WebUtil.settings[name] = value;
177 window.chrome.storage.sync.set(WebUtil.settings);
178 }
179 } else {
180 localStorage.setItem(name, value);
181 }
182 };
183
184 WebUtil.readSetting = function (name, defaultValue) {
185 "use strict";
186 var value;
187 if (window.chrome && window.chrome.storage) {
188 value = WebUtil.settings[name];
189 } else {
190 value = localStorage.getItem(name);
191 }
192 if (typeof value === "undefined") {
193 value = null;
194 }
195 if (value === null && typeof defaultValue !== undefined) {
196 return defaultValue;
197 } else {
198 return value;
199 }
200 };
201
202 WebUtil.eraseSetting = function (name) {
203 "use strict";
204 if (window.chrome && window.chrome.storage) {
205 window.chrome.storage.sync.remove(name);
206 delete WebUtil.settings[name];
207 } else {
208 localStorage.removeItem(name);
209 }
210 };
211
212 /*
213 * Alternate stylesheet selection
214 */
215 WebUtil.getStylesheets = function () {
216 "use strict";
217 var links = document.getElementsByTagName("link");
218 var sheets = [];
219
220 for (var i = 0; i < links.length; i += 1) {
221 if (links[i].title &&
222 links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
223 sheets.push(links[i]);
224 }
225 }
226 return sheets;
227 };
228
229 // No sheet means try and use value from cookie, null sheet used to
230 // clear all alternates.
231 WebUtil.selectStylesheet = function (sheet) {
232 "use strict";
233 if (typeof sheet === 'undefined') {
234 sheet = 'default';
235 }
236
237 var sheets = WebUtil.getStylesheets();
238 for (var i = 0; i < sheets.length; i += 1) {
239 var link = sheets[i];
240 if (link.title === sheet) {
241 Util.Debug("Using stylesheet " + sheet);
242 link.disabled = false;
243 } else {
244 //Util.Debug("Skipping stylesheet " + link.title);
245 link.disabled = true;
246 }
247 }
248 return sheet;
249 };
250
251 WebUtil.injectParamIfMissing = function (path, param, value) {
252 // force pretend that we're dealing with a relative path
253 // (assume that we wanted an extra if we pass one in)
254 path = "/" + path;
255
256 var elem = document.createElement('a');
257 elem.href = path;
258
259 var param_eq = encodeURIComponent(param) + "=";
260 var query;
261 if (elem.search) {
262 query = elem.search.slice(1).split('&');
263 } else {
264 query = [];
265 }
266
267 if (!query.some(function (v) { return v.startsWith(param_eq); })) {
268 query.push(param_eq + encodeURIComponent(value));
269 elem.search = "?" + query.join("&");
270 }
271
272 // some browsers (e.g. IE11) may occasionally omit the leading slash
273 // in the elem.pathname string. Handle that case gracefully.
274 if (elem.pathname.charAt(0) == "/") {
275 return elem.pathname.slice(1) + elem.search + elem.hash;
276 } else {
277 return elem.pathname + elem.search + elem.hash;
278 }
279 };
280
281 // Emulate Element.setCapture() when not supported
282
283 WebUtil._captureRecursion = false;
284 WebUtil._captureProxy = function (e) {
285 // Recursion protection as we'll see our own event
286 if (WebUtil._captureRecursion) return;
287
288 // Clone the event as we cannot dispatch an already dispatched event
289 var newEv = new e.constructor(e.type, e);
290
291 WebUtil._captureRecursion = true;
292 WebUtil._captureElem.dispatchEvent(newEv);
293 WebUtil._captureRecursion = false;
294
295 // Implicitly release the capture on button release
296 if ((e.type === "mouseup") || (e.type === "touchend")) {
297 WebUtil.releaseCapture();
298 }
299 };
300
301 WebUtil.setCapture = function (elem) {
302 if (elem.setCapture) {
303
304 elem.setCapture();
305
306 // IE releases capture on 'click' events which might not trigger
307 elem.addEventListener('mouseup', WebUtil.releaseCapture);
308 elem.addEventListener('touchend', WebUtil.releaseCapture);
309
310 } else {
311 // Safari on iOS 9 has a broken constructor for TouchEvent.
312 // We are fine in this case however, since Safari seems to
313 // have some sort of implicit setCapture magic anyway.
314 if (window.TouchEvent !== undefined) {
315 try {
316 new TouchEvent("touchstart");
317 } catch (TypeError) {
318 return;
319 }
320 }
321
322 var captureElem = document.getElementById("noVNC_mouse_capture_elem");
323
324 if (captureElem === null) {
325 captureElem = document.createElement("div");
326 captureElem.id = "noVNC_mouse_capture_elem";
327 captureElem.style.position = "fixed";
328 captureElem.style.top = "0px";
329 captureElem.style.left = "0px";
330 captureElem.style.width = "100%";
331 captureElem.style.height = "100%";
332 captureElem.style.zIndex = 10000;
333 captureElem.style.display = "none";
334 document.body.appendChild(captureElem);
335
336 captureElem.addEventListener('mousemove', WebUtil._captureProxy);
337 captureElem.addEventListener('mouseup', WebUtil._captureProxy);
338
339 captureElem.addEventListener('touchmove', WebUtil._captureProxy);
340 captureElem.addEventListener('touchend', WebUtil._captureProxy);
341 }
342
343 WebUtil._captureElem = elem;
344 captureElem.style.display = null;
345
346 // We listen to events on window in order to keep tracking if it
347 // happens to leave the viewport
348 window.addEventListener('mousemove', WebUtil._captureProxy);
349 window.addEventListener('mouseup', WebUtil._captureProxy);
350
351 window.addEventListener('touchmove', WebUtil._captureProxy);
352 window.addEventListener('touchend', WebUtil._captureProxy);
353 }
354 };
355
356 WebUtil.releaseCapture = function () {
357 if (document.releaseCapture) {
358
359 document.releaseCapture();
360
361 } else {
362 var captureElem = document.getElementById("noVNC_mouse_capture_elem");
363 WebUtil._captureElem = null;
364 captureElem.style.display = "none";
365
366 window.removeEventListener('mousemove', WebUtil._captureProxy);
367 window.removeEventListener('mouseup', WebUtil._captureProxy);
368
369 window.removeEventListener('touchmove', WebUtil._captureProxy);
370 window.removeEventListener('touchend', WebUtil._captureProxy);
371 }
372 };
373
374
375 // Get language file location
376 WebUtil.getLanguageFileLocation = function () {
377 return '../app/locale/locale-'+Util.Localisation.getLanguageCode()+'.js';
378 };
379
380 // Dynamically load scripts without using document.write()
381 // Reference: http://unixpapa.com/js/dyna.html
382 //
383 // Handles the case where load_scripts is invoked from a script that
384 // itself is loaded via load_scripts. Once all scripts are loaded the
385 // window.onscriptsloaded handler is called (if set).
386 WebUtil.get_include_uri = function (root_dir) {
387 return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI + root_dir + '/' : root_dir + '/';
388 };
389 WebUtil._loading_scripts = [];
390 WebUtil._pending_scripts = [];
391 WebUtil.load_scripts = function (files_by_dir) {
392 "use strict";
393 var head = document.getElementsByTagName('head')[0], script,
394 ls = WebUtil._loading_scripts, ps = WebUtil._pending_scripts;
395
396 var loadFunc = function (e) {
397 while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
398 ls[0].readyState === 'complete')) {
399 // For IE, append the script to trigger execution
400 var s = ls.shift();
401 //console.log("loaded script: " + s.src);
402 head.appendChild(s);
403 }
404 if (!this.readyState ||
405 (Util.Engine.presto && this.readyState === 'loaded') ||
406 this.readyState === 'complete') {
407 if (ps.indexOf(this) >= 0) {
408 this.onload = this.onreadystatechange = null;
409 //console.log("completed script: " + this.src);
410 ps.splice(ps.indexOf(this), 1);
411
412 // Call window.onscriptsload after last script loads
413 if (ps.length === 0 && window.onscriptsload) {
414 window.onscriptsload();
415 }
416 }
417 }
418 };
419
420 var root_dirs = Object.keys(files_by_dir);
421
422 for (var d = 0; d < root_dirs.length; d++) {
423 var root_dir = root_dirs[d];
424 var files = files_by_dir[root_dir];
425
426 for (var f = 0; f < files.length; f++) {
427 script = document.createElement('script');
428 script.type = 'text/javascript';
429 script.src = WebUtil.get_include_uri(root_dir) + files[f];
430 //console.log("loading script: " + script.src);
431 script.onload = script.onreadystatechange = loadFunc;
432 // In-order script execution tricks
433 if (Util.Engine.trident) {
434 // For IE wait until readyState is 'loaded' before
435 // appending it which will trigger execution
436 // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
437 ls.push(script);
438 } else {
439 // For webkit and firefox set async=false and append now
440 // https://developer.mozilla.org/en-US/docs/HTML/Element/script
441 script.async = false;
442 head.appendChild(script);
443 }
444 ps.push(script);
445 }
446 }
447 };
448
449 /* [module] export default WebUtil; */