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