PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.7.11
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.7.11
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / js / render-chartjs.js

render-chartjs.js in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.7.11, at js/render-chartjs.js

537 lines 20.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global console */
2 /* global visualizer */
3 /* global Chart */
4 /* global numeral */
5 /* global moment */
6
7 (function($) {
8 var all_charts;
9 // so that we know which charts belong to our library.
10 var rendered_charts = [];
11
12 function renderChart(id, v) {
13 renderSpecificChart(id, all_charts[id], v);
14 }
15
16 function renderSpecificChart(id, chart, v) {
17 var render, container, series, data, datasets, settings, i, j, row, date, axis, property, format, formatter, type, rows, cols, labels;
18
19 if(chart.library !== 'chartjs'){
20 return;
21 }
22 rendered_charts[id] = 'yes';
23
24 series = chart.series;
25 data = chart.data;
26 settings = chart.settings;
27
28 container = document.getElementById(id);
29 if (container == null) {
30 return;
31 }
32
33 // eliminate the jitter/flicker while editing charts.
34 if(v.is_front == false){ // jshint ignore:line
35 $('#' + id).empty();
36 }
37
38 if($('#' + id + ' canvas').length === 0){
39 $('#' + id).append($('<canvas width="100%" height="90%"></canvas>'));
40 }
41
42 var context = $('#' + id + ' canvas')[0].getContext('2d');
43
44 type = chart.type;
45 switch (chart.type) {
46 case 'column':
47 type = 'bar';
48 break;
49 case 'bar':
50 type = 'bar';
51 settings.indexAxis = 'y';
52 break;
53 case 'pie':
54 // donut is not a setting but a separate chart type.
55 if(typeof settings['custom'] !== 'undefined' && settings['custom']['donut'] === 'true'){
56 type = 'doughnut';
57 }
58 break;
59 }
60
61 rows = [];
62 datasets = [];
63 labels = [];
64
65 for (i = 0; i < data.length; i++) {
66 row = [];
67 for (j = 0; j < series.length; j++) {
68 if (series[j].type === 'date' || series[j].type === 'datetime') {
69 date = new Date(data[i][j]);
70 data[i][j] = null;
71 if (Object.prototype.toString.call(date) === "[object Date]") {
72 if (!isNaN(date.getTime())) {
73 data[i][j] = date;
74 }
75 }
76 }
77 row.push(format_data(data[i][j], j, settings, series));
78 }
79 rows.push(row);
80 }
81
82 // transpose
83 for (j = 0; j < series.length; j++) {
84 row = [];
85 for (i = 0; i < rows.length; i++) {
86 if(j === 0){
87 labels.push(rows[i][j]);
88 }else{
89 row.push(rows[i][j]);
90 }
91 }
92 if(row.length > 0){
93 var $attributes = {label: series[j].label, data: row};
94 switch(chart.type){
95 case 'pie':
96 case 'polarArea':
97 $.extend($attributes, {label: labels});
98 handlePieSeriesSettings($attributes, rows, settings, chart);
99 break;
100 default:
101 handleSeriesSettings($attributes, j - 1, settings, chart);
102 }
103 datasets.push($attributes);
104 }
105 }
106
107 if(v.is_front == false){ // jshint ignore:line
108 // this line needs to be included twice. This is not an error.
109 // if this one is removed, the preview gets messed up.
110 // if this line is included all the time, in this very place (out of the if), the front-end gets messed up.
111 $.extend(settings, { responsive: true, maintainAspectRatio: false });
112 }
113
114 handleSettings(settings, chart);
115
116 var chartjs = new Chart(context, {
117 type: type,
118 data: {
119 labels: labels,
120 datasets: datasets
121 },
122 options: settings,
123 plugins: [{
124 afterRender: function () {
125 var canvas = $( 'canvas' )[0];
126 if ( $( '#chart-img' ).length ) {
127 $( '#chart-img' ).val( canvas.toDataURL() );
128 }
129 },
130 }],
131 });
132
133 // this line needs to be included twice. This is not an error.
134 $.extend(settings, { responsive: true, maintainAspectRatio: false });
135
136 // chart area
137 if(v.is_front == true){ // jshint ignore:line
138 $('#' + id).css('position', 'relative');
139 var height = settings.height.indexOf('%') === -1 ? ( settings.height + 'px' ) : settings.height;
140 var width = settings.width.indexOf('%') === -1 ? ( settings.width + 'px' ) : settings.width;
141 if(settings.height){
142 chartjs.canvas.parentNode.style.height = height;
143 $('#' + id + ' canvas').css('height', height);
144 }
145 if(settings.width){
146 chartjs.canvas.parentNode.style.width = width;
147 $('#' + id + ' canvas').css('width', width);
148 }
149 }
150
151 // allow user to extend the settings.
152 $('body').trigger('visualizer:chart:settings:extend', {id: id, chart: chart, settings: settings});
153
154 $('.loader').remove();
155 }
156
157 function handleSettings(settings, chart){
158 if(typeof settings === 'undefined'){
159 return;
160 }
161
162 // handle some defaults/idiosyncrasies.
163 if(typeof settings['animation'] !== 'undefined' && parseInt(settings['animation']['duration']) === 0){
164 settings['animation']['duration'] = 1000;
165 }
166
167 if(typeof settings['title'] !== 'undefined' && settings['title']['text'] !== ''){
168 settings['title']['display'] = true;
169 }
170
171 if(typeof settings['tooltip'] !== 'undefined' && typeof settings['tooltip']['intersect'] !== 'undefined'){
172 // jshint ignore:line
173 settings['tooltip']['intersect'] = settings['tooltip']['intersect'] == true || parseInt(settings['tooltip']['intersect']) === 1; // jshint ignore:line
174 }
175
176 if(typeof settings['fontName'] !== 'undefined' && settings['fontName'] !== ''){
177 Chart.defaults.font.family = settings['fontName'];
178 delete settings['fontName'];
179 }
180
181 if(typeof settings['fontSize'] !== 'undefined' && settings['fontSize'] !== ''){
182 Chart.defaults.font.size = parseInt(settings['fontSize']);
183 delete settings['fontSize'];
184 }
185
186 // handle legend defaults.
187 if(typeof settings['legend'] !== 'undefined' && typeof settings['legend']['labels'] !== 'undefined') {
188 for(var i in settings['legend']['labels']){
189 if(settings['legend']['labels'][i] !== 'undefined' && settings['legend']['labels'][i] === ''){
190 delete settings['legend']['labels'][i];
191 }
192 }
193 }
194
195 handleAxes(settings, chart);
196
197 override(settings, chart);
198 }
199
200 function handleAxes(settings, chart){
201 if(typeof settings['yAxes'] !== 'undefined' && typeof settings['xAxes'] !== 'undefined'){
202 // stacking has to be defined on both axes.
203 if(typeof settings['yAxes']['stacked_bool'] !== 'undefined'){
204 settings['yAxes']['stacked_bool'] = 'true';
205 }
206 if(typeof settings['xAxes']['stacked_bool'] !== 'undefined'){
207 settings['xAxes']['stacked_bool'] = 'true';
208 }
209 // Bar percentage.
210 if (typeof settings['yAxes']['barPercentage_int'] !=='undefined' && ''!== settings['yAxes']['barPercentage_int']){
211 settings['barPercentage'] = settings['yAxes']['barPercentage_int'];
212 }
213 if (typeof settings['xAxes']['barPercentage_int'] !=='undefined' && ''!== settings['xAxes']['barPercentage_int']){
214 settings['barPercentage'] = settings['xAxes']['barPercentage_int'];
215 }
216 // Bar thickness.
217 if (typeof settings['yAxes']['barThickness'] !=='undefined' && ''!== settings['yAxes']['barThickness']){
218 settings['barThickness'] = settings['yAxes']['barThickness'];
219 }
220 if (typeof settings['xAxes']['barThickness'] !=='undefined' && ''!== settings['xAxes']['barThickness']){
221 settings['barThickness'] = settings['xAxes']['barThickness'];
222 }
223 }
224 configureAxes(settings, 'yAxes', chart);
225 configureAxes(settings, 'xAxes', chart);
226 }
227
228 function configureAxes(settings, axis, chart) {
229 if(typeof settings[axis] !== 'undefined'){
230 var $features = {};
231 for(var i in settings[axis]){
232 var $o = {};
233 if(Array.isArray(settings[axis][i]) || typeof settings[axis][i] === 'object'){
234 for(var j in settings[axis][i]){
235 var $val = '';
236 if(j === 'labelString'){
237 $o['display'] = true;
238 $val = settings[axis][i][j];
239 }else if(i === 'ticks'){
240 // number values under ticks need to be converted to numbers or the library throws a JS error.
241 $val = parseFloat(settings[axis][i][j]);
242 if(isNaN($val)){
243 $val = '';
244 }
245 } else {
246 $val = settings[axis][i][j];
247 }
248 if($val !== ''){
249 $o[j] = $val;
250 }
251 }
252 }else{
253 // usually for attributes that have primitive values.
254 var array = i.split('_');
255 var dataType = 'string';
256 var dataValue = settings[axis][i];
257 if(array.length === 2){
258 dataType = array[1];
259 }
260
261 if(settings[axis][i] === ''){
262 continue;
263 }
264 switch(dataType){
265 case 'bool':
266 dataValue = dataValue === 'true' ? true : false;
267 break;
268 case 'int':
269 dataValue = parseFloat(dataValue);
270 break;
271 }
272 $o = dataValue;
273 // remove the type suffix to get the name of the setting.
274 i = i.replace(/_bool/g, '').replace(/_int/g, '');
275 }
276 $features[i] = $o;
277 }
278 var $scales = {};
279 $scales['scales'] = {};
280 $scales['scales'][axis] = [];
281 if(typeof settings['scales'] !== 'undefined' && typeof settings[axis + 'set'] === 'undefined'){
282 $scales['scales'] = settings['scales'];
283 if(typeof settings['scales'][axis] !== 'undefined'){
284 $scales['scales'][axis] = settings['scales'][axis];
285 }
286 }
287 if(typeof $scales['scales'][axis] === 'undefined'){
288 $scales['scales'][axis] = [];
289 }
290 var $axis = $scales['scales'][axis];
291
292 $axis.push($features);
293 // Migrate xAxes settings to v3.0+
294 if ( $scales.scales && $scales.scales.xAxes ) {
295 for (var x in $scales.scales.xAxes) {
296 $scales.scales.x = {
297 display: $scales.scales.xAxes[x].scaleLabel.display,
298 title: {
299 display:true,
300 text: $scales.scales.xAxes[x].scaleLabel.labelString,
301 color: $scales.scales.xAxes[x].scaleLabel.fontColor,
302 font: {
303 family: $scales.scales.xAxes[x].scaleLabel.fontFamily,
304 size: $scales.scales.xAxes[x].scaleLabel.fontSize
305 }
306 },
307 suggestedMax: $scales.scales.xAxes[x].ticks.suggestedMax || '',
308 suggestedMin: $scales.scales.xAxes[x].ticks.suggestedMin || '',
309 ticks: {
310 maxTicksLimit: $scales.scales.xAxes[x].ticks.maxTicksLimit
311 },
312 stacked: $scales.scales.xAxes[x].stacked || false
313 }
314 }
315 delete $scales.scales.xAxes;
316 }
317 // Migrate yAxes settings to v3.0+
318 if ( $scales.scales && $scales.scales.yAxes ) {
319 for (var y in $scales.scales.yAxes) {
320 $scales.scales.y = {
321 display: $scales.scales.yAxes[y].scaleLabel.display,
322 title: {
323 display:true,
324 text: $scales.scales.yAxes[y].scaleLabel.labelString,
325 color: $scales.scales.yAxes[y].scaleLabel.fontColor,
326 font: {
327 family: $scales.scales.yAxes[y].scaleLabel.fontFamily,
328 size: $scales.scales.yAxes[y].scaleLabel.fontSize
329 }
330 },
331 suggestedMax: $scales.scales.yAxes[y].ticks.suggestedMax || '',
332 suggestedMin: $scales.scales.yAxes[y].ticks.suggestedMin || '',
333 ticks: {
334 maxTicksLimit: $scales.scales.yAxes[y].ticks.maxTicksLimit
335 },
336 stacked: $scales.scales.yAxes[y].stacked || false
337 }
338 }
339 delete $scales.scales.yAxes;
340 }
341 $.extend(settings, $scales);
342
343 // to prevent duplication, indicates that the axis has been set.
344 var $custom = {};
345 $custom[axis + 'set'] = 'yes';
346 $.extend(settings, $custom);
347 }
348
349 // format the axes labels.
350 if(typeof settings[axis + '_format'] !== 'undefined' && settings[axis + '_format'] !== ''){
351 var format = settings[axis + '_format'];
352 var isDateFormat = moment( moment().format( format ),format, true ).isValid();
353 if ( ! isDateFormat ) {
354 switch(axis){
355 case 'xAxes':
356 settings.scales.x.ticks.callback = function(value, index, values){
357 return format_datum(value, format);
358 };
359 break;
360 case 'yAxes':
361 settings.scales.y.ticks.callback = function(value, index, values){
362 return format_datum(value, format);
363 };
364 break;
365 }
366 delete settings[axis + '_format'];
367 }
368 }
369 delete settings[axis];
370 }
371
372 function handlePieSeriesSettings($attributes, rows, settings, chart){
373 if(typeof settings.slices === 'undefined'){
374 return;
375 }
376
377 var atts = [];
378 // collect all the types of attributes
379 for(var j in settings.slices[0]){
380 // weight screws up the rendering for some reason, so we will ignore it.
381 if(j === 'weight') {
382 continue;
383 }
384 atts.push(j);
385 }
386
387 for (j = 0; j < atts.length; j++) {
388 var values = [];
389 for (var i = 0; i < rows.length; i++) {
390 if(typeof settings.slices[i] !== 'undefined' && typeof settings.slices[i][atts[j]] !== 'undefined'){
391 values.push(settings.slices[i][atts[j]]);
392 }
393 }
394 var object = {};
395 object[ atts[ j ] ] = values;
396 $.extend($attributes, object);
397 }
398 }
399
400 function handleSeriesSettings($attributes, j, settings, chart){
401 if(typeof settings.series === 'undefined' || typeof settings.series[j] === 'undefined'){
402 return;
403 }
404 for(var i in settings.series[j]){
405 var $attribute = {};
406 if ( settings.series[j].backgroundColor == '' ) {
407 delete settings.series[j].backgroundColor;
408 }
409 $attribute[i] = settings.series[j][i];
410 $.extend($attributes, $attribute);
411 }
412 }
413
414 function format_datum(datum, format, type){
415 if(format === '' || format === null || typeof format === 'undefined'){
416 return datum;
417 }
418 // if there is no type, this is probably coming from the axes formatting.
419 var removeDollar = true;
420 if(typeof type === 'undefined' || type === null){
421 // we will determine type on the basis of the presence or absence of #.
422 type = 'date';
423 if(format.indexOf('#') !== -1){
424 type = 'number';
425 }
426 removeDollar = false;
427 }
428
429 switch(type) {
430 case 'number':
431 // numeral.js works on 0 instead of # so we just replace that in the ICU pattern set.
432 format = format.replace(/#/g, '0');
433 // we also replace all instance of '$' as that is more relevant for ticks.
434 if(removeDollar){
435 format = format.replace(/\$/g, '');
436 }
437 datum = numeral(datum).format(format);
438 break;
439 case 'date':
440 case 'datetime':
441 case 'timeofday':
442 datum = moment(datum).format(format);
443 break;
444 }
445 return datum;
446 }
447
448 function format_data(datum, j, settings, series){
449 j = j - 1;
450 var format = typeof settings.series !== 'undefined' && typeof settings.series[j] !== 'undefined' ? settings.series[j].format : '';
451 if ( '' === format && typeof settings.yAxes_format !== 'undefined' ) {
452 format = settings.yAxes_format;
453 } else if ( '' === format && typeof settings.xAxes_format !== 'undefined' ) {
454 format = settings.xAxes_format;
455 }
456 return format_datum(datum, format, series[j + 1].type);
457 }
458
459 function override(settings, chart) {
460 if (settings.manual) {
461 try{
462 var options = JSON.parse(settings.manual);
463 $.extend(settings, options);
464 delete settings.manual;
465 }catch(error){
466 console.error("Error while adding manual configuration override " + settings.manual);
467 }
468 }
469 }
470
471
472 function render(v) {
473 for (var id in (all_charts || {})) {
474 renderChart(id, v);
475 }
476 }
477
478 $('body').on('visualizer:render:chart:start', function(event, v){
479 all_charts = v.charts;
480
481 if(v.is_front == true && typeof v.id !== 'undefined'){ // jshint ignore:line
482 renderChart(v.id, v);
483 } else {
484 render(v);
485 }
486
487 // for some reason this needs to be introduced here for dynamic preview updates to work.
488 v.update = function(){
489 renderChart('canvas', v);
490 };
491
492 });
493
494 $('body').on('visualizer:render:specificchart:start', function(event, v){
495 renderSpecificChart(v.id, v.chart, v.v);
496 });
497
498 $('body').on('visualizer:render:currentchart:update', function(event, v){
499 var data = v || event.detail;
500 renderChart('canvas', data.visualizer);
501 });
502
503 // front end actions
504 // 'image' is also called from the library
505 $('body').on('visualizer:action:specificchart', function(event, v){
506 var id = v.id;
507 if(typeof rendered_charts[id] === 'undefined'){
508 return;
509 }
510 var canvas = $('#' + id + ' canvas');
511 switch(v.action){
512 case 'print':
513 var win = window.open();
514 win.document.write("<br><img src='" + canvas[0].toDataURL() + "'/>");
515 win.document.close();
516 win.onload = function () { win.print(); setTimeout(win.close, 500); };
517 break;
518 case 'image':
519 var img = canvas[0].toDataURL();
520 if(img !== ''){
521 var $a = $("<a>"); // jshint ignore:line
522 $a.attr("href", img);
523 $("body").append($a);
524 $a.attr("download", v.dataObj.name);
525 $a[0].click();
526 $a.remove();
527 }else{
528 console.warn("No image generated");
529 }
530 break;
531 }
532 });
533
534 })(jQuery);
535
536
537