Initial commit

master
Ringo Watanabe 6 years ago
commit 399236f7b5

1
.gitignore vendored

@ -0,0 +1 @@
backend/

@ -0,0 +1,4 @@
rtbwpy
node.js
ws node module

@ -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 @@
/home/flandre/srv/rtbw/../api/rtbwpy

@ -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…
Cancel
Save