git ssb

1+

Matt McKegg / mutant



Tree: 5be33c01238870a66c608fed4ece3fbc43069a51

Files: 5be33c01238870a66c608fed4ece3fbc43069a51 / map.js

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

Built with git-ssb-web