Commit 4c817e4a9ce3c28a60cc7547029ee43eb9d9f909
fix bugs and cleanup array, concat, dict
Matt McKegg committed on 9/10/2016, 6:20:37 AMParent: 9ddfe1a36b770dd913a4f5649543b64785ffd4cc
Files changed
array.js | changed |
concat.js | changed |
dict.js | changed |
array.js | ||
---|---|---|
@@ -1,15 +1,21 @@ | ||
1 | +var Value = require('./value') | |
1 | 2 | var LazyWatcher = require('./lib/lazy-watcher') |
2 | -var isReferenceType = require('./lib/is-reference-type') | |
3 | +var isSame = require('./lib/is-same') | |
4 | +var isObservable = require('./is-observable') | |
3 | 5 | var resolve = require('./resolve') |
6 | +var addCollectionMethods = require('./lib/add-collection-methods') | |
4 | 7 | |
5 | 8 | module.exports = Array |
6 | 9 | |
7 | -function Array (defaultValues) { | |
10 | +function Array (defaultValues, opts) { | |
8 | 11 | var object = [] |
9 | 12 | var sources = [] |
10 | 13 | var releases = [] |
14 | + var fixedIndexing = opts && opts.fixedIndexing || false | |
11 | 15 | |
16 | + var comparer = opts && opts.comparer || null | |
17 | + | |
12 | 18 | var binder = LazyWatcher(update, listen, unlisten) |
13 | 19 | binder.value = object |
14 | 20 | |
15 | 21 | if (defaultValues && defaultValues.length) { |
@@ -22,46 +28,48 @@ | ||
22 | 28 | } |
23 | 29 | return binder.addListener(listener) |
24 | 30 | } |
25 | 31 | |
26 | - observable.push = function (args) { | |
27 | - for (var i = 0; i < arguments.length; i++) { | |
28 | - add(arguments[i]) | |
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 | + } | |
29 | 44 | } |
30 | 45 | binder.broadcast() |
46 | + return result | |
31 | 47 | } |
32 | 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 | + | |
33 | 61 | observable.insert = function (valueOrObs, at) { |
62 | + valueOrObs = getObsValue(valueOrObs) | |
34 | 63 | sources.splice(at, 0, valueOrObs) |
35 | 64 | if (binder.live) releases.splice(at, 0, bind(valueOrObs)) |
36 | 65 | object.splice(at, 0, resolve(valueOrObs)) |
37 | 66 | binder.broadcast() |
67 | + return valueOrObs | |
38 | 68 | } |
39 | 69 | |
40 | - observable.get = function (index) { | |
41 | - return sources[index] | |
42 | - } | |
43 | 70 | |
44 | - observable.getLength = function (index) { | |
45 | - return sources.length | |
46 | - } | |
47 | 71 | |
48 | - observable.includes = function (valueOrObs) { | |
49 | - return !!~sources.indexOf(valueOrObs) | |
50 | - } | |
51 | - | |
52 | - observable.indexOf = function (valueOrObs) { | |
53 | - return sources.indexOf(valueOrObs) | |
54 | - } | |
55 | - | |
56 | - observable.forEach = function (fn, context) { | |
57 | - sources.slice().forEach(fn, context) | |
58 | - } | |
59 | - | |
60 | - observable.find = function (fn) { | |
61 | - return sources.find(fn) | |
62 | - } | |
63 | - | |
64 | 72 | observable.indexOf |
65 | 73 | |
66 | 74 | observable.pop = function () { |
67 | 75 | var result = sources.pop() |
@@ -87,39 +95,74 @@ | ||
87 | 95 | binder.broadcast() |
88 | 96 | } |
89 | 97 | |
90 | 98 | observable.delete = function (valueOrObs) { |
91 | - var index = sources.indexOf(valueOrObs) | |
92 | - if (~index) { | |
99 | + observable.deleteAt(sources.indexOf(valueOrObs)) | |
100 | + } | |
101 | + | |
102 | + observable.deleteAt = function (index) { | |
103 | + if (index >= 0 && index < sources.length) { | |
93 | 104 | sources.splice(index, 1) |
94 | 105 | if (binder.live) releases.splice(index, 1).forEach(tryInvoke) |
95 | 106 | object.splice(index, 1) |
96 | 107 | binder.broadcast() |
97 | 108 | } |
98 | 109 | } |
99 | 110 | |
100 | 111 | observable.set = function (values) { |
101 | - unlisten() | |
102 | - sources.length = 0 | |
103 | - releases.length = 0 | |
104 | - object.length = 0 | |
105 | - values.forEach(add) | |
106 | - if (binder.live) { | |
107 | - listen() | |
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 | |
108 | 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 | + } | |
109 | 143 | } |
110 | 144 | } |
111 | 145 | |
112 | 146 | return observable |
113 | 147 | |
114 | 148 | // scoped |
115 | 149 | |
150 | + function getObsValue (valueOrObs) { | |
151 | + if (fixedIndexing && !isObservable(valueOrObs)) { | |
152 | + valueOrObs = Value(valueOrObs) | |
153 | + } | |
154 | + return valueOrObs | |
155 | + } | |
156 | + | |
116 | 157 | function add (valueOrObs) { |
158 | + valueOrObs = getObsValue(valueOrObs) | |
117 | 159 | sources.push(valueOrObs) |
160 | + object.push(resolve(valueOrObs)) | |
118 | 161 | if (binder.live) { |
119 | 162 | releases.push(bind(valueOrObs)) |
120 | 163 | } |
121 | - object.push(resolve(valueOrObs)) | |
164 | + return valueOrObs | |
122 | 165 | } |
123 | 166 | |
124 | 167 | function bind (valueOrObs) { |
125 | 168 | return typeof valueOrObs === 'function' ? valueOrObs(binder.onUpdate) : null |
@@ -137,11 +180,11 @@ | ||
137 | 180 | } |
138 | 181 | |
139 | 182 | function update () { |
140 | 183 | var changed = false |
141 | - sources.forEach(function (key, i) { | |
142 | - var newValue = resolve(observable[key]) | |
143 | - if (newValue !== object[i] || isReferenceType(newValue)) { | |
184 | + sources.forEach(function (source, i) { | |
185 | + var newValue = resolve(source) | |
186 | + if (!isSame(newValue, object[i], comparer)) { | |
144 | 187 | object[i] = newValue |
145 | 188 | changed = true |
146 | 189 | } |
147 | 190 | }) |
concat.js | ||
---|---|---|
@@ -1,52 +1,44 @@ | ||
1 | +var resolve = require('./resolve') | |
2 | +var addCollectionMethods = require('./lib/add-collection-methods') | |
1 | 3 | var computed = require('./computed') |
2 | 4 | |
3 | 5 | module.exports = function Concat (observables) { |
4 | 6 | var values = [] |
5 | 7 | var rawValues = [] |
6 | 8 | |
7 | - var result = computed(observables, function (args) { | |
9 | + var instance = computed.extended(observables, function () { | |
8 | 10 | var index = 0 |
9 | - for (var i = 0; i < arguments.length; i++) { | |
10 | - for (var x = 0; x < arguments[i].length; x++) { | |
11 | - var value = arguments[i][x] | |
12 | - var raw = get(observables[i], x) | |
11 | + | |
12 | + forEach(observables, function (collection) { | |
13 | + forEach(collection, function (item) { | |
14 | + var value = resolve(item) | |
13 | 15 | values[index] = value |
14 | - rawValues[index] = raw | |
16 | + rawValues[index] = item | |
15 | 17 | index += 1 |
16 | - } | |
17 | - } | |
18 | + }) | |
19 | + }) | |
20 | + | |
18 | 21 | values.length = index |
19 | 22 | rawValues.length = index |
20 | 23 | return values |
21 | 24 | }) |
22 | 25 | |
23 | - result.get = function (index) { | |
24 | - return rawValues[index] | |
26 | + var result = function MutantConcat (listener) { | |
27 | + return instance(listener) | |
25 | 28 | } |
26 | 29 | |
27 | - result.getLength = function (index) { | |
28 | - return rawValues.length | |
29 | - } | |
30 | + // getLength, get, indexOf, etc | |
31 | + addCollectionMethods(result, rawValues, instance.checkUpdated) | |
30 | 32 | |
31 | - result.includes = function (valueOrObs) { | |
32 | - return !!~rawValues.indexOf(valueOrObs) | |
33 | - } | |
34 | - | |
35 | - result.indexOf = function (valueOrObs) { | |
36 | - return rawValues.indexOf(valueOrObs) | |
37 | - } | |
38 | - | |
39 | 33 | return result |
40 | 34 | } |
41 | 35 | |
42 | -function get (target, index) { | |
43 | - if (typeof target === 'function' && !target.get) { | |
44 | - target = target() | |
36 | +function forEach (sources, fn) { | |
37 | + if (sources && !sources.forEach) { | |
38 | + sources = resolve(sources) | |
45 | 39 | } |
46 | 40 | |
47 | - if (Array.isArray(target)) { | |
48 | - return target[index] | |
49 | - } else if (target && target.get) { | |
50 | - return target.get(index) | |
41 | + if (sources && sources.forEach) { | |
42 | + sources.forEach(fn) | |
51 | 43 | } |
52 | 44 | } |
dict.js | ||
---|---|---|
@@ -1,17 +1,22 @@ | ||
1 | +var Value = require('./value') | |
1 | 2 | var LazyWatcher = require('./lib/lazy-watcher') |
2 | -var isReferenceType = require('./lib/is-reference-type') | |
3 | +var isSame = require('./lib/is-same') | |
3 | 4 | var resolve = require('./resolve') |
5 | +var isObservable = require('./is-observable') | |
4 | 6 | |
5 | 7 | // TODO: reimplement using LazyWatcher |
6 | 8 | |
7 | 9 | module.exports = Dict |
8 | 10 | |
9 | -function Dict (defaultValues) { | |
11 | +function Dict (defaultValues, opts) { | |
10 | 12 | var object = Object.create({}) |
11 | 13 | var sources = [] |
12 | 14 | var releases = [] |
15 | + var fixedIndexing = opts && opts.fixedIndexing || false | |
13 | 16 | |
17 | + var comparer = opts && opts.comparer || null | |
18 | + | |
14 | 19 | var binder = LazyWatcher(update, listen, unlisten) |
15 | 20 | binder.value = object |
16 | 21 | |
17 | 22 | if (defaultValues) { |
@@ -27,10 +32,12 @@ | ||
27 | 32 | return binder.addListener(listener) |
28 | 33 | } |
29 | 34 | |
30 | 35 | observable.put = function (key, valueOrObs) { |
36 | + valueOrObs = getObsValue(valueOrObs) | |
31 | 37 | put(key, valueOrObs) |
32 | 38 | binder.broadcast() |
39 | + return valueOrObs | |
33 | 40 | } |
34 | 41 | |
35 | 42 | observable.get = function (key) { |
36 | 43 | return sources[key] |
@@ -62,26 +69,53 @@ | ||
62 | 69 | return !!~object.indexOf(valueOrObs) |
63 | 70 | } |
64 | 71 | |
65 | 72 | observable.set = function (values) { |
66 | - Object.keys(sources).forEach(function (key) { | |
67 | - tryInvoke(releases[key]) | |
68 | - delete sources[key] | |
69 | - delete releases[key] | |
70 | - delete object[key] | |
71 | - }) | |
73 | + var keys = values && Object.keys(values) || [] | |
74 | + if (fixedIndexing) { | |
75 | + keys.forEach(function (key) { | |
76 | + if (sources[key]) { | |
77 | + sources[key].set(values[key]) | |
78 | + } else { | |
79 | + put(key, getObsValue(values[key])) | |
80 | + } | |
81 | + }) | |
72 | 82 | |
73 | - Object.keys(values).forEach(function (key) { | |
74 | - put(key, values[key]) | |
75 | - }) | |
83 | + Object.keys(sources).forEach(function (key) { | |
84 | + if (!keys.includes(key)) { | |
85 | + tryInvoke(releases[key]) | |
86 | + delete sources[key] | |
87 | + delete releases[key] | |
88 | + delete object[key] | |
89 | + } | |
90 | + }) | |
91 | + } else { | |
92 | + Object.keys(sources).forEach(function (key) { | |
93 | + tryInvoke(releases[key]) | |
94 | + delete sources[key] | |
95 | + delete releases[key] | |
96 | + delete object[key] | |
97 | + }) | |
76 | 98 | |
77 | - binder.broadcast() | |
99 | + keys.forEach(function (key) { | |
100 | + put(key, values[key]) | |
101 | + }) | |
102 | + | |
103 | + binder.broadcast() | |
104 | + } | |
78 | 105 | } |
79 | 106 | |
80 | 107 | return observable |
81 | 108 | |
82 | 109 | // scoped |
83 | 110 | |
111 | + function getObsValue (valueOrObs) { | |
112 | + if (fixedIndexing && !isObservable(valueOrObs)) { | |
113 | + valueOrObs = Value(valueOrObs) | |
114 | + } | |
115 | + return valueOrObs | |
116 | + } | |
117 | + | |
84 | 118 | function put (key, valueOrObs) { |
85 | 119 | tryInvoke(releases[key]) |
86 | 120 | sources[key] = valueOrObs |
87 | 121 | if (binder.live) { |
@@ -115,9 +149,9 @@ | ||
115 | 149 | function update () { |
116 | 150 | var changed = false |
117 | 151 | Object.keys(sources).forEach(function (key) { |
118 | 152 | var newValue = resolve(sources[key]) |
119 | - if (newValue !== object[key] || isReferenceType(newValue)) { | |
153 | + if (!isSame(newValue, object[key], comparer)) { | |
120 | 154 | object[key] = newValue |
121 | 155 | changed = true |
122 | 156 | } |
123 | 157 | }) |
Built with git-ssb-web