git ssb

1+

Matt McKegg / mutant



Tree: e75defac47630de9da141ca0ca89cba54a030464

Files: e75defac47630de9da141ca0ca89cba54a030464 / map.js

4009 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 }
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
171function 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
183function 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
197function 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
208function deleteEntry (entry) {
209 this.delete(entry)
210}
211

Built with git-ssb-web