telegram-crawler/data/web/tsf.telegram.org/js/stats.js
2022-05-13 12:05:36 +02:00

684 lines
No EOL
18 KiB
JavaScript

window.d3 = window.d3 || bb.d3;
var statShortMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var statShortWeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
function statsFormat(period) {
switch (period) {
case 'minute':
case '5min':
return statsFormat5min;
case 'hour':
return statsFormatHour;
case 'day':
default:
return d3.timeFormat('%a, %b %e %Y');
}
}
function statsFormat5min(time) {
return time.toUTCString().match(/(\d+:\d+):/)[1];
}
function statsFormatHour(time) {
return statShortMonths[time.getUTCMonth()] + ', ' + time.getUTCDate() + ' ' + statsFormat5min(time);
}
function statsFormatDayHour(hour) {
return hour + ':00-' + hour + ':59';
}
function statsFormatFixedKMBTPrecision(kmbt, precision) {
return function (val) {
return statsFormatKMBT(val, kmbt, precision);
}
}
function statsFormatKMBT(val, kmbt, precision) {
if (val == 0) {
return '0';
}
if (kmbt == null) {
kmbt = statsChooseNumKMBT(val)
}
var sval = statsFormatFixedKMBT(val, kmbt);
if (precision == null) {
precision = statsChoosePrecision(sval)
}
return sval.toFixed(precision) + kmbt;
}
function statsFormatFixedKMBT(val, kmbt) {
switch (kmbt) {
case 'K':
return val / 1000;
case 'M':
return val / 1000000;
case 'B':
return val / 1000000000;
case 'T':
return val / 1000000000000;
}
return val
}
function statsChoosePrecision(val) {
var absVal = Math.abs(val);
if (absVal > 10) {
return 0;
}
if (absVal > 1) {
return 1;
}
return 2;
}
function statsChooseNumKMBT(val) {
var absVal = Math.abs(val);
if (absVal >= 1000000000000) {
return 'T';
}
else if (absVal >= 1000000000) {
return 'B';
}
else if (absVal >= 1000000) {
return 'M';
}
else if (absVal >= 2000) {
return 'K';
}
return '';
}
var statsFormatNumber = d3.format(',~r');
function statsFormatTooltipValue(val, a, b, c) {
if (val.toLocaleString) {
return val.toLocaleString();
}
return statsFormatNumber(val)
}
function statsTooltipPosition(dataToShow, tWidth, tHeight, element) {
var $$ = this;
var config = $$.config;
var mousePos = d3.mouse(element);
var left = mousePos[0];
var top = mousePos[1];
var svgLeft = $$.getSvgLeft(true);
var chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
if (isTouchDevice()) {
top -= tHeight + 20;
} else {
top += 20;
}
// Determine tooltip position
var dataScale = $$.x(dataToShow[0].x);
top -= 5;
left = svgLeft + $$.getCurrentPaddingLeft(true) + 20 + ($$.zoomScale ? left : dataScale);
var right = left + tWidth;
if (right > chartRight) {
left = left - tWidth - 50;
}
if (top + tHeight > $$.currentHeight) {
top -= tHeight + 30;
}
if (top < 0) {
top = 0;
}
if (left < 0) {
left = 0;
}
return {top: top, left: left};
}
function statsPieChartLegendTemplate(names) {
return function (dataID, color, data) {
var addClass = ''
if (this.hiddenTargetIds && this.hiddenTargetIds.indexOf(dataID) != -1) {
addClass = ' off'
}
var name = names[dataID]
return '<div class="piechart_legend_item' + addClass + '"><i class="piechart_legend_color" style="background-color: '+color+';"></i><span class="piechart_legend_text">' + name + '</span></div>'
}
}
function statsDefaultSubchartZoomGenerator(part) {
return function(domain) {
if (part === undefined) {
return false;
}
var xDomain = [domain[0][0], domain[1][0]];
var domain = [xDomain[0] + (xDomain[1] - xDomain[0]) * part, xDomain[1]];
// console.log(xDomain, domain);
return domain;
}
}
function statsOnGraphRenderedGenerator(options) {
options = options || {};
return function () {
statsOnGraphRendered.call(this, options);
};
}
function statsOnGraphRendered(options) {
var self = this
var $$ = self.internal || self;
var chart = $$.api || $$;
if ($$._firstTicksUpdate === undefined) {
$$._firstTicksUpdate = true;
statsDoUpdateTicks(chart, $$, !options.noTime)
var targetsLen = $$.data.targets.length || 0;
var xsLen = ($$.data.xs["y0"] || []).length;
if (xsLen * targetsLen < 10000) {
chart.config('transition.duration', 350);
}
}
}
function statsGetTicks(chart, axis, domain) {
var $$ = chart && chart.internal;
var lastAxisTicksKey = '_lastAxisTicks' + axis;
var lastAxisTicks = $$ && $$[lastAxisTicksKey];
if ($$) {
domain = $$[axis].domain() || domain;
}
if (lastAxisTicks) {
var domainDiff = Math.abs(domain[1] - lastAxisTicks.domain[1]) + Math.abs(domain[0] - lastAxisTicks.domain[0]);
if (domainDiff < (domain[1] - domain[0]) * 0.05) {
// console.warn('same ticks', axis, domainDiff / (domain[1] - domain[0]));
return lastAxisTicks.ticks;
}
// console.warn('new ticks', axis, domainDiff / (domain[1] - domain[0]));
}
var result;
if (axis == 'x') {
result = statsUpdateXTicks(chart, $$, domain);
if (chart) {
chart.config('axis.x.tick.format', result.formatter);
chart.config('axis.x.label', {
text: result.label,
position: 'inner-right'
}, false);
}
} else {
result = statsUpdateYTicks(chart, $$, axis, domain)
if (chart) {
chart.config('axis.' + axis + '.tick.format', result.formatter);
if (axis == 'y') {
chart.config('grid.' + axis + '.lines', result.gridLines);
}
}
}
if (chart) {
lastAxisTicks = {
ticks: result.values,
domain: domain,
fomatterID: lastAxisTicks && lastAxisTicks.fomatterID
};
var newFormatterID = result.formatter._id;
if (lastAxisTicks.fomatterID != newFormatterID) {
setTimeout(function() {
chart.flush(true);
}, 0);
lastAxisTicks.fomatterID = newFormatterID;
}
$$[lastAxisTicksKey] = lastAxisTicks;
}
return result.values;
}
function statsDoUpdateTicks(chart, $$, withX) {
if (withX) {
statsGetTicks(chart, 'x');
}
statsGetTicks(chart, 'y');
if ($$['y1']) {
statsGetTicks(chart, 'y1');
}
chart.flush(true);
}
function statsUpdateXTicks(chart, $$, domain) {
domain = $$ && $$.x.domain() || domain;
domain[0] = parseInt(domain[0] / 1000);
domain[1] = parseInt(domain[1] / 1000);
var timeAxis = $$ && $$._timeAxis;
if (timeAxis === undefined) {
timeAxis = new statsTimeAxis({});
if ($$) {
$$._timeAxis = timeAxis;
}
}
// Doesn't look good with margin > 0
var margin = 0 * (domain[1] - domain[0]);
var ticks = timeAxis.tickOffsets([domain[0] + margin, domain[1] - margin]);
var values = ticks.offsets.map(function (t) {
return new Date(t.value * 1000);
});
var formatter = ticks.unit.formatter;
formatter._id = ticks.unit.name;
var labelFormat = '';
if (ticks.unit.name == 'day') {
labelFormat = '%B %Y';
}
else if (ticks.unit.name == 'week' || ticks.unit.name == 'month') {
labelFormat = '%Y';
}
return {
label: d3.timeFormat(labelFormat)(values[values.length - 1]),
formatter: formatter,
values: values
};
}
function statsUpdateYTicks(chart, $$, axis, domain) {
domain = $$ && $$[axis].domain() || domain;
var domainDiff = domain[1] - domain[0];
var kmbt = statsChooseNumKMBT(domainDiff)
var diffKMBT = statsFormatFixedKMBT(domainDiff, kmbt);
var precision = statsChoosePrecision(diffKMBT);
domainDiff *= 0.9; // We don't want values to be in last 10% because of negative padding-top
var domainStip = [domain[0], domain[0] + domainDiff];
var valuesNum = 5;
var values = [];
var gridLines = [];
var decPow = Math.floor(Math.log10(domainDiff / valuesNum));
var decStep = Math.pow(10, decPow);
var last2 = false
while (domainDiff / decStep > valuesNum) {
if (last2) {
last2 = false;
decStep *= 2.5;
} else {
decStep *= 2;
last2 = true;
}
// x 2, 5, 10, 20, 50, 100
}
var start
if (domainStip[0] > 0) {
start = domainStip[0] - (domainStip[0] % decStep) + decStep;
} else {
start = domainStip[0] - (domainStip[0] % decStep);
}
var hasNoEmptyDigits = false;
for (var i = start; i <= domainStip[1]; i += decStep) {
if (axis != 'y2' || (i - domainStip[0]) / domainDiff > 0.1) {
values.push(i)
}
if (precision > 0) {
var formatted = statsFormatFixedKMBT(i, kmbt).toFixed(precision);
if (formatted !== '0' && !formatted.match(/(\.0+)$/)) {
hasNoEmptyDigits = true;
}
}
if ((i - domainStip[0]) / domainDiff > 0.1) {
gridLines.push({value: i})
}
}
if (precision > 0 && !hasNoEmptyDigits) {
precision = 0;
}
var formatter = statsFormatFixedKMBTPrecision(kmbt, precision);
formatter._id = kmbt + '_' + precision;
return {
formatter: formatter,
values: values,
gridLines: gridLines
};
}
function statsInitCustomLegend(chart) {
var names = chart.data.names();
var labels = Object.keys(names);
var chartElement = chart.internal.selectChart.node();
var hidden = chart.internal.hiddenTargetIds;
// console.warn(names, '#' + chartElement.id)
d3.select('#' + chartElement.id)
.insert("div")
.attr("class", "bbchart-custom-legend")
.selectAll("div")
.data(labels)
.enter()
.append("div")
.attr("class", "bbchart-custom-legend-label button-nostyle-item ripple-handler")
.classed("bbchart-custom-legend-label-hidden", function (id) {
return hidden.indexOf(id) != -1;
})
.attr('data-id', function(id, v) {
// console.log(id, v)
return id;
})
.each(function(id) {
d3.select(this)
.insert("span")
.attr("class", "ripple-mask")
.html('<span class="ripple"></span>')
initRipple(this);
d3.select(this)
.insert("span")
.attr("class", "bbchart-custom-legend-label-icon")
.style('background-color', chart.color(id))
.html('<i class="bbchart-custom-legend-label-icon-tick"></i>');
d3.select(this)
.insert("span")
.attr("class", "bbchart-custom-legend-label-text")
.text(names[id])
})
// .on("mouseover", function(id) {
// chart.focus(id);
// })
// .on("mouseout", function(id) {
// chart.revert();
// })
.on("click", function(id) {
var sel = d3.select(this);
sel.classed("bbchart-custom-legend-label-hidden", !sel.classed("bbchart-custom-legend-label-hidden"));
chart.toggle(id);
});
}
/*
Based on Rickshaw source code
https://github.com/shutterstock/rickshaw
Copyright (C) 2011-2017 by Shutterstock Images, LLC
License: MIT
https://github.com/shutterstock/rickshaw/blob/master/LICENSE
*/
function statsTimeFixture() {
var self = this;
this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
this.units = [
{
name: 'decade',
seconds: 86400 * 365.25 * 10,
formatter: function(d) { return (parseInt(d.getUTCFullYear() / 10, 10) * 10) }
}, {
name: 'year',
seconds: 86400 * 365.25,
formatter: function(d) { return d.getUTCFullYear() }
}, {
name: 'month',
seconds: 86400 * 30.5,
formatter: function(d) { return self.months[d.getUTCMonth()] }
}, {
name: 'week',
seconds: 86400 * 7,
formatter: function(d) { return self.formatDate(d) }
}, {
name: 'day',
seconds: 86400,
formatter: function(d) {
if (d.getDay() || true) {
return d.getUTCDate()
}
return self.formatDate(d)
}
}, {
name: '6 hour',
seconds: 3600 * 6,
formatter: function(d) { return self.formatTime(d) }
}, {
name: 'hour',
seconds: 3600,
formatter: function(d) { return self.formatTime(d) }
}, {
name: '15 minute',
seconds: 60 * 15,
formatter: function(d) { return self.formatTime(d) }
}, {
name: 'minute',
seconds: 60,
formatter: function(d) { return d.getUTCMinutes() + 'm' }
}, {
name: '15 second',
seconds: 15,
formatter: function(d) { return d.getUTCSeconds() + 's' }
}, {
name: 'second',
seconds: 1,
formatter: function(d) { return d.getUTCSeconds() + 's' }
}, {
name: 'decisecond',
seconds: 1/10,
formatter: function(d) { return d.getUTCMilliseconds() + 'ms' }
}, {
name: 'centisecond',
seconds: 1/100,
formatter: function(d) { return d.getUTCMilliseconds() + 'ms' }
}
];
this.unit = function(unitName) {
return this.units.filter( function(unit) { return unitName == unit.name } ).shift();
};
this.formatDate = function(d) {
return d3.timeFormat('%b %e')(d);
};
this.formatTime = function(d) {
return d.toUTCString().match(/(\d+:\d+):/)[1];
};
this.ceil = function(time, unit) {
var date, floor, year;
if (unit.name == 'week') {
date = new Date(time * 1000);
date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
floor = (date - date.getUTCDay() * 86400000) / 1000;
if (floor == time) return time;
return (floor + 7 * 86400);
}
if (unit.name == 'month') {
date = new Date(time * 1000);
floor = Date.UTC(date.getUTCFullYear(), date.getUTCMonth()) / 1000;
if (floor == time) return time;
year = date.getUTCFullYear();
var month = date.getUTCMonth();
if (month == 11) {
month = 0;
year = year + 1;
} else {
month += 1;
}
return Date.UTC(year, month) / 1000;
}
if (unit.name == 'year') {
date = new Date(time * 1000);
floor = Date.UTC(date.getUTCFullYear(), 0) / 1000;
if (floor == time) return time;
year = date.getUTCFullYear() + 1;
return Date.UTC(year, 0) / 1000;
}
return Math.ceil(time / unit.seconds) * unit.seconds;
};
};
function statsTimeAxis(args) {
var self = this;
this.fixedTimeUnit = args.timeUnit;
var time = args.timeFixture || new statsTimeFixture();
this.appropriateTimeUnit = function(domain) {
var unit;
var units = time.units;
var rangeSeconds = domain[1] - domain[0];
units.forEach( function(u) {
if (Math.floor(rangeSeconds / u.seconds) >= 2) {
unit = unit || u;
}
} );
return (unit || time.units[time.units.length - 1]);
};
this.tickOffsets = function(domain) {
var unit = this.fixedTimeUnit || this.appropriateTimeUnit(domain);
var count = Math.ceil((domain[1] - domain[0]) / unit.seconds);
var runningTick = domain[0];
var offsets = [];
for (var i = 0; i < count; i++) {
var tickValue = time.ceil(runningTick, unit);
runningTick = tickValue + unit.seconds / 2;
offsets.push( {
value: tickValue,
} );
}
return {
offsets: offsets,
unit: unit
};
};
};
function redraw(el) {
el.offsetTop + 1;
}
function onTextRippleStart(evt) {
var e = d3.getEvent() || evt.originalEvent || evt;
if (document.activeElement === this) return;
var rect = this.getBoundingClientRect();
if (e.type == 'touchstart') {
var clientX = e.targetTouches[0].clientX;
} else {
var clientX = e.clientX;
}
var ripple = this.parentNode.querySelector('.textfield-item-underline');
var rippleX = (clientX - rect.left) / this.offsetWidth * 100;
ripple.style.transition = 'none';
redraw(ripple);
ripple.style.left = rippleX + '%';
ripple.style.right = (100 - rippleX) + '%';
redraw(ripple);
ripple.style.left = '';
ripple.style.right = '';
ripple.style.transition = '';
}
function onRippleStart(evt) {
var e = d3.getEvent() || evt.originalEvent || evt;
var rippleMask = this.querySelector('.ripple-mask');
if (!rippleMask) return;
var rect = rippleMask.getBoundingClientRect();
if (e.type == 'touchstart') {
var clientX = e.targetTouches[0].clientX;
var clientY = e.targetTouches[0].clientY;
} else {
var clientX = e.clientX;
var clientY = e.clientY;
}
var rippleX = (clientX - rect.left) - rippleMask.offsetWidth / 2;
var rippleY = (clientY - rect.top) - rippleMask.offsetHeight / 2;
var ripple = this.querySelector('.ripple');
ripple.style.transition = 'none';
redraw(ripple);
ripple.style.transform = 'translate3d(' + rippleX + 'px, ' + rippleY + 'px, 0) scale3d(0.2, 0.2, 1)';
ripple.style.opacity = 1;
redraw(ripple);
ripple.style.transform = 'translate3d(' + rippleX + 'px, ' + rippleY + 'px, 0) scale3d(1, 1, 1)';
ripple.style.transition = '';
function onRippleEnd(e) {
ripple.style.transitionDuration = '.2s';
ripple.style.opacity = 0;
document.removeEventListener('mouseup', onRippleEnd);
document.removeEventListener('touchend', onRippleEnd);
document.removeEventListener('touchcancel', onRippleEnd);
}
document.addEventListener('mouseup', onRippleEnd);
document.addEventListener('touchend', onRippleEnd);
document.addEventListener('touchcancel', onRippleEnd);
}
function initRipple(el) {
// d3.select(el).on('mousedown touchstart', onTextRippleStart);
d3.select(el).on(isTouchDevice() ? 'touchstart' : 'mousedown', onRippleStart);
};
function destroyRipple(el) {
// d3.select(el).off('mousedown touchstart', onTextRippleStart);
d3.select(el).off(isTouchDevice() ? 'touchstart' : 'mousedown', onRippleStart);
};
function isTouchDevice() {
var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
var mq = function(query) {
return window.matchMedia(query).matches;
}
if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
return true;
}
// include the 'heartz' as a way to have a non matching MQ to help terminate the join
// https://git.io/vznFH
var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
return mq(query);
}