commit
399236f7b5
@ -0,0 +1 @@
|
|||||||
|
backend/
|
@ -0,0 +1,8 @@
|
|||||||
|
Set your reverse proxy to serve www/ statically.
|
||||||
|
Edit config.js to specify the parameters of the node (api) server, and
|
||||||
|
point it to the backend (rtbwpy)
|
||||||
|
Set your reverse proxy to pass config.BASE_URL* to the node server (make
|
||||||
|
sure websockets are sent too)
|
||||||
|
Edit www/js/client.js to point to the correct api location.
|
||||||
|
Start the backend (rtbwpy) in daemon mode.
|
||||||
|
Start the node server
|
@ -0,0 +1,15 @@
|
|||||||
|
var config = {
|
||||||
|
PORT: 8001,
|
||||||
|
POLL_INTERVAL: 10000,
|
||||||
|
|
||||||
|
URL_BASE: "/rtbw",
|
||||||
|
|
||||||
|
PYTHON_EXECUTABLE: "python3",
|
||||||
|
RTBWCTL_FILENAME: "./backend/rtbwctl.py",
|
||||||
|
BACKEND_SOCKET: "./backend/rtbw.sock",
|
||||||
|
|
||||||
|
BUFFER_PURGE_TIME: 300000,//20000,//300000, //5 minutes
|
||||||
|
SUPERBUFFER_PURGE_TIME: 3000000//3000000 //50 minutes
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
@ -0,0 +1,42 @@
|
|||||||
|
var jsoncompress = require('jsoncompress');
|
||||||
|
var cycleAdd = require("./interop").cycleAdd;
|
||||||
|
var cycleAcc = require("./interop").cycleAcc;
|
||||||
|
|
||||||
|
function Cycle()
|
||||||
|
{
|
||||||
|
this.last =0; //last post number of this cycle
|
||||||
|
|
||||||
|
this.number = 0; //number of new posts this cycle
|
||||||
|
this.countries = {}; //stored as <code>: <number of times>
|
||||||
|
this.timestamp = new Date()
|
||||||
|
|
||||||
|
this.threads=0; //number of new threads
|
||||||
|
this.interval=0; //average time between posts
|
||||||
|
this.comments=0; //number of posts with comments
|
||||||
|
this.subjects=0; //number of posts with subjects
|
||||||
|
this.images=0; //number of posts with images
|
||||||
|
this.names=0;//number of posts with names
|
||||||
|
this.trips = 0;//number of posts with tripcodes
|
||||||
|
this.nametrips = 0; //number of name+tripfags
|
||||||
|
this.ids =0; //post ids
|
||||||
|
}
|
||||||
|
|
||||||
|
Cycle.prototype.serialise = function() {
|
||||||
|
return jsoncompress.compress(this, new Cycle());
|
||||||
|
};
|
||||||
|
|
||||||
|
Cycle.unserialise = function(data) {
|
||||||
|
return jsoncompress.decompress(data, new Cycle());
|
||||||
|
};
|
||||||
|
|
||||||
|
Cycle.accumulate = function(ar) {
|
||||||
|
var ret = new Cycle();
|
||||||
|
return cycleAdd(ret, ar);
|
||||||
|
};
|
||||||
|
|
||||||
|
Cycle.average = function(st, ar, n) {
|
||||||
|
var ret = st;
|
||||||
|
return cycleAcc(ret, ar,n);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Cycle = Cycle;
|
@ -0,0 +1,39 @@
|
|||||||
|
const Cycle = require("./cycle").Cycle;
|
||||||
|
const logger = require("./logger");
|
||||||
|
|
||||||
|
class CycleBuffer {
|
||||||
|
constructor(purgeTime) {
|
||||||
|
this.time = purgeTime;
|
||||||
|
this.buffer=[];
|
||||||
|
var th = this;
|
||||||
|
this.onclear = function(sp) {};
|
||||||
|
this.inter = setInterval(function() {
|
||||||
|
th.purge();
|
||||||
|
}, this.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
purge()
|
||||||
|
{
|
||||||
|
this.onclear(this);
|
||||||
|
this.buffer.length=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(cycle) {
|
||||||
|
this.buffer.push(cycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
all() {
|
||||||
|
var ret=[];
|
||||||
|
for(var i=0;i<this.buffer.length;i++)
|
||||||
|
{
|
||||||
|
ret.push(this.buffer[i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
close() {
|
||||||
|
clearInterval(this.inter);
|
||||||
|
this.buffer.length=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CycleBuffer;
|
@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
function cycleAdd(rets, ar)
|
||||||
|
{
|
||||||
|
var ret = rets;
|
||||||
|
for(var i=0;i<ar.length;i++)
|
||||||
|
{
|
||||||
|
if(ar[i].last>ret.last) ret.last = ar[i].last;
|
||||||
|
ret.number+=ar[i].number;
|
||||||
|
|
||||||
|
for(var index in ar[i].countries)
|
||||||
|
{
|
||||||
|
if(index in ret.countries)
|
||||||
|
ret.countries[index]+=ar[i].countries[index];
|
||||||
|
else
|
||||||
|
ret.countries[index]= ar[i].countries[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.threads+=ar[i].threads;
|
||||||
|
ret.comments+=ar[i].comments;
|
||||||
|
ret.subjects+=ar[i].subjects;
|
||||||
|
ret.images+=ar[i].images;
|
||||||
|
ret.names+=ar[i].names;
|
||||||
|
ret.trips+=ar[i].trips;
|
||||||
|
ret.nametrips+=ar[i].nametrips;
|
||||||
|
ret.ids+=ar[i].ids;
|
||||||
|
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function acc(a, m, n)
|
||||||
|
{
|
||||||
|
return ((a*n)+m)/(n+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cycleAcc(rets, ar, n)
|
||||||
|
{
|
||||||
|
var ret = rets;
|
||||||
|
for(var i in ar)
|
||||||
|
{
|
||||||
|
if(ar[i].last>ret.last) ret.last = ar[i].last;
|
||||||
|
ret.number= acc(ret.number, ar[i].number, n);
|
||||||
|
|
||||||
|
/*for(var index in ar[i].countries)
|
||||||
|
{
|
||||||
|
if(index in ret.countries)
|
||||||
|
ret.countries[index]= acc(ret.countries[index], ar[i].countries[index], n);
|
||||||
|
else
|
||||||
|
ret.countries[index]= acc(0, ar[i].countries[index], n);
|
||||||
|
}*/ //TODO: Implement this
|
||||||
|
|
||||||
|
ret.threads= acc(ret.threads, ar[i].threads, n);
|
||||||
|
ret.comments = acc(ret.comments, ar[i].comments, n);
|
||||||
|
ret.subjects = acc(ret.subjects, ar[i].subjects, n);
|
||||||
|
ret.images = acc(ret.images, ar[i].images, n);
|
||||||
|
ret.names = acc(ret.names, ar[i].names, n);
|
||||||
|
ret.trips = acc(ret.trips, ar[i].trips, n);
|
||||||
|
ret.nametrips = acc(ret.nametrips, ar[i].nametrips, n);
|
||||||
|
ret.ids = acc(ret.ids, ar[i].ids, n);
|
||||||
|
|
||||||
|
n+=1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(typeof module !== 'undefined') {
|
||||||
|
module.exports.cycleAdd = cycleAdd;
|
||||||
|
module.exports.cycleAcc = cycleAcc;
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
module.exports.log = function log(message) {
|
||||||
|
console.log("["+new Date().toISOString()+"]: "+message);
|
||||||
|
}
|
@ -0,0 +1,289 @@
|
|||||||
|
var http = require('http');
|
||||||
|
var process = require('child_process');
|
||||||
|
var WebSocketServer = require("ws").Server;
|
||||||
|
var events = require("events");
|
||||||
|
var jsoncompress = require('jsoncompress');
|
||||||
|
|
||||||
|
|
||||||
|
var config = require('./config');
|
||||||
|
var logger = require("./logger");
|
||||||
|
var _cycle = require("./cycle");
|
||||||
|
var CycleBuffer = require("./graphBuffer");
|
||||||
|
|
||||||
|
/// TODO: Buffer cycles. Clear them out every 24 hours
|
||||||
|
/// TODO: Add total aggregate (url /total) that can be served
|
||||||
|
|
||||||
|
function timeAverage(times)
|
||||||
|
{
|
||||||
|
if(times.length<1) return 0;
|
||||||
|
else if(times.length==1) return times[0];
|
||||||
|
else {
|
||||||
|
var cul=0;
|
||||||
|
for(var i=0;i<times.length;i++)
|
||||||
|
{
|
||||||
|
cul+=times[i];
|
||||||
|
}
|
||||||
|
return cul/times.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function diff(t1, t2)
|
||||||
|
{
|
||||||
|
if( t1>t2) return t1-t2;
|
||||||
|
else return t2-t1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bein(hay,nee)
|
||||||
|
{
|
||||||
|
return (nee in hay) || (("has_"+nee) in hay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCandle(buffer)
|
||||||
|
{
|
||||||
|
var cdl = {
|
||||||
|
number: {
|
||||||
|
o:0,
|
||||||
|
c:0,
|
||||||
|
h:0,
|
||||||
|
l:0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(buffer.length<1) return cdl;
|
||||||
|
if(buffer.length<2) {
|
||||||
|
cdl.number.c = cdl.number.o = cdl.number.h = cdl.number.l = buffer[0].number;
|
||||||
|
return cdl;
|
||||||
|
}
|
||||||
|
cdl.number.l = -1;
|
||||||
|
for(var i in buffer)
|
||||||
|
{
|
||||||
|
if(buffer[i].number>cdl.number.h)
|
||||||
|
cdl.number.h = buffer[i].number;
|
||||||
|
if(buffer[i].number<cdl.number.l||cdl.number.l==-1)
|
||||||
|
cdl.number.l = buffer[i].number;
|
||||||
|
|
||||||
|
}
|
||||||
|
cdl.number.o = buffer[0].number;
|
||||||
|
cdl.number.c = buffer[buffer.length-1].number;
|
||||||
|
return cdl;
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastNumber = 0; /*we don't really need this, but it can fix some things*/
|
||||||
|
var lastCycle=new _cycle.Cycle();
|
||||||
|
var allTime = new _cycle.Cycle();
|
||||||
|
var allTimeAverage = new _cycle.Cycle();
|
||||||
|
var allTimeAverageN = 0;
|
||||||
|
var buffer = new CycleBuffer(config.BUFFER_PURGE_TIME);
|
||||||
|
var superbuffer = new CycleBuffer(config.SUPERBUFFER_PURGE_TIME);
|
||||||
|
|
||||||
|
var upSince = new Date();
|
||||||
|
|
||||||
|
cycleEmitter = new events.EventEmitter();
|
||||||
|
|
||||||
|
buffer.onclear = function(bu) {
|
||||||
|
logger.log("Clearing and adding supercycle");
|
||||||
|
var ac = _cycle.Cycle.accumulate(bu.buffer);
|
||||||
|
ac._cycles = bu.buffer.length;
|
||||||
|
ac._candle = createCandle(bu.buffer);
|
||||||
|
superbuffer.add(ac);
|
||||||
|
cycleEmitter.emit("newSuperCycle", ac);
|
||||||
|
};
|
||||||
|
|
||||||
|
superbuffer.onclear = function(bu) {
|
||||||
|
logger.log("Clearing superbuffer");
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.log("Buffer set to clear every "+(config.BUFFER_PURGE_TIME/1000/60)+" minutes");
|
||||||
|
logger.log("Superbuffer set to clear every "+(config.SUPERBUFFER_PURGE_TIME/1000/60)+" minutes");
|
||||||
|
|
||||||
|
|
||||||
|
var server = http.createServer(function(req, res) {
|
||||||
|
if(req.url == config.URL_BASE+"/cycle") {
|
||||||
|
logger.log("Recieved cycle request");
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(lastCycle));
|
||||||
|
}
|
||||||
|
else if(req.url == config.URL_BASE+"/info") {
|
||||||
|
logger.log("Recieved info request");
|
||||||
|
process.execFile(config.PYTHON_EXECUTABLE, [config.RTBWCTL_FILENAME, config.BACKEND_SOCKET, "info"], function(error,stdout,stderr) {
|
||||||
|
var js = undefined;
|
||||||
|
try {
|
||||||
|
js = JSON.parse(stdout);
|
||||||
|
} catch(e) {
|
||||||
|
logger.log("Recieved invalid JSON from info backend call");
|
||||||
|
res.writeHead(500);
|
||||||
|
res.end("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
interval: config.POLL_INTERVAL,
|
||||||
|
purgeInterval: config.BUFFER_PURGE_TIME,
|
||||||
|
superPurgeInterval: config.SUPERBUFFER_PURGE_TIME,
|
||||||
|
backendInterval: js.timeout,
|
||||||
|
board: js.board,
|
||||||
|
upSince: upSince
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if(req.url == config.URL_BASE+"/template")
|
||||||
|
{
|
||||||
|
logger.log("Recieved template request.");
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(new _cycle.Cycle()));
|
||||||
|
}
|
||||||
|
else if(req.url == config.URL_BASE+"/all/average")
|
||||||
|
{
|
||||||
|
logger.log("Recieved all time average request.");
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
allTimeAverage._n = allTimeAverageN;
|
||||||
|
res.end(JSON.stringify(allTimeAverage));
|
||||||
|
}
|
||||||
|
else if(req.url == config.URL_BASE+"/all")
|
||||||
|
{
|
||||||
|
logger.log("Recieved all time stats request.");
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(allTime));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(req.url == config.URL_BASE+"/buffer")
|
||||||
|
{
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(buffer.all()));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(req.url == config.URL_BASE+"/buffer/super")
|
||||||
|
{
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end(JSON.stringify(superbuffer.all()));
|
||||||
|
}
|
||||||
|
else if(req.url.startsWith(config.URL_BASE+"/buffer/"))
|
||||||
|
{
|
||||||
|
var lst = req.url.split("/").slice(-1)[0];
|
||||||
|
if(!isNaN(lst))
|
||||||
|
{
|
||||||
|
lst = Number(lst);
|
||||||
|
var bcpy = buffer.all();
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.writeHead(200);
|
||||||
|
if(bcpy.length<=lst)
|
||||||
|
res.end(JSON.stringify(bcpy));
|
||||||
|
else
|
||||||
|
res.end(JSON.stringify(bcpy.slice(-lst)));
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.writeHead(422);
|
||||||
|
res.end("Invalid request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end("404");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var wss = new WebSocketServer({
|
||||||
|
server: server,
|
||||||
|
autoAcceptConnections: false
|
||||||
|
});
|
||||||
|
|
||||||
|
wss.on('request', function(req) { //why isn't this getting called?
|
||||||
|
var url = require('url').parse(req.httpRequest.url);
|
||||||
|
|
||||||
|
if(url!=config.URL_BASE+"/cycle") return req.reject();
|
||||||
|
|
||||||
|
return req.accept();
|
||||||
|
});
|
||||||
|
|
||||||
|
wss.on('connection', function (ws) {
|
||||||
|
|
||||||
|
/*ws.on('message', function (message) {
|
||||||
|
ws.send("Received: " + message);
|
||||||
|
});*/
|
||||||
|
var f = function(cycle) {
|
||||||
|
ws.send(JSON.stringify({type: "cycle", data: cycle}));
|
||||||
|
};
|
||||||
|
var g = function(supercycle) {
|
||||||
|
ws.send(JSON.stringify({type: "supercycle", data: supercycle}));
|
||||||
|
};
|
||||||
|
logger.log("New connection");
|
||||||
|
ws.on("close", function(x,y) {
|
||||||
|
cycleEmitter.removeListener("newCycle", f);
|
||||||
|
cycleEmitter.removeListener("newSuperCycle", g);
|
||||||
|
logger.log("Connection closed");
|
||||||
|
});
|
||||||
|
cycleEmitter.addListener("newCycle", f);
|
||||||
|
cycleEmitter.addListener("newSuperCycle", g);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(config.PORT);
|
||||||
|
|
||||||
|
logger.log("Listening on port "+config.PORT);
|
||||||
|
|
||||||
|
var backend = setInterval(function() {
|
||||||
|
//logger.log("Polling backend");
|
||||||
|
process.execFile(config.PYTHON_EXECUTABLE, [config.RTBWCTL_FILENAME, config.BACKEND_SOCKET, "get-clear", "--data", lastNumber], function(error,stdout,stderr) {
|
||||||
|
var json = undefined;
|
||||||
|
try {
|
||||||
|
json = JSON.parse(stdout);
|
||||||
|
} catch(e) {
|
||||||
|
logger.log("Recieved invalid JSON from backend.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cycle = new _cycle.Cycle();
|
||||||
|
|
||||||
|
var timeInters= [];
|
||||||
|
|
||||||
|
for (var i=0; i<json.length;i++)
|
||||||
|
{
|
||||||
|
if(json[i].no>lastNumber)
|
||||||
|
lastNumber = json[i].no;
|
||||||
|
cycle.number+=1;
|
||||||
|
if(json[i].country in cycle.countries)
|
||||||
|
cycle.countries[json[i].country]+=1;
|
||||||
|
else
|
||||||
|
cycle.countries[json[i].country]=1;
|
||||||
|
if(! ("thread" in json[i]))
|
||||||
|
cycle.threads+=1;
|
||||||
|
|
||||||
|
if(i<json.length-1)
|
||||||
|
{
|
||||||
|
timeInters.push(diff(Date.parse(json[i].time), Date.parse(json[i+1].time)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bein(json[i], "com"))
|
||||||
|
cycle.comments+=1;
|
||||||
|
if(bein(json[i], "sub"))
|
||||||
|
cycle.subjects+=1;
|
||||||
|
if(bein(json[i], "id") && json[i]["id"] != "")
|
||||||
|
cycle.ids +=1;
|
||||||
|
if(bein(json[i], "filename"))
|
||||||
|
cycle.images+=1;
|
||||||
|
if(bein(json[i], "name") && bein(json[i], "trip"))
|
||||||
|
cycle.nametrips+=1;
|
||||||
|
else if(bein(json[i], "name"))
|
||||||
|
cycle.names+=1;
|
||||||
|
else if(bein(json[i], "trip"))
|
||||||
|
cycle.trips+=1;
|
||||||
|
}
|
||||||
|
cycle.interval = timeAverage(timeInters);
|
||||||
|
cycle.last = lastNumber;
|
||||||
|
|
||||||
|
lastCycle=cycle;
|
||||||
|
buffer.add(cycle);
|
||||||
|
allTime = _cycle.Cycle.accumulate([allTime, cycle]);
|
||||||
|
allTimeAverage = _cycle.Cycle.average(allTimeAverage, [cycle], allTimeAverageN);
|
||||||
|
allTimeAverageN +=1;
|
||||||
|
cycleEmitter.emit("newCycle", lastCycle);
|
||||||
|
});
|
||||||
|
|
||||||
|
}, config.POLL_INTERVAL);
|
||||||
|
|
@ -0,0 +1,152 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="/js/jquery.js"></script>
|
||||||
|
<script src="./js/moment.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.js"></script>
|
||||||
|
<script src="./js/Chart.Financial.js" type="text/javascript"></script>
|
||||||
|
<title>rtbw readout</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload='begin();'>
|
||||||
|
<div id='status' style="position: fixed; right: 10; top: 5; font-style: italic">Connecting...</div>
|
||||||
|
|
||||||
|
<h1>Real-time board watcher readout</h1>
|
||||||
|
<p>
|
||||||
|
Spidering board: <em id='board'></em><br />
|
||||||
|
Server up since: <em id='uptime'></em><br />
|
||||||
|
Cycle interval: <em id='interval'></em><br />
|
||||||
|
Archiver interval: <em id='binterval'></em><br />
|
||||||
|
Server buffer purge interval: <em id='pinterval'></em><br />
|
||||||
|
Server aggregate buffer purge interval: <em id='spinterval'></em><br />
|
||||||
|
</p>
|
||||||
|
<hr></hr>
|
||||||
|
<h2>Total uptime stats</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Total server cycles: <em id='at_upcycle'></em></li>
|
||||||
|
<li>Last no.: <em id='at_last'></em></li>
|
||||||
|
<li>Posts: <em id='at_posts'></em></li><!-- TODO: fill this out></-->
|
||||||
|
<li>Flag share: <em id='at_flagshare'></em> <a class='expand' id='at_flagshare_exp'>+</a></li>
|
||||||
|
<li>Avg. posts per cycle: <em id='at_avg'>0</em></li>
|
||||||
|
</ul>
|
||||||
|
<div style='padding-left: 10px'>
|
||||||
|
<div hidden id='at_flagshare_container' style="border:solid; overflow-y: auto; height: 200px;">
|
||||||
|
<ul>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Posts by flag</h3>
|
||||||
|
<div style="height: 340px; ">
|
||||||
|
<div class='contain' style="height: 300px; width:80%; float:left;">
|
||||||
|
<canvas id='tPostsByFlag'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:right;">
|
||||||
|
<canvas id='tPostsByFlagPie'></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Other shares</h3>
|
||||||
|
<div style="height: 340px;">
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tNmaes'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tImages'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tThreads'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tIds'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tSubjects'></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr></hr>
|
||||||
|
<h2>Session stats</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Session started: <em id='s_started'></em></li>
|
||||||
|
<li>Total session cycles: <em id='s_upcycle'></em></li>
|
||||||
|
<li>Posts: <em id='s_posts'></em></li><!-- TODO: fill this out></-->
|
||||||
|
<li>Flag share: <em id='s_flagshare'></em> <a class='expand' id='s_flagshare_exp'>+</a></li>
|
||||||
|
<li>Avg. posts per cycle: <em id='s_avg'>0</em></li><!--TODO: Move this into another `This session' section></-->
|
||||||
|
</ul>
|
||||||
|
<div style='padding-left: 10px'>
|
||||||
|
<div hidden id='s_flagshare_container' style="border:solid; overflow-y: auto; height: 200px;">
|
||||||
|
<ul>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Posts by flag</h3>
|
||||||
|
<div style="height: 340px; ">
|
||||||
|
<div class='contain' style="height: 300px; width:80%; float:left;">
|
||||||
|
<canvas id='tsPostsByFlag'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:right;">
|
||||||
|
<canvas id='tsPostsByFlagPie'></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Other shares</h3>
|
||||||
|
<div style="height: 340px;">
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tsNmaes'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tsImages'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tsThreads'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tsIds'></canvas>
|
||||||
|
</div>
|
||||||
|
<div class='contain' style="height: 300px; width:20%; float:left;">
|
||||||
|
<canvas id='tsSubjects'></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Average comparisons</h3>
|
||||||
|
<div class='contain' style="height: 300px; width:100%;">
|
||||||
|
<canvas id='tsCycleSpeed'></canvas>
|
||||||
|
</div>
|
||||||
|
<hr></hr>
|
||||||
|
<h2>Aggregate stats</h2>
|
||||||
|
<div class='contain' style="height: 300px; width:100%;">
|
||||||
|
<canvas id='tAgCandle'></canvas>
|
||||||
|
</div>
|
||||||
|
<hr></hr>
|
||||||
|
<h2>Live stats</h2>
|
||||||
|
<div class='contain' id='cycleSpeedC' style='overflox-x: scroll; position: relative; width: 100%; height: 300px;'>
|
||||||
|
<canvas id='cycleSpeed'></canvas>
|
||||||
|
</div>
|
||||||
|
<!-- style='position: absolute; left: 0; top: 0;pointer-events:none;'></-->
|
||||||
|
<div class='contain' style="height: 300px; width:100%;">
|
||||||
|
<canvas id='cycleBar'></canvas>
|
||||||
|
</div>
|
||||||
|
<div style="overflow-y: auto; height: 200px; border: solid;">
|
||||||
|
<table id='cytb' style='width: 100%;'>
|
||||||
|
<thead>
|
||||||
|
<tr id='cytb_headers'>
|
||||||
|
<th>Timestamp</th>
|
||||||
|
<th>Last no.</th>
|
||||||
|
<th>Posts</th>
|
||||||
|
<th>% of avg.</th>
|
||||||
|
<th>Flag share</th>
|
||||||
|
<th>Names</th>
|
||||||
|
<th>Trips</th>
|
||||||
|
<th>Names+trips</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id='cytb_body'>
|
||||||
|
<tr hidden></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="js/cyclefunc.js"></script>
|
||||||
|
<script src="js/client.js"></script>
|
||||||
|
<script src="js/renderer.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,501 @@
|
|||||||
|
/*!
|
||||||
|
* chartjs-chart-financial
|
||||||
|
* Version: 0.1.0
|
||||||
|
*
|
||||||
|
* Copyright 2017 chartjs-chart-financial contributors
|
||||||
|
* Released under the MIT license
|
||||||
|
* https://github.com/chartjs/chartjs-chart-financial/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||||
|
|
||||||
|
},{}],2:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(Chart) {
|
||||||
|
|
||||||
|
Chart.defaults.candlestick = Object.assign({}, Chart.defaults.financial);
|
||||||
|
Chart.defaults.candlestick.scales = {
|
||||||
|
xAxes: [Object.assign({}, Chart.defaults.financial.scales.xAxes[0])],
|
||||||
|
yAxes: [Object.assign({}, Chart.defaults.financial.scales.yAxes[0])]
|
||||||
|
};
|
||||||
|
|
||||||
|
Chart.controllers.candlestick = Chart.controllers.financial.extend({
|
||||||
|
dataElementType: Chart.elements.Candlestick,
|
||||||
|
|
||||||
|
updateElement: function(element, index, reset) {
|
||||||
|
var me = this;
|
||||||
|
var meta = me.getMeta();
|
||||||
|
var dataset = me.getDataset();
|
||||||
|
|
||||||
|
element._xScale = me.getScaleForId(meta.xAxisID);
|
||||||
|
element._yScale = me.getScaleForId(meta.yAxisID);
|
||||||
|
element._datasetIndex = me.index;
|
||||||
|
element._index = index;
|
||||||
|
|
||||||
|
element._model = {
|
||||||
|
datasetLabel: dataset.label || '',
|
||||||
|
// label: '', // to get label value please use dataset.data[index].label
|
||||||
|
|
||||||
|
// Appearance
|
||||||
|
color: dataset.color,
|
||||||
|
borderColor: dataset.borderColor,
|
||||||
|
borderWidth: dataset.borderWidth,
|
||||||
|
};
|
||||||
|
|
||||||
|
me.updateElementGeometry(element, index, reset);
|
||||||
|
|
||||||
|
element.pivot();
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
},{}],3:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(Chart) {
|
||||||
|
|
||||||
|
Chart.defaults.financial = {
|
||||||
|
label: '',
|
||||||
|
|
||||||
|
hover: {
|
||||||
|
mode: 'label'
|
||||||
|
},
|
||||||
|
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
distribution: 'series',
|
||||||
|
categoryPercentage: 0.8,
|
||||||
|
barPercentage: 0.9,
|
||||||
|
ticks: {
|
||||||
|
source: 'data'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
type: 'financialLinear'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
|
||||||
|
tooltips: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(tooltipItem, data) {
|
||||||
|
var o = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].o;
|
||||||
|
var h = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].h;
|
||||||
|
var l = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].l;
|
||||||
|
var c = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].c;
|
||||||
|
|
||||||
|
var fractionalDigitsCount = data.datasets[tooltipItem.datasetIndex].fractionalDigitsCount;
|
||||||
|
if (fractionalDigitsCount !== undefined) {
|
||||||
|
fractionalDigitsCount = Math.max(0, Math.min(100, fractionalDigitsCount));
|
||||||
|
o = o.toFixed(fractionalDigitsCount);
|
||||||
|
h = h.toFixed(fractionalDigitsCount);
|
||||||
|
l = l.toFixed(fractionalDigitsCount);
|
||||||
|
c = c.toFixed(fractionalDigitsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ' O: ' + o + ' H: ' + h + ' L: ' + l + ' C: ' + c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is based off controller.bar.js from the upstream Chart.js library
|
||||||
|
*/
|
||||||
|
Chart.controllers.financial = Chart.controllers.bar.extend({
|
||||||
|
|
||||||
|
dataElementType: Chart.elements.Financial,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
updateElementGeometry: function(element, index, reset) {
|
||||||
|
var me = this;
|
||||||
|
var model = element._model;
|
||||||
|
var vscale = me.getValueScale();
|
||||||
|
var base = vscale.getBasePixel();
|
||||||
|
var horizontal = vscale.isHorizontal();
|
||||||
|
var ruler = me._ruler || me.getRuler();
|
||||||
|
var vpixels = me.calculateBarValuePixels(me.index, index);
|
||||||
|
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
|
||||||
|
var chart = me.chart;
|
||||||
|
var datasets = chart.data.datasets;
|
||||||
|
var indexData = datasets[me.index].data[index];
|
||||||
|
|
||||||
|
model.horizontal = horizontal;
|
||||||
|
model.base = reset ? base : vpixels.base;
|
||||||
|
model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
|
||||||
|
model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
|
||||||
|
model.height = horizontal ? ipixels.size : undefined;
|
||||||
|
model.width = horizontal ? undefined : ipixels.size;
|
||||||
|
model.candleOpen = vscale.getPixelForValue(Number(indexData.o));
|
||||||
|
model.candleHigh = vscale.getPixelForValue(Number(indexData.h));
|
||||||
|
model.candleLow = vscale.getPixelForValue(Number(indexData.l));
|
||||||
|
model.candleClose = vscale.getPixelForValue(Number(indexData.c));
|
||||||
|
},
|
||||||
|
|
||||||
|
draw: function() {
|
||||||
|
var ctx = this.chart.chart.ctx;
|
||||||
|
var elements = this.getMeta().data;
|
||||||
|
var dataset = this.getDataset();
|
||||||
|
var ilen = elements.length;
|
||||||
|
var i = 0;
|
||||||
|
var d;
|
||||||
|
|
||||||
|
Chart.canvasHelpers.clipArea(ctx, this.chart.chartArea);
|
||||||
|
|
||||||
|
for (; i < ilen; ++i) {
|
||||||
|
d = dataset.data[i].o;
|
||||||
|
if (d !== null && d !== undefined && !isNaN(d)) {
|
||||||
|
elements[i].draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Chart.canvasHelpers.unclipArea(ctx);
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
},{}],4:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(Chart) {
|
||||||
|
|
||||||
|
Chart.defaults.ohlc = Object.assign({}, Chart.defaults.financial);
|
||||||
|
Chart.defaults.ohlc.scales = {
|
||||||
|
xAxes: [Object.assign({}, Chart.defaults.financial.scales.xAxes[0])],
|
||||||
|
yAxes: [Object.assign({}, Chart.defaults.financial.scales.yAxes[0])]
|
||||||
|
};
|
||||||
|
Chart.defaults.ohlc.scales.xAxes[0].barPercentage = 1.0;
|
||||||
|
Chart.defaults.ohlc.scales.xAxes[0].categoryPercentage = 1.0;
|
||||||
|
|
||||||
|
Chart.controllers.ohlc = Chart.controllers.financial.extend({
|
||||||
|
|
||||||
|
dataElementType: Chart.elements.Ohlc,
|
||||||
|
|
||||||
|
updateElement: function(element, index, reset) {
|
||||||
|
var me = this;
|
||||||
|
var meta = me.getMeta();
|
||||||
|
var dataset = me.getDataset();
|
||||||
|
element._xScale = me.getScaleForId(meta.xAxisID);
|
||||||
|
element._yScale = me.getScaleForId(meta.yAxisID);
|
||||||
|
element._datasetIndex = me.index;
|
||||||
|
element._index = index;
|
||||||
|
element._model = {
|
||||||
|
datasetLabel: dataset.label || '',
|
||||||
|
lineWidth: dataset.lineWidth,
|
||||||
|
armLength: dataset.armLength,
|
||||||
|
armLengthRatio: dataset.armLengthRatio,
|
||||||
|
color: dataset.color,
|
||||||
|
};
|
||||||
|
me.updateElementGeometry(element, index, reset);
|
||||||
|
element.pivot();
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
},{}],5:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(Chart) {
|
||||||
|
|
||||||
|
var helpers = Chart.helpers;
|
||||||
|
var globalOpts = Chart.defaults.global;
|
||||||
|
|
||||||
|
globalOpts.elements.candlestick = Object.assign(globalOpts.elements.financial, {
|
||||||
|
borderColor: globalOpts.elements.financial.color.unchanged,
|
||||||
|
borderWidth: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
Chart.elements.Candlestick = Chart.elements.Financial.extend({
|
||||||
|
draw: function() {
|
||||||
|
var ctx = this._chart.ctx;
|
||||||
|
var vm = this._view;
|
||||||
|
|
||||||
|
var x = vm.x;
|
||||||
|
var o = vm.candleOpen;
|
||||||
|
var h = vm.candleHigh;
|
||||||
|
var l = vm.candleLow;
|
||||||
|
var c = vm.candleClose;
|
||||||
|
|
||||||
|
ctx.strokeStyle = helpers.getValueOrDefault(vm.borderColor, globalOpts.elements.candlestick.borderColor);
|
||||||
|
ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.candlestick.borderWidth);
|
||||||
|
if (c < o) {
|
||||||
|
ctx.fillStyle = helpers.getValueOrDefault(vm.color ? vm.color.up : undefined, globalOpts.elements.candlestick.color.up);
|
||||||
|
} else if (c > o) {
|
||||||
|
ctx.fillStyle = helpers.getValueOrDefault(vm.color ? vm.color.down : undefined, globalOpts.elements.candlestick.color.down);
|
||||||
|
} else {
|
||||||
|
ctx.fillStyle = helpers.getValueOrDefault(vm.color ? vm.color.unchanged : undefined, globalOpts.elements.candlestick.color.unchanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, h);
|
||||||
|
ctx.lineTo(x, Math.min(o, c));
|
||||||
|
ctx.moveTo(x, l);
|
||||||
|
ctx.lineTo(x, Math.max(o, c));
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillRect(x - vm.width / 2, c, vm.width, o - c);
|
||||||
|
ctx.strokeRect(x - vm.width / 2, c, vm.width, o - c);
|
||||||
|
ctx.closePath();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
},{}],6:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(Chart) {
|
||||||
|
|
||||||
|
var globalOpts = Chart.defaults.global;
|
||||||
|
|
||||||
|
globalOpts.elements.financial = {
|
||||||
|
color: {
|
||||||
|
up: 'rgba(80, 160, 115, 1)',
|
||||||
|
down: 'rgba(215, 85, 65, 1)',
|
||||||
|
unchanged: 'rgba(90, 90, 90, 1)',
|
||||||
|
},
|
||||||
|
fractionalDigitsCount: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
function isVertical(bar) {
|
||||||
|
return bar._view.width !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get the bounds of the candle
|
||||||
|
* @private
|
||||||
|
* @param bar {Chart.Element.financial} the bar
|
||||||
|
* @return {Bounds} bounds of the bar
|
||||||
|
*/
|
||||||
|
function getBarBounds(candle) {
|
||||||
|
var vm = candle._view;
|
||||||
|
var x1, x2, y1, y2;
|
||||||
|
|
||||||
|
var halfWidth = vm.width / 2;
|
||||||
|
x1 = vm.x - halfWidth;
|
||||||
|
x2 = vm.x + halfWidth;
|
||||||
|
y1 = vm.candleHigh;
|
||||||
|
y2 = vm.candleLow;
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
left: x1,
|
||||||
|
top: y1,
|
||||||
|
right: x2,
|
||||||
|
bottom: y2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Chart.elements.Financial = Chart.Element.extend({
|
||||||
|
|
||||||
|
height: function() {
|
||||||
|
var vm = this._view;
|
||||||
|
return vm.base - vm.y;
|
||||||
|
},
|
||||||
|
inRange: function(mouseX, mouseY) {
|
||||||
|
var inRange = false;
|
||||||
|
|
||||||
|
if (this._view) {
|
||||||
|
var bounds = getBarBounds(this);
|
||||||
|
inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inRange;
|
||||||
|
},
|
||||||
|
inLabelRange: function(mouseX, mouseY) {
|
||||||
|
var me = this;
|
||||||
|
if (!me._view) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inRange = false;
|
||||||
|
var bounds = getBarBounds(me);
|
||||||
|
|
||||||
|
if (isVertical(me)) {
|
||||||
|
inRange = mouseX >= bounds.left && mouseX <= bounds.right;
|
||||||
|
} else {
|
||||||
|
inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inRange;
|
||||||
|
},
|
||||||
|
inXRange: function(mouseX) {
|
||||||
|
var bounds = getBarBounds(this);
|
||||||
|
return mouseX >= bounds.left && mouseX <= bounds.right;
|
||||||
|
},
|
||||||
|
inYRange: function(mouseY) {
|
||||||
|
var bounds = getBarBounds(this);
|
||||||
|
return mouseY >= bounds.top && mouseY <= bounds.bottom;
|
||||||
|
},
|
||||||
|
getCenterPoint: function() {
|
||||||
|
var vm = this._view;
|
||||||
|
var x, y;
|
||||||
|
|
||||||
|
var halfWidth = vm.width / 2;
|
||||||
|
x = vm.x - halfWidth;
|
||||||
|
y = (vm.candleHigh + vm.candleLow) / 2;
|
||||||
|
|
||||||
|
return {x: x, y: y};
|
||||||
|
},
|
||||||
|
getArea: function() {
|
||||||
|
var vm = this._view;
|
||||||
|
return vm.width * Math.abs(vm.y - vm.base);
|
||||||
|
},
|
||||||
|
tooltipPosition: function() {
|
||||||
|
var vm = this._view;
|
||||||
|
return {
|
||||||
|
x: vm.x,
|
||||||
|
y: (vm.candleHigh + vm.candleLow) / 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
},{}],7:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(Chart) {
|
||||||
|
|
||||||
|
var helpers = Chart.helpers;
|
||||||
|
var globalOpts = Chart.defaults.global;
|
||||||
|
|
||||||
|
globalOpts.elements.ohlc = Object.assign(globalOpts.elements.financial, {
|
||||||
|
lineWidth: 2,
|
||||||
|
armLength: null,
|
||||||
|
armLengthRatio: 0.8,
|
||||||
|
});
|
||||||
|
|
||||||
|
Chart.elements.Ohlc = Chart.elements.Financial.extend({
|
||||||
|
draw: function() {
|
||||||
|
var ctx = this._chart.ctx;
|
||||||
|
var vm = this._view;
|
||||||
|
|
||||||
|
var x = vm.x;
|
||||||
|
var o = vm.candleOpen;
|
||||||
|
var h = vm.candleHigh;
|
||||||
|
var l = vm.candleLow;
|
||||||
|
var c = vm.candleClose;
|
||||||
|
var armLength = helpers.getValueOrDefault(vm.armLength, globalOpts.elements.ohlc.armLength);
|
||||||
|
var armLengthRatio = helpers.getValueOrDefault(vm.armLengthRatio, globalOpts.elements.ohlc.armLengthRatio);
|
||||||
|
if (armLength === null) {
|
||||||
|
// The width of an ohlc is affected by barPercentage and categoryPercentage
|
||||||
|
// This behavior is caused by extending controller.financial, which extends controller.bar
|
||||||
|
// barPercentage and categoryPercentage are now set to 1.0 (see controller.ohlc)
|
||||||
|
// and armLengthRatio is multipled by 0.5,
|
||||||
|
// so that when armLengthRatio=1.0, the arms from neighbour ohcl touch,
|
||||||
|
// and when armLengthRatio=0.0, ohcl are just vertical lines.
|
||||||
|
armLength = vm.width * armLengthRatio * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c < o) {
|
||||||
|
ctx.strokeStyle = helpers.getValueOrDefault(vm.color ? vm.color.up : undefined, globalOpts.elements.ohlc.color.up);
|
||||||
|
} else if (c > o) {
|
||||||
|
ctx.strokeStyle = helpers.getValueOrDefault(vm.color ? vm.color.down : undefined, globalOpts.elements.ohlc.color.down);
|
||||||
|
} else {
|
||||||
|
ctx.strokeStyle = helpers.getValueOrDefault(vm.color ? vm.color.unchanged : undefined, globalOpts.elements.ohlc.color.unchanged);
|
||||||
|
}
|
||||||
|
ctx.lineWidth = helpers.getValueOrDefault(vm.lineWidth, globalOpts.elements.ohlc.lineWidth);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, h);
|
||||||
|
ctx.lineTo(x, l);
|
||||||
|
ctx.moveTo(x - armLength, o);
|
||||||
|
ctx.lineTo(x, o);
|
||||||
|
ctx.moveTo(x + armLength, c);
|
||||||
|
ctx.lineTo(x, c);
|
||||||
|
ctx.stroke();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
},{}],8:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Chart = require('chart.js');
|
||||||
|
Chart = typeof Chart === 'function' ? Chart : window.Chart;
|
||||||
|
|
||||||
|
require('./scale.financialLinear.js')(Chart);
|
||||||
|
|
||||||
|
require('./element.financial.js')(Chart);
|
||||||
|
require('./element.candlestick.js')(Chart);
|
||||||
|
require('./element.ohlc.js')(Chart);
|
||||||
|
|
||||||
|
require('./controller.financial.js')(Chart);
|
||||||
|
require('./controller.candlestick.js')(Chart);
|
||||||
|
require('./controller.ohlc.js')(Chart);
|
||||||
|
|
||||||
|
},{"./controller.candlestick.js":2,"./controller.financial.js":3,"./controller.ohlc.js":4,"./element.candlestick.js":5,"./element.financial.js":6,"./element.ohlc.js":7,"./scale.financialLinear.js":9,"chart.js":1}],9:[function(require,module,exports){
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function(Chart) {
|
||||||
|
|
||||||
|
var helpers = Chart.helpers;
|
||||||
|
|
||||||
|
var defaultConfig = {
|
||||||
|
position: 'left',
|
||||||
|
ticks: {
|
||||||
|
callback: Chart.Ticks.formatters.linear
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var FinancialLinearScale = Chart.scaleService.getScaleConstructor('linear').extend({
|
||||||
|
|
||||||
|
determineDataLimits: function() {
|
||||||
|
var me = this;
|
||||||
|
var chart = me.chart;
|
||||||
|
var data = chart.data;
|
||||||
|
var datasets = data.datasets;
|
||||||
|
var isHorizontal = me.isHorizontal();
|
||||||
|
|
||||||
|
function IDMatches(meta) {
|
||||||
|
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First Calculate the range
|
||||||
|
me.min = null;
|
||||||
|
me.max = null;
|
||||||
|
|
||||||
|
// Regular charts use x, y values
|
||||||
|
// For the financial chart we have rawValue.h (hi) and rawValue.l (low) for each point
|
||||||
|
helpers.each(datasets, function(dataset, datasetIndex) {
|
||||||
|
var meta = chart.getDatasetMeta(datasetIndex);
|
||||||
|
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
|
||||||
|
helpers.each(dataset.data, function(rawValue) {
|
||||||
|
var high = rawValue.h;
|
||||||
|
var low = rawValue.l;
|
||||||
|
|
||||||
|
if (me.min === null) {
|
||||||
|
me.min = low;
|
||||||
|
} else if (low < me.min) {
|
||||||
|
me.min = low;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (me.max === null) {
|
||||||
|
me.max = high;
|
||||||
|
} else if (high > me.max) {
|
||||||
|
me.max = high;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add whitespace around bars. Axis shouldn't go exactly from min to max
|
||||||
|
var space = (me.max - me.min) * 0.05;
|
||||||
|
me.min -= space;
|
||||||
|
me.max += space;
|
||||||
|
|
||||||
|
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
|
||||||
|
this.handleTickRangeOptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Chart.scaleService.registerScaleType('financialLinear', FinancialLinearScale, defaultConfig);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
},{}]},{},[8]);
|
@ -0,0 +1,89 @@
|
|||||||
|
var client = {
|
||||||
|
api: "https://api.flanchan.moe/rtbw",
|
||||||
|
wsapi: "wss://api.flanchan.moe/rtbw",
|
||||||
|
timer: undefined,
|
||||||
|
info: undefined,
|
||||||
|
template: undefined,
|
||||||
|
allTime: undefined,
|
||||||
|
allTimeAverage: undefined,
|
||||||
|
sessionAllTime: undefined,
|
||||||
|
sessionAllTimeAverage: undefined,
|
||||||
|
startupLast5: undefined,
|
||||||
|
onWSclose: function(){},
|
||||||
|
superBuffer: undefined,
|
||||||
|
superGrapher: function(s){},
|
||||||
|
preloadCycles: 10,
|
||||||
|
start: function(grapher, cb=null, err=null) {
|
||||||
|
client.grapher = grapher;
|
||||||
|
$.ajaxSetup({
|
||||||
|
error: function(x, e) {
|
||||||
|
if(err!=null) err(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.getJSON(client.api+"/info", function(res) {
|
||||||
|
client.info = res;
|
||||||
|
$.getJSON(client.api+"/template", function(template) {
|
||||||
|
client.template = template;
|
||||||
|
client.sessionAllTime = $.extend({}, template);
|
||||||
|
client.sessionAllTimeAverage = $.extend({}, template);
|
||||||
|
client.sessionAllTimeAverage._n = 0;
|
||||||
|
|
||||||
|
$.getJSON(client.api+"/all", function(alltime) {
|
||||||
|
client.allTime = alltime;
|
||||||
|
$.getJSON(client.api+"/all/average", function(aaverage) {
|
||||||
|
client.allTimeAverage = aaverage;
|
||||||
|
$.getJSON(client.api+"/buffer/super", function(sbuf) {
|
||||||
|
client.superBuffer = sbuf;
|
||||||
|
$.getJSON(client.api+"/buffer/"+client.preloadCycles, function(last5) {
|
||||||
|
client.startupLast5 = last5;
|
||||||
|
try {
|
||||||
|
|
||||||
|
var ws = undefined;
|
||||||
|
try {
|
||||||
|
ws = client.ws = new WebSocket(client.wsapi+"/cycle");
|
||||||
|
}catch(e){
|
||||||
|
if(err!=null) err(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ws.onopen = function() {
|
||||||
|
if(cb!==null) cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function() {
|
||||||
|
client.onWSclose();
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onmessage = function(data) {
|
||||||
|
var sdt = JSON.parse(data.data);
|
||||||
|
|
||||||
|
if(sdt.type=="cycle") {
|
||||||
|
var dt = sdt.data;
|
||||||
|
client.allTime = cycleAdd(client.allTime, [dt]);
|
||||||
|
client.sessionAllTime = cycleAdd(client.sessionAllTime, [dt]);
|
||||||
|
client.allTimeAverage = cycleAcc(client.allTimeAverage, [dt], client.allTimeAverage._n);
|
||||||
|
client.allTimeAverage._n +=1;
|
||||||
|
client.sessionAllTimeAverage = cycleAcc(client.sessionAllTimeAverage, [dt], client.sessionAllTimeAverage._n);
|
||||||
|
client.sessionAllTimeAverage._n +=1;
|
||||||
|
client.grapher(dt);
|
||||||
|
}
|
||||||
|
else if(sdt.type=="supercycle")
|
||||||
|
{
|
||||||
|
client.superGrapher(sdt.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch(e) {
|
||||||
|
if(err!=null) err(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
stop: function() {
|
||||||
|
if(client.timer!==undefined) clearInterval(client.timer);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
/home/flandre/srv/rtbw/interop.js
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,788 @@
|
|||||||
|
var totalCan = $("#cycleBar");
|
||||||
|
var speedCan = $('#cycleSpeed');
|
||||||
|
|
||||||
|
var tflagCan = $('#tPostsByFlag');
|
||||||
|
var tflagPieCan = $('#tPostsByFlagPie');
|
||||||
|
var tNmaesCan = $('#tNmaes');
|
||||||
|
var tImagesCan = $('#tImages');
|
||||||
|
var tThreadsCan = $('#tThreads');
|
||||||
|
var tIdsCan= $('#tIds');
|
||||||
|
var tSubjectsCan = $('#tSubjects');
|
||||||
|
|
||||||
|
var tsflagCan = $('#tsPostsByFlag');
|
||||||
|
var tsflagPieCan = $('#tsPostsByFlagPie');
|
||||||
|
var tsNmaesCan = $('#tsNmaes');
|
||||||
|
var tsImagesCan = $('#tsImages');
|
||||||
|
var tsThreadsCan = $('#tsThreads');
|
||||||
|
var tsIdsCan= $('#tsIds');
|
||||||
|
var tsSubjectsCan = $('#tsSubjects');
|
||||||
|
var tsCycleSpeedCan = $('#tsCycleSpeed');
|
||||||
|
//var tsCycleAverageCan = $('#tsCycleAverage');
|
||||||
|
|
||||||
|
var tAgCandleCan = $('#tAgCandle');
|
||||||
|
|
||||||
|
const maxEntries= client.preloadCycles;
|
||||||
|
|
||||||
|
function setStatus(str)
|
||||||
|
{
|
||||||
|
$('#status').text(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lerpRGB(a,b,t)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
r: a.r + (b.r - a.r) * t,
|
||||||
|
g: a.g + (b.g - a.g) * t,
|
||||||
|
b: a.b + (b.b - a.b) * t
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_new_candle(charts, data,update=true,close=null)
|
||||||
|
{
|
||||||
|
//charts.agcandle.data.labels.push(fixtime2(new Date(data.timestamp)));
|
||||||
|
if(!data._candle.number.t)
|
||||||
|
data._candle.number.t = new Date(data.timestamp);
|
||||||
|
if(close!==null)
|
||||||
|
data._candle.number.o = close;
|
||||||
|
|
||||||
|
charts.agcandle.data.datasets[0].data.push(data._candle.number);
|
||||||
|
if(update) charts.agcandle.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNewCandle(charts, close)
|
||||||
|
{
|
||||||
|
if(!!charts.agcandle.data.datasets[0].data[charts.agcandle.data.datasets[0].data.length-1])
|
||||||
|
charts.agcandle.data.datasets[0].data[charts.agcandle.data.datasets[0].data.length-1].c = close;
|
||||||
|
charts.agcandle.data.datasets[0].data.push({o: close, h:close, l:close, c:close, t:new Date()});
|
||||||
|
charts.agcandle.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function total_add_data(chart, label, data, update=true){
|
||||||
|
//chart.addData(value, label);
|
||||||
|
|
||||||
|
if(chart.data.labels.length>=maxEntries)
|
||||||
|
{
|
||||||
|
chart.data.labels.shift();
|
||||||
|
chart.data.datasets[0].data.shift();
|
||||||
|
chart.data.datasets[1].data.shift();
|
||||||
|
chart.data.datasets[2].data.shift();
|
||||||
|
chart.data.datasets[3].data.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.data.labels.push(label);
|
||||||
|
|
||||||
|
chart.data.datasets[0].data.push(data.number);
|
||||||
|
chart.data.datasets[1].data.push(data.names);
|
||||||
|
chart.data.datasets[2].data.push(data.trips);
|
||||||
|
chart.data.datasets[3].data.push(data.nametrips);
|
||||||
|
|
||||||
|
if(update)
|
||||||
|
chart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixtime(now){
|
||||||
|
var
|
||||||
|
h= now.getHours(),
|
||||||
|
m= now.getMinutes(),
|
||||||
|
s= now.getSeconds();
|
||||||
|
return h+":"+m+":"+s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixtime2(now){
|
||||||
|
var
|
||||||
|
y = now.getFullYear(),
|
||||||
|
M = now.getMonth(),
|
||||||
|
d = now.getUTCDate(),
|
||||||
|
h= now.getHours(),
|
||||||
|
m= now.getMinutes(),
|
||||||
|
s= now.getSeconds();
|
||||||
|
return y+"."+(M+1)+"."+d+" "+h+":"+m+":"+s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function grapher(data, charts, update=true) {
|
||||||
|
//cycle graph
|
||||||
|
total_add_data(charts.total, fixtime(new Date(data.timestamp)), data,update);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSection(gr, pfx, allTime, allTimeAverage,data=null)
|
||||||
|
{
|
||||||
|
var share = flagShare(allTime.number, allTime.countries);
|
||||||
|
|
||||||
|
$('#'+pfx+'last').text(allTime.last);
|
||||||
|
$('#'+pfx+'posts').text(allTime.number);
|
||||||
|
$('#'+pfx+'flagshare').text(lcStr(flagShareStr(share),40));
|
||||||
|
|
||||||
|
$('#'+pfx+'avg').text(allTimeAverage.number.toFixed(2));
|
||||||
|
$('#'+pfx+'upcycle').text(allTimeAverage._n);
|
||||||
|
|
||||||
|
$('#'+pfx+'flagshare_container > ul').empty();
|
||||||
|
share.sort(function (a, b) {
|
||||||
|
return a.total < b.total ? 1 : -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
gr.aflag.data.labels.length =
|
||||||
|
gr.aflag.data.datasets[0].data.length = 0;
|
||||||
|
|
||||||
|
gr.aflagpie.data.labels.length =
|
||||||
|
gr.aflagpie.data.datasets[0].data.length =
|
||||||
|
gr.aflagpie.data.datasets[0].backgroundColor.length = 0;
|
||||||
|
|
||||||
|
var cole = {r: 0, g: 0,b: 255};
|
||||||
|
var cols = {r: 255, g: 128, b: 128};
|
||||||
|
|
||||||
|
var colstr = function(x) {
|
||||||
|
return "rgba("+x.r+","+x.g+","+x.b+", 1.0)";
|
||||||
|
};
|
||||||
|
|
||||||
|
for(var i in share) {
|
||||||
|
$('#'+pfx+'flagshare_container > ul').append("<li>"+share[i]["key"]+": "+(share[i]["val"]*100.00).toFixed(2)+"% ("+share[i]["total"]+")</li>");
|
||||||
|
gr.aflag.data.labels.push(share[i]["key"]);
|
||||||
|
gr.aflag.data.datasets[0].data.push(share[i]["total"]);
|
||||||
|
|
||||||
|
gr.aflagpie.data.labels.push(share[i]["key"]);
|
||||||
|
gr.aflagpie.data.datasets[0].data.push(share[i]["total"]);
|
||||||
|
|
||||||
|
gr.aflagpie.data.datasets[0].backgroundColor.push(colstr(lerpRGB(cols,cole,(i/share.length))));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gr.anamepie.data.datasets[0].data[0] = allTime.number-(allTime.names+allTime.trips+allTime.nametrips);
|
||||||
|
gr.anamepie.data.datasets[0].data[1] = allTime.names;
|
||||||
|
gr.anamepie.data.datasets[0].data[2] = allTime.trips;
|
||||||
|
gr.anamepie.data.datasets[0].data[3] = allTime.nametrips;
|
||||||
|
|
||||||
|
gr.aimagepie.data.datasets[0].data[0] = allTime.images;
|
||||||
|
gr.aimagepie.data.datasets[0].data[1] = allTime.number-allTime.images;
|
||||||
|
|
||||||
|
gr.athreadpie.data.datasets[0].data[0] = allTime.threads;
|
||||||
|
gr.athreadpie.data.datasets[0].data[1] = allTime.number-allTime.threads;
|
||||||
|
|
||||||
|
gr.aidspie.data.datasets[0].data[0] = allTime.ids;
|
||||||
|
gr.aidspie.data.datasets[0].data[1] = allTime.number-allTime.ids;
|
||||||
|
|
||||||
|
gr.asubjectspie.data.datasets[0].data[0] = allTime.subjects;
|
||||||
|
gr.asubjectspie.data.datasets[0].data[1] = allTime.threads-allTime.subjects;
|
||||||
|
|
||||||
|
gr.aflag.update();
|
||||||
|
gr.aflagpie.update();
|
||||||
|
gr.anamepie.update();
|
||||||
|
gr.aimagepie.update();
|
||||||
|
gr.athreadpie.update();
|
||||||
|
gr.aidspie.update();
|
||||||
|
gr.asubjectspie.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAllTime(gr,data=null)
|
||||||
|
{
|
||||||
|
updateSection(gr, "at_", client.allTime, client.allTimeAverage,data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flagShare(total, ctr)
|
||||||
|
{
|
||||||
|
var ret = [];
|
||||||
|
for(var key in ctr)
|
||||||
|
{
|
||||||
|
ret.push({key: key, val: ctr[key]/total, total: ctr[key]});
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lcStr(str, mx)
|
||||||
|
{
|
||||||
|
if(str.length>mx)
|
||||||
|
return str.slice(0, mx)+"...";
|
||||||
|
else return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flagShareStr(share)
|
||||||
|
{
|
||||||
|
share.sort(function (a, b) {
|
||||||
|
return a.total < b.total ? 1 : -1;
|
||||||
|
});
|
||||||
|
var strs= [];
|
||||||
|
for(var shr in share)
|
||||||
|
{
|
||||||
|
shr = share[shr];
|
||||||
|
strs.push(shr["key"]+": "+(shr["val"]*100.00).toFixed(2)+"%");
|
||||||
|
}
|
||||||
|
return strs.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
//update session
|
||||||
|
function graphCycle(data, charts,update=true)
|
||||||
|
{
|
||||||
|
|
||||||
|
$('#cytb > tbody > tr:first').before("<tr>"+
|
||||||
|
"<td>"+fixtime(new Date(data.timestamp))+(!update?" <i>(preload)</i>":"")+"</td>"+
|
||||||
|
"<td>"+data.last+"</td>"+
|
||||||
|
"<td>"+data.number+"</td>"+
|
||||||
|
"<td>"+((data.number/client.allTimeAverage.number)*100.00).toFixed(2)+"% "+(update&&(client.sessionAllTimeAverage.number>0)?(((data.number/client.sessionAllTimeAverage.number)*100.00).toFixed(2)+"% (session)"):"")+"</td>"+
|
||||||
|
"<td>"+flagShareStr(flagShare(data.number, data.countries))+"</td>"+
|
||||||
|
"<td>"+data.names+"</td>"+
|
||||||
|
"<td>"+data.trips+"</td>"+
|
||||||
|
"<td>"+data.nametrips+"</td>"+
|
||||||
|
"</tr>");
|
||||||
|
grapher(data, charts,update);
|
||||||
|
|
||||||
|
updateSection(charts, "s_", client.sessionAllTime, client.sessionAllTimeAverage);
|
||||||
|
|
||||||
|
//All time average comparison
|
||||||
|
|
||||||
|
if(charts.speed.data.labels.length>=maxEntries){
|
||||||
|
charts.speed.data.labels.shift();
|
||||||
|
charts.speed.data.datasets[0].data.shift();
|
||||||
|
charts.speed.data.datasets[1].data.shift();
|
||||||
|
charts.speed.data.datasets[2].data.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
charts.speed.data.labels.push(fixtime(new Date(data.timestamp)));
|
||||||
|
charts.speed.data.datasets[0].data.push( ((data.number/client.allTimeAverage.number)*100.00).toFixed(2));
|
||||||
|
charts.speed.data.datasets[1].data.push( data.number);
|
||||||
|
charts.speed.data.datasets[2].data.push( client.allTimeAverage.number);
|
||||||
|
if(update) {
|
||||||
|
charts.speed.update();
|
||||||
|
|
||||||
|
charts.aspeed.data.labels.push(fixtime(new Date(data.timestamp)));
|
||||||
|
charts.aspeed.data.datasets[0].data.push( ((data.number/client.sessionAllTimeAverage.number)*100.00).toFixed(2));
|
||||||
|
charts.aspeed.data.datasets[1].data.push( data.number);
|
||||||
|
charts.aspeed.data.datasets[2].data.push( client.sessionAllTimeAverage.number );
|
||||||
|
charts.aspeed.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Candle
|
||||||
|
|
||||||
|
var lc = charts.agcandle.data.datasets[0].data[charts.agcandle.data.datasets[0].data.length-1];
|
||||||
|
if(lc) {
|
||||||
|
lc.c = data.number;
|
||||||
|
if(data.number>lc.h)
|
||||||
|
lc.h = data.number;
|
||||||
|
if(data.number<lc.l)
|
||||||
|
lc.l = data.number;
|
||||||
|
if(update)
|
||||||
|
charts.agcandle.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function begin() {
|
||||||
|
|
||||||
|
var chartASubjectsPie = new Chart(tSubjectsCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Subject OPs", "Other OPs"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartAIdsPie = new Chart(tIdsCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["IDs", "Imptrash"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "grey"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartAthreadsPie = new Chart(tThreadsCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Threads", "Comments"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartAImagesPie = new Chart(tImagesCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Image posts", "Text posts"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartsANamesPie = new Chart(tNmaesCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Anons", "Namefags", "Tripfags", "Name+tripfags"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green", "cyan", "red"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0,0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
//title: "Names",
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartAFlagsPie = new Chart(tflagPieCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["(unbound)"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor: ["blue","green","red"],
|
||||||
|
//fillColor: ["blue", "green", "red"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [1]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
animation: false,
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var chartAFlags = new Chart(tflagCan, {
|
||||||
|
type: "bar",
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: "Posts",
|
||||||
|
backgroundColor: "rgba(255, 124, 60, 0.8)",
|
||||||
|
data: [],
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
animation: false,
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
yAxes: [{
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/// START SESSION CHARTS
|
||||||
|
|
||||||
|
var chartSSubjectsPie = new Chart(tsSubjectsCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Subject OPs", "Other OPs"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartSIdsPie = new Chart(tsIdsCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["IDs", "Imptrash"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "grey"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartSthreadsPie = new Chart(tsThreadsCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Threads", "Comments"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartSImagesPie = new Chart(tsImagesCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Image posts", "Text posts"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartsSNamesPie = new Chart(tsNmaesCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["Anons", "Namefags", "Tripfags", "Name+tripfags"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor:["blue", "green", "cyan", "red"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [0,0,0,0]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
//title: "Names",
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var chartSFlagsPie = new Chart(tsflagPieCan, {
|
||||||
|
type: "pie",
|
||||||
|
data: {
|
||||||
|
labels: ["(unbound)"],
|
||||||
|
datasets: [{
|
||||||
|
label: "Share",
|
||||||
|
backgroundColor: ["blue","green","red"],
|
||||||
|
//fillColor: ["blue", "green", "red"],
|
||||||
|
borderColor: 'rgba(255,255,255, 0.75)',
|
||||||
|
data: [1]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
animation: false,
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var chartSFlags = new Chart(tsflagCan, {
|
||||||
|
type: "bar",
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: "Posts",
|
||||||
|
backgroundColor: "rgba(255, 124, 60, 0.8)",
|
||||||
|
data: [],
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
animation: false,
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
yAxes: [{
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var chartSCycleSpeed= new Chart(tsCycleSpeedCan, {
|
||||||
|
type:"line",
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label: "% of session avg",
|
||||||
|
yAxisID: "A",
|
||||||
|
borderColor: "rgba(0, 102, 204, 0.6)",
|
||||||
|
backgroundColor: "rgb(0, 204, 255, 0.2)",
|
||||||
|
tension: 0,
|
||||||
|
data:[]
|
||||||
|
}, {
|
||||||
|
fill: false,
|
||||||
|
yAxisID: "B",
|
||||||
|
borderColor: "rgba(255,0,0,0.6)",
|
||||||
|
label: "Number",
|
||||||
|
tension: 0,
|
||||||
|
data:[]
|
||||||
|
}, {
|
||||||
|
fill: false,
|
||||||
|
yAxisID: "B",
|
||||||
|
borderColor: "rgba(0,255,0,0.6)",
|
||||||
|
label: "Average",
|
||||||
|
tension: 0.1,
|
||||||
|
data:[]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
yAxes: [{
|
||||||
|
id: "A",
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
callback: function(label, index, labels) {
|
||||||
|
return label+"%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "B",
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/// END SESSION CHARTS
|
||||||
|
|
||||||
|
|
||||||
|
var chartTotal = new Chart(totalCan, {
|
||||||
|
type: "bar",
|
||||||
|
data: {
|
||||||
|
labels: Array(maxEntries).fill("(unbound)"),
|
||||||
|
datasets: [{
|
||||||
|
label: "Posts",
|
||||||
|
backgroundColor: "rgba(102, 255, 255, 0.6)",
|
||||||
|
data: Array(maxEntries).fill(0)
|
||||||
|
}, {
|
||||||
|
label: "Namefags",
|
||||||
|
backgroundColor: "rgba(102, 255, 102, 0.7)",
|
||||||
|
data: Array(maxEntries).fill(0)
|
||||||
|
}, {
|
||||||
|
label: "Tripfags",
|
||||||
|
backgroundColor: "rgba(204, 255, 102, 0.7)",
|
||||||
|
data: Array(maxEntries).fill(0)
|
||||||
|
}, {
|
||||||
|
label: "Name+tripfags",
|
||||||
|
backgroundColor: "rgba(255, 60, 12, 0.7)",
|
||||||
|
data: Array(maxEntries).fill(0)
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
stacked: true
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
callback: function (value) { if (Number.isInteger(value)) { return value; } }
|
||||||
|
//stepSize: 1
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Total posts per cycle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var chartSpeed = new Chart(speedCan, {
|
||||||
|
type:"line",
|
||||||
|
data: {
|
||||||
|
labels: Array(maxEntries).fill("(unbound)"),
|
||||||
|
datasets: [{
|
||||||
|
yAxisID: "A",
|
||||||
|
borderColor: "rgba(0, 102, 204, 0.6)",
|
||||||
|
backgroundColor: "rgb(0, 204, 255, 0.2)",
|
||||||
|
label: "% of avg",
|
||||||
|
tension: 0,
|
||||||
|
data:Array(maxEntries).fill(0)
|
||||||
|
}, {
|
||||||
|
fill: false,
|
||||||
|
yAxisID: "B",
|
||||||
|
borderColor: "rgba(255,0,0,0.6)",
|
||||||
|
label: "Number",
|
||||||
|
tension: 0,
|
||||||
|
data:Array(maxEntries).fill(0)
|
||||||
|
}, {
|
||||||
|
fill: false,
|
||||||
|
yAxisID: "B",
|
||||||
|
borderColor: "rgba(0,255,0,0.6)",
|
||||||
|
label: "Average",
|
||||||
|
tension: 0.1,
|
||||||
|
data:Array(maxEntries).fill(0)
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
yAxes: [{
|
||||||
|
id: "A",
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
callback: function(label, index, labels) {
|
||||||
|
return label+"%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
id: "B",
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Aggregate stats
|
||||||
|
|
||||||
|
var chartAGCandle = new Chart(tAgCandleCan, {
|
||||||
|
type: "candlestick",
|
||||||
|
data: {
|
||||||
|
//labels: [],
|
||||||
|
datasets: [{
|
||||||
|
label:"Number",
|
||||||
|
data:[]
|
||||||
|
}]
|
||||||
|
}, options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
yAxes: [{
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.onWSclose = function() {
|
||||||
|
setStatus("Dropped");
|
||||||
|
};
|
||||||
|
|
||||||
|
var g_globals = function(x) {
|
||||||
|
x.total = chartTotal;
|
||||||
|
x.speed = chartSpeed;
|
||||||
|
x.agcandle = chartAGCandle;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
|
var g_charts = function() {
|
||||||
|
return g_globals({
|
||||||
|
aflag: chartAFlags,
|
||||||
|
aflagpie: chartAFlagsPie,
|
||||||
|
anamepie: chartsANamesPie,
|
||||||
|
aimagepie: chartAImagesPie,
|
||||||
|
athreadpie: chartAthreadsPie,
|
||||||
|
aidspie: chartAIdsPie,
|
||||||
|
asubjectspie: chartASubjectsPie
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var g_chartsSession = function() {
|
||||||
|
return g_globals({
|
||||||
|
aflag: chartSFlags,
|
||||||
|
aflagpie: chartSFlagsPie,
|
||||||
|
anamepie: chartsSNamesPie,
|
||||||
|
aimagepie: chartSImagesPie,
|
||||||
|
athreadpie: chartSthreadsPie,
|
||||||
|
aidspie: chartSIdsPie,
|
||||||
|
asubjectspie: chartSSubjectsPie,
|
||||||
|
|
||||||
|
aspeed: chartSCycleSpeed
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var g_chartsSuper = function() {
|
||||||
|
return g_globals({
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
client.superGrapher = function(data) {
|
||||||
|
//TODO: new supercycle
|
||||||
|
//add_new_candle(g_chartsSuper(), data);
|
||||||
|
createNewCandle(g_chartsSuper(), data._candle.number.c);
|
||||||
|
};
|
||||||
|
|
||||||
|
client.start(function(data) {
|
||||||
|
//on message function
|
||||||
|
updateAllTime(g_charts(),data);
|
||||||
|
|
||||||
|
graphCycle(data, g_chartsSession());
|
||||||
|
}, function() {
|
||||||
|
//init function
|
||||||
|
$('#interval').text(client.info.interval+" ms");
|
||||||
|
$('#binterval').text(client.info.backendInterval+" s");
|
||||||
|
$('#pinterval').text((client.info.purgeInterval/1000/60).toFixed(2)+" m");
|
||||||
|
$('#spinterval').text((client.info.superPurgeInterval/1000/60/60).toFixed(2)+" h");
|
||||||
|
$('#board').text(client.info.board);
|
||||||
|
$('#uptime').text(fixtime2(new Date(client.info.upSince)));
|
||||||
|
$('#s_started').text(fixtime2(new Date()));
|
||||||
|
|
||||||
|
//load superbuffer
|
||||||
|
for(var i in client.superBuffer) {
|
||||||
|
add_new_candle(g_chartsSuper(), client.superBuffer[i], false, (i>0?client.superBuffer[i-1]._candle.number.c:null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if(client.superBuffer.length<1)
|
||||||
|
{
|
||||||
|
add_new_candle(g_chartsSuper(), {_candle:{number:{o:0,c:0,h:0,l:0,t:new Date()}}}, false);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//add last x from buffer
|
||||||
|
for(var i in client.startupLast5) {
|
||||||
|
graphCycle(client.startupLast5[i], g_charts(),false);
|
||||||
|
}
|
||||||
|
|
||||||
|
chartTotal.update();
|
||||||
|
chartSpeed.update();
|
||||||
|
chartAGCandle.update();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$('#at_flagshare_exp').on("click", function() {
|
||||||
|
if($('#at_flagshare_container').is(":hidden")) {
|
||||||
|
$('#at_flagshare_container').show(1000, function() {
|
||||||
|
|
||||||
|
$('#at_flagshare_exp').text("-");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#at_flagshare_container').hide(1000, function() {
|
||||||
|
$('#at_flagshare_exp').text("+");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#s_flagshare_exp').on("click", function() {
|
||||||
|
if($('#s_flagshare_container').is(":hidden")) {
|
||||||
|
$('#s_flagshare_container').show(1000, function() {
|
||||||
|
|
||||||
|
$('#s_flagshare_exp').text("-");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#s_flagshare_container').hide(1000, function() {
|
||||||
|
$('#s_flagshare_exp').text("+");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateAllTime(g_charts());
|
||||||
|
|
||||||
|
setStatus("Synced");
|
||||||
|
}, function(err) {
|
||||||
|
setStatus("Connection failed");
|
||||||
|
console.log("Error: "+err.toString());
|
||||||
|
client.stop();
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue