git ssb

1+

Matt McKegg / mutant



Tree: ee6233e21fbc30daa9c23e3a3fdf43734c522096

Files: ee6233e21fbc30daa9c23e3a3fdf43734c522096 / array.js

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

Built with git-ssb-web