Files: bf170bc7b3672405705146b57533b5850f041abc / map.js
4116 bytesRaw
1 | var resolve = require('./resolve') |
2 | var LazyWatcher = require('./lib/lazy-watcher') |
3 | |
4 | module.exports = Map |
5 | |
6 | function Map (obs, lambda, opts) { |
7 | var releases = [] |
8 | var binder = LazyWatcher(update, listen, unlisten) |
9 | |
10 | var lastValues = new global.Map() |
11 | var items = [] |
12 | |
13 | var raw = [] |
14 | var values = [] |
15 | var watches = [] |
16 | |
17 | binder.value = values |
18 | |
19 | // incremental update |
20 | var queue = [] |
21 | var maxTime = null |
22 | if (opts && opts.maxTime) { |
23 | maxTime = opts.maxTime |
24 | } |
25 | |
26 | var result = function MutantMap (listener) { |
27 | if (!listener) { |
28 | return binder.getValue() |
29 | } |
30 | return binder.addListener(listener) |
31 | } |
32 | |
33 | result.get = function (index) { |
34 | return raw[index] |
35 | } |
36 | |
37 | result.getLength = function (index) { |
38 | return raw.length |
39 | } |
40 | |
41 | result.includes = function (valueOrObs) { |
42 | return !!~raw.indexOf(valueOrObs) |
43 | } |
44 | |
45 | result.indexOf = function (valueOrObs) { |
46 | return raw.indexOf(valueOrObs) |
47 | } |
48 | |
49 | return result |
50 | |
51 | // scoped |
52 | |
53 | function listen () { |
54 | if (typeof obs === 'function') { |
55 | releases.push(obs(binder.onUpdate)) |
56 | } |
57 | rebindAll() |
58 | } |
59 | |
60 | function unlisten () { |
61 | while (releases.length) { |
62 | releases.pop()() |
63 | } |
64 | rebindAll() |
65 | } |
66 | |
67 | function update () { |
68 | var changed = false |
69 | |
70 | if (items.length !== getLength(obs)) { |
71 | changed = true |
72 | } |
73 | |
74 | var startedAt = Date.now() |
75 | |
76 | for (var i = 0, len = getLength(obs); i < len; i++) { |
77 | var item = get(obs, i) |
78 | var currentItem = items[i] |
79 | items[i] = item |
80 | |
81 | if (typeof item === 'object' || item !== currentItem) { |
82 | if (maxTime && Date.now() - startedAt > maxTime) { |
83 | queueUpdateItem(i) |
84 | } else { |
85 | updateItem(i) |
86 | } |
87 | changed = true |
88 | } |
89 | } |
90 | |
91 | if (changed) { |
92 | // clean up cache |
93 | Array.from(lastValues.keys()).filter(notIncluded, obs).forEach(deleteEntry, lastValues) |
94 | items.length = getLength(obs) |
95 | values.length = items.length |
96 | } |
97 | |
98 | return changed |
99 | } |
100 | |
101 | function queueUpdateItem (i) { |
102 | if (!queue.length) { |
103 | setImmediate(flushQueue) |
104 | } |
105 | if (!~queue.indexOf(i)) { |
106 | queue.push(i) |
107 | } |
108 | } |
109 | |
110 | function flushQueue () { |
111 | var startedAt = Date.now() |
112 | while (queue.length && (!maxTime || Date.now() - startedAt < maxTime)) { |
113 | updateItem(queue.pop()) |
114 | } |
115 | binder.broadcast() |
116 | if (queue.length) { |
117 | setImmediate(flushQueue) |
118 | } |
119 | } |
120 | |
121 | function updateItem (i) { |
122 | var item = get(obs, i) |
123 | if (typeof item === 'object') { |
124 | raw[i] = lambda(item) |
125 | } else { |
126 | if (!lastValues.has(item)) { |
127 | lastValues.set(item, lambda(item)) |
128 | } |
129 | raw[i] = lastValues.get(item) |
130 | } |
131 | values[i] = resolve(raw[i]) |
132 | rebind(i) |
133 | } |
134 | |
135 | function rebind (index) { |
136 | if (watches[index]) { |
137 | watches[index]() |
138 | watches[index] = null |
139 | } |
140 | |
141 | if (binder.live) { |
142 | if (typeof raw[index] === 'function') { |
143 | watches[index] = updateValue(raw[index], index) |
144 | } |
145 | } |
146 | } |
147 | |
148 | function rebindAll () { |
149 | for (var i = 0; i < raw.length; i++) { |
150 | rebind(i) |
151 | } |
152 | } |
153 | |
154 | function updateValue (obs, index) { |
155 | return obs(function (value) { |
156 | if (values[index] !== value || typeof value === 'object') { |
157 | values[index] = value |
158 | binder.broadcast() |
159 | } |
160 | }) |
161 | } |
162 | } |
163 | |
164 | function get (target, index) { |
165 | if (typeof target === 'function' && !target.get) { |
166 | target = target() |
167 | } |
168 | |
169 | if (Array.isArray(target)) { |
170 | return target[index] |
171 | } else if (target && target.get) { |
172 | return target.get(index) |
173 | } |
174 | } |
175 | |
176 | function getLength (target) { |
177 | if (typeof target === 'function' && !target.getLength) { |
178 | target = target() |
179 | } |
180 | |
181 | if (Array.isArray(target)) { |
182 | return target.length |
183 | } else if (target && target.get) { |
184 | return target.getLength() |
185 | } |
186 | |
187 | return 0 |
188 | } |
189 | |
190 | function notIncluded (value) { |
191 | if (this.includes) { |
192 | return !this.includes(value) |
193 | } else if (this.indexOf) { |
194 | return !~this.indexOf(value) |
195 | } else if (typeof this === 'function') { |
196 | var array = this() |
197 | if (array && array.includes) { |
198 | return !array.includes(value) |
199 | } |
200 | } |
201 | return true |
202 | } |
203 | |
204 | function deleteEntry (entry) { |
205 | this.delete(entry) |
206 | } |
207 |
Built with git-ssb-web