git ssb

1+

Matt McKegg / mutant



Tree: 3eb9a1271a24866c0fdbf02fa318537a1d3adb16

Files: 3eb9a1271a24866c0fdbf02fa318537a1d3adb16 / array.js

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

Built with git-ssb-web