Files: e75defac47630de9da141ca0ca89cba54a030464 / map.js
4009 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 | } |
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 | for (var i = 0, len = listeners.length; i < len; i++) { |
158 | listeners[i](values) |
159 | } |
160 | } |
161 | |
162 | function getValue () { |
163 | if (!live || lazy) { |
164 | lazy = false |
165 | update() |
166 | } |
167 | return values |
168 | } |
169 | } |
170 | |
171 | function 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 | |
183 | function 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 | |
197 | function notIncluded (value) { |
198 | if (typeof this === 'function' && !value.includes) { |
199 | var array = this() |
200 | if (array && array.includes) { |
201 | array.includes(value) |
202 | } |
203 | } else { |
204 | return !this.includes(value) |
205 | } |
206 | } |
207 | |
208 | function deleteEntry (entry) { |
209 | this.delete(entry) |
210 | } |
211 |
Built with git-ssb-web