git ssb

1+

Matt McKegg / mutant



Tree: 3bfb6e65a932b5fb11b3c2260c7582699eb17ae5

Files: 3bfb6e65a932b5fb11b3c2260c7582699eb17ae5 / map.js

4148 bytesRaw
1var resolve = require('./resolve')
2
3module.exports = Map
4
5function Map (obs, lambda) {
6 var releases = []
7 var live = false
8 var lazy = false
9 var listeners = []
10
11 var lastValues = new global.Map()
12 var items = []
13
14 var raw = []
15 var values = []
16 var watches = []
17
18 var result = function MutantMap (listener) {
19 if (!listener) {
20 return getValue()
21 }
22
23 if (typeof listener !== 'function') {
24 throw new Error('Listeners must be functions.')
25 }
26
27 listeners.push(listener)
28 listen()
29
30 return function remove () {
31 for (var i = 0, len = listeners.length; i < len; i++) {
32 if (listeners[i] === listener) {
33 listeners.splice(i, 1)
34 break
35 }
36 }
37 if (!listeners.length) {
38 unlisten()
39 }
40 }
41 }
42
43 result.get = function (index) {
44 return raw[index]
45 }
46
47 result.getLength = function (index) {
48 return raw.length
49 }
50
51 result.includes = function (valueOrObs) {
52 return !!~raw.indexOf(valueOrObs)
53 }
54
55 result.indexOf = function (valueOrObs) {
56 return raw.indexOf(valueOrObs)
57 }
58
59 return result
60
61 // scoped
62
63 function listen () {
64 if (!live) {
65 live = true
66 lazy = true
67 if (typeof obs === 'function') {
68 releases.push(obs(onUpdate))
69 }
70 rebindAll()
71 }
72 }
73
74 function unlisten () {
75 if (live) {
76 live = false
77 while (releases.length) {
78 releases.pop()()
79 }
80 rebindAll()
81 }
82 }
83
84 function onUpdate () {
85 if (update()) {
86 broadcast()
87 }
88 }
89
90 function update () {
91 var changed = false
92
93 if (items.length !== getLength(obs)) {
94 changed = true
95 }
96
97 for (var i = 0, len = getLength(obs); i < len; i++) {
98 var item = get(obs, i)
99
100 if (typeof item === 'object') {
101 items[i] = item
102 raw[i] = lambda(item)
103 values[i] = resolve(raw[i])
104 changed = true
105 rebind(i)
106 } else if (item !== items[i]) {
107 items[i] = item
108 if (!lastValues.has(item)) {
109 lastValues.set(item, lambda(item))
110 }
111 raw[i] = lastValues.get(item)
112 values[i] = resolve(raw[i])
113 changed = true
114 rebind(i)
115 }
116 }
117
118 if (changed) {
119 // clean up cache
120 Array.from(lastValues.keys()).filter(notIncluded, obs).forEach(deleteEntry, lastValues)
121 items.length = getLength(obs)
122 values.length = items.length
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 var cachedListeners = listeners.slice(0)
158 for (var i = 0, len = cachedListeners.length; i < len; i++) {
159 cachedListeners[i](values)
160 }
161 }
162
163 function getValue () {
164 if (!live || lazy) {
165 lazy = false
166 update()
167 }
168 return values
169 }
170}
171
172function 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
184function 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
198function 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
212function deleteEntry (entry) {
213 this.delete(entry)
214}
215

Built with git-ssb-web