forked from reviewjs/annotate
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsync-engine.js
More file actions
117 lines (104 loc) · 3.7 KB
/
Copy pathsync-engine.js
File metadata and controls
117 lines (104 loc) · 3.7 KB
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/* =============================================================================
* sync-engine.js — pluggable remote-sync engine for annotate-sync.
* Load AFTER annotate-sync.js. Replaces window.Annotate.sync with the real engine.
* Plugins register via Annotate.sync.register({…}).
* ========================================================================== */
(function () {
if (!window.Annotate || !window.Annotate._internals) return;
var I = window.Annotate._internals;
var DIRTY = {};
var TIMER = null;
var DEBOUNCE = 2000;
var engine = {
plugins: [],
register: function (p) { engine.plugins.push(p); },
anyEnabled: function () {
return engine.plugins.some(function (p) { return p.enabled(); });
},
markDirty: function (c) {
DIRTY[c.id] = c;
clearTimeout(TIMER);
TIMER = setTimeout(engine.flush, DEBOUNCE);
},
flush: function () {
TIMER = null;
var ids = Object.keys(DIRTY);
if (!ids.length) return;
var all = I.dbRead().comments;
engine.plugins.forEach(function (p) {
if (!p.enabled()) return;
ids.forEach(function (id) {
var c = all.filter(function (x) { return x.id === id; })[0];
if (c && !I.pendingDeletes[id]) try { p.push(c); } catch (e) {}
});
});
DIRTY = {};
},
delete: function (ids) {
if (!Array.isArray(ids)) ids = [ids];
ids.forEach(function (id) { delete DIRTY[id]; });
engine.plugins.forEach(function (p) {
if (!p.enabled()) return;
try { p.delete(ids); } catch (e) {}
});
},
pull: function (callback) {
var plugins = engine.plugins.filter(function (p) { return p.enabled(); });
if (!plugins.length) { if (callback) callback([], null); return; }
var remaining = plugins.length;
var allIncoming = [];
plugins.forEach(function (p) {
try {
p.pull(function (incoming, err) {
if (incoming) allIncoming = allIncoming.concat(incoming);
remaining--;
if (remaining === 0) callback(allIncoming, null);
});
} catch (e) { remaining--; if (remaining === 0) callback(allIncoming, null); }
});
},
mergeAll: function (incoming) {
if (!incoming || !incoming.length) return 0;
var d = I.dbRead();
var existing = {};
d.comments.forEach(function (c, i) { existing[c.id] = i; });
var added = 0;
incoming.forEach(function (c) {
if (!c || !c.id) return;
if (existing.hasOwnProperty(c.id)) {
if (c.updatedAt >= (d.comments[existing[c.id]].updatedAt || "")) {
d.comments[existing[c.id]] = c;
}
} else {
d.comments.push(c);
added++;
}
});
I.dbWrite(d);
return added;
},
renderFooter: function (el, n, canShare) {
engine.plugins.forEach(function (p) {
if (!p.renderStatus) return;
try { p.renderStatus(el, n, canShare); } catch (e) {}
});
},
};
window.Annotate.sync = engine;
// Trigger initial pull after all deferred scripts (plugins) have loaded.
// Defer scripts execute during 'interactive' state in document order, so
// we must wait for DOMContentLoaded which fires after the last defer script.
document.addEventListener('DOMContentLoaded', function () {
if (engine.anyEnabled()) engine.flush();
engine.pull(function (incoming, err) {
if (incoming && incoming.length) {
var added = engine.mergeAll(incoming);
if (added > 0) {
I.state.comments = I.pageComments().filter(function (c) { return !I.pendingDeletes[c.id]; });
I.renderAll();
I.renderPanel();
}
}
});
});
})();