Files: 2b5c272499d06a199576e602fc62fb998954ad18 / dict.js
2839 bytesRaw
1 | var LazyWatcher = require('./lib/lazy-watcher') |
2 | var isReferenceType = require('./lib/is-reference-type') |
3 | var resolve = require('./resolve') |
4 | |
5 | // TODO: reimplement using LazyWatcher |
6 | |
7 | module.exports = Dict |
8 | |
9 | function Dict (defaultValues) { |
10 | var object = Object.create({}) |
11 | var sources = [] |
12 | var releases = [] |
13 | |
14 | var binder = LazyWatcher(update, listen, unlisten) |
15 | binder.value = object |
16 | |
17 | if (defaultValues) { |
18 | Object.keys(defaultValues).forEach(function (key) { |
19 | put(key, defaultValues[key]) |
20 | }) |
21 | } |
22 | |
23 | var observable = function MutantDictionary (listener) { |
24 | if (!listener) { |
25 | return binder.getValue() |
26 | } |
27 | return binder.addListener(listener) |
28 | } |
29 | |
30 | observable.put = function (key, valueOrObs) { |
31 | put(key, valueOrObs) |
32 | binder.broadcast() |
33 | } |
34 | |
35 | observable.get = function (key) { |
36 | return sources[key] |
37 | } |
38 | |
39 | observable.keys = function () { |
40 | return Object.keys(sources) |
41 | } |
42 | |
43 | observable.clear = function () { |
44 | Object.keys(sources).forEach(function (key) { |
45 | tryInvoke(releases[key]) |
46 | delete sources[key] |
47 | delete releases[key] |
48 | delete object[key] |
49 | }) |
50 | binder.broadcast() |
51 | } |
52 | |
53 | observable.delete = function (key) { |
54 | tryInvoke(releases[key]) |
55 | delete sources[key] |
56 | delete releases[key] |
57 | delete object[key] |
58 | binder.broadcast() |
59 | } |
60 | |
61 | observable.includes = function (valueOrObs) { |
62 | return !!~object.indexOf(valueOrObs) |
63 | } |
64 | |
65 | 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 | }) |
72 | |
73 | Object.keys(values).forEach(function (key) { |
74 | put(key, values[key]) |
75 | }) |
76 | |
77 | binder.broadcast() |
78 | } |
79 | |
80 | return observable |
81 | |
82 | // scoped |
83 | |
84 | function put (key, valueOrObs) { |
85 | tryInvoke(releases[key]) |
86 | sources[key] = valueOrObs |
87 | if (binder.live) { |
88 | releases[key] = bind(key, valueOrObs) |
89 | } |
90 | object[key] = resolve(valueOrObs) |
91 | } |
92 | |
93 | function bind (key, valueOrObs) { |
94 | return typeof valueOrObs === 'function' ? valueOrObs(updateKey.bind(this, key)) : null |
95 | } |
96 | |
97 | function updateKey (key, value) { |
98 | object[key] = value |
99 | binder.broadcast() |
100 | } |
101 | |
102 | function listen () { |
103 | Object.keys(sources).forEach(function (key) { |
104 | releases[key] = bind(sources[key]) |
105 | }) |
106 | } |
107 | |
108 | function unlisten () { |
109 | Object.keys(sources).forEach(function (key) { |
110 | tryInvoke(releases[key]) |
111 | delete releases[key] |
112 | }) |
113 | } |
114 | |
115 | function update () { |
116 | var changed = false |
117 | Object.keys(sources).forEach(function (key) { |
118 | var newValue = resolve(sources[key]) |
119 | if (newValue !== object[key] || isReferenceType(newValue)) { |
120 | object[key] = newValue |
121 | changed = true |
122 | } |
123 | }) |
124 | return changed |
125 | } |
126 | } |
127 | |
128 | function tryInvoke (func) { |
129 | if (typeof func === 'function') { |
130 | func() |
131 | } |
132 | } |
133 |
Built with git-ssb-web