git ssb

1+

Matt McKegg / mutant



Tree: 0e246ce681989f1a94e5ee1e8c1a6042d587c004

Files: 0e246ce681989f1a94e5ee1e8c1a6042d587c004 / map.js

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

Built with git-ssb-web