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)
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.
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.
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/>.
22 function template_js_preview_link()
25 if (!!document
.getElementById('preview_link'))
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)
31 preview_link_href +
= '&k=' + crypt_key
;
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'))
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
= '';
49 function template_js_download_page()
53 var download_link_href
= 'f.php?h=' + reference
;
54 if (crypt_key
.length
> 0)
56 download_link_href +
= '&k=' + crypt_key
;
58 if (!!document
.getElementById('upload_finished_download_page'))
60 document
.getElementById('upload_link').href
= download_link_href
;
61 document
.getElementById('upload_link_text').innerHTML
= web_root + download_link_href
;
65 function template_js_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
))
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
);
78 function template_js_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
;
87 function template_js_direct_download()
90 // Direct download link
91 var direct_download_link_href
= 'f.php?h=' + reference +
'&d=1';
92 if (crypt_key
.length
> 0)
94 direct_download_link_href +
= '&k=' + crypt_key
;
96 document
.getElementById('direct_link').href
= direct_download_link_href
;
97 document
.getElementById('direct_link_text').innerHTML
= web_root + direct_download_link_href
;
100 function template_js_date_function()
106 document
.getElementById('date').style
.display
= 'none';
109 document
.getElementById('date').innerHTML
=
110 '<span class="datetime"'
112 +
dateToUtcString(date
) +
' (GMT"'
114 +
dateToUtcString(date
) +
' (GMT)">'
115 + date
.format('YYYY-MM-DD hh:mm (GMT O)')
117 document
.getElementById('date').style
.display
= '';
125 header('Content-Type: text/javascript');
126 define('JIRAFEAU_ROOT', dirname(__FILE__
) . '/../');
128 require(JIRAFEAU_ROOT
. 'lib/settings.php');
129 require(JIRAFEAU_ROOT
. 'lib/functions.php');
130 require(JIRAFEAU_ROOT
. 'lib/lang.php');
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']; ?>";
136 var lang_array
= <?php
echo json_lang_generator(null); ?
>;
137 var lang_array_fallback
= <?php
echo json_lang_generator("en"); ?
>;
139 function translate (expr
) {
140 if (lang_array
.hasOwnProperty(expr
)) {
141 var e
= lang_array
[expr
];
145 if (lang_array_fallback
.hasOwnProperty(expr
)) {
146 var e
= lang_array_fallback
[expr
];
150 return "FIXME: " + expr
;
153 function isEmpty(str
) {
154 return (!str ||
0 === str
.length
);
157 // Extend date object with format method
158 Date
.prototype
.format
= function(format
) {
159 format
= format ||
'YYYY-MM-DD hh:mm';
161 var zeropad
= function(number
, length
) {
162 number
= number
.toString();
163 length
= length ||
2;
164 while(number
.length
< length
)
165 number
= '0' + number
;
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()),
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
;
183 pattern
= '(' +
Object.keys(formats
).join(')|(') +
')';
185 return format
.replace(new RegExp(pattern
, 'g'), function(match
) {
186 return formats
[match
];
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));
196 function dateFromUtcTimestamp(datetimestamp
) {
197 return new Date(parseInt(datetimestamp
) * 1000)
200 function dateToUtcString(datelocal
) {
202 datelocal
.getUTCFullYear(),
203 datelocal
.getUTCMonth(),
204 datelocal
.getUTCDate(),
205 datelocal
.getUTCHours(),
206 datelocal
.getUTCMinutes(),
207 datelocal
.getUTCSeconds()
211 function dateToUtcTimestamp(datelocal
) {
213 datelocal
.getUTCFullYear(),
214 datelocal
.getUTCMonth(),
215 datelocal
.getUTCDate(),
216 datelocal
.getUTCHours(),
217 datelocal
.getUTCMinutes(),
218 datelocal
.getUTCSeconds()
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)');
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
)
234 <?php
template_js_download_page(); ?
>
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();?
>
247 function show_link (reference
, delete_code
, crypt_key
, date
)
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']; ?>";
255 <?php
template_js_download_page(); ?
>
257 var filename
= document
.getElementById('file_select').files
[0].name
;
259 <?php
template_js_email_link(); ?
>
261 <?php
template_js_delete_link(); ?
>
262 <?php
template_js_date_function(); ?
>
265 // Preview link (if allowed)
267 var type
= document
.getElementById('file_select').files
[0].type
;
268 <?php
template_js_preview_link(); ?
>
270 <?php
template_js_direct_download();?
>
273 // Hide preview and direct download link if password is set
274 if (document
.getElementById('input_key').value
.length
> 0)
276 if (!!document
.getElementById('preview_link'))
277 document
.getElementById('upload_finished_preview').style
.display
= 'none';
278 document
.getElementById('upload_direct_download').style
.display
= 'none';
282 function show_upload_progression (percentage
, speed
, time_left
)
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']; ?>";
290 function hide_upload_progression ()
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']; ?>";
298 function upload_progress (e
)
300 if (e
== undefined || e
== null ||
!e
.lengthComputable
)
303 // Init time estimation if needed
304 if (upload_time_estimation_total_size
== 0)
305 upload_time_estimation_total_size
= e
.total
;
307 // Compute percentage
308 var p
= Math
.round (e
.loaded
* 100 / e
.total
);
311 p_str
= p
.toString() +
'%';
312 // Update estimation speed
313 upload_time_estimation_add(e
.loaded
);
315 var speed_str
= upload_time_estimation_speed_string();
316 speed_str
= upload_speed_refresh_limiter(speed_str
);
318 var time_str
= chrono_update(upload_time_estimation_time());
320 show_upload_progression (p_str
, speed_str
, time_str
);
323 function control_selected_file_size(max_size
, error_str
)
325 f_size
= document
.getElementById('file_select').files
[0].size
;
326 if (max_size
> 0 && f_size
> max_size
* 1024 * 1024)
328 pop_failure(error_str
);
329 document
.getElementById('send').style
.display
= 'none';
333 // add class to restyle upload form in next step
334 document
.getElementById('upload').setAttribute('class', 'file-selected');
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();
343 function XHRErrorHandler(e
)
345 var text
= "${e.type}: ${e.loaded} bytes transferred"
349 function pop_failure (e
)
351 var text
= "<p>An error occured";
352 if (typeof e
!== 'undefined')
355 document
.getElementById('error_pop').innerHTML
= e
;
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
= '';
363 function add_time_string_to_date(d
, time
)
365 if(typeof(d
) != 'object' ||
!(d
instanceof Date
))
370 if (time
== 'minute')
372 d
.setSeconds (d
.getSeconds() +
60);
377 d
.setSeconds (d
.getSeconds() +
3600);
382 d
.setSeconds (d
.getSeconds() +
86400);
387 d
.setSeconds (d
.getSeconds() +
604800);
390 if (time
== 'fortnight')
392 d
.setSeconds (d
.getSeconds() +
1209600);
397 d
.setSeconds (d
.getSeconds() +
2592000);
400 if (time
== 'quarter')
402 d
.setSeconds (d
.getSeconds() +
7776000);
407 d
.setSeconds (d
.getSeconds() +
31536000);
413 function classic_upload (file
, time
, password
, one_time
)
415 // Delay time estimation init as we can't have file size
416 upload_time_estimation_init(0);
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 ()
424 if (req
.readyState
== 4 && req
.status
== 200)
426 var res
= req
.responseText
;
428 // if response starts with "Error" then show a failure
429 if (/^Error
/.test(res
))
435 res
= res
.split ("\n");
439 // convert time (local time + selected expiry date)
440 var localDatetime
= new Date();
441 if(!add_time_string_to_date(localDatetime
, time
))
443 pop_failure ('Error: Date can not be parsed');
446 expiryDate
= localDatetime
;
449 show_link (res
[0], res
[1], res
[2], expiryDate
);
453 pop_failure ("<?php echo t("ERR_OCC
"); ?>");
456 req
.open ("POST", 'script.php' , true);
458 var form
= new FormData();
459 form
.append ("file", file
);
461 form
.append ("time", time
);
463 form
.append ("key", password
);
465 form
.append ("one_time_download", '1');
469 function check_html5_file_api ()
471 return window
.File
&& window
.FileReader
&& window
.FileList
&& window
.Blob
;
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
;
482 function async_upload_start (max_size
, file
, time
, password
, one_time
)
484 async_global_transfered
= 0;
485 async_global_file
= file
;
486 async_global_max_size
= max_size
;
487 async_global_time
= time
;
489 var req
= new XMLHttpRequest ();
490 req
.addEventListener ("error", XHRErrorHandler
, false);
491 req
.addEventListener ("abort", XHRErrorHandler
, false);
492 req
.onreadystatechange
= function ()
494 if (req
.readyState
== 4 && req
.status
== 200)
496 var res
= req
.responseText
;
498 if (/^Error
/.test(res
))
504 res
= res
.split ("\n");
505 async_global_ref
= res
[0];
507 async_upload_push (code
);
510 req
.open ("POST", 'script.php?init_async' , true);
512 var form
= new FormData();
513 form
.append ("filename", async_global_file
.name
);
514 form
.append ("type", async_global_file
.type
);
516 form
.append ("time", time
);
518 form
.append ("key", password
);
520 form
.append ("one_time_download", '1');
522 // Start time estimation
523 upload_time_estimation_init(async_global_file
.size
);
528 function async_upload_progress (e
)
530 if (e
== undefined || e
== null ||
!e
.lengthComputable
&& async_global_file
.size
!= 0)
533 // Compute percentage
534 var p
= Math
.round ((e
.loaded + async_global_transfered
) * 100 / (async_global_file
.size
));
537 p_str
= p
.toString() +
'%';
538 // Update estimation speed
539 upload_time_estimation_add(e
.loaded + async_global_transfered
);
541 var speed_str
= upload_time_estimation_speed_string();
542 speed_str
= upload_speed_refresh_limiter(speed_str
);
544 var time_str
= chrono_update(upload_time_estimation_time());
546 show_upload_progression (p_str
, speed_str
, time_str
);
549 function async_upload_push (code
)
551 async_global_last_code
= code
;
552 if (async_global_transfered
== async_global_file
.size
)
554 hide_upload_progression ();
555 async_upload_end (code
);
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 ()
564 if (req
.readyState
== 4)
566 if (req
.status
== 200)
568 var res
= req
.responseText
;
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")
575 async_global_max_size
= Math
.max(1, async_global_max_size
- 500);
576 async_upload_push (async_global_last_code
);
579 else if (/^Error
/.test(res
))
585 res
= res
.split ("\n");
587 async_global_transfered
= async_global_transfering
;
588 async_upload_push (code
);
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
);
603 req
.open ("POST", 'script.php?push_async' , true);
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
;
612 var form
= new FormData();
613 form
.append ("ref", async_global_ref
);
614 form
.append ("data", blob
);
615 form
.append ("code", code
);
619 function async_upload_end (code
)
621 var req
= new XMLHttpRequest ();
622 req
.addEventListener ("error", XHRErrorHandler
, false);
623 req
.addEventListener ("abort", XHRErrorHandler
, false);
624 req
.onreadystatechange
= function ()
626 if (req
.readyState
== 4 && req
.status
== 200)
628 var res
= req
.responseText
;
630 if (/^Error
/.test(res
))
636 res
= res
.split ("\n");
638 if (async_global_time
!= 'none')
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');
646 expiryDate
= localDatetime
;
649 show_link (res
[0], res
[1], res
[2], expiryDate
);
652 req
.open ("POST", 'script.php?end_async' , true);
654 var form
= new FormData();
655 form
.append ("ref", async_global_ref
);
656 form
.append ("code", code
);
660 function upload (max_chunk_size
)
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 ())
668 document
.getElementById('file_select').files
[0],
669 document
.getElementById('select_time').value
,
670 document
.getElementById('input_key').value
,
677 document
.getElementById('file_select').files
[0],
678 document
.getElementById('select_time').value
,
679 document
.getElementById('input_key').value
,
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;
690 function upload_time_estimation_init(total_size
)
692 upload_time_estimation_total_size
= total_size
;
693 upload_time_estimation_transfered_size
= 0;
694 upload_time_estimation_moving_average_speed
= 0;
696 upload_time_estimation_transfered_date
= d
.getTime();
699 function upload_time_estimation_add(total_transfered_size
)
701 // Let's compute the current speed
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
);
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
;
717 function upload_time_estimation_speed_string()
720 var s
= upload_time_estimation_moving_average_speed
* 1000;
728 else if (s
< 1000000)
730 res
= Math
.floor(s
/100) / 10;
735 res
= Math
.floor(s
/100000) / 10;
740 return res
.toString() +
' ' + scale
;
743 function milliseconds_to_time_string (milliseconds
)
745 function numberEnding (number
) {
746 return (number
> 1) ?
translate ('PLURAL_ENDING') : '';
749 var temp
= Math
.floor(milliseconds
/ 1000);
750 var years
= Math
.floor(temp
/ 31536000);
752 return years +
' ' +
translate ('YEAR') +
numberEnding(years
);
754 var days
= Math
.floor((temp %
= 31536000) / 86400);
756 return days +
' ' +
translate ('DAY') +
numberEnding(days
);
758 var hours
= Math
.floor((temp %
= 86400) / 3600);
760 return hours +
' ' +
translate ('HOUR') +
numberEnding(hours
);
762 var minutes
= Math
.floor((temp %
= 3600) / 60);
764 return minutes +
' ' +
translate ('MINUTE') +
numberEnding(minutes
);
766 var seconds
= temp %
60;
768 return seconds +
' ' +
translate ('SECOND') +
numberEnding(seconds
);
770 return translate ('LESS_1_SEC');
773 function upload_time_estimation_time()
775 // Estimate remaining time
776 if (upload_time_estimation_moving_average_speed
<= 0)
778 return (upload_time_estimation_total_size
- upload_time_estimation_transfered_size
)
779 / upload_time_estimation_moving_average_speed
;
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
)
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
;
795 chrono_last_update
= d
.getTime();
796 chrono_time_ms
= time_ms
;
798 chrono_time_ms_last_update
= d
.getTime();
801 // Adjust chrono for smooth estimation
802 chrono
= chrono
- (d
.getTime() - chrono_time_ms_last_update
);
804 // Let's update chronometer
807 time_str
= milliseconds_to_time_string (chrono
);
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
)
816 if (d
.getTime() - upload_speed_refresh_limiter_last_update
> 1500)
818 upload_speed_refresh_limiter_last_value
= speed_str
;
819 upload_speed_refresh_limiter_last_update
= d
.getTime();
821 return upload_speed_refresh_limiter_last_value
;
825 document
.addEventListener('DOMContentLoaded', function(event
) {
826 // Search for all datetime fields and convert the time to local timezone
827 convertAllDatetimeFields();
830 // Add copy event listeners
831 function copyLinkToClipboard(link_id
) {
832 var focus
= document
.activeElement
;
833 var e
= document
.getElementById(link_id
);
835 var tmp
= document
.createElement("textarea");
836 document
.body
.appendChild(tmp
);
837 tmp
.textContent
= e
.href
;
839 tmp
.setSelectionRange(0, tmp
.value
.length
);
840 document
.execCommand("copy");
841 document
.body
.removeChild(tmp
);
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
);});
854 function copyTextToClipboard(text_id
){
855 var copyText
= document
.getElementById(text_id
);
857 copyText
.setSelectionRange(0, 99999);
858 navigator
.clipboard
.writeText(copyText
.value
);
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
);});
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
;
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
;
883 function color_scheme_preferences() {
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
) {
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();