Merge branch '3.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WoltLabSuite / Core / Date / Time / Relative.js
1 /**
2 * Transforms <time> elements to display the elapsed time relative to the current time.
3 *
4 * @author Alexander Ebert
5 * @copyright 2001-2018 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 * @module WoltLabSuite/Core/Date/Time/Relative
8 */
9 define(['Dom/ChangeListener', 'Language', 'WoltLabSuite/Core/Date/Util', 'WoltLabSuite/Core/Timer/Repeating'], function(DomChangeListener, Language, DateUtil, Repeating) {
10 "use strict";
11
12 var _elements = elByTag('time');
13 var _isActive = true;
14 var _isPending = false;
15 var _offset = null;
16
17 /**
18 * @exports WoltLabSuite/Core/Date/Time/Relative
19 */
20 return {
21 /**
22 * Transforms <time> elements on init and binds event listeners.
23 */
24 setup: function() {
25 new Repeating(this._refresh.bind(this), 60000);
26
27 DomChangeListener.add('WoltLabSuite/Core/Date/Time/Relative', this._refresh.bind(this));
28
29 document.addEventListener('visibilitychange', this._onVisibilityChange.bind(this));
30 },
31
32 _onVisibilityChange: function () {
33 if (document.hidden) {
34 _isActive = false;
35 _isPending = false;
36 }
37 else {
38 _isActive = true;
39
40 // force immediate refresh
41 if (_isPending) {
42 this._refresh();
43 _isPending = false;
44 }
45 }
46 },
47
48 _refresh: function() {
49 // activity is suspended while the tab is hidden, but force an
50 // immediate refresh once the page is active again
51 if (!_isActive) {
52 if (!_isPending) _isPending = true;
53 return;
54 }
55
56 var date = new Date();
57 var timestamp = (date.getTime() - date.getMilliseconds()) / 1000;
58 if (_offset === null) _offset = timestamp - window.TIME_NOW;
59
60 for (var i = 0, length = _elements.length; i < length; i++) {
61 var element = _elements[i];
62
63 if (!element.classList.contains('datetime') || elData(element, 'is-future-date')) continue;
64
65 var elTimestamp = ~~elData(element, 'timestamp') + _offset;
66 var elDate = elData(element, 'date');
67 var elTime = elData(element, 'time');
68 var elOffset = elData(element, 'offset');
69
70 if (!elAttr(element, 'title')) {
71 elAttr(element, 'title', Language.get('wcf.date.dateTimeFormat').replace(/%date%/, elDate).replace(/%time%/, elTime));
72 }
73
74 // timestamp is less than 60 seconds ago
75 if (elTimestamp >= timestamp || timestamp < (elTimestamp + 60)) {
76 element.textContent = Language.get('wcf.date.relative.now');
77 }
78 // timestamp is less than 60 minutes ago (display 1 hour ago rather than 60 minutes ago)
79 else if (timestamp < (elTimestamp + 3540)) {
80 var minutes = Math.max(Math.round((timestamp - elTimestamp) / 60), 1);
81 element.textContent = Language.get('wcf.date.relative.minutes', { minutes: minutes });
82 }
83 // timestamp is less than 24 hours ago
84 else if (timestamp < (elTimestamp + 86400)) {
85 var hours = Math.round((timestamp - elTimestamp) / 3600);
86 element.textContent = Language.get('wcf.date.relative.hours', { hours: hours });
87 }
88 // timestamp is less than 6 days ago
89 else if (timestamp < (elTimestamp + 518400)) {
90 var midnight = new Date(date.getFullYear(), date.getMonth(), date.getDate());
91 var days = Math.ceil((midnight / 1000 - elTimestamp) / 86400);
92
93 // get day of week
94 var dateObj = DateUtil.getTimezoneDate((elTimestamp * 1000), elOffset * 1000);
95 var dow = dateObj.getDay();
96 var day = Language.get('__days')[dow];
97
98 element.textContent = Language.get('wcf.date.relative.pastDays', { days: days, day: day, time: elTime });
99 }
100 // timestamp is between ~700 million years BC and last week
101 else {
102 element.textContent = Language.get('wcf.date.shortDateTimeFormat').replace(/%date%/, elDate).replace(/%time%/, elTime);
103 }
104 }
105 }
106 };
107 });