crimson 0.1.0
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.
- checksums.yaml +7 -0
- data/.github/workflows/gempush.yml +44 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/.vscode/launch.json +14 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/crimson.gemspec +25 -0
- data/example/ets.rb +22 -0
- data/example/example.rb +66 -0
- data/lib/crimson.js +10 -0
- data/lib/crimson.rb +5 -0
- data/lib/crimson/client.rb +86 -0
- data/lib/crimson/icons/close.png +0 -0
- data/lib/crimson/icons/hide.png +0 -0
- data/lib/crimson/icons/resize.png +0 -0
- data/lib/crimson/mash.rb +9 -0
- data/lib/crimson/model.rb +98 -0
- data/lib/crimson/model_change.rb +20 -0
- data/lib/crimson/notification_bus.rb +35 -0
- data/lib/crimson/object.rb +196 -0
- data/lib/crimson/server.rb +69 -0
- data/lib/crimson/utilities.rb +13 -0
- data/lib/crimson/version.rb +3 -0
- data/lib/crimson/webserver.rb +17 -0
- data/lib/crimson/widgets/bottom_resizer.rb +22 -0
- data/lib/crimson/widgets/desktop.rb +44 -0
- data/lib/crimson/widgets/form.rb +12 -0
- data/lib/crimson/widgets/input.rb +10 -0
- data/lib/crimson/widgets/left_resizer.rb +27 -0
- data/lib/crimson/widgets/resizer.rb +38 -0
- data/lib/crimson/widgets/right_resizer.rb +22 -0
- data/lib/crimson/widgets/taskbar.rb +0 -0
- data/lib/crimson/widgets/titlebar.rb +75 -0
- data/lib/crimson/widgets/top_resizer.rb +27 -0
- data/lib/crimson/widgets/window.rb +134 -0
- data/lib/html/template.html +13 -0
- data/lib/javascript/Application.js +13 -0
- data/lib/javascript/Logger.js +11 -0
- data/lib/javascript/MessageHandler.js +142 -0
- data/lib/javascript/ObjectManager.js +33 -0
- data/lib/javascript/ServerInteractor.js +42 -0
- data/lib/javascript/Utilities.js +26 -0
- metadata +135 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<title>crimson web</title>
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
7
|
+
<script src="crimson.js" type="module"></script>
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
<crimson src="localhost:{PORT}"></crimson>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ServerInteractor from './ServerInteractor.js'
|
|
2
|
+
import ObjectManager from './ObjectManager.js'
|
|
3
|
+
import Logger from './Logger.js'
|
|
4
|
+
|
|
5
|
+
export default class Application {
|
|
6
|
+
constructor(root) {
|
|
7
|
+
this.uri = root.attributes.src.value;
|
|
8
|
+
this.root = root;
|
|
9
|
+
this.logger = new Logger(true);
|
|
10
|
+
this.objectManager = new ObjectManager(this.root, this.logger);
|
|
11
|
+
this.serverInteractor = new ServerInteractor(this.uri, this.objectManager, this.logger);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import Utilities from './Utilities.js'
|
|
2
|
+
|
|
3
|
+
export default class MessageHandler {
|
|
4
|
+
constructor(serverInteractor, objectManager, logger) {
|
|
5
|
+
this.serverInteractor = serverInteractor;
|
|
6
|
+
this.objectManager = objectManager;
|
|
7
|
+
this.logger = logger;
|
|
8
|
+
this.eventHandlers = {};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
handle(message) {
|
|
12
|
+
switch (message.action) {
|
|
13
|
+
case "create": this.handleCreateMessage(message); break;
|
|
14
|
+
case "update": this.handleUpdateMessage(message); break;
|
|
15
|
+
case "destroy": this.handleDestroyMessage(message); break;
|
|
16
|
+
case "invoke": this.handleInvokeMessage(message); break;
|
|
17
|
+
default:
|
|
18
|
+
this.logger.log(`[MessageHandler] Unknown action ${message.action}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
handleCreateMessage(message) {
|
|
23
|
+
const object = document.createElement(message.tag);
|
|
24
|
+
|
|
25
|
+
this.objectManager.insert(message.id, object);
|
|
26
|
+
this.objectManager.get("root").appendChild(object);
|
|
27
|
+
|
|
28
|
+
this.handleUpdateMessage(message);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
handleUpdateMessage(message) {
|
|
32
|
+
Object.keys(message.changes).forEach(change => {
|
|
33
|
+
if (change === "children") {
|
|
34
|
+
this.handleUpdateChildrenMessage(message);
|
|
35
|
+
} else if (change === "events") {
|
|
36
|
+
this.handleUpdateEventsMessage(message);
|
|
37
|
+
} else if (!(change in this.objectManager.get(message.id))) {
|
|
38
|
+
this.logger.log(`[MessageHandler] Unknown attribute '${change}' for ${message.id}`);
|
|
39
|
+
} else if (change === "style") {
|
|
40
|
+
this.handleUpdateStyleMessage(message);
|
|
41
|
+
} else {
|
|
42
|
+
this.handleUpdateAttributeMessage(change, message);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
handleUpdateAttributeMessage(attr, message) {
|
|
48
|
+
const object = this.objectManager.get(message.id);
|
|
49
|
+
|
|
50
|
+
object[attr] = message.changes[attr];
|
|
51
|
+
object.setAttribute(attr, message.changes[attr]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
handleUpdateChildrenMessage(message) {
|
|
55
|
+
const object = this.objectManager.get(message.id);
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < message.changes.children.length; i++) {
|
|
58
|
+
const childId = message.changes.children[i];
|
|
59
|
+
const child = this.objectManager.get(childId);
|
|
60
|
+
|
|
61
|
+
Utilities.insertAt(object, child, i);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
handleUpdateEventsMessage(message) {
|
|
66
|
+
const me = this;
|
|
67
|
+
const object = this.objectManager.get(message.id);
|
|
68
|
+
|
|
69
|
+
// remove previous event handlers
|
|
70
|
+
if (me.eventHandlers[message.id]) {
|
|
71
|
+
Object.keys(me.eventHandlers[message.id]).forEach(eventName => {
|
|
72
|
+
object.removeEventListener(eventName, me.eventHandlers[message.id][eventName]);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
me.eventHandlers[message.id] = {};
|
|
77
|
+
|
|
78
|
+
// add new handlers
|
|
79
|
+
message.changes.events.forEach(eventName => {
|
|
80
|
+
me.eventHandlers[message.id][eventName] = function (e) {
|
|
81
|
+
const data = JSON.parse(Utilities.stringifyEvent(e));
|
|
82
|
+
|
|
83
|
+
if (object.tagName === "FORM") {
|
|
84
|
+
const formData = new FormData(object);
|
|
85
|
+
formData.forEach(function (value, key) {
|
|
86
|
+
data[key] = value;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (eventName === "dragstart") {
|
|
91
|
+
e.dataTransfer.setData("text/plain", object.id);
|
|
92
|
+
} else if (e.dataTransfer) {
|
|
93
|
+
data.target = e.dataTransfer.getData("text/plain");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
me.serverInteractor.send({
|
|
97
|
+
action: "event",
|
|
98
|
+
id: message.id,
|
|
99
|
+
event: eventName,
|
|
100
|
+
data: data
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return false;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
object.addEventListener(eventName, me.eventHandlers[message.id][eventName], false);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
handleUpdateStyleMessage(message) {
|
|
111
|
+
const object = this.objectManager.get(message.id);
|
|
112
|
+
|
|
113
|
+
Object.keys(message.changes.style).forEach(style => {
|
|
114
|
+
object.style[style] = message.changes.style[style];
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
handleDestroyMessage(message) {
|
|
119
|
+
this.objectManager.get(message.id).remove();
|
|
120
|
+
this.objectManager.remove(message.id);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
handleInvokeMessage(message) {
|
|
124
|
+
const object = this.objectManager.get(message.id);
|
|
125
|
+
|
|
126
|
+
if (!(functor in object)) {
|
|
127
|
+
this.logger.log(`[ObjectManager] Trying to invoke a non-existing functor ${message.functor}!`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (let i = 0; i < args.length; i++) {
|
|
132
|
+
if (this.objectManager.contains(args[i]))
|
|
133
|
+
args[i] = this.objectManager.get(args[i]);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.send({
|
|
137
|
+
action: "response",
|
|
138
|
+
token: message.token,
|
|
139
|
+
returnValue: object[functor].apply(object, args)
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default class ObjectManager {
|
|
2
|
+
constructor(root, logger) {
|
|
3
|
+
this.logger = logger;
|
|
4
|
+
this.objects = {
|
|
5
|
+
"root": root
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
get(id) {
|
|
10
|
+
if (this.contains(id)) {
|
|
11
|
+
return this.objects[id];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
throw `[ObjectManager] Attempting to get unknown object ${id}!`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
insert(id, object) {
|
|
18
|
+
if (!this.contains(id)) {
|
|
19
|
+
object.id = id
|
|
20
|
+
this.objects[id] = object;
|
|
21
|
+
} else {
|
|
22
|
+
throw `[ObjectManager] Attempting to insert already existing object ${id}!`
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
contains(id) {
|
|
27
|
+
return id in this.objects;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
remove(id) {
|
|
31
|
+
delete this.objects[id];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import MessageHandler from './MessageHandler.js'
|
|
2
|
+
|
|
3
|
+
export default class ServerInteractor {
|
|
4
|
+
constructor(uri, objectManager, logger) {
|
|
5
|
+
let me = this;
|
|
6
|
+
|
|
7
|
+
me.messageHandler = new MessageHandler(me, objectManager, logger);
|
|
8
|
+
me.logger = logger;
|
|
9
|
+
|
|
10
|
+
me.socket = new WebSocket("ws://" + uri);
|
|
11
|
+
me.socket.onopen = function (event) { me.onOpen(event); };
|
|
12
|
+
me.socket.onmessage = function (event) { me.onMessage(event); };
|
|
13
|
+
me.socket.onclose = function (event) { me.onClose(event); };
|
|
14
|
+
me.socket.onerror = function (error) { me.onError(error); };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
send(message) {
|
|
18
|
+
this.socket.send(JSON.stringify(message));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
onOpen(event) {
|
|
22
|
+
this.logger.log("[ServerInteractor] Connection established");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onMessage(event) {
|
|
26
|
+
this.logger.log(`[ServerInteractor] Data received from server: ${event.data}`);
|
|
27
|
+
|
|
28
|
+
this.messageHandler.handle(JSON.parse(event.data));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
onClose(event) {
|
|
32
|
+
if (event.wasClean) {
|
|
33
|
+
this.logger.log(`[ServerInteractor] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
|
|
34
|
+
} else {
|
|
35
|
+
this.logger.log("[ServerInteractor] Connection died");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
onError(error) {
|
|
40
|
+
this.logger.log(`[ServerInteractor] Received Error: ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default class Utilities {
|
|
2
|
+
static insertAt(parent, child, index) {
|
|
3
|
+
if (!index) {
|
|
4
|
+
index = 0
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if (index >= parent.children.length) {
|
|
8
|
+
parent.appendChild(child)
|
|
9
|
+
} else {
|
|
10
|
+
parent.insertBefore(child, parent.children[index])
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static stringifyEvent(e) {
|
|
15
|
+
const obj = {};
|
|
16
|
+
for (let k in e) {
|
|
17
|
+
obj[k] = e[k];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return JSON.stringify(obj, (k, v) => {
|
|
21
|
+
if (v instanceof Node) return v.id;
|
|
22
|
+
if (v instanceof Window) return "Window";
|
|
23
|
+
return v;
|
|
24
|
+
}, ' ');
|
|
25
|
+
}
|
|
26
|
+
}
|
metadata
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: crimson
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rizwan Qureshi
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-01-02 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.11'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.11'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: minitest
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '5.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '5.0'
|
|
55
|
+
description: This library helps you write server-sided web apps using Ruby.
|
|
56
|
+
email:
|
|
57
|
+
- rizwan@ualberta.ca
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- ".github/workflows/gempush.yml"
|
|
63
|
+
- ".gitignore"
|
|
64
|
+
- ".travis.yml"
|
|
65
|
+
- ".vscode/launch.json"
|
|
66
|
+
- CODE_OF_CONDUCT.md
|
|
67
|
+
- Gemfile
|
|
68
|
+
- LICENSE.txt
|
|
69
|
+
- README.md
|
|
70
|
+
- Rakefile
|
|
71
|
+
- bin/console
|
|
72
|
+
- bin/setup
|
|
73
|
+
- crimson.gemspec
|
|
74
|
+
- doc/images/prelim-ruby-js-comms.png
|
|
75
|
+
- doc/images/temperature-readme-example.PNG
|
|
76
|
+
- example/ets.rb
|
|
77
|
+
- example/example.rb
|
|
78
|
+
- lib/crimson.js
|
|
79
|
+
- lib/crimson.rb
|
|
80
|
+
- lib/crimson/client.rb
|
|
81
|
+
- lib/crimson/icons/close.png
|
|
82
|
+
- lib/crimson/icons/hide.png
|
|
83
|
+
- lib/crimson/icons/resize.png
|
|
84
|
+
- lib/crimson/mash.rb
|
|
85
|
+
- lib/crimson/model.rb
|
|
86
|
+
- lib/crimson/model_change.rb
|
|
87
|
+
- lib/crimson/notification_bus.rb
|
|
88
|
+
- lib/crimson/object.rb
|
|
89
|
+
- lib/crimson/server.rb
|
|
90
|
+
- lib/crimson/utilities.rb
|
|
91
|
+
- lib/crimson/version.rb
|
|
92
|
+
- lib/crimson/webserver.rb
|
|
93
|
+
- lib/crimson/widgets/bottom_resizer.rb
|
|
94
|
+
- lib/crimson/widgets/desktop.rb
|
|
95
|
+
- lib/crimson/widgets/form.rb
|
|
96
|
+
- lib/crimson/widgets/input.rb
|
|
97
|
+
- lib/crimson/widgets/left_resizer.rb
|
|
98
|
+
- lib/crimson/widgets/resizer.rb
|
|
99
|
+
- lib/crimson/widgets/right_resizer.rb
|
|
100
|
+
- lib/crimson/widgets/taskbar.rb
|
|
101
|
+
- lib/crimson/widgets/titlebar.rb
|
|
102
|
+
- lib/crimson/widgets/top_resizer.rb
|
|
103
|
+
- lib/crimson/widgets/window.rb
|
|
104
|
+
- lib/html/template.html
|
|
105
|
+
- lib/javascript/Application.js
|
|
106
|
+
- lib/javascript/Logger.js
|
|
107
|
+
- lib/javascript/MessageHandler.js
|
|
108
|
+
- lib/javascript/ObjectManager.js
|
|
109
|
+
- lib/javascript/ServerInteractor.js
|
|
110
|
+
- lib/javascript/Utilities.js
|
|
111
|
+
homepage: https://github.com/rizwan146/crimson
|
|
112
|
+
licenses:
|
|
113
|
+
- MIT
|
|
114
|
+
metadata: {}
|
|
115
|
+
post_install_message:
|
|
116
|
+
rdoc_options: []
|
|
117
|
+
require_paths:
|
|
118
|
+
- lib
|
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - ">="
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '0'
|
|
129
|
+
requirements: []
|
|
130
|
+
rubyforge_project:
|
|
131
|
+
rubygems_version: 2.7.6.2
|
|
132
|
+
signing_key:
|
|
133
|
+
specification_version: 4
|
|
134
|
+
summary: Develop server-sided web-apps using Ruby!
|
|
135
|
+
test_files: []
|