Files: fa86f12bec35efbf6f363716b3919c8f36beb8ee / map.js
4214 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 | for (var index = items.length; index < raw.length; index++) { |
97 | rebind(index) |
98 | } |
99 | } |
100 | |
101 | return changed |
102 | } |
103 | |
104 | function queueUpdateItem (i) { |
105 | if (!queue.length) { |
106 | setImmediate(flushQueue) |
107 | } |
108 | if (!~queue.indexOf(i)) { |
109 | queue.push(i) |
110 | } |
111 | } |
112 | |
113 | function flushQueue () { |
114 | var startedAt = Date.now() |
115 | while (queue.length && (!maxTime || Date.now() - startedAt < maxTime)) { |
116 | updateItem(queue.pop()) |
117 | } |
118 | binder.broadcast() |
119 | if (queue.length) { |
120 | setImmediate(flushQueue) |
121 | } |
122 | } |
123 | |
124 | function updateItem (i) { |
125 | var item = get(obs, i) |
126 | if (typeof item === 'object') { |
127 | raw[i] = lambda(item) |
128 | } else { |
129 | if (!lastValues.has(item)) { |
130 | lastValues.set(item, lambda(item)) |
131 | } |
132 | raw[i] = lastValues.get(item) |
133 | } |
134 | values[i] = resolve(raw[i]) |
135 | rebind(i) |
136 | } |
137 | |
138 | function rebind (index) { |
139 | if (watches[index]) { |
140 | watches[index]() |
141 | watches[index] = null |
142 | } |
143 | |
144 | if (binder.live) { |
145 | if (typeof raw[index] === 'function') { |
146 | watches[index] = updateValue(raw[index], index) |
147 | } |
148 | } |
149 | } |
150 | |
151 | function rebindAll () { |
152 | for (var i = 0; i < raw.length; i++) { |
153 | rebind(i) |
154 | } |
155 | } |
156 | |
157 | function updateValue (obs, index) { |
158 | return obs(function (value) { |
159 | if (values[index] !== value || typeof value === 'object') { |
160 | values[index] = value |
161 | binder.broadcast() |
162 | } |
163 | }) |
164 | } |
165 | } |
166 | |
167 | function get (target, index) { |
168 | if (typeof target === 'function' && !target.get) { |
169 | target = target() |
170 | } |
171 | |
172 | if (Array.isArray(target)) { |
173 | return target[index] |
174 | } else if (target && target.get) { |
175 | return target.get(index) |
176 | } |
177 | } |
178 | |
179 | function getLength (target) { |
180 | if (typeof target === 'function' && !target.getLength) { |
181 | target = target() |
182 | } |
183 | |
184 | if (Array.isArray(target)) { |
185 | return target.length |
186 | } else if (target && target.get) { |
187 | return target.getLength() |
188 | } |
189 | |
190 | return 0 |
191 | } |
192 | |
193 | function notIncluded (value) { |
194 | if (this.includes) { |
195 | return !this.includes(value) |
196 | } else if (this.indexOf) { |
197 | return !~this.indexOf(value) |
198 | } else if (typeof this === 'function') { |
199 | var array = this() |
200 | if (array && array.includes) { |
201 | return !array.includes(value) |
202 | } |
203 | } |
204 | return true |
205 | } |
206 | |
207 | function deleteEntry (entry) { |
208 | this.delete(entry) |
209 | } |
210 |
Built with git-ssb-web