]> git.p6c8.net - jirafeau.git/blob - lib/functions.js.php
[FEATURE] Convert UTC dates to clients timezone
[jirafeau.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 document.getElementById('options').style.display = '';
265 document.getElementById('send').style.display = '';
266 document.getElementById('error_pop').style.display = 'none';
267 document.getElementById('file_select').style.left = 'inherit';
268 document.getElementById('file_select').style.height = 'inherit';
269 document.getElementById('file_select').style.opacity = '1';
270 }
271 }
272
273 function pop_failure (e)
274 {
275 var text = "An error occured";
276 if (typeof e !== 'undefined')
277 text = e;
278 text = "<p>" + text + "</p>";
279 document.getElementById('error_pop').innerHTML = e;
280
281 document.getElementById('uploading').style.display = 'none';
282 document.getElementById('error_pop').style.display = '';
283 document.getElementById('upload').style.display = '';
284 document.getElementById('send').style.display = '';
285 }
286
287 function add_time_string_to_date(d, time)
288 {
289 if(typeof(d) != 'object' || !(d instanceof Date))
290 {
291 return false;
292 }
293
294 if (time == 'minute')
295 {
296 d.setSeconds (d.getSeconds() + 60);
297 return true;
298 }
299 if (time == 'hour')
300 {
301 d.setSeconds (d.getSeconds() + 3600);
302 return true;
303 }
304 if (time == 'day')
305 {
306 d.setSeconds (d.getSeconds() + 86400);
307 return true;
308 }
309 if (time == 'week')
310 {
311 d.setSeconds (d.getSeconds() + 604800);
312 return true;
313 }
314 if (time == 'month')
315 {
316 d.setSeconds (d.getSeconds() + 2419200);
317 return true;
318 }
319 if (time == 'quarter')
320 {
321 d.setSeconds (d.getSeconds() + 7257600);
322 return true;
323 }
324 if (time == 'year')
325 {
326 d.setSeconds (d.getSeconds() + 29030400);
327 return true;
328 }
329 return false;
330 }
331
332 function classic_upload (url, file, time, password, one_time, upload_password)
333 {
334 // Delay time estimation init as we can't have file size
335 upload_time_estimation_init(0);
336
337 var req = new XMLHttpRequest ();
338 req.upload.addEventListener ("progress", upload_progress, false);
339 req.addEventListener ("error", pop_failure, false);
340 req.addEventListener ("abort", pop_failure, false);
341 req.onreadystatechange = function ()
342 {
343 if (req.readyState == 4 && req.status == 200)
344 {
345 var res = req.responseText;
346
347 // if response starts with "Error" then show a failure
348 if (/^Error/.test(res))
349 {
350 pop_failure (res);
351 return;
352 }
353
354 res = res.split ("\n");
355 var expiryDate = '';
356 if (time != 'none')
357 {
358 // convert time (local time + selected expiry date)
359 var localDatetime = new Date();
360 if(!add_time_string_to_date(localDatetime, time))
361 {
362 pop_failure ('Error: Date can not be parsed');
363 return;
364 }
365 expiryDate = localDatetime;
366 }
367
368 show_link (url, res[0], res[1], res[2], expiryDate);
369 }
370 }
371 req.open ("POST", url + 'script.php' , true);
372
373 var form = new FormData();
374 form.append ("file", file);
375 if (time)
376 form.append ("time", time);
377 if (password)
378 form.append ("key", password);
379 if (one_time)
380 form.append ("one_time_download", '1');
381 if (upload_password.length > 0)
382 form.append ("upload_password", upload_password);
383
384 req.send (form);
385 }
386
387 function check_html5_file_api ()
388 {
389 return window.File && window.FileReader && window.FileList && window.Blob;
390 }
391
392 var async_global_transfered = 0;
393 var async_global_url = '';
394 var async_global_file;
395 var async_global_ref = '';
396 var async_global_max_size = 0;
397 var async_global_time;
398 var async_global_transfering = 0;
399
400 function async_upload_start (url, max_size, file, time, password, one_time, upload_password)
401 {
402 async_global_transfered = 0;
403 async_global_url = url;
404 async_global_file = file;
405 async_global_max_size = max_size;
406 async_global_time = time;
407
408 var req = new XMLHttpRequest ();
409 req.addEventListener ("error", pop_failure, false);
410 req.addEventListener ("abort", pop_failure, false);
411 req.onreadystatechange = function ()
412 {
413 if (req.readyState == 4 && req.status == 200)
414 {
415 var res = req.responseText;
416
417 if (/^Error/.test(res))
418 {
419 pop_failure (res);
420 return;
421 }
422
423 res = res.split ("\n");
424 async_global_ref = res[0];
425 var code = res[1];
426 async_upload_push (code);
427 }
428 }
429 req.open ("POST", async_global_url + 'script.php?init_async' , true);
430
431 var form = new FormData();
432 form.append ("filename", async_global_file.name);
433 form.append ("type", async_global_file.type);
434 if (time)
435 form.append ("time", time);
436 if (password)
437 form.append ("key", password);
438 if (one_time)
439 form.append ("one_time_download", '1');
440 if (upload_password.length > 0)
441 form.append ("upload_password", upload_password);
442
443 // Start time estimation
444 upload_time_estimation_init(async_global_file.size);
445
446 req.send (form);
447 }
448
449 function async_upload_progress (e)
450 {
451 if (e == undefined || e == null || !e.lengthComputable && async_global_file.size != 0)
452 return;
453
454 // Compute percentage
455 var p = Math.round ((e.loaded + async_global_transfered) * 100 / (async_global_file.size));
456 var p_str = ' ';
457 if (p != 100)
458 p_str = p.toString() + '%';
459 // Update estimation speed
460 upload_time_estimation_add(e.loaded + async_global_transfered);
461 // Get speed string
462 var speed_str = upload_time_estimation_speed_string();
463 speed_str = upload_speed_refresh_limiter(speed_str);
464 // Get time string
465 var time_str = chrono_update(upload_time_estimation_time());
466
467 show_upload_progression (p_str, speed_str, time_str);
468 }
469
470 function async_upload_push (code)
471 {
472 if (async_global_transfered == async_global_file.size)
473 {
474 hide_upload_progression ();
475 async_upload_end (code);
476 return;
477 }
478 var req = new XMLHttpRequest ();
479 req.upload.addEventListener ("progress", async_upload_progress, false);
480 req.addEventListener ("error", pop_failure, false);
481 req.addEventListener ("abort", pop_failure, false);
482 req.onreadystatechange = function ()
483 {
484 if (req.readyState == 4 && req.status == 200)
485 {
486 var res = req.responseText;
487
488 if (/^Error/.test(res))
489 {
490 pop_failure (res);
491 return;
492 }
493
494 res = res.split ("\n");
495 var code = res[0]
496 async_global_transfered = async_global_transfering;
497 async_upload_push (code);
498 }
499 }
500 req.open ("POST", async_global_url + 'script.php?push_async' , true);
501
502 var chunk_size = parseInt (async_global_max_size * 0.50);
503 var start = async_global_transfered;
504 var end = start + chunk_size;
505 if (end >= async_global_file.size)
506 end = async_global_file.size;
507 var blob = async_global_file.slice (start, end);
508 async_global_transfering = end;
509
510 var form = new FormData();
511 form.append ("ref", async_global_ref);
512 form.append ("data", blob);
513 form.append ("code", code);
514 req.send (form);
515 }
516
517 function async_upload_end (code)
518 {
519 var req = new XMLHttpRequest ();
520 req.addEventListener ("error", pop_failure, false);
521 req.addEventListener ("abort", pop_failure, false);
522 req.onreadystatechange = function ()
523 {
524 if (req.readyState == 4 && req.status == 200)
525 {
526 var res = req.responseText;
527
528 if (/^Error/.test(res))
529 {
530 pop_failure (res);
531 return;
532 }
533
534 res = res.split ("\n");
535 var expiryDate = '';
536 if (async_global_time != 'none')
537 {
538 // convert time (local time + selected expiry date)
539 var localDatetime = new Date();
540 if(!add_time_string_to_date(localDatetime, async_global_time)) {
541 pop_failure ('Error: Date can not be parsed');
542 return;
543 }
544 expiryDate = localDatetime;
545 }
546
547 show_link (async_global_url, res[0], res[1], res[2], expiryDate);
548 }
549 }
550 req.open ("POST", async_global_url + 'script.php?end_async' , true);
551
552 var form = new FormData();
553 form.append ("ref", async_global_ref);
554 form.append ("code", code);
555 req.send (form);
556 }
557
558 function upload (url, max_size)
559 {
560 if (check_html5_file_api ()
561 && document.getElementById('file_select').files[0].size >= max_size)
562 {
563 async_upload_start (url,
564 max_size,
565 document.getElementById('file_select').files[0],
566 document.getElementById('select_time').value,
567 document.getElementById('input_key').value,
568 document.getElementById('one_time_download').checked,
569 document.getElementById('upload_password').value
570 );
571 }
572 else
573 {
574 classic_upload (url,
575 document.getElementById('file_select').files[0],
576 document.getElementById('select_time').value,
577 document.getElementById('input_key').value,
578 document.getElementById('one_time_download').checked,
579 document.getElementById('upload_password').value
580 );
581 }
582 }
583
584 var upload_time_estimation_total_size = 42;
585 var upload_time_estimation_transfered_size = 42;
586 var upload_time_estimation_transfered_date = 42;
587 var upload_time_estimation_moving_average_speed = 42;
588
589 function upload_time_estimation_init(total_size)
590 {
591 upload_time_estimation_total_size = total_size;
592 upload_time_estimation_transfered_size = 0;
593 upload_time_estimation_moving_average_speed = 0;
594 var d = new Date();
595 upload_time_estimation_transfered_date = d.getTime();
596 }
597
598 function upload_time_estimation_add(total_transfered_size)
599 {
600 // Let's compute the current speed
601 var d = new Date();
602 var speed = upload_time_estimation_moving_average_speed;
603 if (d.getTime() - upload_time_estimation_transfered_date != 0)
604 speed = (total_transfered_size - upload_time_estimation_transfered_size)
605 / (d.getTime() - upload_time_estimation_transfered_date);
606 // Let's compute moving average speed on 30 values
607 var m = (upload_time_estimation_moving_average_speed * 29 + speed) / 30;
608 // Update global values
609 upload_time_estimation_transfered_size = total_transfered_size;
610 upload_time_estimation_transfered_date = d.getTime();
611 upload_time_estimation_moving_average_speed = m;
612 }
613
614 function upload_time_estimation_speed_string()
615 {
616 // speed ms -> s
617 var s = upload_time_estimation_moving_average_speed * 1000;
618 var res = 0;
619 var scale = '';
620 if (s <= 1000)
621 {
622 res = s.toString();
623 scale = "o/s";
624 }
625 else if (s < 1000000)
626 {
627 res = Math.floor(s/100) / 10;
628 scale = "Ko/s";
629 }
630 else
631 {
632 res = Math.floor(s/100000) / 10;
633 scale = "Mo/s";
634 }
635 if (res == 0)
636 return '';
637 return res.toString() + ' ' + scale;
638 }
639
640 function milliseconds_to_time_string (milliseconds)
641 {
642 function numberEnding (number) {
643 return (number > 1) ? 's' : '';
644 }
645
646 var temp = Math.floor(milliseconds / 1000);
647 var years = Math.floor(temp / 31536000);
648 if (years) {
649 return years + ' ' + translate ('year') + numberEnding(years);
650 }
651 var days = Math.floor((temp %= 31536000) / 86400);
652 if (days) {
653 return days + ' ' + translate ('day') + numberEnding(days);
654 }
655 var hours = Math.floor((temp %= 86400) / 3600);
656 if (hours) {
657 return hours + ' ' + translate ('hour') + numberEnding(hours);
658 }
659 var minutes = Math.floor((temp %= 3600) / 60);
660 if (minutes) {
661 return minutes + ' ' + translate ('minute') + numberEnding(minutes);
662 }
663 var seconds = temp % 60;
664 if (seconds) {
665 return seconds + ' ' + translate ('second') + numberEnding(seconds);
666 }
667 return translate ('less than a second');
668 }
669
670 function upload_time_estimation_time()
671 {
672 // Estimate remaining time
673 if (upload_time_estimation_moving_average_speed == 0)
674 return 0;
675 return (upload_time_estimation_total_size - upload_time_estimation_transfered_size)
676 / upload_time_estimation_moving_average_speed;
677 }
678
679 var chrono_last_update = 0;
680 var chrono_time_ms = 0;
681 var chrono_time_ms_last_update = 0;
682 function chrono_update(time_ms)
683 {
684 var d = new Date();
685 var chrono = 0;
686 // Don't update too often
687 if (d.getTime() - chrono_last_update < 3000 &&
688 chrono_time_ms_last_update > 0)
689 chrono = chrono_time_ms;
690 else
691 {
692 chrono_last_update = d.getTime();
693 chrono_time_ms = time_ms;
694 chrono = time_ms;
695 chrono_time_ms_last_update = d.getTime();
696 }
697
698 // Adjust chrono for smooth estimation
699 chrono = chrono - (d.getTime() - chrono_time_ms_last_update);
700
701 // Let's update chronometer
702 var time_str = '';
703 if (chrono > 0)
704 time_str = milliseconds_to_time_string (chrono);
705 return time_str;
706 }
707
708 var upload_speed_refresh_limiter_last_update = 0;
709 var upload_speed_refresh_limiter_last_value = '';
710 function upload_speed_refresh_limiter(speed_str)
711 {
712 var d = new Date();
713 if (d.getTime() - upload_speed_refresh_limiter_last_update > 1500)
714 {
715 upload_speed_refresh_limiter_last_value = speed_str;
716 upload_speed_refresh_limiter_last_update = d.getTime();
717 }
718 return upload_speed_refresh_limiter_last_value;
719 }
720
721 // document.ready()
722 document.addEventListener('DOMContentLoaded', function(event) {
723 // Search for all datetime fields and convert the time to local timezone
724 convertAllDatetimeFields();
725 });

patrick-canterino.de