git ssb

1+

Matt McKegg / mutant



Tree: b424e0dd69696f9db8b920d2b503320da01b5f98

Files: b424e0dd69696f9db8b920d2b503320da01b5f98 / array.js

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

Built with git-ssb-web