git ssb

1+

Matt McKegg / mutant



Tree: f69a37d1862b7bc2b9590d1514f049129243de67

Files: f69a37d1862b7bc2b9590d1514f049129243de67 / map.js

4214 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 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
167function 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
179function 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
193function 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
207function deleteEntry (entry) {
208 this.delete(entry)
209}
210

Built with git-ssb-web