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

patrick-canterino.de