]> git.p6c8.net - jirafeau_project.git/blob - lib/functions.js.php
Define constants only if Sodium is available
[jirafeau_project.git] / lib / functions.js.php
1 <?php
2 /*
3 * Jirafeau, your web file repository
4 * Copyright (C) 2015 Jerome Jutteau <jerome@jutteau.fr>
5 * Copyright (C) 2015 Nicola Spanti (RyDroid) <dev@nicola-spanti.info>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 header('Content-Type: text/javascript');
22 define('JIRAFEAU_ROOT', dirname(__FILE__) . '/../');
23
24 require(JIRAFEAU_ROOT . 'lib/settings.php');
25 require(JIRAFEAU_ROOT . 'lib/functions.php');
26 require(JIRAFEAU_ROOT . 'lib/lang.php');
27
28 ?>
29 // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
30 var web_root = "<?php echo $cfg['web_root']; ?>";
31
32 var lang_array = <?php echo json_lang_generator(null); ?>;
33 var lang_array_fallback = <?php echo json_lang_generator("en"); ?>;
34
35 function translate (expr) {
36 if (lang_array.hasOwnProperty(expr)) {
37 var e = lang_array[expr];
38 if (!isEmpty(e))
39 return e;
40 }
41 if (lang_array_fallback.hasOwnProperty(expr)) {
42 var e = lang_array_fallback[expr];
43 if (!isEmpty(e))
44 return e;
45 }
46 return "FIXME: " + expr;
47 }
48
49 function isEmpty(str) {
50 return (!str || 0 === str.length);
51 }
52
53 // Extend date object with format method
54 Date.prototype.format = function(format) {
55 format = format || 'YYYY-MM-DD hh:mm';
56
57 var zeropad = function(number, length) {
58 number = number.toString();
59 length = length || 2;
60 while(number.length < length)
61 number = '0' + number;
62 return number;
63 },
64 formats = {
65 YYYY: this.getFullYear(),
66 MM: zeropad(this.getMonth() + 1),
67 DD: zeropad(this.getDate()),
68 hh: zeropad(this.getHours()),
69 mm: zeropad(this.getMinutes()),
70 O: (function() {
71 localDate = new Date;
72 sign = (localDate.getTimezoneOffset() > 0) ? '-' : '+';
73 offset = Math.abs(localDate.getTimezoneOffset());
74 hours = zeropad(Math.floor(offset / 60));
75 minutes = zeropad(offset % 60);
76 return sign + hours + ":" + minutes;
77 })()
78 },
79 pattern = '(' + Object.keys(formats).join(')|(') + ')';
80
81 return format.replace(new RegExp(pattern, 'g'), function(match) {
82 return formats[match];
83 });
84 };
85
86 function dateFromUtcString(datestring) {
87 // matches »YYYY-MM-DD hh:mm«
88 var m = datestring.match(/(\d+)-(\d+)-(\d+)\s+(\d+):(\d+)/);
89 return new Date(Date.UTC(+m[1], +m[2] - 1, +m[3], +m[4], +m[5], 0));
90 }
91
92 function dateFromUtcTimestamp(datetimestamp) {
93 return new Date(parseInt(datetimestamp) * 1000)
94 }
95
96 function dateToUtcString(datelocal) {
97 return new Date(
98 datelocal.getUTCFullYear(),
99 datelocal.getUTCMonth(),
100 datelocal.getUTCDate(),
101 datelocal.getUTCHours(),
102 datelocal.getUTCMinutes(),
103 datelocal.getUTCSeconds()
104 ).format();
105 }
106
107 function dateToUtcTimestamp(datelocal) {
108 return (Date.UTC(
109 datelocal.getUTCFullYear(),
110 datelocal.getUTCMonth(),
111 datelocal.getUTCDate(),
112 datelocal.getUTCHours(),
113 datelocal.getUTCMinutes(),
114 datelocal.getUTCSeconds()
115 ) / 1000);
116 }
117
118 function convertAllDatetimeFields() {
119 datefields = document.getElementsByClassName('datetime')
120 for(var i=0; i<datefields.length; i++) {
121 dateUTC = datefields[i].getAttribute('data-datetime');
122 datefields[i].setAttribute('title', dateUTC + ' (GMT)');
123 datefields[i].innerHTML = dateFromUtcString(dateUTC).format('YYYY-MM-DD hh:mm (GMT O)');
124 }
125 }
126
127 function show_link (reference, delete_code, crypt_key, date)
128 {
129 // Upload finished
130 document.getElementById('uploading').style.display = 'none';
131 document.getElementById('upload').style.display = 'none';
132 document.getElementById('upload_finished').style.display = '';
133 document.title = "100% - <?php echo empty($cfg['title']) ? 'Jirafeau' : $cfg['title']; ?>";
134
135 // Download page
136 var download_link_href = 'f.php?h=' + reference;
137 if (crypt_key.length > 0)
138 {
139 download_link_href += '&k=' + crypt_key;
140 }
141 if (!!document.getElementById('upload_finished_download_page'))
142 {
143 document.getElementById('upload_link').href = download_link_href;
144 document.getElementById('upload_link_text').innerHTML = web_root + download_link_href;
145 }
146
147 // Email link
148 var filename = document.getElementById('file_select').files[0].name;
149 var b = encodeURIComponent("<?php echo t("DL"); ?> \"" + filename + "\":") + "%0D" + "%0A";
150 b += encodeURIComponent(web_root + download_link_href) + "%0D" + "%0A";
151 if (false == isEmpty(date))
152 {
153 b += "%0D" + "%0A" + encodeURIComponent("<?php echo t("VALID_UNTIL"); ?>: " + date.format('YYYY-MM-DD hh:mm (GMT O)')) + "%0D" + "%0A";
154 document.getElementById('upload_link_email').href = "mailto:?body=" + b + "&subject=" + encodeURIComponent(filename);
155 }
156
157 // Delete link
158 var delete_link_href = 'f.php?h=' + reference + '&d=' + delete_code;
159 document.getElementById('delete_link').href = delete_link_href;
160 document.getElementById('delete_link_text').innerHTML = web_root + delete_link_href;
161
162 // Validity date
163 if (isEmpty(date))
164 {
165 document.getElementById('date').style.display = 'none';
166 }
167 else {
168 document.getElementById('date').innerHTML = '<span class="datetime" title="'
169 + dateToUtcString(date) + ' (GMT)">'
170 + date.format('YYYY-MM-DD hh:mm (GMT O)')
171 + '</span>';
172 document.getElementById('date').style.display = '';
173 }
174
175 // Preview link (if allowed)
176 if (!!document.getElementById('preview_link'))
177 {
178 document.getElementById('upload_finished_preview').style.display = 'none';
179 var preview_link_href = 'f.php?h=' + reference + '&p=1';
180 if (crypt_key.length > 0)
181 {
182 preview_link_href += '&k=' + crypt_key;
183 }
184
185 // Test if content can be previewed
186 type = document.getElementById('file_select').files[0].type;
187 if ((type.startsWith('image/')
188 || type.startsWith('audio')
189 || type.startsWith('text/plain')
190 || type.startsWith('video/'))
191 && !type.includes('image/svg+xml'))
192 {
193 document.getElementById('preview_link').href = preview_link_href;
194 document.getElementById('preview_link_text').innerHTML = web_root + preview_link_href;
195 document.getElementById('upload_finished_preview').style.display = '';
196 }
197 }
198
199 // Direct download link
200 var direct_download_link_href = 'f.php?h=' + reference + '&d=1';
201 if (crypt_key.length > 0)
202 {
203 direct_download_link_href += '&k=' + crypt_key;
204 }
205 document.getElementById('direct_link').href = direct_download_link_href;
206 document.getElementById('direct_link_text').innerHTML = web_root + direct_download_link_href;
207
208 // Hide preview and direct download link if password is set
209 if (document.getElementById('input_key').value.length > 0)
210 {
211 if (!!document.getElementById('preview_link'))
212 document.getElementById('upload_finished_preview').style.display = 'none';
213 document.getElementById('upload_direct_download').style.display = 'none';
214 }
215 }
216
217 function show_upload_progression (percentage, speed, time_left)
218 {
219 document.getElementById('uploaded_percentage').innerHTML = percentage;
220 document.getElementById('uploaded_speed').innerHTML = speed;
221 document.getElementById('uploaded_time').innerHTML = time_left;
222 document.title = percentage + " - <?php echo empty($cfg['title']) ? 'Jirafeau' : $cfg['title']; ?>";
223 }
224
225 function hide_upload_progression ()
226 {
227 document.getElementById('uploaded_percentage').style.display = 'none';
228 document.getElementById('uploaded_speed').style.display = 'none';
229 document.getElementById('uploaded_time').style.display = 'none';
230 document.title = "<?php echo empty($cfg['title']) ? 'Jirafeau' : $cfg['title']; ?>";
231 }
232
233 function upload_progress (e)
234 {
235 if (e == undefined || e == null || !e.lengthComputable)
236 return;
237
238 // Init time estimation if needed
239 if (upload_time_estimation_total_size == 0)
240 upload_time_estimation_total_size = e.total;
241
242 // Compute percentage
243 var p = Math.round (e.loaded * 100 / e.total);
244 var p_str = ' ';
245 if (p != 100)
246 p_str = p.toString() + '%';
247 // Update estimation speed
248 upload_time_estimation_add(e.loaded);
249 // Get speed string
250 var speed_str = upload_time_estimation_speed_string();
251 speed_str = upload_speed_refresh_limiter(speed_str);
252 // Get time string
253 var time_str = chrono_update(upload_time_estimation_time());
254
255 show_upload_progression (p_str, speed_str, time_str);
256 }
257
258 function control_selected_file_size(max_size, error_str)
259 {
260 f_size = document.getElementById('file_select').files[0].size;
261 if (max_size > 0 && f_size > max_size * 1024 * 1024)
262 {
263 pop_failure(error_str);
264 document.getElementById('send').style.display = 'none';
265 }
266 else
267 {
268 // add class to restyle upload form in next step
269 document.getElementById('upload').setAttribute('class', 'file-selected');
270 // display options
271 document.getElementById('options').style.display = 'block';
272 document.getElementById('send').style.display = 'block';
273 document.getElementById('error_pop').style.display = 'none';
274 document.getElementById('send').focus();
275 }
276 }
277
278 function XHRErrorHandler(e)
279 {
280 var text = "${e.type}: ${e.loaded} bytes transferred"
281 console.log(text)
282 }
283
284 function pop_failure (e)
285 {
286 var text = "<p>An error occured";
287 if (typeof e !== 'undefined')
288 text += ": " + e;
289 text += "</p>";
290 document.getElementById('error_pop').innerHTML = e;
291
292 document.getElementById('uploading').style.display = 'none';
293 document.getElementById('error_pop').style.display = '';
294 document.getElementById('upload').style.display = '';
295 document.getElementById('send').style.display = '';
296 }
297
298 function add_time_string_to_date(d, time)
299 {
300 if(typeof(d) != 'object' || !(d instanceof Date))
301 {
302 return false;
303 }
304
305 if (time == 'minute')
306 {
307 d.setSeconds (d.getSeconds() + 60);
308 return true;
309 }
310 if (time == 'hour')
311 {
312 d.setSeconds (d.getSeconds() + 3600);
313 return true;
314 }
315 if (time == 'day')
316 {
317 d.setSeconds (d.getSeconds() + 86400);
318 return true;
319 }
320 if (time == 'week')
321 {
322 d.setSeconds (d.getSeconds() + 604800);
323 return true;
324 }
325 if (time == 'fortnight')
326 {
327 d.setSeconds (d.getSeconds() + 1209600);
328 return true;
329 }
330 if (time == 'month')
331 {
332 d.setSeconds (d.getSeconds() + 2592000);
333 return true;
334 }
335 if (time == 'quarter')
336 {
337 d.setSeconds (d.getSeconds() + 7776000);
338 return true;
339 }
340 if (time == 'year')
341 {
342 d.setSeconds (d.getSeconds() + 31536000);
343 return true;
344 }
345 return false;
346 }
347
348 function classic_upload (file, time, password, one_time)
349 {
350 // Delay time estimation init as we can't have file size
351 upload_time_estimation_init(0);
352
353 var req = new XMLHttpRequest ();
354 req.upload.addEventListener ("progress", upload_progress, false);
355 req.addEventListener ("error", XHRErrorHandler, false);
356 req.addEventListener ("abort", XHRErrorHandler, false);
357 req.onreadystatechange = function ()
358 {
359 if (req.readyState == 4 && req.status == 200)
360 {
361 var res = req.responseText;
362
363 // if response starts with "Error" then show a failure
364 if (/^Error/.test(res))
365 {
366 pop_failure (res);
367 return;
368 }
369
370 res = res.split ("\n");
371 var expiryDate = '';
372 if (time != 'none')
373 {
374 // convert time (local time + selected expiry date)
375 var localDatetime = new Date();
376 if(!add_time_string_to_date(localDatetime, time))
377 {
378 pop_failure ('Error: Date can not be parsed');
379 return;
380 }
381 expiryDate = localDatetime;
382 }
383
384 show_link (res[0], res[1], res[2], expiryDate);
385 }
386 else
387 {
388 pop_failure ("<?php echo t("ERR_OCC"); ?>");
389 }
390 }
391 req.open ("POST", 'script.php' , true);
392
393 var form = new FormData();
394 form.append ("file", file);
395 if (time)
396 form.append ("time", time);
397 if (password)
398 form.append ("key", password);
399 if (one_time)
400 form.append ("one_time_download", '1');
401 req.send (form);
402 }
403
404 function check_html5_file_api ()
405 {
406 return window.File && window.FileReader && window.FileList && window.Blob;
407 }
408
409 var async_global_transfered = 0;
410 var async_global_file;
411 var async_global_ref = '';
412 var async_global_max_size = 0;
413 var async_global_time;
414 var async_global_transfering = 0;
415 var async_global_last_code;
416
417 function async_upload_start (max_size, file, time, password, one_time)
418 {
419 async_global_transfered = 0;
420 async_global_file = file;
421 async_global_max_size = max_size;
422 async_global_time = time;
423
424 var req = new XMLHttpRequest ();
425 req.addEventListener ("error", XHRErrorHandler, false);
426 req.addEventListener ("abort", XHRErrorHandler, false);
427 req.onreadystatechange = function ()
428 {
429 if (req.readyState == 4 && req.status == 200)
430 {
431 var res = req.responseText;
432
433 if (/^Error/.test(res))
434 {
435 pop_failure (res);
436 return;
437 }
438
439 res = res.split ("\n");
440 async_global_ref = res[0];
441 var code = res[1];
442 async_upload_push (code);
443 }
444 }
445 req.open ("POST", 'script.php?init_async' , true);
446
447 var form = new FormData();
448 form.append ("filename", async_global_file.name);
449 form.append ("type", async_global_file.type);
450 if (time)
451 form.append ("time", time);
452 if (password)
453 form.append ("key", password);
454 if (one_time)
455 form.append ("one_time_download", '1');
456
457 // Start time estimation
458 upload_time_estimation_init(async_global_file.size);
459
460 req.send (form);
461 }
462
463 function async_upload_progress (e)
464 {
465 if (e == undefined || e == null || !e.lengthComputable && async_global_file.size != 0)
466 return;
467
468 // Compute percentage
469 var p = Math.round ((e.loaded + async_global_transfered) * 100 / (async_global_file.size));
470 var p_str = ' ';
471 if (p != 100)
472 p_str = p.toString() + '%';
473 // Update estimation speed
474 upload_time_estimation_add(e.loaded + async_global_transfered);
475 // Get speed string
476 var speed_str = upload_time_estimation_speed_string();
477 speed_str = upload_speed_refresh_limiter(speed_str);
478 // Get time string
479 var time_str = chrono_update(upload_time_estimation_time());
480
481 show_upload_progression (p_str, speed_str, time_str);
482 }
483
484 function async_upload_push (code)
485 {
486 async_global_last_code = code;
487 if (async_global_transfered == async_global_file.size)
488 {
489 hide_upload_progression ();
490 async_upload_end (code);
491 return;
492 }
493 var req = new XMLHttpRequest ();
494 req.upload.addEventListener ("progress", async_upload_progress, false);
495 req.addEventListener ("error", XHRErrorHandler, false);
496 req.addEventListener ("abort", XHRErrorHandler, false);
497 req.onreadystatechange = function ()
498 {
499 if (req.readyState == 4)
500 {
501 if (req.status == 200)
502 {
503 var res = req.responseText;
504
505 // This error may be triggered when Jirafeau does not receive any file in POST.
506 // This may be due to bad php configuration where post_max_size is too low
507 // comparing to upload_max_filesize. Let's retry with lower file size.
508 if (res === "Error 23")
509 {
510 async_global_max_size = Math.max(1, async_global_max_size - 500);
511 async_upload_push (async_global_last_code);
512 return;
513 }
514 else if (/^Error/.test(res))
515 {
516 pop_failure (res);
517 return;
518 }
519
520 res = res.split ("\n");
521 var code = res[0]
522 async_global_transfered = async_global_transfering;
523 async_upload_push (code);
524 return;
525 }
526 else
527 {
528 // lower async_global_max_size and retry
529 // This can occurs in several cases:
530 // - Request Entity Too Large (413) due to server bad configuration relative to PHP configuration
531 // - Server Error (500) which can happen when PHP's `max_execution_time` is too low comparared to sent size
532 async_global_max_size = Math.max(1, parseInt (async_global_max_size * 0.5));
533 async_upload_push (async_global_last_code);
534 return;
535 }
536 }
537 }
538 req.open ("POST", 'script.php?push_async' , true);
539
540 var start = async_global_transfered;
541 var end = start + async_global_max_size;
542 if (end >= async_global_file.size)
543 end = async_global_file.size;
544 var blob = async_global_file.slice (start, end);
545 async_global_transfering = end;
546
547 var form = new FormData();
548 form.append ("ref", async_global_ref);
549 form.append ("data", blob);
550 form.append ("code", code);
551 req.send (form);
552 }
553
554 function async_upload_end (code)
555 {
556 var req = new XMLHttpRequest ();
557 req.addEventListener ("error", XHRErrorHandler, false);
558 req.addEventListener ("abort", XHRErrorHandler, false);
559 req.onreadystatechange = function ()
560 {
561 if (req.readyState == 4 && req.status == 200)
562 {
563 var res = req.responseText;
564
565 if (/^Error/.test(res))
566 {
567 pop_failure (res);
568 return;
569 }
570
571 res = res.split ("\n");
572 var expiryDate = '';
573 if (async_global_time != 'none')
574 {
575 // convert time (local time + selected expiry date)
576 var localDatetime = new Date();
577 if(!add_time_string_to_date(localDatetime, async_global_time)) {
578 pop_failure ('Error: Date can not be parsed');
579 return;
580 }
581 expiryDate = localDatetime;
582 }
583
584 show_link (res[0], res[1], res[2], expiryDate);
585 }
586 }
587 req.open ("POST", 'script.php?end_async' , true);
588
589 var form = new FormData();
590 form.append ("ref", async_global_ref);
591 form.append ("code", code);
592 req.send (form);
593 }
594
595 function upload (max_chunk_size)
596 {
597 var one_time_checkbox = document.getElementById('one_time_download');
598 var one_time = one_time_checkbox !== null ? one_time_checkbox.checked : false;
599 if (check_html5_file_api ())
600 {
601 async_upload_start (
602 max_chunk_size,
603 document.getElementById('file_select').files[0],
604 document.getElementById('select_time').value,
605 document.getElementById('input_key').value,
606 one_time
607 );
608 }
609 else
610 {
611 classic_upload (
612 document.getElementById('file_select').files[0],
613 document.getElementById('select_time').value,
614 document.getElementById('input_key').value,
615 one_time
616 );
617 }
618 }
619
620 var upload_time_estimation_total_size = 42;
621 var upload_time_estimation_transfered_size = 42;
622 var upload_time_estimation_transfered_date = 42;
623 var upload_time_estimation_moving_average_speed = 42;
624
625 function upload_time_estimation_init(total_size)
626 {
627 upload_time_estimation_total_size = total_size;
628 upload_time_estimation_transfered_size = 0;
629 upload_time_estimation_moving_average_speed = 0;
630 var d = new Date();
631 upload_time_estimation_transfered_date = d.getTime();
632 }
633
634 function upload_time_estimation_add(total_transfered_size)
635 {
636 // Let's compute the current speed
637 var d = new Date();
638 var speed = upload_time_estimation_moving_average_speed;
639 if (d.getTime() - upload_time_estimation_transfered_date != 0) {
640 speed = (total_transfered_size - upload_time_estimation_transfered_size)
641 / (d.getTime() - upload_time_estimation_transfered_date);
642 speed = Math.max(0, speed);
643 }
644 // Let's compute moving average speed on 30 values
645 var m = (upload_time_estimation_moving_average_speed * 29 + speed) / 30;
646 // Update global values
647 upload_time_estimation_transfered_size = total_transfered_size;
648 upload_time_estimation_transfered_date = d.getTime();
649 upload_time_estimation_moving_average_speed = m;
650 }
651
652 function upload_time_estimation_speed_string()
653 {
654 // speed ms -> s
655 var s = upload_time_estimation_moving_average_speed * 1000;
656 var res = 0;
657 var scale = '';
658 if (s <= 1000)
659 {
660 res = s.toString();
661 scale = "B/s";
662 }
663 else if (s < 1000000)
664 {
665 res = Math.floor(s/100) / 10;
666 scale = "KB/s";
667 }
668 else
669 {
670 res = Math.floor(s/100000) / 10;
671 scale = "MB/s";
672 }
673 if (res == 0)
674 return '';
675 return res.toString() + ' ' + scale;
676 }
677
678 function milliseconds_to_time_string (milliseconds)
679 {
680 function numberEnding (number) {
681 return (number > 1) ? translate ('PLURAL_ENDING') : '';
682 }
683
684 var temp = Math.floor(milliseconds / 1000);
685 var years = Math.floor(temp / 31536000);
686 if (years) {
687 return years + ' ' + translate ('YEAR') + numberEnding(years);
688 }
689 var days = Math.floor((temp %= 31536000) / 86400);
690 if (days) {
691 return days + ' ' + translate ('DAY') + numberEnding(days);
692 }
693 var hours = Math.floor((temp %= 86400) / 3600);
694 if (hours) {
695 return hours + ' ' + translate ('HOUR') + numberEnding(hours);
696 }
697 var minutes = Math.floor((temp %= 3600) / 60);
698 if (minutes) {
699 return minutes + ' ' + translate ('MINUTE') + numberEnding(minutes);
700 }
701 var seconds = temp % 60;
702 if (seconds) {
703 return seconds + ' ' + translate ('SECOND') + numberEnding(seconds);
704 }
705 return translate ('LESS_1_SEC');
706 }
707
708 function upload_time_estimation_time()
709 {
710 // Estimate remaining time
711 if (upload_time_estimation_moving_average_speed <= 0)
712 return 0;
713 return (upload_time_estimation_total_size - upload_time_estimation_transfered_size)
714 / upload_time_estimation_moving_average_speed;
715 }
716
717 var chrono_last_update = 0;
718 var chrono_time_ms = 0;
719 var chrono_time_ms_last_update = 0;
720 function chrono_update(time_ms)
721 {
722 var d = new Date();
723 var chrono = 0;
724 // Don't update too often
725 if (d.getTime() - chrono_last_update < 3000 &&
726 chrono_time_ms_last_update > 0)
727 chrono = chrono_time_ms;
728 else
729 {
730 chrono_last_update = d.getTime();
731 chrono_time_ms = time_ms;
732 chrono = time_ms;
733 chrono_time_ms_last_update = d.getTime();
734 }
735
736 // Adjust chrono for smooth estimation
737 chrono = chrono - (d.getTime() - chrono_time_ms_last_update);
738
739 // Let's update chronometer
740 var time_str = '';
741 if (chrono > 0)
742 time_str = milliseconds_to_time_string (chrono);
743 return time_str;
744 }
745
746 var upload_speed_refresh_limiter_last_update = 0;
747 var upload_speed_refresh_limiter_last_value = '';
748 function upload_speed_refresh_limiter(speed_str)
749 {
750 var d = new Date();
751 if (d.getTime() - upload_speed_refresh_limiter_last_update > 1500)
752 {
753 upload_speed_refresh_limiter_last_value = speed_str;
754 upload_speed_refresh_limiter_last_update = d.getTime();
755 }
756 return upload_speed_refresh_limiter_last_value;
757 }
758
759 // document.ready()
760 document.addEventListener('DOMContentLoaded', function(event) {
761 // Search for all datetime fields and convert the time to local timezone
762 convertAllDatetimeFields();
763 });
764
765 // Add copy event listeners
766 function copyLinkToClipboard(link_id) {
767 var focus = document.activeElement;
768 var e = document.getElementById(link_id);
769
770 var tmp = document.createElement("textarea");
771 document.body.appendChild(tmp);
772 tmp.textContent = e.href;
773 tmp.focus();
774 tmp.setSelectionRange(0, tmp.value.length);
775 document.execCommand("copy");
776 document.body.removeChild(tmp);
777
778 focus.focus();
779 }
780
781 function addCopyListener(button_id, link_id) {
782 if(document.getElementById(button_id)){
783 document.getElementById(button_id)
784 .addEventListener("click", function() {
785 copyLinkToClipboard(link_id);});
786 }
787 }
788
789 function copyTextToClipboard(text_id){
790 var copyText = document.getElementById(text_id);
791 copyText.select();
792 copyText.setSelectionRange(0, 99999);
793 navigator.clipboard.writeText(copyText.value);
794 }
795
796 function addTextCopyListener(button_id, text_id) {
797 if(document.getElementById(button_id)){
798 document.getElementById(button_id)
799 .addEventListener("click", function() {
800 copyTextToClipboard(text_id);});
801 }
802 }
803
804 function set_dark_mode() {
805 let steel_sheet = "<?php echo 'media/' . $cfg['dark_style'] . '/style.css.php'; ?>";
806 let shortcut_icon = "<?php echo 'media/' . $cfg['dark_style'] . '/favicon.ico'; ?>";
807 document.getElementById('stylesheet').href = steel_sheet;
808 document.getElementById('shortcut_icon').href = shortcut_icon;
809 }
810
811 function set_light_mode() {
812 let steel_sheet = "<?php echo 'media/' . $cfg['style'] . '/style.css.php'; ?>";
813 let shortcut_icon = "<?php echo 'media/' . $cfg['style'] . '/favicon.ico'; ?>";
814 document.getElementById('stylesheet').href = steel_sheet;
815 document.getElementById('shortcut_icon').href = shortcut_icon;
816 }
817
818 function color_scheme_preferences() {
819
820 let dark_mode_steel_sheet = "<?php echo 'media/' . $cfg['dark_style'] . '/style.css.php'; ?>"
821 if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
822 set_dark_mode();
823 } else {
824 set_light_mode();
825 }
826
827 // When user change its preference
828 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', lightMode => {
829 lightMode.matches ? set_dark_mode() : set_light_mode();
830 });
831 }
832
833 // @license-end

patrick-canterino.de