alchemymvc 1.2.5 → 1.2.7
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/LICENSE +0 -0
- package/README.md +0 -0
- package/lib/app/assets/scripts/.gitkeep +0 -0
- package/lib/app/assets/stylesheets/alchemy-info.less +0 -0
- package/lib/app/behaviour/publishable_behaviour.js +0 -0
- package/lib/app/behaviour/revision_behaviour.js +0 -0
- package/lib/app/behaviour/sluggable_behaviour.js +0 -0
- package/lib/app/component/.gitkeep +0 -0
- package/lib/app/conduit/electron_conduit.js +0 -0
- package/lib/app/conduit/http_conduit.js +173 -173
- package/lib/app/conduit/loopback_conduit.js +2 -3
- package/lib/app/conduit/socket_conduit.js +620 -620
- package/lib/app/controller/alchemy_info_controller.js +0 -0
- package/lib/app/datasource/mongo_datasource.js +0 -0
- package/lib/app/element/time_ago.js +2 -4
- package/lib/app/helper/alchemy_helper.js +22 -11
- package/lib/app/helper/client_collection.js +0 -0
- package/lib/app/helper/pagination_helper.js +0 -0
- package/lib/app/helper/router_helper.js +20 -7
- package/lib/app/helper/socket_helper.js +613 -613
- package/lib/app/helper_component/paginate_component.js +0 -0
- package/lib/app/helper_controller/component.js +0 -0
- package/lib/app/helper_controller/conduit.js +0 -0
- package/lib/app/helper_controller/controller.js +0 -0
- package/lib/app/helper_datasource/00-nosql_datasource.js +0 -0
- package/lib/app/helper_datasource/05-fallback_datasource.js +0 -0
- package/lib/app/helper_datasource/idb_datasource.js +0 -0
- package/lib/app/helper_datasource/indexed_db.js +0 -0
- package/lib/app/helper_field/00-objectid_field.js +0 -0
- package/lib/app/helper_field/06-text_field.js +0 -0
- package/lib/app/helper_field/10-number_field.js +0 -0
- package/lib/app/helper_field/boolean_field.js +0 -0
- package/lib/app/helper_field/date_field.js +0 -0
- package/lib/app/helper_field/datetime_field.js +0 -0
- package/lib/app/helper_field/enum_field.js +0 -0
- package/lib/app/helper_field/geopoint_field.js +0 -0
- package/lib/app/helper_field/habtm_field.js +0 -0
- package/lib/app/helper_field/hasoneparent_field.js +0 -0
- package/lib/app/helper_field/html_field.js +0 -0
- package/lib/app/helper_field/integer_field.js +0 -0
- package/lib/app/helper_field/object_field.js +0 -0
- package/lib/app/helper_field/regexp_field.js +0 -0
- package/lib/app/helper_field/schema_field.js +23 -2
- package/lib/app/helper_field/time_field.js +0 -0
- package/lib/app/helper_field/url_field.js +0 -0
- package/lib/app/helper_model/criteria.js +21 -3
- package/lib/app/helper_model/db_query.js +0 -0
- package/lib/app/helper_model/document_list.js +0 -0
- package/lib/app/helper_model/field_config.js +2 -2
- package/lib/app/helper_model/model.js +72 -5
- package/lib/app/model/alchemy_task_model.js +0 -0
- package/lib/app/routes.js +0 -0
- package/lib/app/view/alchemy/info.ejs +0 -0
- package/lib/app/view/error/unknown.ejs +0 -0
- package/lib/app/view/paginate/navlist.ejs +0 -0
- package/lib/bootstrap.js +0 -0
- package/lib/class/behaviour.js +0 -0
- package/lib/class/component.js +0 -0
- package/lib/class/conduit.js +2572 -2552
- package/lib/class/controller.js +4 -1
- package/lib/class/document.js +18 -4
- package/lib/class/document_list.js +0 -0
- package/lib/class/helper.js +0 -0
- package/lib/class/inode.js +0 -0
- package/lib/class/inode_dir.js +0 -0
- package/lib/class/inode_file.js +136 -112
- package/lib/class/inode_list.js +0 -0
- package/lib/class/model.js +1740 -1769
- package/lib/class/path_definition.js +0 -0
- package/lib/class/route.js +0 -0
- package/lib/class/router.js +6 -3
- package/lib/class/session.js +0 -0
- package/lib/class/task.js +0 -0
- package/lib/core/base.js +41 -18
- package/lib/core/client_alchemy.js +48 -2
- package/lib/core/discovery.js +0 -0
- package/lib/core/middleware.js +6 -4
- package/lib/core/routing.js +0 -0
- package/lib/core/socket.js +159 -159
- package/lib/init/alchemy.js +1848 -1823
- package/lib/init/constants.js +0 -0
- package/lib/init/functions.js +8 -4
- package/lib/init/load_functions.js +0 -0
- package/lib/init/requirements.js +101 -101
- package/lib/stages.js +10 -2
- package/package.json +74 -74
|
File without changes
|
package/lib/class/route.js
CHANGED
|
File without changes
|
package/lib/class/router.js
CHANGED
|
@@ -1145,9 +1145,9 @@ RouterClass.setMethod(function getOptions(result) {
|
|
|
1145
1145
|
/**
|
|
1146
1146
|
* Get an object of all the routes in this router and its children
|
|
1147
1147
|
*
|
|
1148
|
-
* @author Jelle De Loecker <jelle@
|
|
1148
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1149
1149
|
* @since 0.2.0
|
|
1150
|
-
* @version 1.
|
|
1150
|
+
* @version 1.2.7
|
|
1151
1151
|
*
|
|
1152
1152
|
* @param {Object} result Optional object to store sectioned results in
|
|
1153
1153
|
*
|
|
@@ -1190,7 +1190,10 @@ RouterClass.setMethod(function getRoutes(result) {
|
|
|
1190
1190
|
controller : route.controller,
|
|
1191
1191
|
action : route.action,
|
|
1192
1192
|
keys : route.keys,
|
|
1193
|
-
has_breadcrumb_assignments : route.has_breadcrumb_assignments
|
|
1193
|
+
has_breadcrumb_assignments : route.has_breadcrumb_assignments || undefined,
|
|
1194
|
+
methods : route.methods,
|
|
1195
|
+
permission : route.permission || undefined,
|
|
1196
|
+
has_permission_assignments : route.has_permission_assignments || undefined,
|
|
1194
1197
|
};
|
|
1195
1198
|
}
|
|
1196
1199
|
|
package/lib/class/session.js
CHANGED
|
File without changes
|
package/lib/class/task.js
CHANGED
|
File without changes
|
package/lib/core/base.js
CHANGED
|
@@ -35,7 +35,7 @@ __Protoblast.getGroup = function getGroup(name) {
|
|
|
35
35
|
*
|
|
36
36
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
37
37
|
* @since 0.3.0
|
|
38
|
-
* @version 1.
|
|
38
|
+
* @version 1.2.6
|
|
39
39
|
*/
|
|
40
40
|
Base.constitute(function setBasicBehaviour() {
|
|
41
41
|
|
|
@@ -79,12 +79,12 @@ Base.constitute(function setBasicBehaviour() {
|
|
|
79
79
|
|
|
80
80
|
// Set the title on the class itself, don't let children inherit it
|
|
81
81
|
if (!this.hasOwnProperty('title')) {
|
|
82
|
-
this.setStatic('title', this.
|
|
82
|
+
this.setStatic('title', this.createClassTitle(), false);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// See if the type_name needs to be set automatically
|
|
86
86
|
if (!this.hasOwnProperty('type_name')) {
|
|
87
|
-
this.setStatic('type_name', this.
|
|
87
|
+
this.setStatic('type_name', this.createClassTypeName(), false);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
return;
|
|
@@ -98,12 +98,12 @@ Base.constitute(function setBasicBehaviour() {
|
|
|
98
98
|
|
|
99
99
|
// See if the type_name needs to be set automatically
|
|
100
100
|
if (!this.hasOwnProperty('type_name')) {
|
|
101
|
-
this.setStatic('type_name', this.
|
|
101
|
+
this.setStatic('type_name', this.createClassTypeName(), false);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// Do the same for the title
|
|
105
105
|
if (!this.hasOwnProperty('title')) {
|
|
106
|
-
this.setStatic('title', this.
|
|
106
|
+
this.setStatic('title', this.createClassTitle(), false);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
return;
|
|
@@ -126,12 +126,12 @@ Base.constitute(function setBasicBehaviour() {
|
|
|
126
126
|
|
|
127
127
|
// See if the type_name needs to be set automatically
|
|
128
128
|
if (!this.hasOwnProperty('type_name')) {
|
|
129
|
-
this.setStatic('type_name',
|
|
129
|
+
this.setStatic('type_name', this.createClassTypeName(name), false);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// Do the same for the title
|
|
133
133
|
if (!this.hasOwnProperty('title')) {
|
|
134
|
-
this.setStatic('title',
|
|
134
|
+
this.setStatic('title', this.createClassTitle(name), false);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// Add this class to the shared group
|
|
@@ -139,21 +139,44 @@ Base.constitute(function setBasicBehaviour() {
|
|
|
139
139
|
});
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
|
-
*
|
|
142
|
+
* Create a type name for this class
|
|
143
|
+
* (An underscored key used in shared groups & enums)
|
|
143
144
|
*
|
|
144
|
-
* @author Jelle De Loecker <jelle@
|
|
145
|
-
* @since 1.
|
|
146
|
-
* @version 1.
|
|
145
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
146
|
+
* @since 1.2.6
|
|
147
|
+
* @version 1.2.6
|
|
147
148
|
*
|
|
148
|
-
* @param {
|
|
149
|
+
* @param {string} suggested_root
|
|
150
|
+
*
|
|
151
|
+
* @return {string}
|
|
149
152
|
*/
|
|
150
|
-
Base.setStatic(function
|
|
153
|
+
Base.setStatic(function createClassTypeName(suggested_root) {
|
|
151
154
|
|
|
152
|
-
if (
|
|
153
|
-
|
|
155
|
+
if (!suggested_root) {
|
|
156
|
+
suggested_root = this.name;
|
|
154
157
|
}
|
|
155
158
|
|
|
156
|
-
|
|
159
|
+
return suggested_root.underscore();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Create a title for this class
|
|
164
|
+
*
|
|
165
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
166
|
+
* @since 1.2.6
|
|
167
|
+
* @version 1.2.6
|
|
168
|
+
*
|
|
169
|
+
* @param {string} suggested_root
|
|
170
|
+
*
|
|
171
|
+
* @return {string}
|
|
172
|
+
*/
|
|
173
|
+
Base.setStatic(function createClassTitle(suggested_root) {
|
|
174
|
+
|
|
175
|
+
if (!suggested_root) {
|
|
176
|
+
suggested_root = this.name;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return suggested_root.titleize();
|
|
157
180
|
});
|
|
158
181
|
|
|
159
182
|
/**
|
|
@@ -345,7 +368,7 @@ Base.setStatic('starts_new_group', false);
|
|
|
345
368
|
*
|
|
346
369
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
347
370
|
* @since 1.1.8
|
|
348
|
-
* @version 1.2.
|
|
371
|
+
* @version 1.2.6
|
|
349
372
|
*
|
|
350
373
|
* @type {Conduit}
|
|
351
374
|
*/
|
|
@@ -359,7 +382,7 @@ Base.setProperty(function conduit() {
|
|
|
359
382
|
result;
|
|
360
383
|
|
|
361
384
|
if (Blast.isBrowser || !(this instanceof Classes.Alchemy.Controller)) {
|
|
362
|
-
renderer = this.
|
|
385
|
+
renderer = this.hawkejs_renderer || this.hawkejs_view || this.renderer || this.view;
|
|
363
386
|
}
|
|
364
387
|
|
|
365
388
|
if (renderer?.conduit) {
|
|
@@ -403,8 +403,6 @@ Alchemy.setMethod(function checksum(value) {
|
|
|
403
403
|
*/
|
|
404
404
|
Alchemy.setMethod(function markLinkElement(element, active_nr) {
|
|
405
405
|
|
|
406
|
-
var mark_wrapper;
|
|
407
|
-
|
|
408
406
|
// Always remove the current classes
|
|
409
407
|
element.classList.remove('active-link');
|
|
410
408
|
element.classList.remove('active-sublink');
|
|
@@ -424,6 +422,30 @@ Alchemy.setMethod(function markLinkElement(element, active_nr) {
|
|
|
424
422
|
}
|
|
425
423
|
});
|
|
426
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Get the configuration for the given route
|
|
427
|
+
*
|
|
428
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
429
|
+
* @since 1.2.7
|
|
430
|
+
* @version 1.2.7
|
|
431
|
+
*
|
|
432
|
+
* @param {String} name The route name
|
|
433
|
+
*
|
|
434
|
+
* @return {Object}
|
|
435
|
+
*/
|
|
436
|
+
Alchemy.setMethod(function routeConfig(name) {
|
|
437
|
+
|
|
438
|
+
let result;
|
|
439
|
+
|
|
440
|
+
if (Blast.isNode) {
|
|
441
|
+
result = Classes.Alchemy.Helper.Router.prototype.routeConfig.call({}, name);
|
|
442
|
+
} else {
|
|
443
|
+
result = hawkejs.scene.general_renderer.helpers.Router.routeConfig(name);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return result;
|
|
447
|
+
});
|
|
448
|
+
|
|
427
449
|
const markLinkElement = Alchemy.prototype.markLinkElement;
|
|
428
450
|
|
|
429
451
|
// From here on, only client-side code is added
|
|
@@ -1112,4 +1134,28 @@ Alchemy.setMethod(function createSchema(parent) {
|
|
|
1112
1134
|
*/
|
|
1113
1135
|
Alchemy.setMethod(function getClientModel(name, init, options) {
|
|
1114
1136
|
return Classes.Alchemy.Client.Base.prototype.getModel.call(this, name, init, options);
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Check if the current client-side user has a certain permission
|
|
1141
|
+
*
|
|
1142
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1143
|
+
* @since 1.2.7
|
|
1144
|
+
* @version 1.2.7
|
|
1145
|
+
*
|
|
1146
|
+
* @param {String} permission
|
|
1147
|
+
*
|
|
1148
|
+
* @return {Boolean}
|
|
1149
|
+
*/
|
|
1150
|
+
Alchemy.setMethod(function hasPermission(permission) {
|
|
1151
|
+
|
|
1152
|
+
let user_data = hawkejs.scene.exposed['acl-user-data'];
|
|
1153
|
+
|
|
1154
|
+
if (user_data && user_data.permissions) {
|
|
1155
|
+
if (user_data.permissions.hasPermission?.(permission)) {
|
|
1156
|
+
return true;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return false;
|
|
1115
1161
|
});
|
package/lib/core/discovery.js
CHANGED
|
File without changes
|
package/lib/core/middleware.js
CHANGED
|
@@ -128,7 +128,7 @@ function getMiddlePaths(paths, ext, new_ext) {
|
|
|
128
128
|
*
|
|
129
129
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
130
130
|
* @since 0.2.0
|
|
131
|
-
* @version
|
|
131
|
+
* @version 1.2.7
|
|
132
132
|
*/
|
|
133
133
|
Alchemy.setMethod(function styleMiddleware(req, res, nextMiddleware) {
|
|
134
134
|
|
|
@@ -243,7 +243,7 @@ Alchemy.setMethod(function styleMiddleware(req, res, nextMiddleware) {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
if (!compiled || !compiled.path) {
|
|
246
|
-
return
|
|
246
|
+
return nextMiddleware();
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
return req.conduit.serveFile(compiled.path, {onError: function onError(err) {
|
|
@@ -306,7 +306,7 @@ Alchemy.setMethod(function sourcemapMiddleware(req, res, nextMiddleware) {
|
|
|
306
306
|
*
|
|
307
307
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
308
308
|
* @since 0.2.0
|
|
309
|
-
* @version 1.
|
|
309
|
+
* @version 1.2.7
|
|
310
310
|
*
|
|
311
311
|
* @param {String} path
|
|
312
312
|
* @param {Object} options
|
|
@@ -414,6 +414,7 @@ Alchemy.setMethod(function minifyScript(path, options, callback) {
|
|
|
414
414
|
compress: {
|
|
415
415
|
keep_fargs : true,
|
|
416
416
|
keep_fnames : true,
|
|
417
|
+
keep_classnames : true,
|
|
417
418
|
hoist_funs : false,
|
|
418
419
|
drop_console : !alchemy.settings.debug,
|
|
419
420
|
dead_code : true,
|
|
@@ -423,7 +424,8 @@ Alchemy.setMethod(function minifyScript(path, options, callback) {
|
|
|
423
424
|
}
|
|
424
425
|
},
|
|
425
426
|
mangle: {
|
|
426
|
-
keep_fnames
|
|
427
|
+
keep_fnames : true,
|
|
428
|
+
keep_classnames : true,
|
|
427
429
|
}
|
|
428
430
|
};
|
|
429
431
|
|
package/lib/core/routing.js
CHANGED
|
File without changes
|
package/lib/core/socket.js
CHANGED
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var types = alchemy.shared('Socket.types'),
|
|
4
|
-
iostream,
|
|
5
|
-
path = alchemy.use('path'),
|
|
6
|
-
fs = alchemy.use('fs');
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The "socket" stage:
|
|
10
|
-
*
|
|
11
|
-
* Create the socket.io listener
|
|
12
|
-
*
|
|
13
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
14
|
-
* @since 0.1.0
|
|
15
|
-
* @version 1.1.3
|
|
16
|
-
*/
|
|
17
|
-
alchemy.sputnik.add(function socket() {
|
|
18
|
-
|
|
19
|
-
var clientPath,
|
|
20
|
-
streamPath,
|
|
21
|
-
io;
|
|
22
|
-
|
|
23
|
-
if (!alchemy.settings.websockets) {
|
|
24
|
-
log.info('Websockets have been disabled');
|
|
25
|
-
return;
|
|
26
|
-
} else {
|
|
27
|
-
if (alchemy.settings.websockets == 'optional') {
|
|
28
|
-
log.info('Websockets have been enabled optionally');
|
|
29
|
-
} else {
|
|
30
|
-
log.info('Websockets have been enabled, clients will automatically connect');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
io = alchemy.use('socket.io');
|
|
35
|
-
|
|
36
|
-
if (!io) {
|
|
37
|
-
return log.error('Could not load socket.io!');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
iostream = alchemy.use('socket.io-stream');
|
|
41
|
-
|
|
42
|
-
// Create the Socket.io listener
|
|
43
|
-
alchemy.io = io.listen(alchemy.server, {serveClient: false});
|
|
44
|
-
|
|
45
|
-
// Get the core client path
|
|
46
|
-
clientPath = path.dirname(alchemy.findModule('socket.io-client').module_path);
|
|
47
|
-
clientPath = path.resolve(clientPath, '..', 'dist', 'socket.io.slim.js');
|
|
48
|
-
|
|
49
|
-
// Get the stream client path
|
|
50
|
-
streamPath = path.dirname(alchemy.findModule('@11ways/socket.io-stream').module_path);
|
|
51
|
-
streamPath = path.resolve(streamPath, 'socket.io-stream.js');
|
|
52
|
-
|
|
53
|
-
// Serve the socket io core file
|
|
54
|
-
Router.use('/scripts/socket.io.js', function getSocketIo(req, res, next) {
|
|
55
|
-
alchemy.minifyScript(clientPath, function gotMinifiedPath(err, mpath) {
|
|
56
|
-
req.conduit.serveFile(mpath || clientPath);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Serve the socket io stream file
|
|
61
|
-
Router.use('/scripts/socket.io-stream.js', function getSocketStream(req, res, next) {
|
|
62
|
-
alchemy.minifyScript(streamPath, function gotMinifiedPath(err, mpath) {
|
|
63
|
-
req.conduit.serveFile(mpath || streamPath);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Handle connections
|
|
69
|
-
*
|
|
70
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
71
|
-
* @since 0.0.1
|
|
72
|
-
* @version 0.2.0
|
|
73
|
-
*/
|
|
74
|
-
alchemy.io.sockets.on('connection', function onConnect(socket){
|
|
75
|
-
|
|
76
|
-
var syncs = {},
|
|
77
|
-
latencies = [],
|
|
78
|
-
latency_avg = 2,
|
|
79
|
-
offset = 0;
|
|
80
|
-
|
|
81
|
-
socket.on('timesync', function gotTimesyncRequest(data) {
|
|
82
|
-
|
|
83
|
-
var received = Date.now(),
|
|
84
|
-
latency;
|
|
85
|
-
|
|
86
|
-
// This is the initial request
|
|
87
|
-
if (data.count == null) {
|
|
88
|
-
data.count = 0;
|
|
89
|
-
data.latency_trip = 0;
|
|
90
|
-
|
|
91
|
-
// Reset the latencies array
|
|
92
|
-
latencies.length = 0;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Do 8 round trips to determine latency
|
|
96
|
-
if (data.latency_trip <= 8) {
|
|
97
|
-
|
|
98
|
-
if (data.last_sent) {
|
|
99
|
-
|
|
100
|
-
// Latency is the timestamp when we received the response
|
|
101
|
-
// minus the timestamp when we sent the request
|
|
102
|
-
latency = received - data.last_sent;
|
|
103
|
-
latencies.push(latency);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Wait before responding
|
|
107
|
-
setTimeout(function waitForLatency() {
|
|
108
|
-
data.last_sent = Date.now();
|
|
109
|
-
socket.emit('timesync', data);
|
|
110
|
-
}, 100 + (data.latency_trip * 150));
|
|
111
|
-
|
|
112
|
-
data.latency_trip++;
|
|
113
|
-
} else if (data.latency_trip) {
|
|
114
|
-
|
|
115
|
-
latency_avg = ~~(Math.median(latencies) / 2);
|
|
116
|
-
offset = (received - latency_avg) - data.client_time;
|
|
117
|
-
|
|
118
|
-
socket.emit('timesync', {offset: offset, latency: latency_avg});
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Wait for the announcement
|
|
123
|
-
socket.once('announce', function gotAnnouncement(data) {
|
|
124
|
-
|
|
125
|
-
var SocketClass,
|
|
126
|
-
class_name;
|
|
127
|
-
|
|
128
|
-
// Try getting the socket class of this type
|
|
129
|
-
if (typeof data.type == 'string') {
|
|
130
|
-
class_name = data.type.classify();
|
|
131
|
-
SocketClass = Classes.Alchemy.Conduit[class_name + 'Socket'];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// If no socket class was found get the regular class
|
|
135
|
-
if (!SocketClass) {
|
|
136
|
-
SocketClass = Classes.Alchemy.Conduit.Socket;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
new SocketClass(socket, data);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Connect to another alchemy instance
|
|
146
|
-
*
|
|
147
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
148
|
-
* @since 0.1.0
|
|
149
|
-
* @version 0.4.0
|
|
150
|
-
*/
|
|
151
|
-
Alchemy.setMethod(function callServer(address, data, callback) {
|
|
152
|
-
|
|
153
|
-
var server = new Classes.ClientSocket();
|
|
154
|
-
|
|
155
|
-
server.reconnect = false;
|
|
156
|
-
|
|
157
|
-
server.connect(address, data, callback);
|
|
158
|
-
|
|
159
|
-
return server;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var types = alchemy.shared('Socket.types'),
|
|
4
|
+
iostream,
|
|
5
|
+
path = alchemy.use('path'),
|
|
6
|
+
fs = alchemy.use('fs');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The "socket" stage:
|
|
10
|
+
*
|
|
11
|
+
* Create the socket.io listener
|
|
12
|
+
*
|
|
13
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
14
|
+
* @since 0.1.0
|
|
15
|
+
* @version 1.1.3
|
|
16
|
+
*/
|
|
17
|
+
alchemy.sputnik.add(function socket() {
|
|
18
|
+
|
|
19
|
+
var clientPath,
|
|
20
|
+
streamPath,
|
|
21
|
+
io;
|
|
22
|
+
|
|
23
|
+
if (!alchemy.settings.websockets) {
|
|
24
|
+
log.info('Websockets have been disabled');
|
|
25
|
+
return;
|
|
26
|
+
} else {
|
|
27
|
+
if (alchemy.settings.websockets == 'optional') {
|
|
28
|
+
log.info('Websockets have been enabled optionally');
|
|
29
|
+
} else {
|
|
30
|
+
log.info('Websockets have been enabled, clients will automatically connect');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
io = alchemy.use('socket.io');
|
|
35
|
+
|
|
36
|
+
if (!io) {
|
|
37
|
+
return log.error('Could not load socket.io!');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
iostream = alchemy.use('socket.io-stream');
|
|
41
|
+
|
|
42
|
+
// Create the Socket.io listener
|
|
43
|
+
alchemy.io = io.listen(alchemy.server, {serveClient: false});
|
|
44
|
+
|
|
45
|
+
// Get the core client path
|
|
46
|
+
clientPath = path.dirname(alchemy.findModule('socket.io-client').module_path);
|
|
47
|
+
clientPath = path.resolve(clientPath, '..', 'dist', 'socket.io.slim.js');
|
|
48
|
+
|
|
49
|
+
// Get the stream client path
|
|
50
|
+
streamPath = path.dirname(alchemy.findModule('@11ways/socket.io-stream').module_path);
|
|
51
|
+
streamPath = path.resolve(streamPath, 'socket.io-stream.js');
|
|
52
|
+
|
|
53
|
+
// Serve the socket io core file
|
|
54
|
+
Router.use('/scripts/socket.io.js', function getSocketIo(req, res, next) {
|
|
55
|
+
alchemy.minifyScript(clientPath, function gotMinifiedPath(err, mpath) {
|
|
56
|
+
req.conduit.serveFile(mpath || clientPath);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Serve the socket io stream file
|
|
61
|
+
Router.use('/scripts/socket.io-stream.js', function getSocketStream(req, res, next) {
|
|
62
|
+
alchemy.minifyScript(streamPath, function gotMinifiedPath(err, mpath) {
|
|
63
|
+
req.conduit.serveFile(mpath || streamPath);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Handle connections
|
|
69
|
+
*
|
|
70
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
71
|
+
* @since 0.0.1
|
|
72
|
+
* @version 0.2.0
|
|
73
|
+
*/
|
|
74
|
+
alchemy.io.sockets.on('connection', function onConnect(socket){
|
|
75
|
+
|
|
76
|
+
var syncs = {},
|
|
77
|
+
latencies = [],
|
|
78
|
+
latency_avg = 2,
|
|
79
|
+
offset = 0;
|
|
80
|
+
|
|
81
|
+
socket.on('timesync', function gotTimesyncRequest(data) {
|
|
82
|
+
|
|
83
|
+
var received = Date.now(),
|
|
84
|
+
latency;
|
|
85
|
+
|
|
86
|
+
// This is the initial request
|
|
87
|
+
if (data.count == null) {
|
|
88
|
+
data.count = 0;
|
|
89
|
+
data.latency_trip = 0;
|
|
90
|
+
|
|
91
|
+
// Reset the latencies array
|
|
92
|
+
latencies.length = 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Do 8 round trips to determine latency
|
|
96
|
+
if (data.latency_trip <= 8) {
|
|
97
|
+
|
|
98
|
+
if (data.last_sent) {
|
|
99
|
+
|
|
100
|
+
// Latency is the timestamp when we received the response
|
|
101
|
+
// minus the timestamp when we sent the request
|
|
102
|
+
latency = received - data.last_sent;
|
|
103
|
+
latencies.push(latency);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Wait before responding
|
|
107
|
+
setTimeout(function waitForLatency() {
|
|
108
|
+
data.last_sent = Date.now();
|
|
109
|
+
socket.emit('timesync', data);
|
|
110
|
+
}, 100 + (data.latency_trip * 150));
|
|
111
|
+
|
|
112
|
+
data.latency_trip++;
|
|
113
|
+
} else if (data.latency_trip) {
|
|
114
|
+
|
|
115
|
+
latency_avg = ~~(Math.median(latencies) / 2);
|
|
116
|
+
offset = (received - latency_avg) - data.client_time;
|
|
117
|
+
|
|
118
|
+
socket.emit('timesync', {offset: offset, latency: latency_avg});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Wait for the announcement
|
|
123
|
+
socket.once('announce', function gotAnnouncement(data) {
|
|
124
|
+
|
|
125
|
+
var SocketClass,
|
|
126
|
+
class_name;
|
|
127
|
+
|
|
128
|
+
// Try getting the socket class of this type
|
|
129
|
+
if (typeof data.type == 'string') {
|
|
130
|
+
class_name = data.type.classify();
|
|
131
|
+
SocketClass = Classes.Alchemy.Conduit[class_name + 'Socket'];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// If no socket class was found get the regular class
|
|
135
|
+
if (!SocketClass) {
|
|
136
|
+
SocketClass = Classes.Alchemy.Conduit.Socket;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
new SocketClass(socket, data);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Connect to another alchemy instance
|
|
146
|
+
*
|
|
147
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
148
|
+
* @since 0.1.0
|
|
149
|
+
* @version 0.4.0
|
|
150
|
+
*/
|
|
151
|
+
Alchemy.setMethod(function callServer(address, data, callback) {
|
|
152
|
+
|
|
153
|
+
var server = new Classes.ClientSocket();
|
|
154
|
+
|
|
155
|
+
server.reconnect = false;
|
|
156
|
+
|
|
157
|
+
server.connect(address, data, callback);
|
|
158
|
+
|
|
159
|
+
return server;
|
|
160
160
|
});
|