git ssb

1+

Matt McKegg / mutant



Tree: f3288b0f154b91b996580e0c68b99ea612698769

Files: f3288b0f154b91b996580e0c68b99ea612698769 / array.js

5988 bytesRaw
1var Value = require('./value')
2var LazyWatcher = require('./lib/lazy-watcher')
3var isSame = require('./lib/is-same')
4var isObservable = require('./is-observable')
5var resolve = require('./resolve')
6var addCollectionMethods = require('./lib/add-collection-methods')
7var forEach = require('./for-each')
8
9module.exports = Array
10
11function Array (defaultValues, opts) {
12 var object = []
13 var sources = []
14 var objectReleases = []
15 var fixedIndexing = opts && opts.fixedIndexing || false
16
17 var releases = []
18 var comparer = opts && opts.comparer || null
19
20 var binder = LazyWatcher(update, listen, unlisten)
21 binder.value = object
22
23 if (opts && opts.idle) binder.idle = true
24 if (opts && opts.nextTick) binder.nextTick = true
25
26 if (defaultValues && defaultValues.length) {
27 forEach(defaultValues, add)
28 }
29
30 var observable = function MutantArray (listener) {
31 if (!listener) {
32 return binder.getValue()
33 }
34 return binder.addListener(listener)
35 }
36
37 // getLength, get, indexOf, etc
38 addCollectionMethods(observable, sources)
39
40 observable.push = function (item) {
41 var result = null
42 if (arguments.length === 1) {
43 result = add(item)
44 } else {
45 result = []
46 for (var i = 0; i < arguments.length; i++) {
47 result.push(add(arguments[i]))
48 }
49 }
50 binder.broadcast()
51 return result
52 }
53
54 observable.put = function (index, valueOrObs) {
55 valueOrObs = getObsValue(valueOrObs)
56 sources[index] = valueOrObs
57 object[index] = resolve(valueOrObs)
58 if (binder.live) {
59 tryInvoke(objectReleases[index])
60 objectReleases[index] = bind(valueOrObs)
61 }
62 binder.broadcast()
63 return valueOrObs
64 }
65
66 observable.insert = function (valueOrObs, at) {
67 valueOrObs = getObsValue(valueOrObs)
68 sources.splice(at, 0, valueOrObs)
69 if (binder.live) objectReleases.splice(at, 0, bind(valueOrObs))
70 object.splice(at, 0, resolve(valueOrObs))
71 binder.broadcast()
72 return valueOrObs
73 }
74
75 observable.pop = function () {
76 var result = sources.pop()
77 if (binder.live) tryInvoke(objectReleases.pop())
78 object.pop()
79 binder.broadcast()
80 return result
81 }
82
83 observable.shift = function () {
84 var result = sources.shift()
85 if (binder.live) tryInvoke(objectReleases.shift())
86 object.shift()
87 binder.broadcast()
88 return result
89 }
90
91 observable.clear = function () {
92 objectReleases.forEach(tryInvoke)
93 sources.length = 0
94 objectReleases.length = 0
95 object.length = 0
96 binder.broadcast()
97 }
98
99 observable.delete = function (valueOrObs) {
100 observable.deleteAt(sources.indexOf(valueOrObs))
101 }
102
103 observable.deleteAt = function (index) {
104 if (index >= 0 && index < sources.length) {
105 sources.splice(index, 1)
106 if (binder.live) objectReleases.splice(index, 1).forEach(tryInvoke)
107 object.splice(index, 1)
108 binder.broadcast()
109 }
110 }
111
112 observable.transaction = function (cb) {
113 binder.transaction(observable, cb)
114 }
115
116 observable.set = function (values) {
117 var changed = false
118 if (fixedIndexing) {
119 var length = (values && values.length) || 0
120 for (var i = 0; i < length; i++) {
121 if (isObservable(values[i])) {
122 if (values[i] !== sources[i]) {
123 tryInvoke(objectReleases[i])
124 sources[i] = values[i]
125 object[i] = resolve(sources[i])
126 changed = true
127 if (binder.live) {
128 objectReleases[i] = bind(sources[i])
129 }
130 }
131 } else if (sources[i] && sources[i]._type === 'MutantArrayValue') {
132 if (!isSame(sources[i](), values[i], comparer)) {
133 sources[i].set(values[i])
134 object[i] = resolve(sources[i])
135 changed = true
136 }
137 } else {
138 tryInvoke(objectReleases[i])
139 sources[i] = getObsValue(values[i])
140 object[i] = resolve(sources[i])
141 changed = true
142 if (binder.live) {
143 objectReleases[i] = bind(sources[i])
144 }
145 }
146 }
147 for (var index = length; index < sources.length; index++) {
148 tryInvoke(objectReleases[index])
149 changed = true
150 }
151
152 if (changed) {
153 objectReleases.length = length
154 sources.length = length
155 object.length = length
156 binder.broadcast()
157 }
158 } else {
159 unlisten()
160 sources.length = 0
161 objectReleases.length = 0
162 object.length = 0
163 forEach(values, add)
164 if (binder.live) {
165 listen()
166 binder.broadcast()
167 }
168 }
169 }
170
171 return observable
172
173 // scoped
174
175 function getObsValue (valueOrObs) {
176 if (fixedIndexing && !isObservable(valueOrObs)) {
177 valueOrObs = Value(valueOrObs)
178 valueOrObs._type = 'MutantArrayValue'
179 }
180 return valueOrObs
181 }
182
183 function add (valueOrObs) {
184 valueOrObs = getObsValue(valueOrObs)
185 sources.push(valueOrObs)
186 object.push(resolve(valueOrObs))
187 if (binder.live) {
188 objectReleases.push(bind(valueOrObs))
189 }
190 return valueOrObs
191 }
192
193 function bind (valueOrObs) {
194 return typeof valueOrObs === 'function' ? valueOrObs(binder.onUpdate) : null
195 }
196
197 function listen () {
198 sources.forEach(function (obs, i) {
199 objectReleases[i] = bind(obs)
200 })
201
202 if (opts && opts.onListen) {
203 var release = opts.onListen()
204 if (typeof release === 'function') {
205 releases.push(release)
206 }
207 }
208 }
209
210 function unlisten () {
211 objectReleases.forEach(tryInvoke)
212 objectReleases.length = 0
213
214 while (releases.length) {
215 tryInvoke(releases.pop())
216 }
217
218 if (opts && opts.onUnlisten) {
219 opts.onUnlisten()
220 }
221 }
222
223 function update () {
224 var changed = false
225 sources.forEach(function (source, i) {
226 var newValue = resolve(source)
227 if (!isSame(newValue, object[i], comparer)) {
228 object[i] = newValue
229 changed = true
230 }
231 })
232 return changed
233 }
234}
235
236function tryInvoke (func) {
237 if (typeof func === 'function') {
238 func()
239 }
240}
241

Built with git-ssb-web