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