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

patrick-canterino.de