git ssb

1+

Matt McKegg / mutant



Tree: be0df157d2e308a5ea7014d2d266d2d48f87699d

Files: be0df157d2e308a5ea7014d2d266d2d48f87699d / map.js

4128 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 for (var i = 0, len = listeners.length; i < len; i++) {
159 listeners[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