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: []
|