add a couple of more utils

This commit is contained in:
pratu16x7 2017-11-20 04:58:11 +05:30
parent 4a570faab6
commit 78727b0e7a
11 changed files with 288 additions and 285 deletions

View File

@ -1,20 +1,20 @@
function $(expr, con) { function $$1(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
} }
$.create = (tag, o) => { $$1.create = (tag, o) => {
var element = document.createElement(tag); var element = document.createElement(tag);
for (var i in o) { for (var i in o) {
var val = o[i]; var val = o[i];
if (i === "inside") { if (i === "inside") {
$(val).appendChild(element); $$1(val).appendChild(element);
} }
else if (i === "around") { else if (i === "around") {
var ref = $(val); var ref = $$1(val);
ref.parentNode.insertBefore(element, ref); ref.parentNode.insertBefore(element, ref);
element.appendChild(ref); element.appendChild(ref);
@ -66,7 +66,7 @@ function getElementContentWidth(element) {
return element.clientWidth - padding; return element.clientWidth - padding;
} }
$.bind = (element, o) => { $$1.bind = (element, o) => {
if (element) { if (element) {
for (var event in o) { for (var event in o) {
var callback = o[event]; var callback = o[event];
@ -78,7 +78,7 @@ $.bind = (element, o) => {
} }
}; };
$.unbind = (element, o) => { $$1.unbind = (element, o) => {
if (element) { if (element) {
for (var event in o) { for (var event in o) {
var callback = o[event]; var callback = o[event];
@ -90,7 +90,7 @@ $.unbind = (element, o) => {
} }
}; };
$.fire = (target, type, properties) => { $$1.fire = (target, type, properties) => {
var evt = document.createEvent("HTMLEvents"); var evt = document.createEvent("HTMLEvents");
evt.initEvent(type, true, true ); evt.initEvent(type, true, true );
@ -102,6 +102,59 @@ $.fire = (target, type, properties) => {
return target.dispatchEvent(evt); return target.dispatchEvent(evt);
}; };
/**
* Returns the value of a number upto 2 decimal places.
* @param {Number} d Any number
*/
function floatTwo(d) {
return parseFloat(d.toFixed(2));
}
/**
* Returns whether or not two given arrays are equal.
* @param {Array} arr1 First array
* @param {Array} arr2 Second array
*/
function arraysEqual(arr1, arr2) {
if(arr1.length !== arr2.length) return false;
let areEqual = true;
arr1.map((d, i) => {
if(arr2[i] !== d) areEqual = false;
});
return areEqual;
}
/**
* Shuffles array in place. ES6 version
* @param {Array} array An array containing the items.
*/
/**
* Fill an array with extra points
* @param {Array} array Array
* @param {Number} count number of filler elements
* @param {Object} element element to fill with
* @param {Boolean} start fill at start?
*/
function fillArray(array, count, element, start=false) {
if(!element) {
element = start ? array[0] : array[array.length - 1];
}
let fillerArray = new Array(Math.abs(count)).fill(element);
array = start ? fillerArray.concat(array) : array.concat(fillerArray);
return array;
}
/**
* Returns pixel width of string.
* @param {String} string
* @param {Number} charWidth Width of single char in pixels
*/
function getStringWidth(string, charWidth) {
return (string+"").length * charWidth;
}
function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) { function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
let height, y; let height, y;
if (yTop <= zeroLine) { if (yTop <= zeroLine) {
@ -126,9 +179,25 @@ function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
return [height, y]; return [height, y];
} }
function equilizeNoOfPositions(old_x, old_y, new_x, new_y) {
let extra_count = new_x.length - old_x.length;
if(extra_count >= 0) {
// Substitute current unit set with a squiggled one (more units at end)
// in order to animate to stretch it later to new points
old_x = fillArray(old_x, extra_count);
old_y = fillArray(old_y, extra_count);
} else {
// Modify the new points to have extra points
// with the same position at end, old positions will squeeze in
new_x = fillArray(new_x, extra_count);
new_y = fillArray(new_y, extra_count);
}
return [old_x, old_y, new_x, new_y];
}
// Constants used // Constants used
function $$1(expr, con) { function $$2(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
} }
@ -139,10 +208,10 @@ function createSVG(tag, o) {
var val = o[i]; var val = o[i];
if (i === "inside") { if (i === "inside") {
$$1(val).appendChild(element); $$2(val).appendChild(element);
} }
else if (i === "around") { else if (i === "around") {
var ref = $$1(val); var ref = $$2(val);
ref.parentNode.insertBefore(element, ref); ref.parentNode.insertBefore(element, ref);
element.appendChild(ref); element.appendChild(ref);
@ -694,43 +763,6 @@ function getMaxCheckpoint(value, distribution) {
return distribution.filter(d => d < value).length; return distribution.filter(d => d < value).length;
} }
/**
* Returns the value of a number upto 2 decimal places.
* @param {Number} d Any number
*/
function floatTwo(d) {
return parseFloat(d.toFixed(2));
}
/**
* Returns whether or not two given arrays are equal.
* @param {Array} arr1 First array
* @param {Array} arr2 Second array
*/
function arraysEqual(arr1, arr2) {
if(arr1.length !== arr2.length) return false;
let areEqual = true;
arr1.map((d, i) => {
if(arr2[i] !== d) areEqual = false;
});
return areEqual;
}
/**
* Shuffles array in place. ES6 version
* @param {Array} array An array containing the items.
*/
/**
* Returns pixel width of string.
* @param {String} string
* @param {Number} charWidth Width of single char in pixels
*/
function getStringWidth(string, charWidth) {
return (string+"").length * charWidth;
}
class SvgTip { class SvgTip {
constructor({ constructor({
parent = null, parent = null,
@ -763,7 +795,7 @@ class SvgTip {
} }
make_tooltip() { make_tooltip() {
this.container = $.create('div', { this.container = $$1.create('div', {
inside: this.parent, inside: this.parent,
className: 'graph-svg-tip comparison', className: 'graph-svg-tip comparison',
innerHTML: `<span class="title"></span> innerHTML: `<span class="title"></span>
@ -793,7 +825,7 @@ class SvgTip {
this.list_values.map((set, i) => { this.list_values.map((set, i) => {
const color = this.colors[i] || 'black'; const color = this.colors[i] || 'black';
let li = $.create('li', { let li = $$1.create('li', {
styles: { styles: {
'border-top': `3px solid ${color}` 'border-top': `3px solid ${color}`
}, },
@ -911,7 +943,7 @@ const COMPATIBLE_CHARTS = {
heatmap: [] heatmap: []
}; };
// TODO: Needs structure as per only labels/datasets // Needs structure as per only labels/datasets
const COLOR_COMPATIBLE_CHARTS = { const COLOR_COMPATIBLE_CHARTS = {
bar: ['line', 'scatter'], bar: ['line', 'scatter'],
line: ['scatter', 'bar'], line: ['scatter', 'bar'],
@ -945,6 +977,7 @@ class BaseChart {
this.subtitle = subtitle; this.subtitle = subtitle;
this.data = data; this.data = data;
this.oldData = Object.assign({}, data);
this.specific_values = data.specific_values || []; this.specific_values = data.specific_values || [];
this.summary = summary; this.summary = summary;
@ -989,7 +1022,7 @@ class BaseChart {
setColors(colors, type) { setColors(colors, type) {
this.colors = colors; this.colors = colors;
// TODO: Needs structure as per only labels/datasets // Needs structure as per only labels/datasets
const list = type === 'percentage' || type === 'pie' const list = type === 'percentage' || type === 'pie'
? this.data.labels ? this.data.labels
: this.data.datasets; : this.data.datasets;
@ -1068,7 +1101,7 @@ class BaseChart {
setup_base_values() {} setup_base_values() {}
setup_container() { setup_container() {
this.container = $.create('div', { this.container = $$1.create('div', {
className: 'chart-container', className: 'chart-container',
innerHTML: `<h6 class="title">${this.title}</h6> innerHTML: `<h6 class="title">${this.title}</h6>
<h6 class="sub-title uppercase">${this.subtitle}</h6> <h6 class="sub-title uppercase">${this.subtitle}</h6>
@ -1106,7 +1139,9 @@ class BaseChart {
); );
} }
setup_components() { } setup_components() {}
setup_values() {}
setup_utils() {}
make_tooltip() { make_tooltip() {
this.tip = new SvgTip({ this.tip = new SvgTip({
@ -1116,11 +1151,10 @@ class BaseChart {
this.bind_tooltip(); this.bind_tooltip();
} }
show_summary() {} show_summary() {}
show_custom_summary() { show_custom_summary() {
this.summary.map(d => { this.summary.map(d => {
let stats = $.create('div', { let stats = $$1.create('div', {
className: 'stats', className: 'stats',
innerHTML: `<span class="indicator"> innerHTML: `<span class="indicator">
<i style="background:${d.color}"></i> <i style="background:${d.color}"></i>
@ -1167,31 +1201,10 @@ class BaseChart {
on_down_arrow() {} on_down_arrow() {}
on_enter_key() {} on_enter_key() {}
get_data_point(index=this.current_index) { updateData() {}
// check for length
let data_point = {
index: index
};
let y = this.y[0];
['svg_units', 'y_tops', 'values'].map(key => {
let data_key = key.slice(0, key.length-1);
data_point[data_key] = y[key][index];
});
data_point.label = this.x[index];
return data_point;
}
update_current_data_point(index) { getDataPoint() {}
index = parseInt(index); updateCurrentDataPoint() {}
if(index < 0) index = 0;
if(index >= this.x.length) index = this.x.length - 1;
if(index === this.current_index) return;
this.current_index = index;
$.fire(this.parent, "data-select", this.get_data_point());
}
// Objects
setup_utils() { }
makeDrawAreaComponent(className, transform='') { makeDrawAreaComponent(className, transform='') {
return makeSVGGroup(this.draw_area, className, transform); return makeSVGGroup(this.draw_area, className, transform);
@ -1211,11 +1224,12 @@ class AxisChart extends BaseChart {
this.format_tooltip_x = args.format_tooltip_x; this.format_tooltip_x = args.format_tooltip_x;
this.zero_line = this.height; this.zero_line = this.height;
// this.old_values = {};
} }
validate_and_prepare_data() { validate_and_prepare_data() {
this.y.forEach(function(d, i) {
d.index = i;
}, this);
return true; return true;
} }
@ -1426,8 +1440,8 @@ class AxisChart extends BaseChart {
if(this.raw_chart_args.hasOwnProperty("init") && !this.raw_chart_args.init) { if(this.raw_chart_args.hasOwnProperty("init") && !this.raw_chart_args.init) {
this.y.map((d, i) => { this.y.map((d, i) => {
d.svg_units = []; d.svg_units = [];
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[i]);
this.make_new_units(d, i); this.make_new_units(d);
this.calc_y_dependencies(); this.calc_y_dependencies();
}); });
return; return;
@ -1438,8 +1452,8 @@ class AxisChart extends BaseChart {
} }
this.y.map((d, i) => { this.y.map((d, i) => {
d.svg_units = []; d.svg_units = [];
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[i]);
this.make_new_units(d, i); this.make_new_units(d);
}); });
} }
@ -1451,8 +1465,8 @@ class AxisChart extends BaseChart {
data.push({values: d.values}); data.push({values: d.values});
d.svg_units = []; d.svg_units = [];
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[i]);
this.make_new_units(d, i); this.make_new_units(d);
}); });
setTimeout(() => { setTimeout(() => {
@ -1471,12 +1485,12 @@ class AxisChart extends BaseChart {
} }
} }
make_new_units(d, i) { make_new_units(d) {
this.make_new_units_for_dataset( this.make_new_units_for_dataset(
this.x_axis_positions, this.x_axis_positions,
d.y_tops, d.y_tops,
this.colors[i], this.colors[d.index],
i, d.index,
this.y.length this.y.length
); );
} }
@ -1606,7 +1620,7 @@ class AxisChart extends BaseChart {
this.sum_units this.sum_units
); );
// this.make_path && this.make_path(d, i, old_x, old_y, this.colors[i]); // this.make_path && this.make_path(d, old_x, old_y, this.colors[i]);
this.updating = false; this.updating = false;
} }
@ -1621,13 +1635,13 @@ class AxisChart extends BaseChart {
show_averages() { show_averages() {
this.old_specific_values = this.specific_values.slice(); this.old_specific_values = this.specific_values.slice();
this.y.map((d, i) => { this.y.map(d => {
let sum = 0; let sum = 0;
d.values.map(e => {sum+=e;}); d.values.map(e => {sum+=e;});
let average = sum/d.values.length; let average = sum/d.values.length;
this.specific_values.push({ this.specific_values.push({
title: "AVG" + " " + (i+1), title: "AVG" + " " + (d.index+1),
line_type: "dashed", line_type: "dashed",
value: average, value: average,
auto: 1 auto: 1
@ -1664,10 +1678,8 @@ class AxisChart extends BaseChart {
this.old_y_values = this.y.map(d => d.values); this.old_y_values = this.y.map(d => d.values);
this.no_of_extra_pts = new_x.length - this.x.length;
// Just update values prop, setup_x/y() will do the rest // Just update values prop, setup_x/y() will do the rest
if(new_y) this.y.map((d, i) => {d.values = new_y[i].values;}); if(new_y) this.y.map(d => {d.values = new_y[d.index].values;});
if(new_x) this.x = new_x; if(new_x) this.x = new_x;
this.setup_x(); this.setup_x();
@ -1747,34 +1759,38 @@ class AxisChart extends BaseChart {
} }
animate_graphs() { animate_graphs() {
this.y.map((d, i) => { this.y.map(d => {
// Pre-prep, equilize no of positions between old and new // Pre-prep, equilize no of positions between old and new
let [old_x, old_y, new_x, new_y] = this.calc_old_and_new_postions(d, i); let [old_x, old_y, new_x, new_y] = equilizeNoOfPositions(
if(this.no_of_extra_pts >= 0) { this.x_old_axis_positions.slice(),
this.make_path && this.make_path(d, i, old_x, old_y, this.colors[i]); this.old_y_axis_tops[d.index].slice(),
this.make_new_units_for_dataset(old_x, old_y, this.colors[i], i, this.y.length); this.x_axis_positions.slice(),
d.y_tops.slice()
);
if(new_x.length - old_x.length > 0) {
this.make_path && this.make_path(d, old_x, old_y, this.colors[d.index]);
this.make_new_units_for_dataset(old_x, old_y, this.colors[d.index], d.index, this.y.length);
} }
d.path && this.animate_path(d, i, old_x, old_y, new_x, new_y); d.path && this.animate_path(d, new_x, new_y);
this.animate_units(d, i, old_x, old_y, new_x, new_y); this.animate_units(d, old_x, old_y, new_x, new_y);
}); });
// TODO: replace with real units // TODO: replace with real units
setTimeout(() => { setTimeout(() => {
this.y.map((d, i) => { this.y.map(d => {
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[d.index]);
this.make_new_units(d, i); this.make_new_units(d);
}); });
}, 400); }, 400);
} }
animate_path(d, i, old_x, old_y, new_x, new_y) { animate_path(d, new_x, new_y) {
const newPointsList = new_y.map((y, i) => (new_x[i] + ',' + y)); const newPointsList = new_y.map((y, i) => (new_x[i] + ',' + y));
const newPathStr = newPointsList.join("L");
this.elements_to_animate = this.elements_to_animate this.elements_to_animate = this.elements_to_animate
.concat(this.animator['path'](d, newPathStr)); .concat(this.animator['path'](d, newPointsList.join("L")));
} }
animate_units(d, index, old_x, old_y, new_x, new_y) { animate_units(d, old_x, old_y, new_x, new_y) {
let type = this.unit_args.type; let type = this.unit_args.type;
d.svg_units.map((unit, i) => { d.svg_units.map((unit, i) => {
@ -1783,51 +1799,12 @@ class AxisChart extends BaseChart {
{unit:unit, array:d.svg_units, index: i}, // unit, with info to replace where it came from in the data {unit:unit, array:d.svg_units, index: i}, // unit, with info to replace where it came from in the data
new_x[i], new_x[i],
new_y[i], new_y[i],
index, d.index,
this.y.length this.y.length
)); ));
}); });
} }
calc_old_and_new_postions(d, i) {
let old_x = this.x_old_axis_positions.slice();
let new_x = this.x_axis_positions.slice();
let old_y = this.old_y_axis_tops[i].slice();
let new_y = d.y_tops.slice();
const last_old_x_pos = old_x[old_x.length - 1];
const last_old_y_pos = old_y[old_y.length - 1];
const last_new_x_pos = new_x[new_x.length - 1];
const last_new_y_pos = new_y[new_y.length - 1];
if(this.no_of_extra_pts >= 0) {
// First substitute current path with a squiggled one
// (that looks the same but has more points at end),
// then animate to stretch it later to new points
// (new points already have more points)
// Hence, the extra end points will correspond to current(old) positions
let filler_x = new Array(Math.abs(this.no_of_extra_pts)).fill(last_old_x_pos);
let filler_y = new Array(Math.abs(this.no_of_extra_pts)).fill(last_old_y_pos);
old_x = old_x.concat(filler_x);
old_y = old_y.concat(filler_y);
} else {
// Just modify the new points to have extra points
// with the same position at end
let filler_x = new Array(Math.abs(this.no_of_extra_pts)).fill(last_new_x_pos);
let filler_y = new Array(Math.abs(this.no_of_extra_pts)).fill(last_new_y_pos);
new_x = new_x.concat(filler_x);
new_y = new_y.concat(filler_y);
}
return [old_x, old_y, new_x, new_y];
}
make_anim_x_axis(height, text_start_at, axis_line_class) { make_anim_x_axis(height, text_start_at, axis_line_class) {
// Animate X AXIS to account for more or less axis lines // Animate X AXIS to account for more or less axis lines
@ -1993,6 +1970,29 @@ class AxisChart extends BaseChart {
]); ]);
} }
getDataPoint(index=this.current_index) {
// check for length
let data_point = {
index: index
};
let y = this.y[0];
['svg_units', 'y_tops', 'values'].map(key => {
let data_key = key.slice(0, key.length-1);
data_point[data_key] = y[key][index];
});
data_point.label = this.x[index];
return data_point;
}
updateCurrentDataPoint(index) {
index = parseInt(index);
if(index < 0) index = 0;
if(index >= this.x.length) index = this.x.length - 1;
if(index === this.current_index) return;
this.current_index = index;
$.fire(this.parent, "data-select", this.getDataPoint());
}
set_avg_unit_width_and_x_offset() { set_avg_unit_width_and_x_offset() {
// Set the ... you get it // Set the ... you get it
this.avg_unit_width = this.width/(this.x.length - 1); this.avg_unit_width = this.width/(this.x.length - 1);
@ -2012,7 +2012,7 @@ class AxisChart extends BaseChart {
} }
calc_y_dependencies() { calc_y_dependencies() {
this.y_min_tops = new Array(this.x_axis_positions.length).fill(9999); this.y_min_tops = new Array(this.x.length).fill(9999);
this.y.map(d => { this.y.map(d => {
d.y_tops = d.values.map( val => floatTwo(this.zero_line - val * this.multiplier)); d.y_tops = d.values.map( val => floatTwo(this.zero_line - val * this.multiplier));
d.y_tops.map( (y_top, i) => { d.y_tops.map( (y_top, i) => {
@ -2051,7 +2051,7 @@ class BarChart extends AxisChart {
// Just make one out of the first element // Just make one out of the first element
let index = this.x.length - 1; let index = this.x.length - 1;
let unit = this.y[0].svg_units[index]; let unit = this.y[0].svg_units[index];
this.update_current_data_point(index); this.updateCurrentDataPoint(index);
if(this.overlay) { if(this.overlay) {
this.overlay.parentNode.removeChild(this.overlay); this.overlay.parentNode.removeChild(this.overlay);
@ -2073,7 +2073,7 @@ class BarChart extends AxisChart {
units_array.map(unit => { units_array.map(unit => {
unit.addEventListener('click', () => { unit.addEventListener('click', () => {
let index = unit.getAttribute('data-point-index'); let index = unit.getAttribute('data-point-index');
this.update_current_data_point(index); this.updateCurrentDataPoint(index);
}); });
}); });
} }
@ -2093,11 +2093,11 @@ class BarChart extends AxisChart {
} }
on_left_arrow() { on_left_arrow() {
this.update_current_data_point(this.current_index - 1); this.updateCurrentDataPoint(this.current_index - 1);
} }
on_right_arrow() { on_right_arrow() {
this.update_current_data_point(this.current_index + 1); this.updateCurrentDataPoint(this.current_index + 1);
} }
set_avg_unit_width_and_x_offset() { set_avg_unit_width_and_x_offset() {
@ -2162,19 +2162,19 @@ class LineChart extends AxisChart {
} }
make_paths() { make_paths() {
this.y.map((d, i) => { this.y.map(d => {
this.make_path(d, i, this.x_axis_positions, d.y_tops, d.color || this.colors[i]); this.make_path(d, this.x_axis_positions, d.y_tops, d.color || this.colors[d.index]);
}); });
} }
make_path(d, i, x_positions, y_positions, color) { make_path(d, x_positions, y_positions, color) {
let points_list = y_positions.map((y, i) => (x_positions[i] + ',' + y)); let points_list = y_positions.map((y, i) => (x_positions[i] + ',' + y));
let points_str = points_list.join("L"); let points_str = points_list.join("L");
this.paths_groups[i].textContent = ''; this.paths_groups[d.index].textContent = '';
d.path = makePath("M"+points_str, 'line-graph-path', color); d.path = makePath("M"+points_str, 'line-graph-path', color);
this.paths_groups[i].appendChild(d.path); this.paths_groups[d.index].appendChild(d.path);
if(this.heatline) { if(this.heatline) {
let gradient_id = makeGradient(this.svg_defs, color); let gradient_id = makeGradient(this.svg_defs, color);
@ -2182,16 +2182,16 @@ class LineChart extends AxisChart {
} }
if(this.region_fill) { if(this.region_fill) {
this.fill_region_for_dataset(d, i, color, points_str); this.fill_region_for_dataset(d, color, points_str);
} }
} }
fill_region_for_dataset(d, i, color, points_str) { fill_region_for_dataset(d, color, points_str) {
let gradient_id = makeGradient(this.svg_defs, color, true); let gradient_id = makeGradient(this.svg_defs, color, true);
let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`; let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`;
d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`); d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`);
this.paths_groups[i].appendChild(d.regionPath); this.paths_groups[d.index].appendChild(d.regionPath);
} }
} }
@ -2250,19 +2250,19 @@ class PercentageChart extends BaseChart {
} }
make_draw_area() { make_draw_area() {
this.chart_div = $.create('div', { this.chart_div = $$1.create('div', {
className: 'div', className: 'div',
inside: this.chart_wrapper inside: this.chart_wrapper
}); });
this.chart = $.create('div', { this.chart = $$1.create('div', {
className: 'progress-chart', className: 'progress-chart',
inside: this.chart_div inside: this.chart_div
}); });
} }
setup_components() { setup_components() {
this.percentage_bar = $.create('div', { this.percentage_bar = $$1.create('div', {
className: 'progress', className: 'progress',
inside: this.chart inside: this.chart
}); });
@ -2307,7 +2307,7 @@ class PercentageChart extends BaseChart {
this.grand_total = this.slice_totals.reduce((a, b) => a + b, 0); this.grand_total = this.slice_totals.reduce((a, b) => a + b, 0);
this.slices = []; this.slices = [];
this.slice_totals.map((total, i) => { this.slice_totals.map((total, i) => {
let slice = $.create('div', { let slice = $$1.create('div', {
className: `progress-bar`, className: `progress-bar`,
inside: this.percentage_bar, inside: this.percentage_bar,
styles: { styles: {
@ -2341,7 +2341,7 @@ class PercentageChart extends BaseChart {
? this.formatted_labels : this.labels; ? this.formatted_labels : this.labels;
this.legend_totals.map((d, i) => { this.legend_totals.map((d, i) => {
if(d) { if(d) {
let stats = $.create('div', { let stats = $$1.create('div', {
className: 'stats', className: 'stats',
inside: this.stats_wrapper inside: this.stats_wrapper
}); });
@ -2550,7 +2550,7 @@ class PieChart extends BaseChart {
const color = this.colors[i]; const color = this.colors[i];
if(d) { if(d) {
let stats = $.create('div', { let stats = $$1.create('div', {
className: 'stats', className: 'stats',
inside: this.stats_wrapper inside: this.stats_wrapper
}); });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,6 @@
import { offset } from '../utils/dom'; import { offset } from '../utils/dom';
import { UnitRenderer, makeXLine, makeYLine } from '../utils/draw'; import { UnitRenderer, makeXLine, makeYLine } from '../utils/draw';
import { equilizeNoOfPositions } from '../utils/draw-utils';
import { Animator } from '../utils/animate'; import { Animator } from '../utils/animate';
import { runSVGAnimation } from '../utils/animation'; import { runSVGAnimation } from '../utils/animation';
import { calcIntervals } from '../utils/intervals'; import { calcIntervals } from '../utils/intervals';
@ -19,11 +20,12 @@ export default class AxisChart extends BaseChart {
this.format_tooltip_x = args.format_tooltip_x; this.format_tooltip_x = args.format_tooltip_x;
this.zero_line = this.height; this.zero_line = this.height;
// this.old_values = {};
} }
validate_and_prepare_data() { validate_and_prepare_data() {
this.y.forEach(function(d, i) {
d.index = i;
}, this);
return true; return true;
} }
@ -234,8 +236,8 @@ export default class AxisChart extends BaseChart {
if(this.raw_chart_args.hasOwnProperty("init") && !this.raw_chart_args.init) { if(this.raw_chart_args.hasOwnProperty("init") && !this.raw_chart_args.init) {
this.y.map((d, i) => { this.y.map((d, i) => {
d.svg_units = []; d.svg_units = [];
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[i]);
this.make_new_units(d, i); this.make_new_units(d);
this.calc_y_dependencies(); this.calc_y_dependencies();
}); });
return; return;
@ -246,8 +248,8 @@ export default class AxisChart extends BaseChart {
} }
this.y.map((d, i) => { this.y.map((d, i) => {
d.svg_units = []; d.svg_units = [];
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[i]);
this.make_new_units(d, i); this.make_new_units(d);
}); });
} }
@ -259,8 +261,8 @@ export default class AxisChart extends BaseChart {
data.push({values: d.values}); data.push({values: d.values});
d.svg_units = []; d.svg_units = [];
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[i]);
this.make_new_units(d, i); this.make_new_units(d);
}); });
setTimeout(() => { setTimeout(() => {
@ -279,12 +281,12 @@ export default class AxisChart extends BaseChart {
} }
} }
make_new_units(d, i) { make_new_units(d) {
this.make_new_units_for_dataset( this.make_new_units_for_dataset(
this.x_axis_positions, this.x_axis_positions,
d.y_tops, d.y_tops,
this.colors[i], this.colors[d.index],
i, d.index,
this.y.length this.y.length
); );
} }
@ -414,7 +416,7 @@ export default class AxisChart extends BaseChart {
this.sum_units this.sum_units
); );
// this.make_path && this.make_path(d, i, old_x, old_y, this.colors[i]); // this.make_path && this.make_path(d, old_x, old_y, this.colors[i]);
this.updating = false; this.updating = false;
} }
@ -429,13 +431,13 @@ export default class AxisChart extends BaseChart {
show_averages() { show_averages() {
this.old_specific_values = this.specific_values.slice(); this.old_specific_values = this.specific_values.slice();
this.y.map((d, i) => { this.y.map(d => {
let sum = 0; let sum = 0;
d.values.map(e => {sum+=e;}); d.values.map(e => {sum+=e;});
let average = sum/d.values.length; let average = sum/d.values.length;
this.specific_values.push({ this.specific_values.push({
title: "AVG" + " " + (i+1), title: "AVG" + " " + (d.index+1),
line_type: "dashed", line_type: "dashed",
value: average, value: average,
auto: 1 auto: 1
@ -472,10 +474,8 @@ export default class AxisChart extends BaseChart {
this.old_y_values = this.y.map(d => d.values); this.old_y_values = this.y.map(d => d.values);
this.no_of_extra_pts = new_x.length - this.x.length;
// Just update values prop, setup_x/y() will do the rest // Just update values prop, setup_x/y() will do the rest
if(new_y) this.y.map((d, i) => {d.values = new_y[i].values;}); if(new_y) this.y.map(d => {d.values = new_y[d.index].values;});
if(new_x) this.x = new_x; if(new_x) this.x = new_x;
this.setup_x(); this.setup_x();
@ -555,34 +555,38 @@ export default class AxisChart extends BaseChart {
} }
animate_graphs() { animate_graphs() {
this.y.map((d, i) => { this.y.map(d => {
// Pre-prep, equilize no of positions between old and new // Pre-prep, equilize no of positions between old and new
let [old_x, old_y, new_x, new_y] = this.calc_old_and_new_postions(d, i); let [old_x, old_y, new_x, new_y] = equilizeNoOfPositions(
if(this.no_of_extra_pts >= 0) { this.x_old_axis_positions.slice(),
this.make_path && this.make_path(d, i, old_x, old_y, this.colors[i]); this.old_y_axis_tops[d.index].slice(),
this.make_new_units_for_dataset(old_x, old_y, this.colors[i], i, this.y.length); this.x_axis_positions.slice(),
d.y_tops.slice()
);
if(new_x.length - old_x.length > 0) {
this.make_path && this.make_path(d, old_x, old_y, this.colors[d.index]);
this.make_new_units_for_dataset(old_x, old_y, this.colors[d.index], d.index, this.y.length);
} }
d.path && this.animate_path(d, i, old_x, old_y, new_x, new_y); d.path && this.animate_path(d, new_x, new_y);
this.animate_units(d, i, old_x, old_y, new_x, new_y); this.animate_units(d, old_x, old_y, new_x, new_y);
}); });
// TODO: replace with real units // TODO: replace with real units
setTimeout(() => { setTimeout(() => {
this.y.map((d, i) => { this.y.map(d => {
this.make_path && this.make_path(d, i, this.x_axis_positions, d.y_tops, this.colors[i]); this.make_path && this.make_path(d, this.x_axis_positions, d.y_tops, this.colors[d.index]);
this.make_new_units(d, i); this.make_new_units(d);
}); });
}, 400); }, 400);
} }
animate_path(d, i, old_x, old_y, new_x, new_y) { animate_path(d, new_x, new_y) {
const newPointsList = new_y.map((y, i) => (new_x[i] + ',' + y)); const newPointsList = new_y.map((y, i) => (new_x[i] + ',' + y));
const newPathStr = newPointsList.join("L");
this.elements_to_animate = this.elements_to_animate this.elements_to_animate = this.elements_to_animate
.concat(this.animator['path'](d, newPathStr)); .concat(this.animator['path'](d, newPointsList.join("L")));
} }
animate_units(d, index, old_x, old_y, new_x, new_y) { animate_units(d, old_x, old_y, new_x, new_y) {
let type = this.unit_args.type; let type = this.unit_args.type;
d.svg_units.map((unit, i) => { d.svg_units.map((unit, i) => {
@ -591,51 +595,12 @@ export default class AxisChart extends BaseChart {
{unit:unit, array:d.svg_units, index: i}, // unit, with info to replace where it came from in the data {unit:unit, array:d.svg_units, index: i}, // unit, with info to replace where it came from in the data
new_x[i], new_x[i],
new_y[i], new_y[i],
index, d.index,
this.y.length this.y.length
)); ));
}); });
} }
calc_old_and_new_postions(d, i) {
let old_x = this.x_old_axis_positions.slice();
let new_x = this.x_axis_positions.slice();
let old_y = this.old_y_axis_tops[i].slice();
let new_y = d.y_tops.slice();
const last_old_x_pos = old_x[old_x.length - 1];
const last_old_y_pos = old_y[old_y.length - 1];
const last_new_x_pos = new_x[new_x.length - 1];
const last_new_y_pos = new_y[new_y.length - 1];
if(this.no_of_extra_pts >= 0) {
// First substitute current path with a squiggled one
// (that looks the same but has more points at end),
// then animate to stretch it later to new points
// (new points already have more points)
// Hence, the extra end points will correspond to current(old) positions
let filler_x = new Array(Math.abs(this.no_of_extra_pts)).fill(last_old_x_pos);
let filler_y = new Array(Math.abs(this.no_of_extra_pts)).fill(last_old_y_pos);
old_x = old_x.concat(filler_x);
old_y = old_y.concat(filler_y);
} else {
// Just modify the new points to have extra points
// with the same position at end
let filler_x = new Array(Math.abs(this.no_of_extra_pts)).fill(last_new_x_pos);
let filler_y = new Array(Math.abs(this.no_of_extra_pts)).fill(last_new_y_pos);
new_x = new_x.concat(filler_x);
new_y = new_y.concat(filler_y);
}
return [old_x, old_y, new_x, new_y];
}
make_anim_x_axis(height, text_start_at, axis_line_class) { make_anim_x_axis(height, text_start_at, axis_line_class) {
// Animate X AXIS to account for more or less axis lines // Animate X AXIS to account for more or less axis lines
@ -801,6 +766,29 @@ export default class AxisChart extends BaseChart {
]); ]);
} }
getDataPoint(index=this.current_index) {
// check for length
let data_point = {
index: index
};
let y = this.y[0];
['svg_units', 'y_tops', 'values'].map(key => {
let data_key = key.slice(0, key.length-1);
data_point[data_key] = y[key][index];
});
data_point.label = this.x[index];
return data_point;
}
updateCurrentDataPoint(index) {
index = parseInt(index);
if(index < 0) index = 0;
if(index >= this.x.length) index = this.x.length - 1;
if(index === this.current_index) return;
this.current_index = index;
$.fire(this.parent, "data-select", this.getDataPoint());
}
set_avg_unit_width_and_x_offset() { set_avg_unit_width_and_x_offset() {
// Set the ... you get it // Set the ... you get it
this.avg_unit_width = this.width/(this.x.length - 1); this.avg_unit_width = this.width/(this.x.length - 1);
@ -820,7 +808,7 @@ export default class AxisChart extends BaseChart {
} }
calc_y_dependencies() { calc_y_dependencies() {
this.y_min_tops = new Array(this.x_axis_positions.length).fill(9999); this.y_min_tops = new Array(this.x.length).fill(9999);
this.y.map(d => { this.y.map(d => {
d.y_tops = d.values.map( val => floatTwo(this.zero_line - val * this.multiplier)); d.y_tops = d.values.map( val => floatTwo(this.zero_line - val * this.multiplier));
d.y_tops.map( (y_top, i) => { d.y_tops.map( (y_top, i) => {

View File

@ -25,7 +25,7 @@ export default class BarChart extends AxisChart {
// Just make one out of the first element // Just make one out of the first element
let index = this.x.length - 1; let index = this.x.length - 1;
let unit = this.y[0].svg_units[index]; let unit = this.y[0].svg_units[index];
this.update_current_data_point(index); this.updateCurrentDataPoint(index);
if(this.overlay) { if(this.overlay) {
this.overlay.parentNode.removeChild(this.overlay); this.overlay.parentNode.removeChild(this.overlay);
@ -47,7 +47,7 @@ export default class BarChart extends AxisChart {
units_array.map(unit => { units_array.map(unit => {
unit.addEventListener('click', () => { unit.addEventListener('click', () => {
let index = unit.getAttribute('data-point-index'); let index = unit.getAttribute('data-point-index');
this.update_current_data_point(index); this.updateCurrentDataPoint(index);
}); });
}); });
} }
@ -67,11 +67,11 @@ export default class BarChart extends AxisChart {
} }
on_left_arrow() { on_left_arrow() {
this.update_current_data_point(this.current_index - 1); this.updateCurrentDataPoint(this.current_index - 1);
} }
on_right_arrow() { on_right_arrow() {
this.update_current_data_point(this.current_index + 1); this.updateCurrentDataPoint(this.current_index + 1);
} }
set_avg_unit_width_and_x_offset() { set_avg_unit_width_and_x_offset() {

View File

@ -16,7 +16,7 @@ const COMPATIBLE_CHARTS = {
heatmap: [] heatmap: []
}; };
// TODO: Needs structure as per only labels/datasets // Needs structure as per only labels/datasets
const COLOR_COMPATIBLE_CHARTS = { const COLOR_COMPATIBLE_CHARTS = {
bar: ['line', 'scatter'], bar: ['line', 'scatter'],
line: ['scatter', 'bar'], line: ['scatter', 'bar'],
@ -50,6 +50,7 @@ export default class BaseChart {
this.subtitle = subtitle; this.subtitle = subtitle;
this.data = data; this.data = data;
this.oldData = Object.assign({}, data);
this.specific_values = data.specific_values || []; this.specific_values = data.specific_values || [];
this.summary = summary; this.summary = summary;
@ -94,7 +95,7 @@ export default class BaseChart {
setColors(colors, type) { setColors(colors, type) {
this.colors = colors; this.colors = colors;
// TODO: Needs structure as per only labels/datasets // Needs structure as per only labels/datasets
const list = type === 'percentage' || type === 'pie' const list = type === 'percentage' || type === 'pie'
? this.data.labels ? this.data.labels
: this.data.datasets; : this.data.datasets;
@ -211,7 +212,9 @@ export default class BaseChart {
); );
} }
setup_components() { } setup_components() {}
setup_values() {}
setup_utils() {}
make_tooltip() { make_tooltip() {
this.tip = new SvgTip({ this.tip = new SvgTip({
@ -221,7 +224,6 @@ export default class BaseChart {
this.bind_tooltip(); this.bind_tooltip();
} }
show_summary() {} show_summary() {}
show_custom_summary() { show_custom_summary() {
this.summary.map(d => { this.summary.map(d => {
@ -272,31 +274,10 @@ export default class BaseChart {
on_down_arrow() {} on_down_arrow() {}
on_enter_key() {} on_enter_key() {}
get_data_point(index=this.current_index) { updateData() {}
// check for length
let data_point = {
index: index
};
let y = this.y[0];
['svg_units', 'y_tops', 'values'].map(key => {
let data_key = key.slice(0, key.length-1);
data_point[data_key] = y[key][index];
});
data_point.label = this.x[index];
return data_point;
}
update_current_data_point(index) { getDataPoint() {}
index = parseInt(index); updateCurrentDataPoint() {}
if(index < 0) index = 0;
if(index >= this.x.length) index = this.x.length - 1;
if(index === this.current_index) return;
this.current_index = index;
$.fire(this.parent, "data-select", this.get_data_point());
}
// Objects
setup_utils() { }
makeDrawAreaComponent(className, transform='') { makeDrawAreaComponent(className, transform='') {
return makeSVGGroup(this.draw_area, className, transform); return makeSVGGroup(this.draw_area, className, transform);

View File

@ -57,19 +57,19 @@ export default class LineChart extends AxisChart {
} }
make_paths() { make_paths() {
this.y.map((d, i) => { this.y.map(d => {
this.make_path(d, i, this.x_axis_positions, d.y_tops, d.color || this.colors[i]); this.make_path(d, this.x_axis_positions, d.y_tops, d.color || this.colors[d.index]);
}); });
} }
make_path(d, i, x_positions, y_positions, color) { make_path(d, x_positions, y_positions, color) {
let points_list = y_positions.map((y, i) => (x_positions[i] + ',' + y)); let points_list = y_positions.map((y, i) => (x_positions[i] + ',' + y));
let points_str = points_list.join("L"); let points_str = points_list.join("L");
this.paths_groups[i].textContent = ''; this.paths_groups[d.index].textContent = '';
d.path = makePath("M"+points_str, 'line-graph-path', color); d.path = makePath("M"+points_str, 'line-graph-path', color);
this.paths_groups[i].appendChild(d.path); this.paths_groups[d.index].appendChild(d.path);
if(this.heatline) { if(this.heatline) {
let gradient_id = makeGradient(this.svg_defs, color); let gradient_id = makeGradient(this.svg_defs, color);
@ -77,15 +77,15 @@ export default class LineChart extends AxisChart {
} }
if(this.region_fill) { if(this.region_fill) {
this.fill_region_for_dataset(d, i, color, points_str); this.fill_region_for_dataset(d, color, points_str);
} }
} }
fill_region_for_dataset(d, i, color, points_str) { fill_region_for_dataset(d, color, points_str) {
let gradient_id = makeGradient(this.svg_defs, color, true); let gradient_id = makeGradient(this.svg_defs, color, true);
let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`; let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`;
d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`); d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`);
this.paths_groups[i].appendChild(d.regionPath); this.paths_groups[d.index].appendChild(d.regionPath);
} }
} }

View File

@ -1,3 +1,5 @@
import { fillArray } from '../utils/helpers';
export function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) { export function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
let height, y; let height, y;
if (yTop <= zeroLine) { if (yTop <= zeroLine) {
@ -21,3 +23,19 @@ export function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
return [height, y]; return [height, y];
} }
export function equilizeNoOfPositions(old_x, old_y, new_x, new_y) {
let extra_count = new_x.length - old_x.length;
if(extra_count >= 0) {
// Substitute current unit set with a squiggled one (more units at end)
// in order to animate to stretch it later to new points
old_x = fillArray(old_x, extra_count);
old_y = fillArray(old_y, extra_count);
} else {
// Modify the new points to have extra points
// with the same position at end, old positions will squeeze in
new_x = fillArray(new_x, extra_count);
new_y = fillArray(new_y, extra_count);
}
return [old_x, old_y, new_x, new_y];
}

View File

@ -37,6 +37,22 @@ export function shuffle(array) {
return array; return array;
} }
/**
* Fill an array with extra points
* @param {Array} array Array
* @param {Number} count number of filler elements
* @param {Object} element element to fill with
* @param {Boolean} start fill at start?
*/
export function fillArray(array, count, element, start=false) {
if(!element) {
element = start ? array[0] : array[array.length - 1];
}
let fillerArray = new Array(Math.abs(count)).fill(element);
array = start ? fillerArray.concat(array) : array.concat(fillerArray);
return array;
}
/** /**
* Returns pixel width of string. * Returns pixel width of string.
* @param {String} string * @param {String} string