Files: 073f2ca837054c3b04113dd573e1a9e8b0c6ed29 / struct.js
2493 bytesRaw
1 | var Value = require('./value') |
2 | var LazyWatcher = require('./lib/lazy-watcher') |
3 | var isSame = require('./lib/is-same') |
4 | |
5 | module.exports = Struct |
6 | |
7 | var blackList = { |
8 | 'length': 'Clashes with `Function.prototype.length`.\n', |
9 | 'name': 'Clashes with `Function.prototype.name`\n', |
10 | 'destroy': '`destroy` is a reserved key of struct\n' |
11 | } |
12 | |
13 | function Struct (properties, opts) { |
14 | var object = Object.create({}) |
15 | var releases = [] |
16 | var binder = LazyWatcher(update, listen, unlisten) |
17 | binder.value = object |
18 | |
19 | if (opts && opts.nextTick) { |
20 | binder.nextTick = true |
21 | } |
22 | |
23 | var comparer = opts && opts.comparer || null |
24 | |
25 | var observable = function MutantStruct (listener) { |
26 | if (!listener) { |
27 | return binder.getValue() |
28 | } |
29 | return binder.addListener(listener) |
30 | } |
31 | |
32 | var keys = Object.keys(properties) |
33 | var suspendBroadcast = false |
34 | |
35 | keys.forEach(function (key) { |
36 | if (blackList.hasOwnProperty(key)) { |
37 | throw new Error("Cannot create a struct with a key named '" + key + "'.\n" + blackList[key]) |
38 | } |
39 | |
40 | var obs = typeof properties[key] === 'function' |
41 | ? properties[key] |
42 | : Value(properties[key]) |
43 | |
44 | object[key] = obs() |
45 | observable[key] = obs |
46 | }) |
47 | |
48 | observable.set = function (values) { |
49 | var lastValue = suspendBroadcast |
50 | suspendBroadcast = true |
51 | values = values || {} |
52 | |
53 | // update inner observables |
54 | keys.forEach(function (key) { |
55 | if (observable[key]() !== values[key]) { |
56 | observable[key].set(values[key]) |
57 | } |
58 | }) |
59 | |
60 | // store additional keys (but don't create observables) |
61 | Object.keys(values).forEach(function (key) { |
62 | if (!(key in properties)) { |
63 | object[key] = values[key] |
64 | } |
65 | }) |
66 | |
67 | suspendBroadcast = lastValue |
68 | if (!suspendBroadcast) { |
69 | binder.broadcast() |
70 | } |
71 | } |
72 | |
73 | return observable |
74 | |
75 | // scoped |
76 | |
77 | function listen () { |
78 | keys.map(function (key) { |
79 | var obs = observable[key] |
80 | releases.push(obs(function (val) { |
81 | if (!isSame(val, object[key], comparer)) { |
82 | object[key] = val |
83 | if (!suspendBroadcast) { |
84 | binder.broadcast(object) |
85 | } |
86 | } |
87 | })) |
88 | }) |
89 | } |
90 | |
91 | function unlisten () { |
92 | while (releases.length) { |
93 | releases.pop()() |
94 | } |
95 | } |
96 | |
97 | function update () { |
98 | var changed = false |
99 | keys.forEach(function (key) { |
100 | var newValue = observable[key]() |
101 | if (!isSame(newValue, object[key], comparer)) { |
102 | object[key] = observable[key]() |
103 | changed = true |
104 | } |
105 | }) |
106 | return changed |
107 | } |
108 | } |
109 |
Built with git-ssb-web