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