primate 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -36
- package/debris.json +1 -2
- package/package.json +7 -10
- package/source/{server/App.js → App.js} +14 -19
- package/source/Bundler.js +43 -0
- package/source/{server/Directory.js → Directory.js} +0 -0
- package/source/{server/EagerPromise.js → EagerPromise.js} +0 -0
- package/source/{server/File.js → File.js} +0 -0
- package/source/Router.js +28 -0
- package/source/Server.js +103 -0
- package/source/Session.js +26 -0
- package/source/{server/attributes.js → attributes.js} +0 -0
- package/source/{server/cache.js → cache.js} +0 -0
- package/source/{server/conf.js → conf.js} +1 -1
- package/source/{server/crypto.js → crypto.js} +0 -0
- package/source/{server/domain → domain}/Domain.js +8 -10
- package/source/{server/domain → domain}/Field.js +0 -0
- package/source/{server/domain → domain}/Predicate.js +0 -0
- package/source/{server/domain → domain}/domains.js +0 -0
- package/source/{server/errors → errors}/InternalServer.js +0 -0
- package/source/{server/errors → errors}/Predicate.js +0 -0
- package/source/{server/errors.js → errors.js} +0 -0
- package/source/{server/exports.js → exports.js} +4 -2
- package/source/{server/extend_object.js → extend_object.js} +0 -0
- package/source/handlers/DOM/Node.js +184 -0
- package/source/{server/view → handlers/DOM}/Parser.js +22 -18
- package/source/handlers/html.js +27 -0
- package/source/handlers/http.js +6 -0
- package/source/handlers/redirect.js +10 -0
- package/source/{server/servers/http-codes.json → http-codes.json} +0 -0
- package/source/{server/invariants.js → invariants.js} +0 -0
- package/source/{server/log.js → log.js} +0 -0
- package/source/{server/servers/mimes.json → mimes.json} +0 -0
- package/source/preset/primate.json +12 -4
- package/source/preset/static/index.html +0 -2
- package/source/preset/stores/default.js +2 -0
- package/source/{server/sanitize.js → sanitize.js} +0 -0
- package/source/{server/store → store}/Memory.js +0 -0
- package/source/{server/store → store}/Store.js +1 -1
- package/source/{server/types → types}/Array.js +0 -0
- package/source/{server/types → types}/Boolean.js +0 -0
- package/source/{server/types → types}/Date.js +0 -0
- package/source/{server/types → types}/Domain.js +0 -0
- package/source/{server/types → types}/Instance.js +0 -0
- package/source/{server/types → types}/Number.js +0 -0
- package/source/{server/types → types}/Object.js +0 -0
- package/source/{server/types → types}/Primitive.js +0 -0
- package/source/{server/types → types}/Storeable.js +0 -0
- package/source/{server/types → types}/String.js +0 -0
- package/source/{server/types → types}/errors/Array.json +0 -0
- package/source/{server/types → types}/errors/Boolean.json +0 -0
- package/source/{server/types → types}/errors/Date.json +0 -0
- package/source/{server/types → types}/errors/Number.json +0 -0
- package/source/{server/types → types}/errors/Object.json +0 -0
- package/source/{server/types → types}/errors/String.json +0 -0
- package/source/{server/types.js → types.js} +0 -0
- package/source/client/Action.js +0 -157
- package/source/client/App.js +0 -16
- package/source/client/Client.js +0 -61
- package/source/client/Context.js +0 -47
- package/source/client/Element.js +0 -249
- package/source/client/Node.js +0 -13
- package/source/client/Session.js +0 -27
- package/source/client/View.js +0 -89
- package/source/client/document.js +0 -6
- package/source/client/exports.js +0 -15
- package/source/preset/client/Element.js +0 -2
- package/source/preset/client/app.js +0 -2
- package/source/preset/data/stores/default.js +0 -2
- package/source/server/Action.js +0 -100
- package/source/server/Bundler.js +0 -177
- package/source/server/Context.js +0 -97
- package/source/server/Projector.js +0 -86
- package/source/server/Router.js +0 -52
- package/source/server/Session.js +0 -45
- package/source/server/servers/Dynamic.js +0 -57
- package/source/server/servers/Server.js +0 -5
- package/source/server/servers/Static.js +0 -118
- package/source/server/servers/content-security-policy.json +0 -7
- package/source/server/view/TreeNode.js +0 -197
- package/source/server/view/View.js +0 -35
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import Parser from "./Parser.js";
|
|
2
|
+
|
|
3
|
+
const replacement_regex = /^\$([0-9]*)$/;
|
|
4
|
+
const data_regex = /\${([^}]*)}/g;
|
|
5
|
+
const replace = (attribute, source) => {
|
|
6
|
+
if (attribute.includes(".")) {
|
|
7
|
+
const index = attribute.indexOf(".");
|
|
8
|
+
const left = attribute.slice(0, index);
|
|
9
|
+
const rest = attribute.slice(index+1);
|
|
10
|
+
if (source[left] !== undefined) {
|
|
11
|
+
return replace(rest, source[left]);
|
|
12
|
+
}
|
|
13
|
+
} else {
|
|
14
|
+
return source[attribute];
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const fulfill = (attribute, source) => {
|
|
18
|
+
if (source === undefined) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
let value;
|
|
22
|
+
const matches = [...attribute.matchAll(data_regex)];
|
|
23
|
+
if (matches.length > 0) {
|
|
24
|
+
for (const match of matches) {
|
|
25
|
+
const [key] = match;
|
|
26
|
+
const new_value = replace(match[1], source);
|
|
27
|
+
value = attribute.replace(key, new_value);
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
value = replace(attribute, source);
|
|
31
|
+
}
|
|
32
|
+
return value;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default class Node {
|
|
36
|
+
#data;
|
|
37
|
+
|
|
38
|
+
constructor(parent, content = "div", data) {
|
|
39
|
+
if (parent !== undefined) {
|
|
40
|
+
this.parent = parent;
|
|
41
|
+
this.parent.attach(this);
|
|
42
|
+
}
|
|
43
|
+
this.#data = data;
|
|
44
|
+
this.attributes = {};
|
|
45
|
+
if (content !== undefined) {
|
|
46
|
+
const [tag_name, ...attributes] = content.split(" ");
|
|
47
|
+
this.tag_name = tag_name;
|
|
48
|
+
for (const attribute of attributes
|
|
49
|
+
.map(a => a.replaceAll("\"", ""))
|
|
50
|
+
.filter(a => a.includes("="))) {
|
|
51
|
+
const position = attribute.indexOf("=");
|
|
52
|
+
const key = attribute.slice(0, position);
|
|
53
|
+
const value = attribute.slice(position+1);
|
|
54
|
+
this.attributes[key] = value;
|
|
55
|
+
if (this.data) {
|
|
56
|
+
const result = replacement_regex.exec(value);
|
|
57
|
+
if (result !== null) {
|
|
58
|
+
this.attributes[key] = this.data[result[1]];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.children = [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get data() {
|
|
67
|
+
if (this.#data !== undefined) {
|
|
68
|
+
return this.#data;
|
|
69
|
+
} else {
|
|
70
|
+
return this.parent?.data;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
set data(value) {
|
|
75
|
+
this.#data = value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
attach(child) {
|
|
79
|
+
this.children.push(child);
|
|
80
|
+
child.parent = this;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
replace(child, replacement) {
|
|
84
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
85
|
+
if (child === this.children[i]) {
|
|
86
|
+
replacement.parent = this;
|
|
87
|
+
this.children.splice(i, 1, replacement);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async render() {
|
|
93
|
+
let tag = "<" + this.tag_name;
|
|
94
|
+
for (const [key, value] of Object.entries(this.attributes)) {
|
|
95
|
+
if (value === undefined) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
tag += " " + key + "=";
|
|
99
|
+
if (!value.startsWith("\"")) {
|
|
100
|
+
tag += "\"";
|
|
101
|
+
}
|
|
102
|
+
tag += value;
|
|
103
|
+
if (!value.endsWith("\"")) {
|
|
104
|
+
tag += "\"";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (this.auto_closing) {
|
|
108
|
+
tag += "/>";
|
|
109
|
+
} else {
|
|
110
|
+
tag += ">";
|
|
111
|
+
for (const child of this.children) {
|
|
112
|
+
tag += await child.render();
|
|
113
|
+
}
|
|
114
|
+
if (this.text) {
|
|
115
|
+
tag += await this.text;
|
|
116
|
+
}
|
|
117
|
+
tag += "</" + this.tag_name + ">";
|
|
118
|
+
}
|
|
119
|
+
return tag;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
compose(components) {
|
|
123
|
+
if (components[this.tag_name]) {
|
|
124
|
+
return Parser.parse(components[this.tag_name], this.attributes);
|
|
125
|
+
}
|
|
126
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
127
|
+
this.children[i] = this.children[i].compose(components);
|
|
128
|
+
}
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
clone(parent, data) {
|
|
133
|
+
const cloned = new Node(parent, this.tag_name, data);
|
|
134
|
+
cloned.text = this.text;
|
|
135
|
+
for (const attribute in this.attributes) {
|
|
136
|
+
cloned.attributes[attribute] = this.attributes[attribute];
|
|
137
|
+
}
|
|
138
|
+
for (const child of this.children) {
|
|
139
|
+
child.clone(cloned);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async expand() {
|
|
144
|
+
if (this.attributes["data-for"] !== undefined) {
|
|
145
|
+
const key = this.attributes["data-for"];
|
|
146
|
+
delete this.attributes["data-for"];
|
|
147
|
+
const value = await this.data[key];
|
|
148
|
+
const arr = Array.isArray(value) ? value : [value];
|
|
149
|
+
const newparent = new Node();
|
|
150
|
+
for (const val of arr) {
|
|
151
|
+
this.clone(newparent, val);
|
|
152
|
+
}
|
|
153
|
+
this.parent.replace(this, newparent);
|
|
154
|
+
return newparent.expand();
|
|
155
|
+
}
|
|
156
|
+
for (const attribute in this.attributes) {
|
|
157
|
+
if (attribute.startsWith("data-")) {
|
|
158
|
+
const fulfilled = fulfill(this.attributes[attribute], this.data);
|
|
159
|
+
switch(attribute) {
|
|
160
|
+
case "data-value":
|
|
161
|
+
if (this.tag_name === "input") {
|
|
162
|
+
this.attributes.value = fulfilled;
|
|
163
|
+
} else {
|
|
164
|
+
this.text = fulfilled;
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
case "data-href":
|
|
168
|
+
fulfill(this.attributes[attribute], this.data);
|
|
169
|
+
this.attributes.href = fulfilled;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
delete this.attributes[attribute];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
176
|
+
this.children[i] = await this.children[i].expand();
|
|
177
|
+
}
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
unfold(components) {
|
|
182
|
+
return this.compose(components).expand();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Node from "./Node.js";
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
3
|
+
const open_tag = "open_tag";
|
|
4
|
+
const close_tag = "close_tag";
|
|
5
|
+
const open_and_close_tag = "open_and_close_tag";
|
|
6
6
|
const last_index = -1;
|
|
7
7
|
|
|
8
8
|
export default class Parser {
|
|
9
|
-
constructor(html) {
|
|
9
|
+
constructor(html, data) {
|
|
10
10
|
this.html = html;
|
|
11
|
-
this.result = [];
|
|
12
11
|
this.index = 0;
|
|
12
|
+
this.result = [];
|
|
13
13
|
this.buffer = "";
|
|
14
14
|
this.balance = 0;
|
|
15
15
|
this.reading_tag = false;
|
|
16
|
-
this.node = new
|
|
16
|
+
this.node = new Node(undefined, undefined, data);
|
|
17
17
|
this.tree = this.node;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
remove_whitespace() {
|
|
21
|
+
this.html = this.html.replace(/[\n\t\r]/gu, "");
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
get previous() {
|
|
@@ -33,12 +33,12 @@ export default class Parser {
|
|
|
33
33
|
return this.at(this.index+1);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
at(index) {
|
|
37
|
+
return this.html[index];
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
open_tag() {
|
|
41
|
-
this.node = new
|
|
41
|
+
this.node = new Node(this.node, this.buffer);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
close_tag() {
|
|
@@ -49,6 +49,7 @@ export default class Parser {
|
|
|
49
49
|
|
|
50
50
|
open_and_close_tag() {
|
|
51
51
|
this.open_tag();
|
|
52
|
+
this.node.auto_closing = true;
|
|
52
53
|
this.close_tag();
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -59,7 +60,7 @@ export default class Parser {
|
|
|
59
60
|
this.reading_tag = false;
|
|
60
61
|
// if the previous character is '/', it's an open and close tag
|
|
61
62
|
if (this.previous === "/") {
|
|
62
|
-
this.tag =
|
|
63
|
+
this.tag = open_and_close_tag;
|
|
63
64
|
this.balance--;
|
|
64
65
|
this.buffer = this.buffer.slice(0, last_index);
|
|
65
66
|
}
|
|
@@ -77,17 +78,21 @@ export default class Parser {
|
|
|
77
78
|
process_not_reading_tag() {
|
|
78
79
|
// encountered '<'
|
|
79
80
|
if (this.current === "<") {
|
|
81
|
+
this.node.text = this.buffer;
|
|
82
|
+
this.buffer = "";
|
|
80
83
|
// mark as inside tag
|
|
81
84
|
this.reading_tag = true;
|
|
82
85
|
if (this.next === "/") {
|
|
83
86
|
// next character is slash, this is a close tag
|
|
84
|
-
this.tag =
|
|
87
|
+
this.tag = close_tag;
|
|
85
88
|
this.balance--;
|
|
86
89
|
} else {
|
|
87
90
|
// this is an open tag (or open-and-close)
|
|
88
|
-
this.tag =
|
|
91
|
+
this.tag = open_tag;
|
|
89
92
|
this.balance++;
|
|
90
93
|
}
|
|
94
|
+
} else {
|
|
95
|
+
this.buffer += this.current;
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
|
|
@@ -109,14 +114,13 @@ export default class Parser {
|
|
|
109
114
|
return this.tree;
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
// TODO: ignore comments in the analysed html
|
|
113
117
|
parse() {
|
|
114
118
|
this.remove_whitespace();
|
|
115
119
|
do {} while (this.read());
|
|
116
120
|
return this.return_checked();
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
static parse(html) {
|
|
120
|
-
return new Parser(html).parse();
|
|
123
|
+
static parse(html, data) {
|
|
124
|
+
return new Parser(html, data).parse();
|
|
121
125
|
}
|
|
122
126
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Parser from "./DOM/Parser.js";
|
|
2
|
+
import conf from "../conf.js";
|
|
3
|
+
import Directory from "../Directory.js";
|
|
4
|
+
import File from "../File.js";
|
|
5
|
+
|
|
6
|
+
const last = -1;
|
|
7
|
+
const {paths: {"components": path}} = conf();
|
|
8
|
+
const components = {};
|
|
9
|
+
if (await File.exists(path)) {
|
|
10
|
+
const names = await Directory.list(path);
|
|
11
|
+
for (const name of names) {
|
|
12
|
+
components[name.slice(0, -5)] = await File.read(`${path}/${name}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default async (strings, ...keys) => {
|
|
17
|
+
const awaited_keys = await Promise.all(keys);
|
|
18
|
+
const body = await (await Parser.parse(strings
|
|
19
|
+
.slice(0, last)
|
|
20
|
+
.map((string, i) => `${string}$${i}`)
|
|
21
|
+
.join("") + strings[strings.length+last], awaited_keys)
|
|
22
|
+
.unfold(components))
|
|
23
|
+
.render();
|
|
24
|
+
const code = 200;
|
|
25
|
+
const headers = {"Content-Type": "text/html"};
|
|
26
|
+
return {code, body, headers};
|
|
27
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const last = -1;
|
|
2
|
+
|
|
3
|
+
export default async (strings, ...keys) => {
|
|
4
|
+
const awaited_keys = await Promise.all(keys);
|
|
5
|
+
const Location = strings
|
|
6
|
+
.slice(0, last)
|
|
7
|
+
.map((string, i) => string + awaited_keys[i])
|
|
8
|
+
.join("") + strings[strings.length+last];
|
|
9
|
+
return {"code": 302, "headers": {Location}};
|
|
10
|
+
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -13,13 +13,21 @@
|
|
|
13
13
|
"ssl": {
|
|
14
14
|
"key": "ssl/default.key",
|
|
15
15
|
"cert": "ssl/default.crt"
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
"csp": {
|
|
18
|
+
"default-src": "'self'",
|
|
19
|
+
"object-src": "'none'",
|
|
20
|
+
"frame-ancestors": "'none'",
|
|
21
|
+
"form-action": "'self'",
|
|
22
|
+
"base-uri": "'self'"
|
|
23
|
+
},
|
|
24
|
+
"same-site": "Strict"
|
|
17
25
|
},
|
|
18
26
|
"paths": {
|
|
19
|
-
"client": "client",
|
|
20
27
|
"domains": "domains",
|
|
21
28
|
"public": "public",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
29
|
+
"static": "static",
|
|
30
|
+
"routes": "routes",
|
|
31
|
+
"components": "components"
|
|
24
32
|
}
|
|
25
33
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/source/client/Action.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import Element from "../Element.js";
|
|
2
|
-
import View from "./View.js";
|
|
3
|
-
import {origin_base, origin} from "./document.js";
|
|
4
|
-
|
|
5
|
-
export default class Action {
|
|
6
|
-
constructor(name, session) {
|
|
7
|
-
this.name = name;
|
|
8
|
-
this.session = session;
|
|
9
|
-
this.listeners = [];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
before() {}
|
|
13
|
-
|
|
14
|
-
enter(data) {
|
|
15
|
-
if (data.location) {
|
|
16
|
-
location.href = data.location;
|
|
17
|
-
} else {
|
|
18
|
-
this.create_view(data);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
create_view(data, root, save_history = true) {
|
|
23
|
-
if (save_history) {
|
|
24
|
-
if (data.url !== document.location.href.replace(origin, "")) {
|
|
25
|
-
history.pushState({}, "", data.url);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
this.view = new View(data, this, root);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
create_and_return_view(data, root) {
|
|
32
|
-
return new View(data, this, root);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get_form_data(target) {
|
|
36
|
-
const payload = {};
|
|
37
|
-
const {elements} = target;
|
|
38
|
-
for (let i = 0; i < elements.length; i++) {
|
|
39
|
-
const element = elements[i];
|
|
40
|
-
const type = Element.value(element, "type");
|
|
41
|
-
const data_value = Element.value(element, "data-value");
|
|
42
|
-
switch (element.localName) {
|
|
43
|
-
case "input":
|
|
44
|
-
case "textarea":
|
|
45
|
-
if (type === "checkbox") {
|
|
46
|
-
payload[data_value] = element.checked;
|
|
47
|
-
} else if (type === "file") {
|
|
48
|
-
payload[data_value] = element.getAttribute("base64");
|
|
49
|
-
} else if (type === "submit") {
|
|
50
|
-
// do nothing
|
|
51
|
-
} else {
|
|
52
|
-
payload[data_value] = element.value;
|
|
53
|
-
}
|
|
54
|
-
break;
|
|
55
|
-
case "select":
|
|
56
|
-
payload[data_value] = Element
|
|
57
|
-
.value(element.children[element.selectedIndex], "value");
|
|
58
|
-
break;
|
|
59
|
-
default:
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return payload;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async onsubmit(event) {
|
|
67
|
-
const {target} = event;
|
|
68
|
-
const stop = this.fire("submit", event);
|
|
69
|
-
if (stop) {
|
|
70
|
-
return event.preventDefault();
|
|
71
|
-
}
|
|
72
|
-
event.preventDefault();
|
|
73
|
-
const data = await this.write(target.action, this.get_form_data(target));
|
|
74
|
-
return data.type === "update"
|
|
75
|
-
? this.view.update(data.payload)
|
|
76
|
-
: this.session.run(data);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async onclick(event) {
|
|
80
|
-
const stop = await this.fire("click", event);
|
|
81
|
-
if (stop) {
|
|
82
|
-
return event.preventDefault();
|
|
83
|
-
}
|
|
84
|
-
let target;
|
|
85
|
-
if (event.target.shadowRoot !== null &&
|
|
86
|
-
event.target.shadowRoot.activeElement !== null) {
|
|
87
|
-
target = event.target.shadowRoot.activeElement.closest("a");
|
|
88
|
-
} else {
|
|
89
|
-
target = event.target.closest("a");
|
|
90
|
-
}
|
|
91
|
-
if (target !== null) {
|
|
92
|
-
const href = Element.value(target, "href");
|
|
93
|
-
const url = new URL(href, origin_base);
|
|
94
|
-
if (event.button === 0 && url.href.startsWith(origin_base)) {
|
|
95
|
-
event.preventDefault();
|
|
96
|
-
if (href !== "") {
|
|
97
|
-
await this.session.run(await this.read(href));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
onchange() {
|
|
104
|
-
const stop = this.fire("change", event);
|
|
105
|
-
if (stop) {
|
|
106
|
-
event.preventDefault();
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
oninput() {
|
|
111
|
-
this.fire("input", event);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
fire(type, event) {
|
|
115
|
-
const listeners = this.listeners[type];
|
|
116
|
-
if (listeners !== undefined) {
|
|
117
|
-
const {target} = event;
|
|
118
|
-
for (const listener of listeners) {
|
|
119
|
-
if (event.target.closest(listener.selector)) {
|
|
120
|
-
listener.listener(event, target);
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
on(selector, event, listener) {
|
|
130
|
-
this.listeners[event] = this.listeners[event] ?? [];
|
|
131
|
-
const index = this.listeners[event]
|
|
132
|
-
.findIndex(listener => listener.selector === selector);
|
|
133
|
-
const object = {selector, listener};
|
|
134
|
-
const not_found = -1;
|
|
135
|
-
if (index === not_found) {
|
|
136
|
-
this.listeners[event].push(object);
|
|
137
|
-
} else {
|
|
138
|
-
this.listeners[event].splice(index, 1, object);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
_(selector) {
|
|
143
|
-
return new Element(selector, this);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
get selector() {
|
|
147
|
-
return selector => new Element(selector, this);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
read(pathname) {
|
|
151
|
-
return this.session.read(pathname);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
write(pathname, payload) {
|
|
155
|
-
return this.session.write(pathname, payload);
|
|
156
|
-
}
|
|
157
|
-
}
|
package/source/client/App.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import Client from "./Client.js";
|
|
2
|
-
|
|
3
|
-
export default class App {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.definitions = {};
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
run() {
|
|
9
|
-
this.client = new Client();
|
|
10
|
-
this.client.open();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
define(name, predicate) {
|
|
14
|
-
this.definitions[name] = predicate;
|
|
15
|
-
}
|
|
16
|
-
}
|
package/source/client/Client.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import Session from "./Session.js";
|
|
2
|
-
import {base, host, origin_base} from "./document.js";
|
|
3
|
-
|
|
4
|
-
const events = ["submit", "click", "change", "input"];
|
|
5
|
-
let back = undefined;
|
|
6
|
-
const location = `wss://${host}${base}`;
|
|
7
|
-
|
|
8
|
-
export default class Client {
|
|
9
|
-
constructor(conf) {
|
|
10
|
-
this.conf = conf;
|
|
11
|
-
this.session = new Session(this);
|
|
12
|
-
|
|
13
|
-
window.onpopstate = async event => {
|
|
14
|
-
const {pathname, search} = event.target.location;
|
|
15
|
-
return this.session.run(await this.read(pathname + search));
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
for (const key of events) {
|
|
19
|
-
window.addEventListener(key, event => this.session.on(key, event));
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
open() {
|
|
24
|
-
return this.connection?.readyState === 1 ? this
|
|
25
|
-
: new Promise(resolve => {
|
|
26
|
-
this.connection = new WebSocket(location);
|
|
27
|
-
this.connection.addEventListener("message", ({data}) => data === "open"
|
|
28
|
-
? resolve(this)
|
|
29
|
-
: this.receive(JSON.parse(data)));
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async receive(data) {
|
|
34
|
-
if (back === undefined) {
|
|
35
|
-
await this.session.execute(data);
|
|
36
|
-
} else {
|
|
37
|
-
back(data);
|
|
38
|
-
back = undefined;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
read(url) {
|
|
43
|
-
return this.send("read", url);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
write(url, payload) {
|
|
47
|
-
return this.send("write", url, payload);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async send(type, url, payload = {}) {
|
|
51
|
-
await this.open();
|
|
52
|
-
const {pathname, search} = new URL(url, origin_base);
|
|
53
|
-
const _url = pathname + search;
|
|
54
|
-
return new Promise(resolve => {
|
|
55
|
-
back = data => resolve(data);
|
|
56
|
-
this.connection.send(JSON.stringify({
|
|
57
|
-
type, pathname, search, payload, "url": _url,
|
|
58
|
-
}));
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|