Files: 306cee2cea33ec6d7ac5025d5033784134e56141 / struct.js
2631 bytesRaw
1 | var Value = require('./value') |
2 | var LazyWatcher = require('./lib/lazy-watcher') |
3 | var isSame = require('./lib/is-same') |
4 | var extend = require('xtend') |
5 | |
6 | module.exports = Struct |
7 | |
8 | var blackList = { |
9 | 'length': 'Clashes with `Function.prototype.length`.\n', |
10 | 'name': 'Clashes with `Function.prototype.name`\n', |
11 | 'set': '`set` is a reserved key of struct\n' |
12 | } |
13 | |
14 | function Struct (properties, opts) { |
15 | var object = Object.create({}) |
16 | var releases = [] |
17 | var binder = LazyWatcher(update, listen, unlisten) |
18 | binder.value = object |
19 | |
20 | if (opts && opts.nextTick) binder.nextTick = true |
21 | if (opts && opts.idle) binder.idle = true |
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, opts) { |
49 | var lastValue = suspendBroadcast |
50 | |
51 | suspendBroadcast = true |
52 | values = values || {} |
53 | |
54 | if (opts && opts.merge) { |
55 | values = extend(object, values) |
56 | } |
57 | |
58 | // update inner observables |
59 | keys.forEach(function (key) { |
60 | if (observable[key]() !== values[key]) { |
61 | observable[key].set(values[key]) |
62 | } |
63 | }) |
64 | |
65 | // store additional keys (but don't create observables) |
66 | Object.keys(values).forEach(function (key) { |
67 | if (!(key in properties)) { |
68 | object[key] = values[key] |
69 | } |
70 | }) |
71 | |
72 | suspendBroadcast = lastValue |
73 | if (!suspendBroadcast) { |
74 | binder.broadcast() |
75 | } |
76 | } |
77 | |
78 | return observable |
79 | |
80 | // scoped |
81 | |
82 | function listen () { |
83 | keys.map(function (key) { |
84 | var obs = observable[key] |
85 | releases.push(obs(function (val) { |
86 | if (!isSame(val, object[key], comparer)) { |
87 | object[key] = val |
88 | if (!suspendBroadcast) { |
89 | binder.broadcast(object) |
90 | } |
91 | } |
92 | })) |
93 | }) |
94 | } |
95 | |
96 | function unlisten () { |
97 | while (releases.length) { |
98 | releases.pop()() |
99 | } |
100 | } |
101 | |
102 | function update () { |
103 | var changed = false |
104 | keys.forEach(function (key) { |
105 | var newValue = observable[key]() |
106 | if (!isSame(newValue, object[key], comparer)) { |
107 | object[key] = observable[key]() |
108 | changed = true |
109 | } |
110 | }) |
111 | return changed |
112 | } |
113 | } |
114 |
Built with git-ssb-web