File: /var/www/adila/wp-content/plugins/visualizer/js/render-chartjs.js
/* global console */
/* global visualizer */
/* global Chart */
/* global numeral */
/* global moment */
(function($) {
var all_charts;
// so that we know which charts belong to our library.
var rendered_charts = [];
function renderChart(id, v) {
renderSpecificChart(id, all_charts[id], v);
}
function renderSpecificChart(id, chart, v) {
var render, container, series, data, datasets, settings, i, j, row, date, axis, property, format, formatter, type, rows, cols, labels;
if(chart.library !== 'chartjs'){
return;
}
rendered_charts[id] = 'yes';
series = chart.series;
data = chart.data;
settings = chart.settings;
container = document.getElementById(id);
if (container == null) {
return;
}
// eliminate the jitter/flicker while editing charts.
if(v.is_front == false){ // jshint ignore:line
$('#' + id).empty();
}
if($('#' + id + ' canvas').length === 0){
$('#' + id).append($('<canvas width="100%" height="90%"></canvas>'));
}
var context = $('#' + id + ' canvas')[0].getContext('2d');
type = chart.type;
switch (chart.type) {
case 'column':
type = 'bar';
break;
case 'bar':
type = 'bar';
settings.indexAxis = 'y';
break;
case 'pie':
// donut is not a setting but a separate chart type.
if(typeof settings['custom'] !== 'undefined' && settings['custom']['donut'] === 'true'){
type = 'doughnut';
}
break;
}
rows = [];
datasets = [];
labels = [];
for (i = 0; i < data.length; i++) {
row = [];
for (j = 0; j < series.length; j++) {
if (series[j].type === 'date' || series[j].type === 'datetime') {
date = new Date(data[i][j]);
data[i][j] = null;
if (Object.prototype.toString.call(date) === "[object Date]") {
if (!isNaN(date.getTime())) {
data[i][j] = date;
}
}
}
row.push(format_data(data[i][j], j, settings, series));
}
rows.push(row);
}
// transpose
for (j = 0; j < series.length; j++) {
row = [];
for (i = 0; i < rows.length; i++) {
if(j === 0){
labels.push(rows[i][j]);
}else{
row.push(rows[i][j]);
}
}
if(row.length > 0){
var $attributes = {label: series[j].label, data: row};
switch(chart.type){
case 'pie':
case 'polarArea':
$.extend($attributes, {label: labels});
handlePieSeriesSettings($attributes, rows, settings, chart);
break;
default:
handleSeriesSettings($attributes, j - 1, settings, chart);
}
datasets.push($attributes);
}
}
if(v.is_front == false){ // jshint ignore:line
// this line needs to be included twice. This is not an error.
// if this one is removed, the preview gets messed up.
// if this line is included all the time, in this very place (out of the if), the front-end gets messed up.
$.extend(settings, { responsive: true, maintainAspectRatio: false });
}
handleSettings(settings, chart);
const getChart = Chart.getChart(context);
if ( getChart ) {
return;
}
// Format series label.
settings.plugins.tooltip = {
callbacks: {
label: function(context) {
var label = '';
if ( 'object' === typeof context.dataset.label ) {
label = context.label || '';
} else {
label = context.dataset.label || '';
}
if ( label ) {
label += ': ';
}
var format = context.dataset.format || '';
if ( format ) {
var lastSuffix = numeral(context.formattedValue).format(format).replace(/[0-9]/g, '');
label += context.formattedValue + lastSuffix;
} else {
format = 'undefined' !== typeof context.chart.config._config.options.format ? context.chart.config._config.options.format : '';
if ( format ) {
label += format_datum( context.formattedValue, format );
} else {
label += context.formattedValue;
}
}
return label;
}
}
};
var chartjs = new Chart(context, {
type: type,
data: {
labels: labels,
datasets: datasets
},
options: settings,
plugins: [{
afterRender: function () {
var canvas = $( 'canvas' )[0];
if ( $( '#chart-img' ).length ) {
$( '#chart-img' ).val( canvas.toDataURL() );
}
}
}],
});
// this line needs to be included twice. This is not an error.
$.extend(settings, { responsive: true, maintainAspectRatio: false });
// chart area
if(v.is_front == true){ // jshint ignore:line
$('#' + id).css('position', 'relative');
var height = settings.height.indexOf('%') === -1 ? ( settings.height + 'px' ) : settings.height;
var width = settings.width.indexOf('%') === -1 ? ( settings.width + 'px' ) : settings.width;
if(settings.height){
chartjs.canvas.parentNode.style.height = height;
$('#' + id + ' canvas').css('height', height);
}
if(settings.width){
chartjs.canvas.parentNode.style.width = width;
$('#' + id + ' canvas').css('width', width);
}
}
// allow user to extend the settings.
$('body').trigger('visualizer:chart:settings:extend', {id: id, chart: chart, settings: settings});
$('.loader').remove();
}
function handleSettings(settings, chart){
if(typeof settings === 'undefined'){
return;
}
// handle some defaults/idiosyncrasies.
if(typeof settings['animation'] !== 'undefined' && parseInt(settings['animation']['duration']) === 0){
settings['animation']['duration'] = 1000;
}
if(typeof settings['tooltip'] !== 'undefined' && typeof settings['tooltip']['intersect'] !== 'undefined'){
// jshint ignore:line
settings['tooltip']['intersect'] = settings['tooltip']['intersect'] == true || parseInt(settings['tooltip']['intersect']) === 1; // jshint ignore:line
}
if(typeof settings['fontName'] !== 'undefined' && settings['fontName'] !== ''){
Chart.defaults.font.family = settings['fontName'];
delete settings['fontName'];
}
if(typeof settings['fontSize'] !== 'undefined' && settings['fontSize'] !== ''){
Chart.defaults.font.size = parseInt(settings['fontSize']);
delete settings['fontSize'];
}
// handle legend defaults.
if(typeof settings['legend'] !== 'undefined' && typeof settings['legend']['labels'] !== 'undefined') {
for(var i in settings['legend']['labels']){
if(settings['legend']['labels'][i] !== 'undefined' && settings['legend']['labels'][i] === ''){
delete settings['legend']['labels'][i];
}
}
}
settings.plugins = {
legend: settings.legend,
};
if(typeof settings['title'] !== 'undefined' && settings['title']['text'] !== ''){
settings.plugins['title'] = {};
settings.plugins['title']['display'] = true;
settings.plugins['title']['text'] = settings['title']['text'];
}
handleAxes(settings, chart);
override(settings, chart);
}
function handleAxes(settings, chart){
if(typeof settings['yAxes'] !== 'undefined' && typeof settings['xAxes'] !== 'undefined'){
// stacking has to be defined on both axes.
if(typeof settings['yAxes']['stacked_bool'] !== 'undefined'){
settings['yAxes']['stacked_bool'] = 'true';
}
if(typeof settings['xAxes']['stacked_bool'] !== 'undefined'){
settings['xAxes']['stacked_bool'] = 'true';
}
// Bar percentage.
if (typeof settings['yAxes']['barPercentage_int'] !=='undefined' && ''!== settings['yAxes']['barPercentage_int']){
settings['barPercentage'] = settings['yAxes']['barPercentage_int'];
}
if (typeof settings['xAxes']['barPercentage_int'] !=='undefined' && ''!== settings['xAxes']['barPercentage_int']){
settings['barPercentage'] = settings['xAxes']['barPercentage_int'];
}
// Bar thickness.
if (typeof settings['yAxes']['barThickness'] !=='undefined' && ''!== settings['yAxes']['barThickness']){
settings['barThickness'] = settings['yAxes']['barThickness'];
}
if (typeof settings['xAxes']['barThickness'] !=='undefined' && ''!== settings['xAxes']['barThickness']){
settings['barThickness'] = settings['xAxes']['barThickness'];
}
}
configureAxes(settings, 'yAxes', chart);
configureAxes(settings, 'xAxes', chart);
}
function configureAxes(settings, axis, chart) {
if(typeof settings[axis] !== 'undefined'){
var $features = {};
for(var i in settings[axis]){
var $o = {};
if(Array.isArray(settings[axis][i]) || typeof settings[axis][i] === 'object'){
for(var j in settings[axis][i]){
var $val = '';
if(j === 'labelString'){
$o['display'] = true;
$val = settings[axis][i][j];
}else if(i === 'ticks'){
// number values under ticks need to be converted to numbers or the library throws a JS error.
$val = parseFloat(settings[axis][i][j]);
if(isNaN($val)){
$val = '';
}
} else {
$val = settings[axis][i][j];
}
if($val !== ''){
$o[j] = $val;
}
}
}else{
// usually for attributes that have primitive values.
var array = i.split('_');
var dataType = 'string';
var dataValue = settings[axis][i];
if(array.length === 2){
dataType = array[1];
}
if(settings[axis][i] === ''){
continue;
}
switch(dataType){
case 'bool':
dataValue = dataValue === 'true' ? true : false;
break;
case 'int':
dataValue = parseFloat(dataValue);
break;
}
$o = dataValue;
// remove the type suffix to get the name of the setting.
i = i.replace(/_bool/g, '').replace(/_int/g, '');
}
$features[i] = $o;
}
var $scales = {};
$scales['scales'] = {};
$scales['scales'][axis] = [];
if(typeof settings['scales'] !== 'undefined' && typeof settings[axis + 'set'] === 'undefined'){
$scales['scales'] = settings['scales'];
if(typeof settings['scales'][axis] !== 'undefined'){
$scales['scales'][axis] = settings['scales'][axis];
}
}
if(typeof $scales['scales'][axis] === 'undefined'){
$scales['scales'][axis] = [];
}
var $axis = $scales['scales'][axis];
$axis.push($features);
// Migrate xAxes settings to v3.0+
if ( $scales.scales && $scales.scales.xAxes ) {
for (var x in $scales.scales.xAxes) {
$scales.scales.x = {
display: $scales.scales.xAxes[x].scaleLabel.display,
title: {
display:true,
text: $scales.scales.xAxes[x].scaleLabel.labelString,
color: $scales.scales.xAxes[x].scaleLabel.fontColor,
font: {
family: $scales.scales.xAxes[x].scaleLabel.fontFamily,
size: $scales.scales.xAxes[x].scaleLabel.fontSize
}
},
suggestedMax: $scales.scales.xAxes[x].ticks.suggestedMax || '',
suggestedMin: $scales.scales.xAxes[x].ticks.suggestedMin || '',
ticks: {
maxTicksLimit: $scales.scales.xAxes[x].ticks.maxTicksLimit
},
stacked: $scales.scales.xAxes[x].stacked || false
}
}
delete $scales.scales.xAxes;
}
// Migrate yAxes settings to v3.0+
if ( $scales.scales && $scales.scales.yAxes ) {
for (var y in $scales.scales.yAxes) {
$scales.scales.y = {
display: $scales.scales.yAxes[y].scaleLabel.display,
title: {
display:true,
text: $scales.scales.yAxes[y].scaleLabel.labelString,
color: $scales.scales.yAxes[y].scaleLabel.fontColor,
font: {
family: $scales.scales.yAxes[y].scaleLabel.fontFamily,
size: $scales.scales.yAxes[y].scaleLabel.fontSize
}
},
suggestedMax: $scales.scales.yAxes[y].ticks.suggestedMax || '',
suggestedMin: $scales.scales.yAxes[y].ticks.suggestedMin || '',
ticks: {
maxTicksLimit: $scales.scales.yAxes[y].ticks.maxTicksLimit
},
stacked: $scales.scales.yAxes[y].stacked || false
}
}
delete $scales.scales.yAxes;
}
$.extend(settings, $scales);
// to prevent duplication, indicates that the axis has been set.
var $custom = {};
$custom[axis + 'set'] = 'yes';
$.extend(settings, $custom);
}
// format the axes labels.
if(typeof settings[axis + '_format'] !== 'undefined' && settings[axis + '_format'] !== ''){
var format = settings[axis + '_format'];
var isDateFormat = moment( moment().format( format ),format, true ).isValid();
if ( ! isDateFormat ) {
switch(axis){
case 'xAxes':
settings.scales.x.ticks.callback = function(value, index, values){
return format_datum(value, format);
};
break;
case 'yAxes':
settings.scales.y.ticks.callback = function(value, index, values){
return format_datum(value, format);
};
break;
}
delete settings[axis + '_format'];
}
}
delete settings[axis];
}
function handlePieSeriesSettings($attributes, rows, settings, chart){
if(typeof settings.slices === 'undefined'){
return;
}
var atts = [];
// collect all the types of attributes
for(var j in settings.slices[0]){
// weight screws up the rendering for some reason, so we will ignore it.
if(j === 'weight') {
continue;
}
atts.push(j);
}
for (j = 0; j < atts.length; j++) {
var values = [];
for (var i = 0; i < rows.length; i++) {
if(typeof settings.slices[i] !== 'undefined' && typeof settings.slices[i][atts[j]] !== 'undefined'){
values.push(settings.slices[i][atts[j]]);
}
}
var object = {};
object[ atts[ j ] ] = values;
$.extend($attributes, object);
}
}
function handleSeriesSettings($attributes, j, settings, chart){
if(typeof settings.series === 'undefined' || typeof settings.series[j] === 'undefined'){
return;
}
for(var i in settings.series[j]){
var $attribute = {};
if ( settings.series[j].backgroundColor == '' ) {
delete settings.series[j].backgroundColor;
}
$attribute[i] = settings.series[j][i];
$.extend($attributes, $attribute);
}
}
function format_datum(datum, format, type){
if(format === '' || format === null || typeof format === 'undefined'){
return datum;
}
// if there is no type, this is probably coming from the axes formatting.
var removeDollar = true;
if(typeof type === 'undefined' || type === null){
// we will determine type on the basis of the presence or absence of #.
type = 'date';
if(format.indexOf('#') !== -1){
type = 'number';
}
removeDollar = false;
}
switch(type) {
case 'number':
// numeral.js works on 0 instead of # so we just replace that in the ICU pattern set.
format = format.replace(/#/g, '0').replace(/%/g, '');
// we also replace all instance of '$' as that is more relevant for ticks.
if(removeDollar){
format = format.replace(/\$/g, '');
}
datum = numeral(datum).format(format);
break;
case 'date':
case 'datetime':
case 'timeofday':
datum = moment(datum).format(format);
break;
}
return datum;
}
function format_data(datum, j, settings, series){
j = j - 1;
var format = typeof settings.series !== 'undefined' && typeof settings.series[j] !== 'undefined' ? settings.series[j].format : '';
if ( '' === format && typeof settings.yAxes_format !== 'undefined' ) {
format = settings.yAxes_format;
} else if ( '' === format && typeof settings.xAxes_format !== 'undefined' ) {
format = settings.xAxes_format;
}
return format_datum(datum, format, series[j + 1].type);
}
function override(settings, chart) {
if (settings.manual) {
try{
var options = JSON.parse(settings.manual);
$.extend(settings, options);
delete settings.manual;
}catch(error){
console.error("Error while adding manual configuration override " + settings.manual);
}
}
}
function render(v) {
for (var id in (all_charts || {})) {
renderChart(id, v);
}
}
$('body').on('visualizer:render:chart:start', function(event, v){
all_charts = v.charts;
if(v.is_front == true && typeof v.id !== 'undefined'){ // jshint ignore:line
renderChart(v.id, v);
} else {
render(v);
}
// for some reason this needs to be introduced here for dynamic preview updates to work.
v.update = function(){
renderChart('canvas', v);
};
});
$('body').on('visualizer:render:specificchart:start', function(event, v){
renderSpecificChart(v.id, v.chart, v.v);
});
$('body').on('visualizer:render:currentchart:update', function(event, v){
var data = v || event.detail;
renderChart('canvas', data.visualizer);
});
// front end actions
// 'image' is also called from the library
$('body').on('visualizer:action:specificchart', function(event, v){
var id = v.id;
if(typeof rendered_charts[id] === 'undefined'){
return;
}
var canvas = $('#' + id + ' canvas');
switch(v.action){
case 'print':
var win = window.open();
win.document.write("<br><img src='" + canvas[0].toDataURL() + "'/>");
win.document.close();
win.onload = function () { win.print(); setTimeout(win.close, 500); };
break;
case 'image':
var img = canvas[0].toDataURL();
if(img !== ''){
var $a = $("<a>"); // jshint ignore:line
$a.attr("href", img);
$("body").append($a);
$a.attr("download", v.dataObj.name);
$a[0].click();
$a.remove();
}else{
console.warn("No image generated");
}
break;
}
});
})(jQuery);