git ssb

1+

Matt McKegg / mutant



Tree: 408284391d679877e10234efd7de780b0d75d121

Files: 408284391d679877e10234efd7de780b0d75d121 / array.js

4789 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 if (fixedIndexing) {
114 var length = values && values.length || 0
115 for (var i = 0; i < length; i++) {
116 if (!sources[i]) {
117 var valueOrObs = getObsValue(values[i])
118 sources[i] = valueOrObs
119 object[i] = resolve(valueOrObs)
120 if (binder.live) {
121 releases[i] = bind(valueOrObs)
122 }
123 } else {
124 sources[i].set(values[i])
125 }
126 }
127 for (var index = length; index < sources.length; index++) {
128 tryInvoke(releases[index])
129 }
130 releases.length = length
131 sources.length = length
132 object.length = length
133 binder.broadcast()
134 } else {
135 unlisten()
136 sources.length = 0
137 releases.length = 0
138 object.length = 0
139 forEach(values, add)
140 if (binder.live) {
141 listen()
142 binder.broadcast()
143 }
144 }
145 }
146
147 return observable
148
149 // scoped
150
151 function getObsValue (valueOrObs) {
152 if (fixedIndexing && !isObservable(valueOrObs)) {
153 valueOrObs = Value(valueOrObs)
154 }
155 return valueOrObs
156 }
157
158 function add (valueOrObs) {
159 valueOrObs = getObsValue(valueOrObs)
160 sources.push(valueOrObs)
161 object.push(resolve(valueOrObs))
162 if (binder.live) {
163 releases.push(bind(valueOrObs))
164 }
165 return valueOrObs
166 }
167
168 function bind (valueOrObs) {
169 return typeof valueOrObs === 'function' ? valueOrObs(binder.onUpdate) : null
170 }
171
172 function listen () {
173 sources.forEach(function (obs, i) {
174 releases[i] = bind(obs)
175 })
176 }
177
178 function unlisten () {
179 releases.forEach(tryInvoke)
180 releases.length = 0
181 }
182
183 function update () {
184 var changed = false
185 sources.forEach(function (source, i) {
186 var newValue = resolve(source)
187 if (!isSame(newValue, object[i], comparer)) {
188 object[i] = newValue
189 changed = true
190 }
191 })
192 return changed
193 }
194}
195
196function tryInvoke (func) {
197 if (typeof func === 'function') {
198 func()
199 }
200}
201

Built with git-ssb-web