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