git ssb

1+

Matt McKegg / mutant



Tree: bf170bc7b3672405705146b57533b5850f041abc

Files: bf170bc7b3672405705146b57533b5850f041abc / map.js

4116 bytesRaw
1var resolve = require('./resolve')
2var LazyWatcher = require('./lib/lazy-watcher')
3
4module.exports = Map
5
6function 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
164function 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
176function 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
190function 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
204function deleteEntry (entry) {
205 this.delete(entry)
206}
207

Built with git-ssb-web