map.jsView |
---|
9 | 9 | |
10 | 10 | |
11 | 11 | var comparer = opts && opts.comparer || null |
12 | 12 | var releases = [] |
13 | | - var invalidateReleases = new global.WeakMap() |
14 | 13 | var binder = LazyWatcher(update, listen, unlisten) |
15 | 14 | |
| 15 | + var itemInvalidators = new global.Map() |
16 | 16 | var lastValues = new global.Map() |
17 | 17 | var rawSet = new global.Set() |
18 | 18 | |
19 | 19 | var items = [] |
49 | 49 | releases.push(obs(binder.onUpdate)) |
50 | 50 | } |
51 | 51 | rebindAll() |
52 | 52 | |
| 53 | + Array.from(itemInvalidators.values).forEach(function (invalidators) { |
| 54 | + invalidators.forEach(function (invalidator) { |
| 55 | + invalidator.release = invalidator.observable(invalidate.bind(null, invalidator)) |
| 56 | + }) |
| 57 | + }) |
| 58 | + |
53 | 59 | if (opts && opts.onListen) { |
54 | 60 | opts.onListen() |
55 | 61 | } |
56 | 62 | } |
60 | 66 | releases.pop()() |
61 | 67 | } |
62 | 68 | rebindAll() |
63 | 69 | |
| 70 | + Array.from(itemInvalidators.values).forEach(function (invalidators) { |
| 71 | + invalidators.forEach(invokeRelease) |
| 72 | + }) |
| 73 | + |
64 | 74 | if (opts && opts.onUnlisten) { |
65 | 75 | opts.onUnlisten() |
66 | 76 | } |
67 | 77 | } |
79 | 89 | var item = get(obs, i) |
80 | 90 | var currentItem = items[i] |
81 | 91 | items[i] = item |
82 | 92 | |
83 | | - if (!isSame(item, currentItem, comparer)) { |
| 93 | + if (!isSame(item, currentItem, comparer) || (!binder.live && checkInvalidated(item))) { |
84 | 94 | if (maxTime && Date.now() - startedAt > maxTime) { |
85 | 95 | queueUpdateItem(i) |
86 | 96 | } else { |
87 | 97 | updateItem(i) |
93 | 103 | if (changed) { |
94 | 104 | |
95 | 105 | var oldLength = raw.length |
96 | 106 | var newLength = getLength(obs) |
97 | | - Array.from(lastValues.keys()).filter(notIncluded, obs).forEach(deleteEntry, lastValues) |
| 107 | + Array.from(lastValues.keys()).filter(notIncluded, obs).forEach(removeItem) |
98 | 108 | items.length = newLength |
99 | 109 | values.length = newLength |
100 | 110 | raw.length = newLength |
101 | 111 | for (var index = newLength; index < oldLength; index++) { |
102 | 112 | rebind(index) |
103 | 113 | } |
104 | | - Array.from(rawSet.values()).filter(notIncluded, raw).forEach(notifyRemoved) |
| 114 | + Array.from(rawSet.values()).filter(notIncluded, raw).forEach(removeMapped) |
105 | 115 | } |
106 | 116 | |
107 | 117 | return changed |
108 | 118 | } |
109 | 119 | |
| 120 | + function checkInvalidated (item) { |
| 121 | + if (itemInvalidators.has(item)) { |
| 122 | + return itemInvalidators.get(item).some(function (invalidator) { |
| 123 | + lastValues.delete(invalidator.item) |
| 124 | + return !isSame(invalidator.currentValue, resolve(invalidator.observable), comparer) |
| 125 | + }) |
| 126 | + } |
| 127 | + } |
| 128 | + |
110 | 129 | function queueUpdateItem (i) { |
111 | 130 | if (!queue.length) { |
112 | 131 | setImmediate(flushQueue) |
113 | 132 | } |
127 | 146 | } |
128 | 147 | } |
129 | 148 | |
130 | 149 | function invalidateOn (item, obs) { |
131 | | - if (!invalidateReleases.has(item)) { |
132 | | - invalidateReleases.set(item, []) |
| 150 | + if (!itemInvalidators.has(item)) { |
| 151 | + itemInvalidators.set(item, []) |
133 | 152 | } |
134 | | - invalidateReleases.get(item).push(obs(invalidate.bind(null, item))) |
| 153 | + |
| 154 | + var invalidators = itemInvalidators.get(item) |
| 155 | + var invalidator = { |
| 156 | + currentValue: resolve(obs), |
| 157 | + observable: obs, |
| 158 | + item: item, |
| 159 | + release: null |
| 160 | + } |
| 161 | + |
| 162 | + invalidators.push(invalidator) |
| 163 | + |
| 164 | + if (binder.live) { |
| 165 | + invalidator.release = invalidator.observable(invalidate.bind(null, invalidator)) |
| 166 | + } |
135 | 167 | } |
136 | 168 | |
137 | 169 | function addInvalidateCallback (item) { |
138 | 170 | return invalidateOn.bind(null, item) |
139 | 171 | } |
140 | 172 | |
141 | | - function notifyRemoved (item) { |
142 | | - rawSet.delete(item) |
143 | | - invalidateReleases.delete(item) |
| 173 | + function removeItem (item) { |
| 174 | + lastValues.delete(item) |
| 175 | + if (itemInvalidators.has(item)) { |
| 176 | + itemInvalidators.get(item).forEach(invokeRelease) |
| 177 | + itemInvalidators.delete(item) |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + function removeMapped (mappedItem) { |
| 182 | + rawSet.delete(mappedItem) |
144 | 183 | if (opts && opts.onRemove) { |
145 | | - opts.onRemove(item) |
| 184 | + opts.onRemove(mappedItem) |
146 | 185 | } |
147 | 186 | } |
148 | 187 | |
149 | | - function invalidate (item) { |
| 188 | + function invalidate (entry) { |
150 | 189 | var changed = false |
151 | 190 | var length = getLength(obs) |
152 | | - lastValues.delete(item) |
| 191 | + lastValues.delete(entry.item) |
153 | 192 | for (var i = 0; i < length; i++) { |
154 | | - if (get(obs, i) === item) { |
| 193 | + if (get(obs, i) === entry.item) { |
155 | 194 | updateItem(i) |
156 | 195 | changed = true |
157 | 196 | } |
158 | 197 | } |
163 | 202 | |
164 | 203 | function updateItem (i) { |
165 | 204 | var item = get(obs, i) |
166 | 205 | if (!lastValues.has(item) || !isSame(item, item, comparer)) { |
| 206 | + if (itemInvalidators.has(item)) { |
| 207 | + itemInvalidators.get(item).forEach(invokeRelease) |
| 208 | + itemInvalidators.delete(item) |
| 209 | + } |
167 | 210 | var newValue = lambda(item, addInvalidateCallback(item)) |
168 | 211 | if (newValue !== raw[i]) { |
169 | 212 | raw[i] = newValue |
170 | 213 | } |
248 | 291 | |
249 | 292 | function deleteEntry (entry) { |
250 | 293 | this.delete(entry) |
251 | 294 | } |
| 295 | + |
| 296 | +function invokeRelease (item) { |
| 297 | + if (item.release) { |
| 298 | + item.release() |
| 299 | + item.release = null |
| 300 | + } |
| 301 | +} |