git ssb

1+

Matt McKegg / mutant



Tree: aacf3bddbfecb924c25697a24bafc3bb0b1b86ad

Files: aacf3bddbfecb924c25697a24bafc3bb0b1b86ad / map.js

4185 bytesRaw
1var resolve = require('./resolve')
2var computed = require('./computed')
3
4module.exports = Map
5
6function 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
173function 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
185function 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
199function 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
213function deleteEntry (entry) {
214 this.delete(entry)
215}
216

Built with git-ssb-web