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

patrick-canterino.de