PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.3.4
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.3.4
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.3.4, at js/render-chartjs.js

430 lines 15.0 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 = 'horizontalBar';
51 break;
52 case 'pie':
53 // donut is not a setting but a separate chart type.
54 if(typeof settings['custom'] !== 'undefined' && settings['custom']['donut'] === 'true'){
55 type = 'doughnut';
56 }
57 break;
58 }
59
60 rows = [];
61 datasets = [];
62 labels = [];
63
64 for (i = 0; i < data.length; i++) {
65 row = [];
66 for (j = 0; j < series.length; j++) {
67 if (series[j].type === 'date' || series[j].type === 'datetime') {
68 date = new Date(data[i][j]);
69 data[i][j] = null;
70 if (Object.prototype.toString.call(date) === "[object Date]") {
71 if (!isNaN(date.getTime())) {
72 data[i][j] = date;
73 }
74 }
75 }
76 row.push(format_data(data[i][j], j, settings, series));
77 }
78 rows.push(row);
79 }
80
81 // transpose
82 for (j = 0; j < series.length; j++) {
83 row = [];
84 for (i = 0; i < rows.length; i++) {
85 if(j === 0){
86 labels.push(rows[i][j]);
87 }else{
88 row.push(rows[i][j]);
89 }
90 }
91 if(row.length > 0){
92 var $attributes = {label: series[j].label, data: row};
93 switch(chart.type){
94 case 'pie':
95 case 'polarArea':
96 $.extend($attributes, {label: labels});
97 handlePieSeriesSettings($attributes, rows, settings, chart);
98 break;
99 default:
100 handleSeriesSettings($attributes, j - 1, settings, chart);
101 }
102 datasets.push($attributes);
103 }
104 }
105
106 if(v.is_front == false){ // jshint ignore:line
107 // this line needs to be included twice. This is not an error.
108 // if this one is removed, the preview gets messed up.
109 // if this line is included all the time, in this very place (out of the if), the front-end gets messed up.
110 $.extend(settings, { responsive: true, maintainAspectRatio: false });
111 }
112
113 handleSettings(settings, chart);
114
115 var chartjs = new Chart(context, {
116 type: type,
117 data: {
118 labels: labels,
119 datasets: datasets
120 },
121 options: settings
122 });
123
124 // this line needs to be included twice. This is not an error.
125 $.extend(settings, { responsive: true, maintainAspectRatio: false });
126
127 // chart area
128 if(v.is_front == true){ // jshint ignore:line
129 $('#' + id).css('position', 'relative');
130 chartjs.canvas.parentNode.style.height = settings.height;
131 chartjs.canvas.parentNode.style.width = settings.width;
132 }
133
134 // allow user to extend the settings.
135 $('body').trigger('visualizer:chart:settings:extend', {id: id, chart: chart, settings: settings});
136
137 $('.loader').remove();
138 }
139
140 function handleSettings(settings, chart){
141 if(typeof settings === 'undefined'){
142 return;
143 }
144
145 // handle some defaults/idiosyncrasies.
146 if(typeof settings['animation'] !== 'undefined' && parseInt(settings['animation']['duration']) === 0){
147 settings['animation']['duration'] = 1000;
148 }
149
150 if(typeof settings['title'] !== 'undefined' && settings['title']['text'] !== ''){
151 settings['title']['display'] = true;
152 }
153
154 if(typeof settings['tooltip'] !== 'undefined' && typeof settings['tooltip']['intersect'] !== 'undefined'){
155 // jshint ignore:line
156 settings['tooltip']['intersect'] = settings['tooltip']['intersect'] == true || parseInt(settings['tooltip']['intersect']) === 1; // jshint ignore:line
157 }
158
159 if(typeof settings['fontName'] !== 'undefined' && settings['fontName'] !== ''){
160 Chart.defaults.global.defaultFontFamily = settings['fontName'];
161 delete settings['fontName'];
162 }
163
164 if(typeof settings['fontSize'] !== 'undefined' && settings['fontSize'] !== ''){
165 Chart.defaults.global.defaultFontSize = settings['fontSize'];
166 delete settings['fontSize'];
167 }
168
169 // handle legend defaults.
170 if(typeof settings['legend'] !== 'undefined' && typeof settings['legend']['labels'] !== 'undefined') {
171 for(var i in settings['legend']['labels']){
172 if(settings['legend']['labels'][i] !== 'undefined' && settings['legend']['labels'][i] === ''){
173 delete settings['legend']['labels'][i];
174 }
175 }
176 }
177
178 handleAxes(settings, chart);
179
180 override(settings, chart);
181 }
182
183 function handleAxes(settings, chart){
184 if(typeof settings['yAxes'] !== 'undefined' && typeof settings['xAxes'] !== 'undefined'){
185 // stacking has to be defined on both axes.
186 if(typeof settings['yAxes']['stacked_bool'] !== 'undefined'){
187 settings['xAxes']['stacked_bool'] = 'true';
188 }
189 if(typeof settings['xAxes']['stacked_bool'] !== 'undefined'){
190 settings['yAxes']['stacked_bool'] = 'true';
191 }
192 }
193 configureAxes(settings, 'yAxes', chart);
194 configureAxes(settings, 'xAxes', chart);
195 }
196
197 function configureAxes(settings, axis, chart) {
198 if(typeof settings[axis] !== 'undefined'){
199 var $features = {};
200 for(var i in settings[axis]){
201 var $o = {};
202 if(Array.isArray(settings[axis][i]) || typeof settings[axis][i] === 'object'){
203 for(var j in settings[axis][i]){
204 var $val = '';
205 if(j === 'labelString'){
206 $o['display'] = true;
207 $val = settings[axis][i][j];
208 }else if(i === 'ticks'){
209 // number values under ticks need to be converted to numbers or the library throws a JS error.
210 $val = parseFloat(settings[axis][i][j]);
211 if(isNaN($val)){
212 $val = '';
213 }
214 } else {
215 $val = settings[axis][i][j];
216 }
217 if($val !== ''){
218 $o[j] = $val;
219 }
220 }
221 }else{
222 // usually for attributes that have primitive values.
223 var array = i.split('_');
224 var dataType = 'string';
225 var dataValue = settings[axis][i];
226 if(array.length === 2){
227 dataType = array[1];
228 }
229
230 if(settings[axis][i] === ''){
231 continue;
232 }
233 switch(dataType){
234 case 'bool':
235 dataValue = dataValue === 'true' ? true : false;
236 break;
237 case 'int':
238 dataValue = parseFloat(dataValue);
239 break;
240 }
241 $o = dataValue;
242 // remove the type suffix to get the name of the setting.
243 i = i.replace(/_bool/g, '').replace(/_int/g, '');
244 }
245 $features[i] = $o;
246 }
247 var $scales = {};
248 $scales['scales'] = {};
249 $scales['scales'][axis] = [];
250 if(typeof settings['scales'] !== 'undefined' && typeof settings[axis + 'set'] === 'undefined'){
251 $scales['scales'] = settings['scales'];
252 if(typeof settings['scales'][axis] !== 'undefined'){
253 $scales['scales'][axis] = settings['scales'][axis];
254 }
255 }
256 if(typeof $scales['scales'][axis] === 'undefined'){
257 $scales['scales'][axis] = [];
258 }
259 var $axis = $scales['scales'][axis];
260
261 $axis.push($features);
262 $.extend(settings, $scales);
263
264 // to prevent duplication, indicates that the axis has been set.
265 var $custom = {};
266 $custom[axis + 'set'] = 'yes';
267 $.extend(settings, $custom);
268 }
269
270 // format the axes labels.
271 if(typeof settings[axis + '_format'] !== 'undefined' && settings[axis + '_format'] !== ''){
272 var format = settings[axis + '_format'];
273 switch(axis){
274 case 'xAxes':
275 settings.scales.xAxes[0].ticks.callback = function(value, index, values){
276 return format_datum(value, format);
277 };
278 break;
279 case 'yAxes':
280 settings.scales.yAxes[0].ticks.callback = function(value, index, values){
281 return format_datum(value, format);
282 };
283 break;
284 }
285 delete settings[axis + '_format'];
286 }
287 delete settings[axis];
288 }
289
290 function handlePieSeriesSettings($attributes, rows, settings, chart){
291 if(typeof settings.slices === 'undefined'){
292 return;
293 }
294
295 var atts = [];
296 // collect all the types of attributes
297 for(var j in settings.slices[0]){
298 // weight screws up the rendering for some reason, so we will ignore it.
299 if(j === 'weight') {
300 continue;
301 }
302 atts.push(j);
303 }
304
305 for (j = 0; j < atts.length; j++) {
306 var values = [];
307 for (var i = 0; i < rows.length; i++) {
308 if(typeof settings.slices[i] !== 'undefined' && typeof settings.slices[i][atts[j]] !== 'undefined'){
309 values.push(settings.slices[i][atts[j]]);
310 }
311 }
312 var object = {};
313 object[ atts[ j ] ] = values;
314 $.extend($attributes, object);
315 }
316 }
317
318 function handleSeriesSettings($attributes, j, settings, chart){
319 if(typeof settings.series === 'undefined' || typeof settings.series[j] === 'undefined'){
320 return;
321 }
322 for(var i in settings.series[j]){
323 var $attribute = {};
324 $attribute[i] = settings.series[j][i];
325 $.extend($attributes, $attribute);
326 }
327 }
328
329 function format_datum(datum, format, type){
330 if(format === '' || format === null || typeof format === 'undefined'){
331 return datum;
332 }
333 // if there is no type, this is probably coming from the axes formatting.
334 var removeDollar = true;
335 if(typeof type === 'undefined' || type === null){
336 // we will determine type on the basis of the presence or absence of #.
337 type = 'date';
338 if(format.indexOf('#') !== -1){
339 type = 'number';
340 }
341 removeDollar = false;
342 }
343
344 switch(type) {
345 case 'number':
346 // numeral.js works on 0 instead of # so we just replace that in the ICU pattern set.
347 format = format.replace(/#/g, '0');
348 // we also replace all instance of '$' as that is more relevant for ticks.
349 if(removeDollar){
350 format = format.replace(/\$/g, '');
351 }
352 datum = numeral(datum).format(format);
353 break;
354 case 'date':
355 case 'datetime':
356 case 'timeofday':
357 datum = moment(datum).format(format);
358 break;
359 }
360 return datum;
361 }
362
363 function format_data(datum, j, settings, series){
364 j = j - 1;
365 var format = typeof settings.series !== 'undefined' && typeof settings.series[j] !== 'undefined' ? settings.series[j].format : '';
366 return format_datum(datum, format, series[j + 1].type);
367 }
368
369 function override(settings, chart) {
370 if (settings.manual) {
371 try{
372 var options = JSON.parse(settings.manual);
373 $.extend(settings, options);
374 delete settings.manual;
375 }catch(error){
376 console.error("Error while adding manual configuration override " + settings.manual);
377 }
378 }
379 }
380
381
382 function render(v) {
383 for (var id in (all_charts || {})) {
384 renderChart(id, v);
385 }
386 }
387
388 if(typeof visualizer !== 'undefined'){
389 // called while updating the chart.
390 visualizer.update = function(){
391 renderChart('canvas', visualizer);
392 };
393 }
394
395 $('body').on('visualizer:render:chart:start', function(event, v){
396 all_charts = v.charts;
397 render(v);
398
399 // for some reason this needs to be introduced here for dynamic preview updates to work.
400 v.update = function(){
401 renderChart('canvas', v);
402 };
403
404 });
405
406 $('body').on('visualizer:render:specificchart:start', function(event, v){
407 renderSpecificChart(v.id, v.chart, v.v);
408 });
409
410 // front end actions
411 $('body').on('visualizer:action:specificchart', function(event, v){
412 switch(v.action){
413 case 'print':
414 var id = v.id;
415 if(typeof rendered_charts[id] === 'undefined'){
416 return;
417 }
418 var canvas = $('#' + id + ' canvas');
419 var win = window.open();
420 win.document.write("<br><img src='" + canvas[0].toDataURL() + "'/>");
421 win.document.close();
422 win.onload = function () { win.print(); setTimeout(win.close, 500); };
423 break;
424 }
425 });
426
427 })(jQuery);
428
429
430