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

patrick-canterino.de