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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gempush.yml +44 -0
  3. data/.gitignore +9 -0
  4. data/.travis.yml +4 -0
  5. data/.vscode/launch.json +14 -0
  6. data/CODE_OF_CONDUCT.md +49 -0
  7. data/Gemfile +11 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +10 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/crimson.gemspec +25 -0
  14. data/example/ets.rb +22 -0
  15. data/example/example.rb +66 -0
  16. data/lib/crimson.js +10 -0
  17. data/lib/crimson.rb +5 -0
  18. data/lib/crimson/client.rb +86 -0
  19. data/lib/crimson/icons/close.png +0 -0
  20. data/lib/crimson/icons/hide.png +0 -0
  21. data/lib/crimson/icons/resize.png +0 -0
  22. data/lib/crimson/mash.rb +9 -0
  23. data/lib/crimson/model.rb +98 -0
  24. data/lib/crimson/model_change.rb +20 -0
  25. data/lib/crimson/notification_bus.rb +35 -0
  26. data/lib/crimson/object.rb +196 -0
  27. data/lib/crimson/server.rb +69 -0
  28. data/lib/crimson/utilities.rb +13 -0
  29. data/lib/crimson/version.rb +3 -0
  30. data/lib/crimson/webserver.rb +17 -0
  31. data/lib/crimson/widgets/bottom_resizer.rb +22 -0
  32. data/lib/crimson/widgets/desktop.rb +44 -0
  33. data/lib/crimson/widgets/form.rb +12 -0
  34. data/lib/crimson/widgets/input.rb +10 -0
  35. data/lib/crimson/widgets/left_resizer.rb +27 -0
  36. data/lib/crimson/widgets/resizer.rb +38 -0
  37. data/lib/crimson/widgets/right_resizer.rb +22 -0
  38. data/lib/crimson/widgets/taskbar.rb +0 -0
  39. data/lib/crimson/widgets/titlebar.rb +75 -0
  40. data/lib/crimson/widgets/top_resizer.rb +27 -0
  41. data/lib/crimson/widgets/window.rb +134 -0
  42. data/lib/html/template.html +13 -0
  43. data/lib/javascript/Application.js +13 -0
  44. data/lib/javascript/Logger.js +11 -0
  45. data/lib/javascript/MessageHandler.js +142 -0
  46. data/lib/javascript/ObjectManager.js +33 -0
  47. data/lib/javascript/ServerInteractor.js +42 -0
  48. data/lib/javascript/Utilities.js +26 -0
  49. 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,11 @@
1
+ export default class Logger {
2
+ constructor(enabled) {
3
+ this.enabled = enabled
4
+ }
5
+
6
+ log(message) {
7
+ if(this.enabled) {
8
+ console.log(message);
9
+ }
10
+ }
11
+ }
@@ -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: []