nodegame-widgets
Version:
Collections of useful and reusable javascript / HTML snippets for nodeGame
372 lines (323 loc) • 11.5 kB
JavaScript
/**
* # DebugWall
* Copyright(c) 2021 Stefano Balietti
* MIT Licensed
*
* Creates a wall where all incoming and outgoing messages are printed
*
* www.nodegame.org
*/
(function(node) {
"use strict";
node.widgets.register('DebugWall', DebugWall);
// ## Meta-data
DebugWall.version = '1.1.0';
DebugWall.description = 'Intercepts incoming and outgoing messages, and ' +
'logs and prints them numbered and timestamped. Warning! Modifies ' +
'core functions, therefore its usage in production is ' +
'not recommended.';
DebugWall.title = 'Debug Wall';
DebugWall.className = 'debugwall';
// ## Dependencies
DebugWall.dependencies = {
JSUS: {}
};
/**
* ## DebugWall constructor
*
* Creates a new DebugWall oject
*/
function DebugWall() {
/**
* ### DebugWall.buttonsDiv
*
* Div contains controls for the display info inside the wall.
*/
this.buttonsDiv = null;
/**
* ### DebugWall.hidden
*
* Keep tracks of what is hidden in the wall
*/
this.hiddenTypes = {};
/**
* ### DebugWall.counterIn
*
* Counts number of incoming message printed on wall
*/
this.counterIn = 0;
/**
* ### DebugWall.counterOut
*
* Counts number of outgoing message printed on wall
*/
this.counterOut = 0;
/**
* ### DebugWall.counterLog
*
* Counts number of log entries printed on wall
*/
this.counterLog = 0;
/**
* ### DebugWall.wall
*
* The table element in which to write
*/
this.wall = null;
/**
* ### DebugWall.wallDiv
*
* The div element containing the wall (for scrolling)
*/
this.wallDiv = null;
/**
* ### DebugWall.origMsgInCb
*
* The original function that receives incoming msgs
*/
this.origMsgInCb = null;
/**
* ### DebugWall.origMsgOutCb
*
* The original function that sends msgs
*/
this.origMsgOutCb = null;
/**
* ### DebugWall.origLogCb
*
* The original log callback
*/
this.origLogCb = null;
}
// ## DebugWall methods
/**
* ### DebugWall.init
*
* Initializes the instance
*
* @param {object} opts Optional. Configuration options
*
* - msgIn: If FALSE, incoming messages are ignored.
* - msgOut: If FALSE, outgoing messages are ignored.
* - log: If FALSE, log messages are ignored.
* - hiddenTypes: An object containing what is currently hidden
* in the wall.
*/
DebugWall.prototype.init = function(opts) {
var that;
that = this;
if (opts.msgIn !== false) {
this.origMsgInCb = node.socket.onMessage;
node.socket.onMessage = function(msg) {
that.write('in', that.makeTextIn(msg));
that.origMsgInCb.call(node.socket, msg);
};
}
if (opts.msgOut !== false) {
this.origMsgOutCb = node.socket.send;
node.socket.send = function(msg) {
that.write('out', that.makeTextOut(msg));
that.origMsgOutCb.call(node.socket, msg);
};
}
if (opts.log !== false) {
this.origLogCb = node.log;
node.log = function(txt, level, prefix) {
that.write(level || 'info',
that.makeTextLog(txt, level, prefix));
that.origLogCb.call(node, txt, level, prefix);
};
}
if (opts.hiddenTypes) {
if ('object' !== typeof opts.hiddenTypes) {
throw new TypeError('DebugWall.init: hiddenTypes must be ' +
'object. Found: ' + opts.hiddenTypes);
}
this.hiddenTypes = opts.hiddenTypes;
}
this.on('destroyed', function() {
if (that.origLogCb) node.log = that.origLogCb;
if (that.origMsgOutCb) node.socket.send = that.origMsgOutCb;
if (that.origMsgInCb) node.socket.onMessage = that.origMsgInCb;
});
};
DebugWall.prototype.append = function() {
var displayIn, displayOut, displayLog, that;
var btnGroup, cb;
this.buttonsDiv = W.add('div', this.bodyDiv, {
className: 'wallbuttonsdiv'
});
btnGroup = W.add('div', this.buttonsDiv, {
className: 'btn-group',
role: 'group',
'aria-label': 'Toggle visibility of messages on wall'
});
// Incoming.
W.add('input', btnGroup, {
id: 'debug-wall-incoming',
// name: 'debug-wall-check',
className: 'btn-check',
autocomplete: "off",
checked: true,
type: 'checkbox'
});
displayIn = W.add('label', btnGroup, {
className: "btn btn-outline-primary",
'for': "debug-wall-incoming",
innerHTML: 'Incoming'
});
// Outgoing.
W.add('input', btnGroup, {
id: 'debug-wall-outgoing',
className: 'btn-check',
// name: 'debug-wall-check',
autocomplete: "off",
checked: true,
type: 'checkbox'
});
displayOut = W.add('label', btnGroup, {
className: "btn btn-outline-primary",
'for': "debug-wall-outgoing",
innerHTML: 'Outgoing'
});
// Log.
W.add('input', btnGroup, {
id: 'debug-wall-log',
className: 'btn-check',
// name: 'debug-wall-check',
autocomplete: "off",
checked: true,
type: 'checkbox'
});
displayLog = W.add('label', btnGroup, {
className: "btn btn-outline-primary",
'for': "debug-wall-log",
innerHTML: 'Log'
});
that = this;
W.add('button', this.buttonsDiv, {
className: "btn btn-outline-danger me-2",
innerHTML: 'Clear'
})
.onclick = function() { that.clear(); };
this.buttonsDiv.appendChild(btnGroup);
cb = function(type) {
var items, i, vis, className;
className = 'wall_' + type;
items = that.wall.getElementsByClassName(className);
if (!items || !items.length) return;
vis = items[0].style.display === '' ? 'none' : '';
for (i = 0; i < items.length; i++) {
items[i].style.display = vis;
}
that.hiddenTypes[type] = !!vis;
};
displayIn.onclick = function() { cb('in'); };
displayOut.onclick = function() { cb('out'); };
displayLog.onclick = function() { cb('log'); };
this.wallDiv = W.add('div', this.bodyDiv, { className: 'walldiv' });
this.wall = W.add('table', this.wallDiv);
};
/**
* ### DebugWall.write
*
* Writes argument as first entry of this.wall if document is fully loaded
*
* @param {string} type 'in', 'out', or 'log' (different levels)
* @param {string} text The text to write
*/
DebugWall.prototype.shouldHide = function(type) {
return this.hiddenTypes[type];
};
/**
* ### DebugWall.write
*
* Writes argument as first entry of this.wall if document is fully loaded
*
* @param {string} type 'in', 'out', or 'log' (different levels)
* @param {string} text The text to write
*/
DebugWall.prototype.clear = function() {
this.wall.innerHTML = '';
};
/**
* ### DebugWall.write
*
* Writes argument as first entry of this.wall if document is fully loaded
*
* @param {string} type 'in', 'out', or 'log' (different levels)
* @param {string} text The text to write
*/
DebugWall.prototype.write = function(type, text) {
var spanContainer, spanDots, spanExtra, counter, className;
var limit;
var TR, TDtext;
if (this.isAppended()) {
counter = type === 'in' ? ++this.counterIn :
(type === 'out' ? ++this.counterOut : ++this.counterLog);
limit = 200;
className = 'wall_' + type;
TR = W.add('tr', this.wall, { className: className });
if (type !== 'in' && type !== 'out') TR.className += ' wall_log';
if (this.shouldHide(type, text)) TR.style.display = 'none';
W.add('td', TR, { innerHTML: counter });
W.add('td', TR, { innerHTML: type });
W.add('td', TR, { innerHTML: J.getTimeM()});
TDtext = W.add('td', TR);
if (text.length > limit) {
spanContainer = W.add('span', TDtext, {
className: className + '_click' ,
innerHTML: text.substr(0, limit)
});
spanExtra = W.add('span', spanContainer, {
className: className + '_extra',
innerHTML: text.substr(limit, text.length),
id: 'wall_' + type + '_' + counter,
style: { display: 'none' }
});
spanDots = W.add('span', spanContainer, {
className: className + '_dots',
innerHTML: ' ...',
id: 'wall_' + type + '_' + counter
});
spanContainer.onclick = function() {
if (spanDots.style.display === 'none') {
spanDots.style.display = '';
spanExtra.style.display = 'none';
}
else {
spanDots.style.display = 'none';
spanExtra.style.display = '';
}
};
}
else {
spanContainer = W.add('span', TDtext, {
innerHTML: text
});
}
this.wallDiv.scrollTop = this.wallDiv.scrollHeight;
}
else {
node.warn('Wall not appended, cannot write.');
}
};
DebugWall.prototype.makeTextIn = function(msg) {
var text, d;
d = new Date(msg.created);
text = d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() +
':' + d.getMilliseconds();
text += ' | ' + msg.to + ' | ' + msg.target +
' | ' + msg.action + ' | ' + msg.text + ' | ' + msg.data;
return text;
};
DebugWall.prototype.makeTextOut = function(msg) {
var text;
text = msg.from + ' | ' + msg.target + ' | ' + msg.action + ' | ' +
msg.text + ' | ' + msg.data;
return text;
};
DebugWall.prototype.makeTextLog = function(text) {
return text;
};
})(node);