-
Notifications
You must be signed in to change notification settings - Fork 0
/
reactor.js
105 lines (90 loc) · 2.91 KB
/
reactor.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
/******************************************************************************/
// The MIT Licence (MIT)
// Copyright (C) 2018 Dmitry Vasilev
// This file is part of reactive.js
/******************************************************************************/
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
const ARGUMENT_NAMES = /([^\s,]+)/g;
const Arguments = func => {
const str = func.toString().replace(STRIP_COMMENTS, '');
const args = str.split('=>',1)[0].trim();
return args.charAt(0) == '(' && args[args.length - 1] == ')'
? args.substr(1, args.length - 2).match(ARGUMENT_NAMES)
: [args];
}
const Reactive = function() {
const state = {};
const functions = {};
const edges = {};
const args = {};
const scripts = {};
const invoke = property => {
functions[property]();
};
const depthFirstSearch = property => {
const visited = {};
const nodeList = [];
const search = node => {
if (!visited[node]) {
visit(node);
nodeList.push(node);
}
};
const visit = node => {
visited[node] = true;
edges[node] && edges[node].forEach(search);
}
visit(property);
return nodeList;
}
const setValue = (property, value) => {
if (state[property] !== value) {
state[property] = value;
depthFirstSearch(property).reverse().forEach(invoke);
};
}
const allDefined = dependencies => {
const args = [];
return dependencies.every(property => {
if (state[property] !== undefined) {
args.push(state[property]);
return true;
}
}) ? args : undefined;
};
const setFn = (property, func) => {
scripts[property] = func;
args[property] = Arguments(func)
args[property].forEach(input => {
(edges[input] = edges[input] || []).push(property);
});
functions[property] = () => {
const a = allDefined(args[property]);
if (a) state[property] = func(...a);
};
}
function Apply(command) {
if (command === 'debug') {
return {
functions,
state,
edges,
args,
scripts
}
}
}
return new Proxy(Apply, {
get: (target, name) => name in state ? state[name] : `Key "${name}" does't exist`,
set: (target, property, value, receiver) => {
if (typeof value === 'function') setFn(property, value);
else setValue(property, value);
return true;
},
apply: (target, thisArg, argumentsList) => target(...argumentsList),
deleteProperty: function(target, property) {
console.log('Not implemented')
}
});
};
module.exports = Reactive;