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

patrick-canterino.de