forked from tiadrop/face
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Face.Parts.General.js
101 lines (91 loc) · 2.93 KB
/
Face.Parts.General.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
const round = n => Math.round(n);
const plural = (num, noun) => {
if (num == 1) return `1 ${noun}`;
return `${num} ${noun}s`;
};
face.register("Face.Parts.TimeAgo", el => {
const startTime = el.data.timestamp;
delete el.data.timestamp;
const get = () => {
const currentTime = Math.floor((new Date()).getTime() / 1000);
const seconds = currentTime - startTime;
if (seconds < 120) return plural(seconds, "second");
const minutes = round(seconds / 60);
if (minutes < 120) return plural(minutes, "minute");
const hours = round(minutes / 60);
if (hours < 48) return plural(hours, "hour");
const days = round(hours / 24);
if (days < 14) return plural(days, "day");
const weeks = round(days / 7);
if (weeks < 52) return plural(weeks, "week");
const years = round(weeks / 52);
return plural(years, "year");
}
const update = () => el.content = get();
update();
// interval is permanent and references el
setInterval(update, 1000);
});
const delegateIds = {};
let delegatePingInterval = null;
const getFormInfo = form => {
const data = {};
form.qsa("input,textarea,select").forEach(el => {
if (el.attribs.name) data[el.attribs.name] = el.value;
});
return data;
};
face.register("Face.Parts.DelegateButton", el => {
const delegateId = el.data.delegateId;
delete el.data.delegateId;
delegateIds[delegateId] = el;
if (delegatePingInterval === null) {
delegatePingInterval = setInterval(() => {
if(Object.keys(delegateIds).length) fetch('.Face/delegate/ping', {
method: "POST",
body: Object.keys(delegateIds).join('/')
});
}, 60000);
}
const once = el.data.once;
if (once) delete el.data.once;
let expireTime = el.data.expires;
let expireInterval = null;
if (expireTime) {
delete el.data.expires;
expireInterval = setInterval(() => {
if ((new Date()).getTime() / 1000 > expireTime) {
clearInterval(expireInterval);
expireInterval = null;
delete delegateIds[delegateId];
el.classes.add("Face_DelegateButton_expired");
el.attribs.title = "Action expired";
// todo client-side indicator of expiry
el.enabled = false;
}
}, 1000); // checking every second to account for browsers' potential interval restrictions eg in background tabs
}
el.on("click", () => {
const request = {
method: "POST",
};
let form = el.parent;
while (form) {
if (form.tag == "form") break;
form = form.parent;
}
if (form) request.body = JSON.stringify(getFormInfo(form));
fetch(".Face/delegate/" + delegateId, request).then(r => r.text()).then(s => {
if (once) {
el.enabled = false;
delete delegateIds[delegateId];
if (expireInterval !== null) clearInterval(expireInterval);
expireInterval = null;
}
// todo: work out means to have server determine what happens here; hmm
// likely a similar 'rpc' setup to C_B's and a configurable script pool reference
// ^ could include automatic dependency loading similar to element scripts
}).catch(e => {
});
});
});