Files: a1abe9b483bc5d56548a660d97c1e2290a176d19 / map.js
4128 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 | for (var i = 0, len = listeners.length; i < len; i++) { |
159 | listeners[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