First attempt at fixed-up js library component, inspired by Glitch's emitter pattern in #613.

This commit is contained in:
Griatch 2016-02-08 16:17:22 +01:00
parent f0c9128ff8
commit 7fa088c036

View file

@ -43,9 +43,6 @@ The client is composed of two parts:
- /server/portal/websocket_client.py - the portal-side component - /server/portal/websocket_client.py - the portal-side component
- this file - the javascript component handling dynamic content - this file - the javascript component handling dynamic content
A
messages sent to the client is one of two modes: messages sent to the client is one of two modes:
OOB("func1",args, "func2",args, ...) - OOB command executions, this will OOB("func1",args, "func2",args, ...) - OOB command executions, this will
call unique javascript functions call unique javascript functions
@ -54,117 +51,152 @@ messages sent to the client is one of two modes:
*/ */
//
// Custom OOB functions
// functions defined here can be called by name by the server. For
// example input OOB{"echo":(args),{kwargs}} will trigger a function named
// echo(args, kwargs). The commands the server understands is set by
// settings.OOB_PLUGIN_MODULES
(function() { (function() {
var id = 0; var cmdid = 0;
var map = {}; var cmdmap = {};
var Evennia = { var Evennia = {
debug: true, debug: true,
// called by the frontend to send a command to the backend // Client -> Evennia.
cmd: function (cmd, params, callback) { // Called by the frontend to send a command to Evennia.
var msg = params ? [cmd, [params], {}] : [cmd, [], {}]; //
params.id = id++; // Args:
// cmdname (str): String identifier to call
log('cmd called with following args:', cmd, params, callback); // kwargs (obj): Data argument for calling as cmdname(kwargs)
// callback (func): If given, will be given an eventual return
websocket.send('OOB' + JSON.stringify(msg)); // value from the backend.
//
send: function (cmdname, kwargs, callback) {
kwargs.cmdid = cmdid++;
var data = kwargs ? [cmdname, kwargs] : [cmdname, {}];
if (typeof callback === 'function') { if (typeof callback === 'function') {
map[id] = callback; this.cmdmap[cmdid] = callback;
}
this.connection.send(JSON.stringify(kwargs));
log('cmd called with following args:', cmd, params, callback);
},
// Evennia -> Client.
// Called by the backend to emit an event to the global emitter
//
// Args:
// event (event): Event received from Evennia
// data (obj):
//
emit: function (cmdname, data) {
if (data.cmdid) {
this.cmdmap[data.cmdid].apply(this, [data]);
delete this.cmdmap[cmddata.cmdid];
}
else {
this.emitter.emit(cmdname, data);
} }
}, },
// called by the backend to emit an event to the global emitter // Initializer.
emit: function (event, data) { // startup Evennia emitter and connection.
if (data.id) { //
map[data.id].apply(this, [event, data]); // Args:
delete map[data.id]; // opts (obj):
} // emitter - custom emitter. If not given,
Evennia.emitter.emit(event, data); // will use a default emitter. Must have
}, // an "emit" function.
// connection - This defaults to a WebsocketConnection,
// startup Evennia emitter and connection // but could also be the CometConnection or another
init: function (opts) { // custom protocol. Must have a 'send' method and
opts = opts || {}; // make use of Evennia.emit to return data to Client.
Evennia.emitter = opts.emitter || new Emitter(); //
Evennia.websocket = new Connection(); init: function (kwargs) {
kwargs = kwargs || {};
this.emitter = kwargs.emitter || new DefaultEmitter();
this.connection = kwargs.connection || new WebsocketConnection();
} }
}; };
// wrapper for websocket setup // Websocket Connector
var Connection = function () { //
var WebsocketConnection = function () {
var websocket = new WebSocket(wsurl); var websocket = new WebSocket(wsurl);
websocket.onopen = function (event) { // Handle Websocket open event
this.websocket.onopen = function (event) {
log('Websocket connection openened.');
Evennia.emit('socket:open', event); Evennia.emit('socket:open', event);
}; };
websocket.onclose = function (event) { // Handle Websocket close event
log('WebSocket connection closed.') this.websocket.onclose = function (event) {
log('WebSocket connection closed.');
Evennia.emit('socket:close', event); Evennia.emit('socket:close', event);
}; };
websocket.onmessage = function (event) { // Handle websocket errors
if (typeof event.data !== 'string' && event.data.length < 0) { this.websocket.onerror = function (event) {
return;
}
// only acceptable mode is OOB
var mode = event.data.substr(0, 3);
if (mode === 'OOB') {
// parse the rest of the response
var res = JSON.parse(event.data.substr(3));
log(res);
var cmd = res[0];
var args = res[1];
Evennia.emit(cmd, args[0]);
}
};
websocket.onerror = function (event) {
log("Websocket error to ", wsurl, event); log("Websocket error to ", wsurl, event);
Evennia.emit('socket:error', data); Evennia.emit('socket:error', data);
}; };
// Handle incoming websocket data
this.websocket.onmessage = function (event) {
var data = event.data
if (typeof data !== 'string' && data.length < 0) {
return;
}
// Parse the incoming data, send to emitter
// Incoming data is on the form [cmdname, kwargs]
data = JSON.parse(data);
Evennia.emit(data[0], data[1]]);
};
return websocket; return websocket;
} }
// basic emitter, can be overridden in evennia init // AJAX/COMET Connector
var Emitter = function () { //
var map = {}; CometConnection = function() {
};
// Basic emitter to distribute data being sent to the client from
// the Server. An alternative can be overridden in Evennia.init.
//
var DefaultEmitter = function () {
var cmdmap = {};
// emit to listeners of given event // Emit data to all listeners tied to a given cmdname
var emit = function (event, params) { //
log('emit', event, params); // Args:
if (map[event] && map[event] === Array) { // cmdname (str): Name of command, used to find
map[event].forEach(function (val) { // all listeners to this call; each will be
val.apply(this, params); // called as function(kwargs).
}); // kwargs (obj): Argument to the listener.
//
var emit = function (cmdname, kwargs) {
log('emit', cmdname, kwargs);
if (this.cmdmap[cmdname]) {
this.cmdmap[cmdname].apply(this, kwargs);
};
} }
}; };
// bind listener to event, only allow one instance of handler // Bind listener to event
var on = function (event, handler) { //
if (!map[event]) { // Args:
map[event] = []; // cmdname (str): Name of event to handle.
// listener (function): Function taking one argument,
// to listen to cmdname events.
//
var on = function (cmdname, listener) {
if typeof(listener === 'function') {
this.cmdmap[cmdname] = listener;
} }
if (map[event].indexOf(handler) >= 0) {
return;
}
map[event].push(handler);
}; };
// remove handler from event // remove handling of this cmdname
var off = function (event, handler) { //
if (map[event]) { // Args:
var listener = map[event].indexOf(handler); // cmdname (str): Name of event to handle
if (listener >= 0) { //
map[event].splice(listener, 1); var off = function (cmdname) {
} delete this.cmdmap[cmdname]
} }
}; };
@ -178,13 +210,17 @@ messages sent to the client is one of two modes:
window.Evennia = Evennia; window.Evennia = Evennia;
})(); })();
function log() { // helper logging function
// Args:
// msg (str): Message to log to console.
//
function log(msg) {
if (Evennia.debug) { if (Evennia.debug) {
console.log(arguments); console.log(msg);
} }
} }
// Callback function - called when page has finished loading (kicks the client into gear) // Called when page has finished loading (kicks the client into gear)
$(document).ready(function(){ $(document).ready(function(){
// remove the "no javascript" warning, since we obviously have javascript // remove the "no javascript" warning, since we obviously have javascript
$('#noscript').remove(); $('#noscript').remove();