Files: b645b2baf1b153c97d7f9d13ee6b47556d710d1c / dict.js
3595 bytesRaw
1 | var Value = require('./value') |
2 | var LazyWatcher = require('./lib/lazy-watcher') |
3 | var isSame = require('./lib/is-same') |
4 | var resolve = require('./resolve') |
5 | var isObservable = require('./is-observable') |
6 | var forEachPair = require('./for-each-pair') |
7 | var addLookupMethods = require('./lib/add-lookup-methods') |
8 | |
9 | module.exports = Dict |
10 | |
11 | function Dict (defaultValues, opts) { |
12 | var object = Object.create({}) |
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) { |
23 | forEachPair(defaultValues, put) |
24 | } |
25 | |
26 | var observable = function MutantDictionary (listener) { |
27 | if (!listener) { |
28 | return binder.getValue() |
29 | } |
30 | return binder.addListener(listener) |
31 | } |
32 | |
33 | addLookupMethods(observable, sources) |
34 | |
35 | observable.put = function (key, valueOrObs) { |
36 | valueOrObs = getObsValue(valueOrObs) |
37 | put(key, valueOrObs) |
38 | binder.broadcast() |
39 | return valueOrObs |
40 | } |
41 | |
42 | observable.clear = function () { |
43 | Object.keys(sources).forEach(function (key) { |
44 | tryInvoke(releases[key]) |
45 | delete sources[key] |
46 | delete releases[key] |
47 | delete object[key] |
48 | }) |
49 | binder.broadcast() |
50 | } |
51 | |
52 | observable.delete = function (key) { |
53 | tryInvoke(releases[key]) |
54 | delete sources[key] |
55 | delete releases[key] |
56 | delete object[key] |
57 | binder.broadcast() |
58 | } |
59 | |
60 | observable.includes = function (valueOrObs) { |
61 | return !!~object.indexOf(valueOrObs) |
62 | } |
63 | |
64 | observable.set = function (values) { |
65 | if (fixedIndexing) { |
66 | var keys = [] |
67 | |
68 | forEachPair(values, function (key, value) { |
69 | keys.push(key) |
70 | if (sources[key]) { |
71 | sources[key].set(value) |
72 | } else { |
73 | put(key, getObsValue(value)) |
74 | } |
75 | }) |
76 | |
77 | Object.keys(sources).forEach(function (key) { |
78 | if (!keys.includes(key)) { |
79 | tryInvoke(releases[key]) |
80 | delete sources[key] |
81 | delete releases[key] |
82 | delete object[key] |
83 | } |
84 | }) |
85 | } else { |
86 | Object.keys(sources).forEach(function (key) { |
87 | tryInvoke(releases[key]) |
88 | delete sources[key] |
89 | delete releases[key] |
90 | delete object[key] |
91 | }) |
92 | |
93 | forEachPair(values, put) |
94 | binder.broadcast() |
95 | } |
96 | } |
97 | |
98 | return observable |
99 | |
100 | // scoped |
101 | |
102 | function getObsValue (valueOrObs) { |
103 | if (fixedIndexing && !isObservable(valueOrObs)) { |
104 | valueOrObs = Value(valueOrObs) |
105 | } |
106 | return valueOrObs |
107 | } |
108 | |
109 | function put (key, valueOrObs) { |
110 | tryInvoke(releases[key]) |
111 | sources[key] = valueOrObs |
112 | if (binder.live) { |
113 | releases[key] = bind(key, valueOrObs) |
114 | } |
115 | object[key] = resolve(valueOrObs) |
116 | } |
117 | |
118 | function bind (key, valueOrObs) { |
119 | return typeof valueOrObs === 'function' ? valueOrObs(updateKey.bind(this, key)) : null |
120 | } |
121 | |
122 | function updateKey (key, value) { |
123 | object[key] = value |
124 | binder.broadcast() |
125 | } |
126 | |
127 | function listen () { |
128 | Object.keys(sources).forEach(function (key) { |
129 | releases[key] = bind(sources[key]) |
130 | }) |
131 | } |
132 | |
133 | function unlisten () { |
134 | Object.keys(sources).forEach(function (key) { |
135 | tryInvoke(releases[key]) |
136 | delete releases[key] |
137 | }) |
138 | } |
139 | |
140 | function update () { |
141 | var changed = false |
142 | Object.keys(sources).forEach(function (key) { |
143 | var newValue = resolve(sources[key]) |
144 | if (!isSame(newValue, object[key], comparer)) { |
145 | object[key] = newValue |
146 | changed = true |
147 | } |
148 | }) |
149 | return changed |
150 | } |
151 | } |
152 | |
153 | function tryInvoke (func) { |
154 | if (typeof func === 'function') { |
155 | func() |
156 | } |
157 | } |
158 |
Built with git-ssb-web